给 AI 加记忆,大部分人的第一步是错的:把所有觉得有用的东西写进系统提示词,希望它“都记住”。
结果通常是反的。它开始忽略你真正在意的规则,开始在无关的地方引用三个月前的旧结论,开始答非所问——不是记不住,是被塞满了。
我用一套纯 Markdown 的文件系统做这件事,跑了几个月,现在有 254 条记忆、86 条规则、225 篇日志。没有向量库,没有 embedding,没有任何数据库。核心只有一件事:
记忆不是“存多少”,是分层之后每层只装它该装的。
三层,各装什么
L1 常驻层 ── 每条消息都跟着走 ── 只放"祈使句"
L2 索引层 ── 按需读一行 ── 只放"什么情况下展开哪篇"
L3 正文层 ── 用到才读全文 ── 放结论、数据、步骤、坑
L1 常驻层:系统提示词 / CLAUDE.md /
AGENTS.md。它跟着每一条消息进上下文,所以它的成本是“字节数
× 消息数”。这里只该放两样东西:
- 必须每轮都生效的祈使句(“不要用 rm 删用户数据,一律进废纸篓”)
- 入口地址(“开工前先读 BOOTSTRAP.md 和 INDEX.md”)
不该放:知识、结论、历史、项目背景。这些放进来,就等于每轮都在为一个可能用不上的事实付费。
L2 索引层:每个目录一个
_index.md,一行一条,格式固定:
- [标题](路径) — ≤60 字触发条件
它回答的不是“这篇写了什么”,而是“什么情况下你该展开它”。
L3 正文层:真正的结论、命令、参数、验证数据。只有被索引命中时才读。
判据:一条信息该进哪层
不用凭感觉,问三个问题:
| 问题 | 答“是” | 答“否” |
|---|---|---|
| 每条消息都需要它吗? | L1 常驻 | 往下问 |
| 能用一句“什么情况下用”定位它吗? | L2 索引 + L3 正文 | 它可能不是记忆 |
| 需要精确复现(命令/参数/数字)吗? | L3 正文全文 | L2 一行就够 |
第三问容易被忽略。结论可以压成一句话,命令不能。“重启服务要带
--accept-dns=false”压成“注意 DNS 参数”,下次照样踩。
比“记什么”更重要的是“记在哪”
我一开始把所有东西都往一个 memory/
目录里塞,很快就乱了:同一件事在日志里写了一遍、在任务里写了一遍、在记忆里又写了一遍,三份内容互相不一致,AI
读到哪份算哪份。
后来按“这份记录回答哪个问题”切成五块,落点互斥:
| 模块 | 回答的问题 | 装什么 |
|---|---|---|
| 日志 | 某轮发生了什么 | 过程、断点、恢复线索 |
| 任务 | 做到哪了,下一步 | 状态机:进行中/暂停/阻塞/完成 |
| 记忆 | 以后要复用什么结论 | 一条一个结论 |
| 对象 | 有哪些实体,怎么关联 | 服务器、仓库、工具、服务 |
| 规则 | 必须遵守什么 | 可执行的约束 + 优先级 |
关键不是这五块本身,是那句“落点互斥”:
- 过程流水只进日志,日志不是任务进度的真源
- 长期进度只进任务,任务不是知识库
- 可复用结论只进记忆,一条记忆只表达一个结论
- 强约束只进规则,规则要能执行,不能只是“注意一下”
一件事只写一处,其他处放链接。同一事实有两个真源,就一定会有一次不一致。
索引的硬指标:必须比正文便宜
这是整套东西里最反直觉、也最有效的一条规则:
索引条目必须比正文便宜。装摘要的索引,等于把正文抄了一遍。
如果索引行写了根因、步骤、参数、验证数字,那“读索引”就没有比“读正文”省任何东西——你会干脆全读,然后爆掉。
所以给了两条可计算的验收线:
- 破折号后 > 60 字 = 不合格
- 单个索引文件“总字符 / 条目数” > 110 = 整体超配,该批次回迁细节
这里有个我自己踩过的验收陷阱,值得单独说:
只检查“超长”是无效验收。 因为只有
- [标题](路径)、破折号后什么都没有的条目,会被算成 0
字,然后被判“合格”。我第一批回迁就是这么把 93
条无说明的条目全部放过,还报告了“整改完成”。
正确做法是两个都要查:缺失(破折号后为空)和超长。查一个漏一个。
预算:让“懒”变成规则
分层之后还需要一个东西约束“读多少”,否则 AI 会把所有索引都读一遍。
我的预算写死在入口文件里,是可执行的数字,不是“尽量少读”:
整读索引 ≤ 2 个
展开正文 ≤ 3 篇,按命中强度排序
第 4 篇起 必须先说出"为什么非读不可"
恢复中断 只看最近 3 条日志标题,展开 1 篇
还有个更细的技巧:两级读取——先标题,后正文。
索引文件本身也可能很大(我那个记忆索引 2.6 万字符、254 条),整读它同样超预算。所以先用一条命令只取标题和路径:
grep -oE '^- \[[^]]+\]\([^)]+\)' memory/_index.md
扫完标题再决定展开哪几篇。索引的索引,就是标题。
为什么不用向量库
经常被问:为什么不直接上 embedding + 检索?
三个原因,都是实际跑下来才确认的:
- 我的语料规模根本用不上。 273 个文件、几十万字,全量 grep 一次是毫秒级。向量检索解决的是“百万级文档里找相似”,我这里的问题是“从 20 个候选里挑 3 个”。
- 检索会丢结构。 我需要的不只是“相关内容”,还要“这条记忆属于哪个项目、什么时候验证过、置信度多少、和哪条规则冲突”。这些是字段,不是相似度。
- 可审计。 纯文本文件我能一眼看出 AI 读了什么、漏了什么、写错了哪一行。黑盒检索做不到这一点,而排查“AI 为什么犯这个错”时,这个能力是决定性的。
顺带一个观察:记忆系统的瓶颈从来不是“存不下”,是“写进去的质量”。 一条含糊的记忆(“注意配置”)比没有记忆更糟,因为它会挤掉真正有用的那条。
规则和记忆要分开,因为它们的性质不同
这是我犯过最贵的一个错:把风格要求、行为约束、项目背景混在一起写成一大段“提示词”,然后期待它每次都遵守。
事实是:
- 记忆是事实,写进去就成立,AI 读到就用
- 规则是约束,写进去只是提高了遵守的概率
后者这句话很重要:提示词不是编译器。 我遇到过全局规则明确写了“注释只描述当前代码行为”,模型仍然写出“后端接口就绪后替换”这类未来态注释——规则是加载了的,也读到了,就是没执行。
所以判断标准是:
| 违反的后果 | 该放哪 |
|---|---|
| 只是风格不一致,人能顺手改 | 提示词就够 |
| 会导致数据损坏 / 上线事故 / 静默失败 | 必须补可执行检查:lint、grep 扫描、提交前检查、CI 阻断 |
不能接受的错误,不能只靠反复强调。 强调只会让你在下次犯同样的错时更生气。
局限,说清楚
- 这套东西是给单人 + 少量项目设计的。多人协同时“谁改了什么”需要另一层,我的做法是全部进 git,但这只解决了追溯,没解决冲突。
- 254 条记忆里一定有失效的。我的做法是每条带
updated和confidence,但没有自动过期机制——靠“读到它时顺手核对”。这是当前最大的漏洞。 - 索引维护是纯人工成本。新建文档必须同一次操作里更新索引,漏一次就产生一个“不存在的文档”。我把它写成了硬规则,但执行依赖 AI 自觉,而 AI 会忘。
最后一句:
好的记忆系统不是让 AI 记住更多,是让它每次都能找到“这次只需要那一条”。