很多人做 Agent 的“记忆”,最后都会变成两种极端:
- 极端 A:把所有聊天记录都拼到 prompt 里,直到窗口爆掉、成本爆掉、输出开始胡。
- 极端 B:只保留最近几轮,模型经常忘记目标、反复问同样的问题。
这两种做法的共同问题是:你把记忆当成了文本拼接,而不是系统设计。
这篇文章给你一个可落地的答案:把 Agent 的记忆拆成三层,并且为每一层定义清晰的职责、存储形态、注入规则和评估指标。
延伸阅读(相关基础):
零、先看一个真实会翻车的失败案例:摘要压缩后把关键约束弄丢了
很多团队会在上下文快满时做一件看起来很合理的事:
“把历史对话总结成一段摘要,再继续往下聊。”
问题是,如果你只压缩自然语言,不保留结构化约束,系统会在几轮之后出现一种非常隐蔽但很致命的错误。
0.1 失败现象
一个采购支持 Agent 在前几轮已经明确拿到用户约束:
- 预算上限 20 万
- 只能推荐华东可交付方案
- 不能使用海外云服务
对话进入第 9 轮后,系统触发摘要压缩,把最近 8 轮合成一段“用户希望尽快获得稳定方案,重点关注交付效率与长期可维护性”的文字描述。
结果到了第 10 轮,模型开始推荐预算 35 万的方案,还引用了需要海外区域部署的产品。
0.2 根因定位
这个问题通常不是“模型太笨”,而是记忆管线设计错了:
- 摘要层:把硬约束压成了模糊偏好
- 状态层:没有独立保存
budget_cap、region_scope、compliance_constraints - 注入层:Prompt 里只有 narrative summary,没有 machine-checkable state
- 校验层:输出前没有做约束对照校验
0.3 修复策略
正确修复不是“把摘要写得更长”,而是把工作记忆拆成两层:
- 摘要层:只保留语境与任务进展
- 状态层:把关键约束固化为结构化字段
例如:
{
"goal": "筛选可交付的企业采购方案",
"constraints": {
"budget_cap": 200000,
"region_scope": ["cn-east"],
"forbidden_options": ["overseas_cloud"]
},
"stage": "vendor_comparison",
"open_questions": ["交付周期是否可放宽到 45 天"]
}
0.4 回归测试怎么做
把这个案例写进你的评估集,至少做三类断言:
- 摘要压缩前后,关键约束字段保持一致
- 推荐结果不突破预算上限
- 输出 validator 能拦截违反地域/合规限制的候选项
0.5 一组可对外讲清楚的指标
你不需要一上来就有完美数据,但至少要能追踪以下口径:
| 指标 | 修复前常见问题 | 修复后目标 |
|---|---|---|
constraint_violation_rate | 摘要后约束丢失,出现越界推荐 | < 2% |
avg_context_tokens | 原文全塞导致成本持续上涨 | 稳定在预算内 |
long_context_success_rate | 对话轮次一长成功率明显下降 | 在长对话中保持可接受波动 |
这类“记忆丢字段”的失败案例,比泛泛谈 RAG 更能说明你理解了系统为什么会失控。
一、先统一术语:Agent 里“记忆”到底是什么?
在工程视角里,记忆不等于聊天记录。你至少要区分三类:
- 短期记忆(Short-term / Context):最近几轮对话原文 + 当前任务输入。
- 长期记忆(Long-term / Profile & Facts):用户偏好、身份信息、长期目标、可复用事实。
- 外部记忆(External / Evidence):来自知识库/文档/数据库/工具的证据片段(RAG/工具结果)。
你要做的不是“更聪明地拼接”,而是回答四个工程问题:
- 放什么? 什么信息值得进入上下文,什么必须留在外部。
- 何时放? 什么时候注入摘要、什么时候检索、什么时候拒答/追问。
- 怎么放? 注入协议(结构、顺序、来源标签、引用约束)。
- 怎么证明有效? 用指标与回放验证“记住了该记的、忘掉了该忘的”。
二、一个可复用的“记忆管线”(Memory Pipeline)
推荐你把每次用户输入都走同一条管线,而不是“想到什么塞什么”。
2.1 输入到输出的最小闭环
每次请求的流程可以抽象成:
- Build State:从会话存储加载结构化状态(目标、约束、已完成步骤)。
- Summarize:必要时对历史对话做摘要/压缩(生成新的工作记忆)。
- Retrieve Evidence:根据当前问题检索外部证据(RAG / DB / 工具)。
- Assemble Prompt:按协议把短期 + 工作记忆 + 证据拼装成上下文。
- Generate:模型生成。
- Validate:校验是否满足结构/引用/边界;不满足则触发修复回路。
- Persist:把本轮关键信息写回会话存储(状态/摘要/索引)。
如果你已经在做 Agent 项目含金量升级,可以把这段作为“工程闭环”的核心叙事:
2.2 为什么一定要有“结构化状态”(State),而不是只有文本摘要?
文本摘要很容易出现两类不可见错误:
- 丢字段:忘了用户的限制条件、截止日期、预算、地域等。
- 歪语义:把“不要 A”总结成“更倾向 A”。
更稳妥的做法是把“工作记忆”拆为两部分:
- 摘要(narrative summary):让模型保持语境(可读的短段落)。
- 结构化状态(state JSON):让系统保持约束(可校验的字段)。
你甚至可以要求模型输出/更新状态时必须符合 schema:
2.3 一张文字版流程图:把记忆链路固定下来
如果你要把记忆系统讲给团队成员、面试官,或者写进技术设计文档,下面这张文字版流程图足够实用:
USER_INPUT
-> LOAD_SESSION_STATE
-> CHECK_TOKEN_BUDGET
-> IF overflow: SUMMARIZE_HISTORY
-> RETRIEVE_EVIDENCE
-> ASSEMBLE_CONTEXT
-> GENERATE
-> VALIDATE_CONSTRAINTS
-> IF failed: REPAIR_OR_ASK
-> PERSIST
-> WRITE_SUMMARY
-> WRITE_STRUCTURED_STATE
-> WRITE_RETRIEVAL_TRACE
这个流程的价值在于:每一个节点都能打点、回放、做 A/B 对比,而不是把“记忆好不好”全交给主观感觉判断。
三、上下文窗口管理:不要问“放多少轮”,要问“放哪些信息”
窗口管理的目标是:在固定 token 预算下最大化任务成功率。
3.1 推荐的上下文预算分配(可直接用)
以一个“面向生产”的默认配比作为起点(你可以按业务调整):
- 30%:短期原文(最近 3~8 轮)
- 20%:摘要 + 结构化状态(工作记忆)
- 40%:外部证据(RAG/工具结果)
- 10%:系统协议(角色、输出结构、工具策略、失败策略)
3.2 截断不是策略,策略是“分层与触发条件”
把“何时摘要、何时检索、何时清理”做成显式条件:
- 触发摘要:上下文超过阈值(如 70% token 预算)或任务阶段推进(从收集信息→执行)。
- 触发检索:用户问题涉及事实/文档/历史决策,且状态里不存在可靠答案。
- 触发追问:状态缺关键字段(如缺少时间范围/地区/产品版本),或证据不足。
你会发现这和 Prompt 的“失败回路/协议”强相关:
四、长期记忆:你必须回答“写入标准”与“删除标准”
长期记忆最容易翻车的点不是“不会存”,而是:什么该写?写错了怎么办?
4.1 长期记忆的写入标准(Write Policy)
建议把长期记忆限定为:
- 稳定偏好:比如语言风格、格式偏好、常用单位、时区。
- 身份事实:但要最小化(不要存敏感信息,或至少做脱敏/加密)。
- 长期目标:例如“准备 AI Agent 面试,目标公司 X,周期 8 周”。
不建议写入:
- 一次性信息(本轮临时需求)
- 模型推测(“用户可能喜欢…”)
- 未经确认的事实
4.2 删除与纠错:长期记忆需要“可回收”
你至少要有:
- 过期策略:TTL(比如 30/90 天)
- 覆盖策略:同字段新值覆盖旧值,但保留审计轨迹
- 用户可控:提供“查看/删除/禁用记忆”入口(即使是后台开关)
这会显著提升你的项目“可信度”叙事:记忆不是玄学,而是可治理。
4.3 写入/读取时机表:什么时候该写,什么时候不能乱写
长期记忆系统一旦没有“触发条件”,很容易变成垃圾仓库。下面这个表可以直接作为默认策略起点:
| 触发条件 | 写入内容 | 存储位置 | TTL 建议 | 备注 |
|---|---|---|---|---|
| 用户明确表达长期偏好 | 语言、格式、输出偏好 | profile store | 90 天 | 必须允许用户关闭 |
| 用户确认长期目标 | 学习路线、项目目标、周期 | goal state | 30~90 天 | 目标变化时需覆盖旧值 |
| 本轮临时任务约束 | 预算、地区、截止时间 | session state | 当前会话或短 TTL | 不要升级成长期记忆 |
| 工具返回权威事实 | 工单号、订单号、版本号 | evidence cache / state | 按业务周期 | 需要来源与时间戳 |
| 模型推测用户意图 | 不写入 | 无 | 无 | 推测不应进入长期存储 |
读取时也建议分层:
- 先读 session state,确认本轮约束与阶段
- 再读 profile / long-term goal,补稳定偏好
- 最后按当前问题检索 external evidence
顺序反了,系统就容易被旧偏好、陈旧事实或无关证据干扰。
五、外部记忆(RAG/工具结果):关键在“证据注入协议”
RAG 的常见失败不是检索不到,而是:检索到了也没被模型正确使用。
5.1 给证据打上来源与置信度
把证据注入做成统一结构(示例):
source_id: 文档/URL/记录主键snippet: 证据片段timestamp: 证据产生时间confidence: 检索置信度或排序分scope: 适用范围/版本/地域
然后在系统协议里写成硬约束:
- 没证据就拒答或追问
- 有证据必须引用 source_id
- 证据冲突要列出冲突并请求用户决策
这类约束通常需要 schema + validator 才真正可靠。
5.2 “RAG + 引用证据 + 拒答”是一整套,不要拆开
你可以把输出结构定义成:
answer: 给用户的答案citations: 引用的 source_id 列表open_questions: 当前缺失的关键字段actions_taken: 调用了哪些工具/检索
这样你才能在回放时判定错误来自:检索、注入、还是生成。
六、会话持久化:多用户隔离与幂等是底线
如果你的 Agent 面向多人使用,你至少要保证:
- 会话隔离:
user_id + conversation_id作为分区键 - 写入幂等:同一条消息重复提交不会污染状态(用 request_id 去重)
- 并发一致性:同一会话的并发请求要么排队,要么用乐观锁合并状态
很多“记忆很乱”的问题,本质上不是 LLM,而是会话存储的并发写入。
七、怎么评估“记忆做得好”?给你一套最小指标
不要只看主观体验。一个能持续迭代的记忆系统,至少要有“可采集口径 + 评估集规模 + 目标区间”。下面这套指标足够作为第一版仪表盘:
| 指标 | 说明 | 如何采集 | 建议评估集 | 目标区间 |
|---|---|---|---|---|
repeat_question_rate | 模型是否重复追问已知信息 | 对多轮会话做规则比对,标记重复询问字段 | 20~50 条长对话 | 越低越好 |
constraint_violation_rate | 是否违反已知约束 | 输出后跑 constraint validator | 20~50 条带约束任务 | < 2% |
long_context_success_rate | 对话轮次变长后是否仍完成任务 | 按 8 轮、12 轮、16 轮分组评估 | 至少 20 条长上下文样本 | 不随轮次明显塌陷 |
citation_rate | 需要证据的问题是否给出引用 | 检查输出结构里的 citations/source_id | 20 条证据密集问题 | 应保持稳定 |
avg_context_tokens | 每轮平均上下文成本 | 在 assemble 阶段统一记录 token | 全量线上样本抽样 | 在预算阈值内 |
如果你要进一步提高说服力,可以把这些指标按版本做 before/after 对比,而不是只报一个绝对值。
每周做一次回放时,建议把失败样本分类到:
- 摘要丢字段
- 状态字段更新错误
- 检索噪声(召回差/重排差)
- 注入协议不清晰
- 输出校验缺失
这就是可迭代的工程闭环。
八、落地清单:把“记忆”从 Demo 升级到可上线
你可以按这个顺序做(每步都能独立交付):
- 把“工作记忆”做成 摘要 + 结构化状态
- 做窗口预算与触发条件(摘要/检索/追问)
- 把外部证据做成统一结构 + 引用约束
- 增加 validator:无证据拒答、字段缺失追问
- 加入会话隔离/幂等/并发一致性
- 建立最小指标与回放机制
如果你希望团队能直接执行,可以把它再细化成发布前检查清单:
- 是否已经定义 session state schema
- 是否明确哪些字段允许进长期记忆
- 是否有摘要触发阈值与回滚策略
- 是否对证据注入打了
source_id - 是否有至少 20 条长对话评估样本
- 是否能对“约束违反”做自动拦截
记忆系统最终不是“记住更多”,而是在有限预算内稳定保留任务真正需要的信息。
下一篇如果你要继续把系统做“能抗压”,就该进入并发与可靠性:
而如果你的系统已经开始接入多用户资料、业务字段和长期偏好,下一步要看的就是权限与最小暴露:


