把 MyContext 当成长期记忆,是最常见的误用:会话历史会随着轮次不断追加,而用户或 Agent 的长期偏好需要跨会话稳定复用,两者的生命周期完全不同。写进同一个存储位置,轻则上下文膨胀、每轮都携带早已过期的临时信息,重则偏好串场、上一个任务的设定跑进下一个任务。通常可行的做法是:先判断一条信息是「只对当前任务有效」还是「下次还要用」,再决定它进入会话历史还是偏好存储。判断依据可以从配置项、日志和页面行为里核对,不必依赖任何未公开的接口。
判断方向:会话历史回答「这轮对话说了什么」,长期偏好回答「这个用户或这个 Agent 一贯要什么」。前者按会话隔离并设置过期,后者用带用户维度或 Agent 维度的键长期保存。操作上先标注每轮信息的生命周期,再核对历史保留范围与偏好键的组成,最后用一条临时问题和一条偏好问题验证。边界是:哪些信息算稳定偏好仍需结合业务确认,不能凭一次对话就固化。
回放一段多轮会话,标出临时信息与长期偏好
取一段真实的多轮会话,按轮次逐条标注,判断标准是看这条信息有没有带时间限定词或任务限定词。带「这次、先、帮我、这个、刚才」的,通常只服务当前任务;带「以后、默认、我一直、记住、我习惯」的,往往是跨会话偏好。
| 轮次 | 输入片段 | 生命周期 | 建议存储位置 |
|---|---|---|---|
| 1 | 帮我把这段 SQL 的 where 条件改一下 | 临时 | 会话历史 |
| 2 | 表名换成 orders_2024 | 临时 | 会话历史 |
| 3 | 以后回答都用中文,不要展开解释 | 跨会话 | 偏好存储 |
| 4 | 这次先只给结论 | 临时(受「这次」限定) | 会话历史 |
| 5 | 我的时区是 UTC+8 | 跨会话(确认长期有效时) | 偏好存储 |
第 4 轮最容易误判:它和偏好「不要展开解释」字面接近,但「这次」把它锁在本次任务内。标注完成后,把「跨会话」那一列提取出来,就是偏好存储应当接收的候选清单;「临时」那一列留在会话历史里,随会话结束一起过期。如果两类信息混在一张表里,偏好会被下次临时任务覆盖,临时信息也会被当成默认设定带进新会话。
检查会话历史的保留周期和读取范围
这一步的目标是确认临时对话不会变成事实上的长期记忆。先找到历史相关的配置项,再看它按什么维度读取。
# 通用配置骨架,字段名以实际实现为准
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)。验证方式是:在同一会话里追问前几轮的内容,正常应能读到;另开一个新会话问同样的问题,正常情况下不应读到上一个会话的临时信息。如果新会话仍能读到旧对话,说明读取范围过宽,或历史数据没有按会话隔离。
核对偏好存储的键是否跨会话稳定
偏好串场基本都出在键的组成上。把当前实际使用的键模板列出来,逐段检查:
- 是否包含 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 要不要进键。
用两类问题验证分工是否正确
配置改完后,用两条问题做一次端到端验证,观察返回上下文中出现和缺失的内容。
[新会话]
Q1(临时问题): 刚才我让你把表名换成什么了?
Q2(偏好问题): 请按我的语言偏好回答,总结一下今天的任务。
期望的上下文拼装:
Q1 -> history: 空,不含上一会话的 orders_2024
Q2 -> preferences: lang=zh,来自 user_id / agent_id 维度
验证时不要只看最终回答,要看这次请求实际拼出来的上下文——日志、调试面板或接口返回里的 context 字段都可以。对照三件事:
- 新会话能准确答出老会话的临时信息 → 读取范围过宽,或历史未按会话隔离;
- 新会话读不到语言偏好 → 偏好键带了 session_id,或偏好压根没写入;
- 两条都符合预期 → 会话历史与偏好存储的分工基本成立。
如果 Q1 的答案来自模型自己的猜测而不是上下文,容易造成「看起来正确」的假象,所以务必核对拼装结果而不是仅看回答文本。这类验证建议在修改键模板或调整保留轮次后各跑一次,避免配置漂移后两类数据再次混流。