长期记忆与检索:什么该进提示词
记住一切等于什么都记不住。记忆系统的价值在于取舍。
学完这节你能做到
- 区分会话内状态、项目知识与长期偏好三类记忆,各用不同存法
- 设计一次检索注入,控制它占用的上下文预算
- 避免记忆污染:过时的偏好比没有偏好更糟
「让 agent 记住一切」听起来很美。实际做出来的效果通常是: 它记住了三个月前的一条过时偏好,然后固执地按它执行。
记忆系统的难点从来不是存,是取舍。
三类记忆,三种载体
把「记忆」拆开,很多问题会自动消失:
| 类型 | 例子 | 存哪 | 生命周期 |
|---|---|---|---|
| 会话内状态 | 这次任务改了哪些文件、当前进展 | 会话本身(+ 压缩摘要) | 一次任务 |
| 项目知识 | 用 bun 不用 npm、这个目录是生成产物 | AGENTS.md、skill、仓库文档 | 跟着仓库 |
| 长期偏好 | 「我要简短的回答」「先给结论」 | 全局 AGENTS.md | 跨项目、很少变 |
看清这个划分之后会发现:大多数「需要记忆系统」的需求,其实是需要写文件。 项目知识写进仓库、偏好写进全局配置,两者都是纯文本、可审查、可 diff、可版本控制。
Pi 的做法正是如此:上下文文件从 ~/.pi/agent/AGENTS.md、
各级父目录、当前目录三处层叠加载。没有神秘的向量库,就是文件。
文件方案的上限比多数人以为的高得多。当项目知识增长到「一份 AGENTS.md 放不下」时, 下一步是拆成多个 skill(按需装载),而不是直接上 RAG。
会话内状态:外置比压缩好
长任务里最有效的记忆手段特别朴素:让 agent 把进展写进文件。
<!-- TODO.md,由 agent 自己维护 -->
## 目标
把 40 处 fetchLegacy( 换成 fetchV2(
## 进展
- [x] src/api/(31 处)—— 已改,typecheck 通过
- [ ] src/web/(12 处)—— 进行中
- [ ] tests/(4 处)
## 决定
- src/api/legacy-shim.ts 的 3 处**不改**:那是兼容层,语义不同
好处是压缩发生时这些信息不会丢 —— 摘要可能丢细节,文件不会。
Pi 的 compaction 摘要里也确实有 ## Progress 和 ## Next Steps 段落,
但能落到文件的东西就别指望摘要。
「没有内置待办」是它明确列出的取舍之一,替代方案就是一个 TODO.md。 理由和这里一样:文件是可见、可编辑、可版本控制的,而内置状态不是。
什么时候真的需要检索
三个条件同时满足才值得上 RAG:
- 知识量远超上下文窗口(比如整个 wiki、几年的工单)。
- 每次任务只需要其中很小一部分。
- 「哪一部分」无法提前确定(能确定的话,写 skill 让它按需读就行)。
不满足的常见情况:
- 「几十页的内部文档」→ 拆成几个 skill,比向量库准得多。
- 「代码仓库」→
grep通常比语义检索准。代码检索的关键词是精确的。 - 「最近几次会话的内容」→ 会话文件本身可以搜,不需要 embedding。
检索注入的三条纪律
真要做检索,把它当成「往上下文里花钱」来设计:
① 给它预算。 检索结果最多占多少 token,写死。 超了就砍条数,不要因为「相关度都还行」就全塞进去。
const MAX_INJECT = 3000
let used = 0
const picked = []
for (const doc of ranked) {
if (used + doc.tokens > MAX_INJECT) break
picked.push(doc); used += doc.tokens
}
② 带来源和时间。 每条注入内容标上出处和更新时间, 让模型能判断可信度,也让人能追溯:
[知识库 · 部署手册 · 更新于 2026-03-11]
预发环境的发布需要走 xxx 流水线……
③ 注入到消息里,不要注入系统提示。 又是缓存那件事 ——
检索结果每轮都不一样,塞进系统提示等于每轮缓存全废。
用 before_agent_start 返回 message 是对的位置。
记忆污染:比没有记忆更糟
一条三个月前的记录写着「部署用 deploy.sh」,而现在早就换成流水线了。 agent 拿着它反复出错,而且说得很有信心。
四条防污染:
| 做法 | 说明 |
|---|---|
| 带时间戳,并让模型看见 | 「更新于 2026-03-11」会让它对旧信息保持怀疑 |
| 可失效 | 给记忆条目设 TTL,或者绑定到某个版本/分支 |
| 单一事实来源 | 同一件事只记一处。记两处,早晚不一致 |
| 写入要有门槛 | 别让 agent 自动把每个结论都写成长期记忆 |
最后一条最容易被忽视。「自动记住有用的东西」听起来很智能,实际结果是 知识库里塞满了它当时的猜测。写入长期记忆应该是显式动作 —— 人说「记住这个」,或者至少有一道审阅。
它记下的往往是「本次任务里的临时结论」,而不是普遍事实。 下一次任务里,这条临时结论会以「已知事实」的姿态出现, 而且没有任何标记提示它只是一次猜测。
一个务实的组合
绝大多数团队用这套就够,几乎不需要写代码:
全局 ~/.pi/agent/AGENTS.md —— 个人偏好(10 行以内)
项目 AGENTS.md —— 项目硬约束与事实(50 行以内)
skill —— 长流程、低频知识(按需装载)
TODO.md / 笔记文件 —— 长任务的进展外置
会话树 + /tree —— 回到任意历史节点
真正需要向量检索的,通常是产品形态(比如接客服知识库), 而不是工程 agent 的日常。
团队有一份持续更新的「线上环境速查表」(各服务的域名、端口、负责人,约 200 行,每周都在变)。最合适的处理是?
这一节的结论
- 拆成会话内状态、项目知识、长期偏好三类,各用各的载体。
- 大多数「记忆」需求其实是「写文件」—— 可审查、可 diff、可版本控制。
- 长任务的进展外置到 TODO.md,别指望压缩摘要。
- RAG 的三个前提:知识量远超窗口、每次只用一小部分、无法提前确定是哪部分。
- 注入要有预算、带来源和时间、放在消息里而不是系统提示。
- 长期记忆的写入必须有门槛 —— agent 自动记下的临时结论是污染的主要来源。