最近在学习和梳理 Agent 的过程中,我花了不少时间去想一个问题:
Agent 的记忆到底应该怎么设计?
一开始我的理解其实很直接:
Agent 需要记忆,那就把历史信息存下来。
数据少的时候放文件,数据多了以后放数据库;如果还需要语义检索,就接上 Embedding 和 Vector DB。
这个思路当然没有错。
但越往后梳理,我越觉得,它其实只回答了一个问题:
信息存在哪里?
而没有真正回答:
Agent Memory 应该怎么设计?
对于一个 Agent 来说,真正困难的可能不是怎么把一条信息塞进数据库,而是:
- 这条信息到底值不值得记?
- 应该记多久?
- 它只属于当前任务,还是下次任务依然有效?
- 下一次执行任务的时候,Agent 怎么知道应该把它想起来?
- 如果新旧两条记忆发生冲突,又应该相信谁?
沿着这些问题继续往下想,我逐渐形成了一个自己的理解:
Memory 不是 Agent 后面外挂的一个数据库,而是一套围绕知识生命周期建立起来的机制。
数据库只是最后负责承载数据的地方。
真正决定一套 Memory 好不好用的,反而是:
什么值得记,什么时候应该想起来,以及这些记忆以后应该如何影响 Agent。
一、先搞清楚:Memory 和 Storage 不是一回事
很多 Agent 架构图里,我们经常能看到类似这样的结构:
Agent
↓
Memory
↓
Vector DB
第一次看到的时候,很容易自然地把二者画上等号:
Memory ≈ Vector DB
但后来我觉得,这两个概念其实应该拆开来看。
Vector DB、SQLite、Markdown、Redis……
这些东西首先解决的是:
信息最终保存在哪里。
也就是 Storage。
而 Memory 真正应该解决的是:
什么信息应该进入系统,以及这些信息未来应该怎样重新影响 Agent。
这有点像:
文件系统 ≠ 硬盘
硬盘负责保存数据。
但文件系统还需要解决:
目录怎么组织、文件怎么索引、权限怎么管理、内容怎么更新、数据怎么读取……
所以我们不会因为数据最终落在 SSD 上,就说:
SSD 就是文件系统。
Agent Memory 也是类似的。
Memory 决定的是「内容与生命周期」
它需要回答:
什么值得记?
什么时候应该召回?
这条记忆属于哪个范围?
它什么时候应该失效?
它怎样影响 Agent?
而 Storage 决定的是「载体」:
存在哪里?
怎么写?
怎么查询?
怎么持久化?
因此如果现在让我从 0 开始考虑 Agent Memory,我不会第一时间问:
用 pgvector 还是 Milvus?
我反而会先问:
Agent 到底需要记住哪些东西?
因为我觉得,这个问题才是整个 Memory 设计真正的起点。
二、第一类 Memory:先解决「这次任务别忘了」
假设一个 Agent 正在完成一次比较复杂的代码修改。
在执行过程中,它可能已经:
- 阅读了十几个文件;
- 理清了一部分调用链;
- 确认某个模块不能直接修改;
- 尝试过一种方案,但是测试失败;
- 排除了一个错误方向;
- 已经修改了两个文件;
- 还有几个调用方没有检查。
这些信息都需要被记住。
否则 Agent 每完成几步就重新分析一遍,整个任务很容易失去连续性。
我把这一类记忆理解成:
Task Memory。
它关注的不是:
几个月之后我还能不能想起这件事?
而是:
当前这次任务,我进行到哪里了?
例如可以维护一个很简单的运行时状态:
{
goal: "...",
findings: [],
decisions: [],
filesRead: [],
filesModified: [],
errors: [],
testResults: [],
nextActions: []
}
这里有一个我觉得很重要的点:
不是所有 Memory 都应该持久化。
比如:
我刚刚读过 user.ts。
对于当前任务来说,这条信息很重要。
但任务结束以后,它可能几乎没有任何长期价值。
因此 Task Memory 最简单的实现,可能就是:
如果需要任务恢复、Crash Recovery 或者调试,再把当前状态定期 Snapshot 到一个临时 JSON。
不需要一上来就往数据库里塞。
所以我现在比较喜欢用两句话理解 Agent 的两类记忆:
短期记忆解决「这次别忘」。
长期记忆解决「下次别重新学」。
这两个问题虽然都叫 Memory,但背后的生命周期其实完全不同。
三、什么东西才值得进入长期 Memory?
接下来才是我觉得最有意思的问题:
到底什么东西值得长期保存?
假设 Agent 完成一次任务以后产生了 100 条信息。
显然不能全部留下来。
例如下面这些信息:
我刚刚读过 user.ts
getUser 函数定义在 user.ts
这个文件里面有三个函数
它们虽然都是真的,但我觉得通常没有什么长期保存的必要。
因为 Agent 下一次重新打开 Repository,很容易再次获得这些信息。
换句话说:
如果某条信息可以通过当前环境低成本重新推导出来,就没必要急着把它写进长期 Memory。
真正值得保存的,往往是另一类东西。
例如:
Vue template 中禁止使用 optional chaining
某类非交易链路必须走 invoke
payment-sdk 只能调用,不能直接修改
code=117 在当前业务中代表用户主动取消
这些信息和前面最大的区别是什么?
它们背后往往包含了一些代码本身无法完整表达的上下文。
例如:
历史事故
业务约束
团队约定
架构边界
兼容性问题
特殊运行规则
Agent 如果不知道这些事情,哪怕代码能力再强,也可能在下一次任务中重新踩坑。
所以目前我对 Long-term Memory 的理解是:
那些无法通过当前环境低成本重新推导出来,并且未来任务仍然可能复用的知识。
这里其实可以得到一个很实用的判断标准:
越容易重新推导的信息,长期记忆价值越低
例如:
getUser 在哪个文件?
搜索一次代码就知道了。
长期保存价值比较低。
越依赖隐性上下文的信息,长期记忆价值越高
例如:
为什么 payment-sdk 不能直接改?
这背后可能涉及团队边界和历史架构决策。
如果不记录下来,下次 Agent 很可能根本不知道这条约束存在。
因此我会把长期记忆的价值粗略理解成:
重新获取成本高
▲
│
│ 更值得
│ 长期保存
│
│
│
│
└──────────────▶
跨任务复用价值
简单来说:
越难重新获得,同时未来又越可能再次使用的信息,越值得进入长期 Memory。
四、Memory 最重要的可能不是「写入」,而是 Memory Gate
继续往下想,很快就会遇到一个更麻烦的问题。
Agent 每次任务都会发现很多东西。
如果没有任何筛选机制,Memory 最后可能会变成这样:
Memory
├── 有用知识
├── 当前任务临时状态
├── 一次偶然现象
├── Agent 的猜测
├── 已经过期的结论
├── 重复信息
└── 错误经验
Memory 越来越大。
但 Agent 不一定越来越聪明。
甚至可能刚好相反。
因为一旦一条错误知识进入长期 Memory,它以后就可能不断影响 Agent 的判断。
例如一次线上问题排查过程中:
重启服务之后,问题消失了。
这是一条真实的 Observation。
但是如果 Agent直接把它总结成:
遇到这个问题时应该重启服务。
事情就开始危险了。
因为:
现象相关 ≠ 根因成立
一次结果并不能证明真正的因果关系。
所以我觉得长期 Memory 前面必须存在一道门。
我把它理解成:
Memory Gate。
Agent 在执行任务时可以产生很多 Observation。
但是 Observation 不应该直接进入 Long-term Memory。
它应该先成为:
Memory Candidate
然后经过 Memory Gate 判断:
这东西到底有没有资格成为长期知识?
Memory Gate 可以判断什么?
第一版其实不需要特别复杂。
我觉得至少可以看几个维度。
1. Reusability
未来还会不会再次使用?
如果这个知识只对当前一次任务成立,就不应该进入长期 Memory。
2. Scope
它到底影响多大的范围?
可能只是:
一个函数
也可能属于:
Repository
Team
Global
Scope 不一样,未来召回的策略也应该不同。
3. Confidence
这个结论到底有多确定?
它是:
Agent 猜的
还是:
通过代码验证
甚至:
通过测试 + 文档 + 人工确认
两者显然不能拥有相同权重。
4. Verified
有没有真正验证过?
我觉得这一点尤其重要。
Agent 可以产生假设。
但:
假设不应该轻易升级成长期知识。
5. Stability
这个知识稳定吗?
例如:
某个临时灰度开关现在是 true
可能明天就变了。
但:
这个 Repository 禁止直接修改 payment-sdk
可能半年以后还成立。
这两类信息的生命周期显然不同。
6. Cost
如果忘记了,下次重新获取的成本高不高?
例如:
某函数在第 120 行
重新搜索成本很低。
而:
这个业务规则是因为 2025 年发生过一次线上事故才建立的
重新获取的成本可能很高。
后者显然更值得保存。
所以最终 Memory Gate 的输出可能不只是:
{
"remember": true
}
而可以逐渐变成:
{
"shouldRemember": true,
"scope": "repository",
"confidence": 0.92,
"verified": true,
"lifetime": "long_term",
"reason": "该规则无法直接从代码推导,并且可能影响后续任务"
}
这样做以后,我觉得 Memory 才真正开始从:
保存历史信息
转向:
筛选未来真正有价值的知识。
而这可能才是 Agent Memory 最重要的一步。
因为一套 Memory 系统真正应该追求的,并不是:
什么都记住。
而是:
让不确定的信息留在「经验」,让确定且有价值的信息进入「记忆」。
五、记住之后,更重要的是“该想起来的时候能想起来”
Memory Gate 解决的是“什么值得记”,但信息存下来以后,还有另一个问题:
下一次任务开始时,Agent 怎么知道该把哪条 Memory 找回来?
显然不能把所有长期记忆都塞进 Context。
如果项目里已经有几百上千条 Memory,每次全量注入只会带来更多噪声。
所以 Memory 做大以后,重点会从“存储”转向“召回”。
例如当前任务是修改 credit/passwordless,那真正应该出现的,可能只有:
非交易链路必须走 invoke
payment-sdk 不能直接修改
code=117 表示用户主动取消
而不是整个项目的所有历史经验。
所以一个比较自然的过程是:
Current Task
↓
理解任务主题
↓
召回相关 Memory
↓
过滤 / 排序
↓
注入 Context
这时候再看 Vector DB,我觉得它的位置就很清楚了:
Vector DB 不是 Memory 本身,它只是 Memory Retrieval 的一种实现方式。
数据少的时候,Markdown + Tag + Keyword Search 可能就够了。
只有当 Memory 真的多到传统检索不够用时,再考虑 Embedding、Vector DB 或 Rerank。
所以 Memory 做得好不好,并不是看:
存了多少条。
而是看:
真正需要某条经验的时候,它有没有出现。
六、Memory 最终不一定还是 Memory
这是我觉得整套设计里最有意思的一点。
假设 Agent 第一次踩到一个坑:
Vue template 中不能使用 optional chaining
第一次出现时,把它记录进 Memory 很合理。
下一次再修改类似代码时,Memory 提醒 Agent:
这里要注意这个问题。
但如果同一个问题已经出现了很多次呢?
这时候还要每次都:
召回 Memory
↓
LLM 阅读
↓
LLM 判断
↓
希望它不要再犯
其实就有点浪费了。
因为当一条经验被反复验证以后,它已经不再只是“经验”,而更接近一条确定性的规则。
这时候就可以继续升级:
第一次踩坑时:
先记录经验。
反复出现以后:
用 Hook 自动提醒或检查。
当规则已经足够确定:
直接交给 Lint、CI 或 Static Rule。
这背后其实是一个很简单的原则:
让不确定的问题留给 LLM,让已经确定的问题逐渐交给确定性的程序。
所以 Memory 不一定是知识沉淀的终点。
有些 Memory 最好的结果,反而是最后“消失”了。
因为它已经变成了系统本身的一部分。
写在最后
回头看,我一开始理解 Agent Memory 时,关注的是:
信息应该存在哪里?
但继续往下梳理之后,我觉得真正重要的是:
什么值得记,什么时候应该想起来,以及什么时候已经不应该再依赖“记忆”。
Task Memory 解决“这次别忘”。
Long-term Memory 解决“下次别重新学”。
Memory Gate 负责避免什么都往里面塞。
Retrieval 负责让真正有用的经验在正确的时候出现。
而当一条经验被反复验证以后,它还可以继续从:
Memory → Hook → Rule → Automation
往前演进。
所以现在如果让我用一句话概括 Agent Memory,我会更倾向于:
好的 Memory,不是让 Agent 记住越来越多,而是让真正有价值的经验,在需要的时候重新影响它。
而那些已经足够确定的经验,最终不应该永远只是“记忆”。
它们应该成为系统能力。