消息、任务状态与可纠错记忆
“记得我”至少包含三件不同的事:模型本轮收到哪些消息、任务完成到了哪一步、用户有哪些可长期复用的偏好。会话历史适合还原对话,任务字段适合程序判断,长期记忆需要来源与有效期;把三者塞进一个摘要,会让未经验证的模型结论变成业务事实。
用最少上下文保留必要证据
模型窗口同时容纳系统指令、工具定义、用户输入、检索材料和输出。字符数不是 token 数;下面以“条目数”演示选择策略,真实预算必须使用对应 tokenizer 或供应商计量。程序保存完整审计轨迹,再选择少量信息发给模型,不应为了省 token 删除唯一原始证据。
memories = [
{"tenant": "a", "fact": "喜欢中文", "source": "user:7", "expires": 30, "verified": True},
{"tenant": "a", "fact": "订单已退款", "source": "model:8", "expires": 30, "verified": False},
{"tenant": "b", "fact": "内部预算", "source": "user:9", "expires": 30, "verified": True},
]
def context_for(tenant, now, limit=2):
visible = [m for m in memories if m["tenant"] == tenant
and m["verified"] and m["expires"] > now]
return [{"fact": m["fact"], "source": m["source"]} for m in visible[:limit]]
assert context_for("a", 10) == [{"fact": "喜欢中文", "source": "user:7"}]
assert context_for("a", 30) == []
print(context_for("a", 10))过滤在模型调用之前完成。未验证退款不能通过长期记忆进入下一轮;过期边界采用严格大于,恰好到期就失效;B 租户的内容根本不进入模型。这不等于来源可靠性已自动验证,verified 应由业务查询或明确的人类确认写入。
摘要怎样避免改变含义
把“订单 O7 的退款请求已提交,到账未知”缩成“订单已退款”会丢失状态。可靠摘要保留目标、已证实事实、来源 ID、未解决问题和下一步;金额、日期、否定与权限不能只靠自然语言压缩。结构化任务状态可以保存 refund_status=pending,消息摘要只解释为什么等待。
RAG 知识库回答“有哪些资料”,长期用户记忆回答“这个用户明确偏好什么”,任务状态回答“这个请求实际做过什么”。权限撤销后必须同时裁剪检索、缓存和历史,而不只删长期记忆。修正偏好时使用新版本或删除旧事实,不能让相互冲突的两条记忆由模型投票决定。
旧式全量聊天历史容易造成 token 增长和旧指令污染。维护时先打印角色与来源分布,保留工具调用与结果成对关系,再做窗口截断;不能从数组中间截到一个孤立工具结果。
练习
给 context_for 增加 source 撤销集合;user:7 被撤销后,a 租户在 now=10 时也应返回空列表。不要依靠模型忽略已撤销内容。