聊到第十轮就忘了开头 / Hy Memory 2.0 的记忆到底落在哪一层?

文章导读
判断“聊到第十轮就忘了开头”这件事,先别急着调上下文窗口,也先别假定长期记忆坏了。更稳的做法是把这句话拆成两条链路:一条是会话上下文,客户端每轮把历史消息拼进请求;另一条是跨会话记忆,模型之外的存储异步写入、下一轮再检索注入。然后用“开头那句偏好在第几轮第一次不再被引用”这个可观测的轮次,定位问题落在哪一层。只有轮次确定下来了,改动才有归因价值;否则同时调窗口和记忆阈值,之后无论好坏都说不清是哪一
📋 目录
  1. 壹 先记录开头那句话在第几轮开始失效
  2. 贰 分清会话上下文和跨会话记忆两条链路
  3. 叁 只留当前会话、关掉长期记忆再跑一遍
  4. 肆 打开长期记忆,检查写入触发条件
  5. 伍 确定层次后只改对应那一层的参数
A A

判断“聊到第十轮就忘了开头”这件事,先别急着调上下文窗口,也先别假定长期记忆坏了。更稳的做法是把这句话拆成两条链路:一条是会话上下文,客户端每轮把历史消息拼进请求;另一条是跨会话记忆,模型之外的存储异步写入、下一轮再检索注入。然后用“开头那句偏好在第几轮第一次不再被引用”这个可观测的轮次,定位问题落在哪一层。只有轮次确定下来了,改动才有归因价值;否则同时调窗口和记忆阈值,之后无论好坏都说不清是哪一处起的作用。

先别动参数:把开头那句偏好做成可判定的探针,逐轮记录它第一次不被引用发生在第几轮;再关掉长期记忆复跑一遍。失效轮次不变,问题在会话上下文的拼装与截断;失效消失,问题在长期记忆的写入条件或检索注入。

先记录开头那句话在第几轮开始失效

“忘了”是个模糊感受,需要先变成轮次号。做法是在会话第一句放一条可判定的偏好,比如“回复一律用无序列表,不要用表格,称呼我为老周”。选它是因为行为可验证:返回里有没有用列表、有没有用表格、有没有出现称呼,一眼能看出来,不依赖模型自称“我记得”。

接着按固定序列提问,每轮只问一个问题,避免一个问题里塞多个诉求导致无法判断是哪句丢了。记录方式建议直接用一张表或一个 CSV 文件,字段固定为:轮次、提问内容、返回里是否体现开头信息、证据片段(把返回中体现或不体现的那一句原样抄下来)、备注。证据片段这一列不能省,它决定了后面复跑时能不能做前后对比。

turn, question, used_opening, evidence_quote, note
1, 帮我列三个做笔记的工具, yes, "按你的要求用列表写", 正常
2, 这三个怎么选, yes, "老周,建议先看第二点", 称呼仍在
3, 换个角度再说说, no, 直接给了表格, 首次失效?

判定标准建议写死:只要返回里出现了被明确禁止的形式(表格),或者称呼消失,就记为 not used。连续两轮不复现才算稳定失效,单轮波动先记为待观察,避免把一次措辞变化当成链路故障。

分清会话上下文和跨会话记忆两条链路

这两条链路经常被混成一句“记忆不好使”,但它们的证据位置完全不同。会话上下文是每次请求时客户端自己拼的,你能在请求体里看到全部内容;跨会话记忆是外部存储加检索注入,你在请求体里只能看到被检索回来的片段被拼进去的那一部分。分开看,才能知道该查日志还是查存储。

聊到第十轮就忘了开头 / Hy Memory 2.0 的记忆到底落在哪一层?
r>
对比项会话上下文跨会话记忆
写入时机每轮请求前由客户端把历史消息追加进 messages某一轮结束后(通常是异步)由服务抽取事实写入存储
生命周期跟随当前会话,受窗口上限和截断策略约束,会话结束即散跨会话保留,可被后续任意会话检索到,受作用域(用户/会话/助手)约束
可观察位置请求体的 messages 条数与长度、服务端请求日志、token 计数、是否有截断标记记忆列表接口或管理页面的写入记录、检索命中片段、注入位置在 prompt 中的那一段

对照这张表看第一步的记录表:如果失效轮次大致与历史消息被截断的位置吻合(通常是 token 到上限附近,或者历史只保留最近 N 轮),嫌疑在会话上下文;如果失效轮次和截断位置对不上,或者从第一轮起就没生效,嫌疑在写入或检索。

只留当前会话、关掉长期记忆再跑一遍

这一步是为了把两条链路物理隔离。配置位置一般在两处之一:客户端或网关的全局记忆开关,或者每次请求里的记忆字段。不同实现的字段名不一样,常见的是 memory.enabled、use_memory、long_term_memory 之类,先在配置文件或请求构造代码里搜 memory 关键字确认自己的名字,不要照抄别人的字段名。

POST /v1/chat
{
  "session_id": "probe-20240101-a",
  "memory": { "enabled": false },
  "messages": [
    { "role": "user", "content": "回复一律用无序列表,不要用表格,称呼我为老周" },
    { "role": "user", "content": "帮我列三个做笔记的工具" }
  ]
}

复跑步骤建议按顺序做:新建会话(不要复用旧 session_id,否则旧历史会带进来)、确认记忆开关为关闭、用完全相同的开头句和提问序列重跑、记录到第二张表、把两张表的失效轮次并排放。同时检查请求体里 messages 的实际长度,看在失效那一轮前后,历史是不是被裁掉了开头的系统提示或第一条用户消息。

结果判读:关掉长期记忆后失效轮次明显提前或消失不了但位置和第一步一致,说明问题在会话上下文;关掉后开头信息反而一直保留到更后面、或者问题消失,说明原本的失效是被记忆链路里的某些内容干扰,或者记忆开关根本没起作用、需要先确认配置是否真的生效。

聊到第十轮就忘了开头 / Hy Memory 2.0 的记忆到底落在哪一层?

打开长期记忆,检查写入触发条件

如果关掉长期记忆后问题消失,就要回到写入侧。多数实现的记忆写入不是每轮都触发,而是一组条件同时满足才抽事实入库。逐项核对下面这张清单,字段名不确定的先用占位符在自己的配置里找对应项。

  • 最小轮次 {min_turns}:会话是否达到触发写入所需的轮数再聊的,第一轮就期待它记住可能本来就不成立。
  • 重要性阈值 {importance_threshold} 或评分模型:开头那句偏好是否被判为“值得记”,格式偏好、称呼类信息常被判定为低价值。
  • 显式记忆指令:是否需要用户说“记住这一点”才写入,如果是,测试时加一句显式指令做对照。
  • 消息长度下限 {min_message_length}:过短的首句可能被跳过。
  • 作用域 {scope}:写入的是 user 级还是 session 级,session 级在关闭会话后就查不到。
  • 异步延迟 {write_delay}:写入是后台任务,聊完立刻查存储可能还没落库。
  • 去重与覆盖策略:同一事实重复写入是否被合并,或旧的被新的覆盖。

验证顺序是:先聊够轮次,再去记忆列表接口或管理页面查这条偏好是否存在。查不到,问题在写入侧,改触发条件;查得到但下一轮请求里没有被拼进 prompt,问题在检索侧,改检索参数或相似度阈值,而不是继续调写入阈值。

确定层次后只改对应那一层的参数

一次只改一项,这是能不能归因的前提。会话上下文层可动的是历史保留轮数、窗口上限、是否保留首条系统消息;记忆层可动的是最小轮次、重要性阈值、检索条数、相似度门槛。两类参数混着改,复测结果再好也无法说明是哪一处修的。

改动项旧值新值复测轮次开头信息是否被引用备注
历史保留轮数6121-15第 11 轮起失效失效点后移,未消除
最小写入轮次521-10稳定引用仅此一项生效

风险边界也要写进记录:加大历史保留会推高每轮请求的体积和成本,长会话里更容易撞上窗口上限;调低重要性阈值会让大量琐碎信息入库,检索时更容易命中噪声,表现为回答里出现与本轮无关的旧内容。改动后除了看开头信息是否被引用,也要顺手看一眼返回里有没有混进不属于当前话题的片段,两边一起判断才算完整。