MyContext 不是长期记忆 / 先分清会话历史与偏好存储

文章导读
把 MyContext 当成长期记忆,是最常见的误用:会话历史会随着轮次不断追加,而用户或 Agent 的长期偏好需要跨会话稳定复用,两者的生命周期完全不同。写进同一个存储位置,轻则上下文膨胀、每轮都携带早已过期的临时信息,重则偏好串场、上一个任务的设定跑进下一个任务。通常可行的做法是:先判断一条信息是「只对当前任务有效」还是「下次还要用」,再决定它进入会话历史还是偏好存储。判断依据可以从配置项、
📋 目录
  1. 一 回放一段多轮会话,标出临时信息与长期偏好
  2. 二 检查会话历史的保留周期和读取范围
  3. 三 核对偏好存储的键是否跨会话稳定
  4. 四 用两类问题验证分工是否正确
A A

把 MyContext 当成长期记忆,是最常见的误用:会话历史会随着轮次不断追加,而用户或 Agent 的长期偏好需要跨会话稳定复用,两者的生命周期完全不同。写进同一个存储位置,轻则上下文膨胀、每轮都携带早已过期的临时信息,重则偏好串场、上一个任务的设定跑进下一个任务。通常可行的做法是:先判断一条信息是「只对当前任务有效」还是「下次还要用」,再决定它进入会话历史还是偏好存储。判断依据可以从配置项、日志和页面行为里核对,不必依赖任何未公开的接口。

判断方向:会话历史回答「这轮对话说了什么」,长期偏好回答「这个用户或这个 Agent 一贯要什么」。前者按会话隔离并设置过期,后者用带用户维度或 Agent 维度的键长期保存。操作上先标注每轮信息的生命周期,再核对历史保留范围与偏好键的组成,最后用一条临时问题和一条偏好问题验证。边界是:哪些信息算稳定偏好仍需结合业务确认,不能凭一次对话就固化。

回放一段多轮会话,标出临时信息与长期偏好

取一段真实的多轮会话,按轮次逐条标注,判断标准是看这条信息有没有带时间限定词或任务限定词。带「这次、先、帮我、这个、刚才」的,通常只服务当前任务;带「以后、默认、我一直、记住、我习惯」的,往往是跨会话偏好。

轮次输入片段生命周期建议存储位置
1帮我把这段 SQL 的 where 条件改一下临时会话历史
2表名换成 orders_2024临时会话历史
3以后回答都用中文,不要展开解释跨会话偏好存储
4这次先只给结论临时(受「这次」限定)会话历史
5我的时区是 UTC+8跨会话(确认长期有效时)偏好存储

第 4 轮最容易误判:它和偏好「不要展开解释」字面接近,但「这次」把它锁在本次任务内。标注完成后,把「跨会话」那一列提取出来,就是偏好存储应当接收的候选清单;「临时」那一列留在会话历史里,随会话结束一起过期。如果两类信息混在一张表里,偏好会被下次临时任务覆盖,临时信息也会被当成默认设定带进新会话。

MyContext 不是长期记忆 / 先分清会话历史与偏好存储

检查会话历史的保留周期和读取范围

这一步的目标是确认临时对话不会变成事实上的长期记忆。先找到历史相关的配置项,再看它按什么维度读取。

# 通用配置骨架,字段名以实际实现为准
context:
  history:
    max_turns: 20        # 保留最近多少轮
    ttl: 24h             # 超时后不再读取
    read_scope: session  # session | user | global
  preferences:
    store: profile
    key_template: '{user_id}:{agent_id}:{key}'

重点看三处:保留轮次(max_turns 或等价字段,决定上下文里最多拼进多少条历史)、过期规则(ttl 或按会话结束清理,决定旧对话会不会被反复读取)、读取范围(read_scope 是 session 还是 user/global)。验证方式是:在同一会话里追问前几轮的内容,正常应能读到;另开一个新会话问同样的问题,正常情况下不应读到上一个会话的临时信息。如果新会话仍能读到旧对话,说明读取范围过宽,或历史数据没有按会话隔离。

核对偏好存储的键是否跨会话稳定

偏好串场基本都出在键的组成上。把当前实际使用的键模板列出来,逐段检查:

MyContext 不是长期记忆 / 先分清会话历史与偏好存储
  • 是否包含 user_id(或 tenant_id)——多用户场景必须,否则 A 的偏好会落到 B 头上;
  • 是否包含 agent_id——多 Agent 或多人格场景必须,否则同一个用户在不同 Agent 之间偏好互相污染;
  • 是否包含 session_id——只要包含,这条数据本质上就是会话级数据,不该放在偏好存储;
  • 是否包含环境维度(env、workspace)——测试与正式环境共用偏好时尤其需要。
# 反例:带 session 维度,新会话读不到语言偏好
pref:lang:{session_id}

# 较稳妥:以用户 + Agent 维度稳定复用
pref:lang:{user_id}:{agent_id}

检查动作是列出偏好存储里近期的键:如果大量键里带 session id 或轮次号,基本可以判定偏好被写成了会话数据;反过来,如果会话历史里出现了带 user_id 的长期键,说明分工反了。需要结合环境确认的一点是,同一个用户在多个 Agent 间是否应该共享偏好,这个取舍决定了 agent_id 要不要进键。

用两类问题验证分工是否正确

配置改完后,用两条问题做一次端到端验证,观察返回上下文中出现和缺失的内容。

MyContext 不是长期记忆 / 先分清会话历史与偏好存储
[新会话]
Q1(临时问题): 刚才我让你把表名换成什么了?
Q2(偏好问题): 请按我的语言偏好回答,总结一下今天的任务。

期望的上下文拼装:
Q1 -> history: 空,不含上一会话的 orders_2024
Q2 -> preferences: lang=zh,来自 user_id / agent_id 维度

验证时不要只看最终回答,要看这次请求实际拼出来的上下文——日志、调试面板或接口返回里的 context 字段都可以。对照三件事:

  • 新会话能准确答出老会话的临时信息 → 读取范围过宽,或历史未按会话隔离;
  • 新会话读不到语言偏好 → 偏好键带了 session_id,或偏好压根没写入;
  • 两条都符合预期 → 会话历史与偏好存储的分工基本成立。

如果 Q1 的答案来自模型自己的猜测而不是上下文,容易造成「看起来正确」的假象,所以务必核对拼装结果而不是仅看回答文本。这类验证建议在修改键模板或调整保留轮次后各跑一次,避免配置漂移后两类数据再次混流。