记忆系统设计 (Memory Systems)
很多 Agent Demo 一开始都挺聪明,聊到第 6 轮以后就开始“失忆”。上一句刚说完的约束忘了,用户偏好记不住,前面已经查过的资料又查一遍。问题通常不在模型本身,而在记忆系统没设计好。
如果把 Agent 当成一个长期协作的助手,记忆不是可选项,而是核心能力。没有记忆,Agent 更像一次性问答机器人;有了记忆,它才开始像一个真的做过事的搭档。
[PROMPT_LAB_BANNER]
1. 先把“记忆”分清楚
在 Agent 系统里,常见的记忆大概可以分成三层。这个划分不是学术定义,而是工程上比较好落地:
| 类型 | 对应实现 | 典型作用 | 持续时间 |
|---|---|---|---|
| 感官记忆 | 当前输入、上传文件、截图、网页内容 | 暂存这一轮刚拿到的原始材料 | 秒级到分钟级 |
| 短期记忆 | Context Window、会话状态、摘要 | 维持当前任务连续性 | 一次会话内 |
| 长期记忆 | Vector DB、关系表、Graph DB、用户 Profile | 记住跨会话知识、偏好、历史决策 | 天级到永久 |
一个很常见的误区是,把所有东西都塞进长期记忆。其实没必要。像“用户刚刚上传了一张报错截图”这种信息,过了当前排错场景就没什么价值;但“用户偏好 TypeScript、项目统一用 pnpm、代码注释写英文”这种约束,就值得留下来。
2. 短期记忆:别迷信超长上下文
现在很多模型上下文都很长,看起来像是“那我全部塞进去就好了”。实际做起来通常会踩三个坑:
- 成本会很快失控,特别是多 Agent 协作时。
- 模型响应会变慢,工具调用前的思考也会拖长。
- 中间信息最容易被忽略,也就是常说的
Lost in the Middle。
所以短期记忆的重点不是“越多越好”,而是“把当前任务真正需要的内容留下来”。
策略 A:滑动窗口
最简单的做法,就是只保留最近的 N 轮对话或最近的若干 token。
- 好处是实现简单,成本稳定,出了问题也容易调试。
- 问题是早期但关键的约束很容易掉出去,比如“这个仓库必须兼容 Node 18”或者“用户不想改数据库 schema”。
这种方案适合:
- 一次性问答
- 任务很短的客服类流程
- 对历史依赖不强的工具型 Agent
策略 B:会话摘要
更实用的方式,是在对话变长以后自动生成一段工作摘要,把已经确认的目标、限制和中间结论压缩下来。
比如真实项目里,一段摘要可能长这样:
当前任务是修复支付页白屏;仓库用 Next.js 14;问题只在 production 复现;用户已经确认不是后端接口超时;不要动埋点逻辑,优先排查 hydration mismatch。
这样的摘要比原始 20 轮对话更有价值,因为它保留了“任务状态”,而不是保留所有聊天痕迹。
策略 C:任务状态单独存
如果 Agent 不只是聊天,而是在跑一个多步骤任务,建议把任务状态从对话里剥离出来,单独存成结构化对象,比如:
{
"goal": "修复生产环境白屏问题",
"constraints": [
"不要修改数据库结构",
"必须兼容 Node 18"
],
"completed_steps": [
"已确认本地无法复现",
"已检查接口响应正常"
],
"next_step": "排查 hydration mismatch"
}
这比单纯依赖聊天记录稳很多。尤其是多 Agent 场景,状态对象可以直接共享,不需要每个 Agent 重新“读懂”一遍上下文。
3. 长期记忆:什么时候该上 RAG
当 Agent 需要记住跨会话的信息时,就该考虑长期记忆了。常见场景包括:
- 公司内部知识库
- 历史项目决策
- 用户长期偏好
- 上次任务已经验证过的解决方案
这时候最常见的方案还是 RAG。
基础 RAG 流程
一个标准流程通常包括这几步:
- Chunking:把文档切成可检索的小块。
- Embedding:把文本转成向量。
- Storage:存进向量数据库,例如 Pinecone、Weaviate、ChromaDB。
- Retrieval:用户提问时检索相关内容,再注入模型上下文。
这个流程大家都知道,但真正影响效果的往往不是“有没有 RAG”,而是下面这些细节:
- chunk 切得太大,召回准但上下文浪费严重
- chunk 切得太碎,关键词命中高但语义断裂
- 文档更新后没重建索引,导致 Agent 读到过期知识
- 检索回来的内容没做去重和排序,模型拿到一堆重复片段
混合检索通常比纯向量更靠谱
只用向量检索,经常会在专有名词、错误码、配置项上翻车。
例如用户搜的是 TS2304、use server、Claude Code hooks 这种词,关键词匹配通常比纯语义相似更直接。所以更稳的做法一般是:
向量检索 + 关键词检索 + rerank
工程上不用一开始就做得很重,但如果你发现 Agent 总是“差一点才找到对的文档”,优先看检索链路,而不是先怪模型。
4. 有些记忆不该存成向量
不是所有信息都适合走 RAG。
像用户资料、权限、偏好、固定约束,这类信息通常需要精确读写。你不希望用户明明说过“回复请用中文”,结果下一轮因为向量召回不稳定,Agent 又切回英文。
这类信息更适合做成结构化记忆,比如用户 Profile:
{
"user_id": "123",
"preferences": {
"language": "zh-CN",
"coding_style": "TypeScript",
"theme": "Dark"
},
"facts": [
"住在墨尔本",
"有一只叫旺财的狗"
]
}
然后在关键节点显式更新,而不是等模型自己“猜”。
一个实用原则是:
- 模糊知识,用检索
- 明确事实,用结构化存储
- 当前任务状态,用会话状态或任务对象
这样拆开以后,系统会稳定很多。
5. 记忆不是只会增长,还要会清理
很多团队做到后面会发现,真正难的不是“记住”,而是“记得太多”。
如果什么都存,最后会出现几个问题:
- 噪音越来越多,召回质量下降
- 过期信息和新信息冲突
- 用户偶然提过一次的话被系统当成长期偏好
所以长期记忆需要定期整理。常见做法包括:
- 重要性评分:低价值信息不入库
- 时间衰减:很久没命中的内容逐步降权
- 冲突解决:新事实出现时,替换或标记旧事实
- 记忆合并:把重复碎片整理成更高层的总结
举个例子,用户一周内说过三次“最近都在看 RAG 和 Agent”,那它可能值得提升为长期兴趣;但如果只是一次随口提到“今晚要买牛奶”,真的没必要永远记住。
6. 实际落地时,我更建议这样选型
如果你现在正准备做 Agent,不用一开始就上最复杂的记忆架构。可以按复杂度逐步加:
轻量版
- 最近几轮对话
- 一段自动摘要
- 少量结构化用户偏好
适合单 Agent、任务较短的产品。
进阶版
- 会话摘要
- RAG 知识库
- 用户 Profile
- 任务状态对象
适合已经有明确工作流的 AI 助手、代码助手或内部知识问答系统。
重度版
- 混合检索
- 多层记忆存储
- 记忆评分与清理机制
- 多 Agent 共享状态
这类设计更适合复杂代理系统,但维护成本也明显更高。
小结
一个能长期协作的 Agent,核心不只是“模型更强”,而是它有没有把该记住的东西记住,把不该背着走的噪音丢掉。
如果只记一句话,我会选这个:
短期记忆解决连贯性,长期记忆解决可复用性,结构化记忆解决确定性。
这三件事分不清,Agent 很容易看起来什么都知道,实际一做事就开始忘。分清以后,很多“模型不稳定”的问题,最后都会变成普通的系统设计问题。