先说一句得罪人的话:大部分「Agent 记忆」的讨论,讨论的其实是「怎么把更多东西塞进上下文」。真正的记忆工程不在那儿——它在于什么时候忘、忘多少、以及凭什么相信还记得的东西。
这篇不写综述,写解剖。原料是我自己的一台记忆系统:它已经陪我做完了五六个文章站、几轮项目迭代、无数次“上次那个坑是怎么填的”。它很土——就是一堆 Markdown 文件;也很硬——四个月里它救回来的时间,比我写在它身上的时间多得多。下面每一章,都对应它身上一个具体的部件。
文中所有数字与事故均来自这套系统与同源的几个 Agent 工程的真实记录;四枚小样可在页面上直接操作,参数是你改的,规律是真的。
NOT CONTEXT
上下文不是记忆:三个把人带沟里的错觉
窗口从 8k 涨到 200k、涨到 1M,最容易得出的错误结论是:“记忆问题被窗口解决了”。
拆解 · 01错觉一:窗口够大,就不需要记忆。窗口是工作台,记忆是仓库。工作台再大,你也得决定今天把什么搬上来——而且搬上来的东西越多,模型在里面的注意力被稀释得越厉害。你可以把一整个仓库倒在一个 1M 的窗口里,然后得到一个「什么都看到、什么都没抓住」的回复。
错觉二:记住 = 塞进去。塞进去的是文本,不是知识。同一件事说了五遍、时间线互相打架、旧结论压在最新结论上面——这些不是“信息更多”,是噪声更多。记忆的第一性原理不是“存”,是可被正确召回。
错觉三:记得越多越好。每多一条过期事实,就多一次把模型带向旧世界的机会。一个只有三十条事实、但条条新鲜可验的记忆,比三百条半真半假的记忆值钱得多。
下面这枚小样把第一层错觉演出来:同一套固定开销下,记忆和历史在抢同一个预算。把它切到 64k 的窗口,看看谁先被挤出去。
小样 01 · 上下文预算
固定开销 30k:系统提示 6k · 工具 12k · 规则 6k · 输出预留 6k。剩下的才是历史与记忆的战场。
一个真实的账单:我这套工具集里光工具说明就占了 12k 上下。也就是说,在 64k 窗口里,还没开始干活,四分之一已经没了。**“记忆不够”很多时候不是记忆的问题,是固定开销太肥。**
THREE TIERS
三层结构与一根索引:记忆的解剖图
把“记忆”当成一个整体讨论,永远不会得出可执行的设计。它至少是三层,各层有自己的物理规律。
拆解 · 02| 层 | 放什么 | 容量 / 寿命 | 失效方式 |
|---|---|---|---|
| 工作记忆 | 当前任务的目标、约束、最近几步 | 上下文窗口内,任务结束即弃 | 被截断(静默丢失) |
| 情景记忆 | 发生过什么:日志、时间线、决策记录 | 磁盘级,按需检索 | 找不回来(索引坏了) |
| 语义记忆 | 提炼后的稳定事实:偏好、约定、结论 | 几十到几百条,长期 | 过时、矛盾、被污染 |
把索引当路由器,把事实当文件
我用的这套语义记忆非常土:一条事实一个 Markdown 文件,文件头是一小段结构化元数据——名字、一句话描述、类型(用户/反馈/项目/参考);正文是事实本身,末尾用 [[名字]] 链接到相关事实;外加一个 MEMORY.md 索引,每条一行“标题 + 链接 + 一句钩子”。
为什么这么分?因为三层各自的“读法”不同:索引是每次都要读的那一层——它必须极短、极稳、只负责“路由”;事实文件是按需打开的那一层——它必须自包含、可独立理解;链接是图的那一层——它让“同一条事实”在多个语境里被找到。
---
name: gpt-image-only-rule
description: 生图只用 GPT 系;失败要停下提醒,不许换其他模型顶包
metadata:
type: feedback
---
2026-10-17 用户明确要求:只用 gpt-image-2.x……
**Why:** GPT 系出图的质感和服从度他认……
**How to apply:** 渠道全挂就停下汇报,禁止降级。
相关:[[ai-image-studio-channels]]、[[playable-storybook-site]]
注意 description 这一行不是给读者看的摘要,是给召回器看的。召回器先拿它和当前任务比对、决定要不要打开这个文件——它是索引的索引。这一行的写法值得单独练:写“用户在 2026-09 学过 python”,而不是“关于 python”。
再说一个反直觉的工程结论:文件系统是很好的记忆底座。不是因为它先进,而是因为它给你三样模型给不了的东西——可读(人能用 grep 查)、可版本化(git diff 能看见每一次记忆的变更)、可回滚(写坏了 checkout)。记忆系统第一年最常发生的事故不是“记不住”,是“记错了还改不回来”。
WRITE PATH
写入:六道闸,一次只放一条好事实过去
记忆系统的质量,八成在写入那一刻就定了。读取再聪明,也救不回一条写歪的记忆。
拆解 · 03- 闸一 · 值不值得记
只记“下次会用到、且不写下来就会忘”的东西。一次性的任务状态不记(任务结束就死),能从代码里读出来的事实不记(代码才是源)。判据一句话:这条事实过期了吗?过期后有没有害?
- 闸二 · 记成什么形状
一条事实一个文件,而不是把十条挤进一个大日志。粒度越细,后面越好改、越好删、越好链接。这也是一句老话的现代版:写时多花一秒,读时省下一小时。
- 闸三 · 撞车了怎么办
新事实和旧事实矛盾时,只有三种正确姿势:取代(旧档案里标 superseded,新事实指向它)、合并(写成一个带时间线的事实)、或明确保留两条并标注“尚未裁决”。最差的姿势是追加——让两条矛盾的记忆并存,然后祈祷召回器选对。
- 闸四 · 留证据
每条事实带来源:哪一天、因为什么事故、链接到哪次复盘。没有来源的事实,在冲突时永远输——因为你没法判断它是否过期。
- 闸五 · 版本化
记忆是文件,文件在 git 里。写入是 commit,改错是 revert。别小看这一条:它把“记忆污染”从灾难降级成一次回滚。
- 闸六 · 写明触发条件
描述里要写“什么时候该用它”,而不只是“它是什么”。记忆不是百科全书,是行动指南——召回器需要的是“该不该现在打开我”,不是一段简介。
第三道闸值得单独玩一遍。下面这张冲突卡是真实形状:三个月前的决定与今天的决定撞车了。三种策略,你各点一次,看看记忆仓库变成什么样。
小样 02 · 写入冲突
仓库现状 · 2 条事实
矛盾风险:评一下三种策略的落点。
三种策略都“记住了”,但只有一种让未来的召回是安全的。
真实事故:我曾在同一套记忆里留下“git push 走 https”和“必须切 SSH”两条结论,相隔一个月,都没写触发条件。结果下一轮会话里,Agent 先抽到旧结论,白折腾了二十分钟。从那以后,写入闸二里多了一条:矛盾必须当天裁决,不许过夜。
RECALL
召回:不是搜索,是一次带预算的排序
写入决定了记忆的“质量上限”,召回决定了它的“命中下限”。而召回最容易被做错的地方,是只按相似度排序。
拆解 · 04一次像样的召回至少要走四步:
- 候选:多渠道取并集
关键词命中(精确、便宜)、向量相似(模糊、能跨措辞)、图邻居(顺着链接走一步)、最近使用(上下文热度)。只靠一种,会稳定漏掉某一类。
- 打分:三个维度,缺一不可
相关性解决“像不像”,新鲜度解决“还对不对”,重要性解决“值不值得占位置”。只按相关性排,最典型的事故就是:召回了一条 2024 年的“最佳实践”,它确实很像,但已经被推翻。
- 预算:名额有限,按分数截断
召回不是越多越好。给召回开一个 6k 的预算,按分数从高到低塞,塞不下就停。**预算就是记忆的“注意力带宽”**——它比排序算法更能决定记忆系统的实际效果。
- 注入:把事实变成可执行的内容
同一条事实,用“事实陈述”注入还是用“触发条件 + 动作”注入,效果差很远。记忆进上下文时,最好带着“当什么情况发生时,该怎么做”的形状。
小样 03 · 召回打分
这个项目的部署方式是什么?按“相关 0.6 · 新鲜 0.2 · 重要 0.2”排序,预算 3 条。
把“新鲜”与“重要”一起拉到 0:新旧两条部署结论的分数会从差 0.22 缩到只差 0.01——排序从此由噪声决定。
工程上最划算的一招:把“新鲜度”写成衰减函数而不是布尔值。事实不是“有效/失效”,而是“30 天前 0.9、90 天前 0.6、一年前 0.3”。这样即使排序出了偏差,旧事实也不会瞬间挤掉新的——它是慢慢退潮的。
HYGIENE
遗忘与卫生:记忆会腐烂、会中毒、会自我强化
人脑忘记是特性,机器记忆忘记是功能。没有遗忘的记忆系统,一定会烂掉。
拆解 · 05| 病 | 症状 | 处方 |
|---|---|---|
| 腐烂 | 三个月前还成立的事实,今天照旧被召回 | TTL + 新鲜度衰减;过期进“待复核”队列而不是直接删 |
| 冲突 | 两条事实互相打架,召回随机选一个 | 写入时强制裁决(取代/合并);巡检时标红待裁决对 |
| 污染 | 错误来源被当成事实;一次误记长期扩散 | 证据链(来源 + 日期 + 关联事故);可疑来源打标,不参与召回 |
| 膨胀 | 索引越来越长,每次都要读一遍 | 索引只留路由钩子;超过上限就合并同类项、降级冷事实 |
还有一种更隐蔽的病:自我强化。Agent 把自己的输出写回记忆,下一轮又把它当作“事实”读进来——一次小的偏差就这样被逐步镀成“经验”。对策是把记忆分成两栏:观察到的事实与推导出的结论,后者必须带置信度和复核日期。
下面这枚小样把时间轴交给你:往前推 180 天,看哪些卡片该退役、哪两张该被标红、哪张已经被污染源带歪了。
小样 04 · 记忆卫生
TTL 到期不是“删除”,是“进入待复核”——先降权,再问人。
我这套系统里最值钱的一个习惯:记忆文件定期做一次“体检”式重读。不是为了改文笔,是为了抓三件事——过期结论、互相矛盾的条目、以及“当时写得含糊、现在看不懂”的描述。每一轮体检,平均能抓出 2–3 条该退役的旧事实。
PROOF
怎么证明它有用:把“感觉记得住”换成四个数
记忆系统最容易被自欺:它看起来在帮忙。要证明它真的在帮忙,得让它考试。
拆解 · 06四个指标,按造价从低到高:
- 召回命中率
准备 30 个「这次该想起什么」的查询,人工标注正确答案,测 Top-3 命中率。造价:一个下午。收益:这是唯一能反映“记忆到底有没有用”的数字。
- 矛盾检出率
故意埋 10 对互相矛盾的事实,看系统能不能在巡检时标出来。漏检的矛盾,会在真实任务里变成一次先验错误。
- 回归集
把“已经踩过的坑”写成固定用例:给出任务描述,要求 Agent 必须引用某条记忆。每次改记忆系统跑一遍,防止修了 A 忘了 B。
- A/B:带记忆 vs 不带记忆
同一批任务跑两遍——有记忆、无记忆,比成功率、比人工纠错次数、比平均轮数。这是唯一能回答“值不值得”的实验;如果 A/B 打平,说明你记的可能都是废话。
最诚实的指标其实是“被纠正的次数”:每轮任务里,人需要说几次“不对,不是这样”。记忆做得好,这个数字会稳定下降;记忆做歪了,它会稳定上升——因为它开始自信地重复错误。
上线前的记忆体检表
结语:慢记忆,快模型
模型会越来越快、越来越长、越来越便宜。而这篇文章里的每一条——粒度、裁决、衰减、证据、预算——都不会因此失效,因为它们解决的不是“能力不足”,而是“状态的管理”。状态管理没有摩尔定律。
所以我的建议反而很朴:先从一张索引表、一个事实一个文件开始,把写入的六道闸装起来。等它长到两百条,你自然会遇到遗忘与污染的问题——那时候你已经有一个可以体检、可以回滚、可以做 A/B 的系统了。到那一步,记忆就不再是“给模型的提示词”,而是你产品的一部分。
速查卡 · QUICK REFERENCE
- 窗口是工作台,记忆是仓库;塞进去 ≠ 记住
- 三层:工作 / 情景 / 语义,各有各的失效方式
- 一条事实一个文件;索引只做路由;
[[链接]]织图 - 写入六道闸:值得记 → 粒度 → 裁决 → 证据 → 版本 → 触发条件
- 召回 = 候选并集 + 三维打分 + 预算截断 + 可执行注入
- 新鲜度用衰减;TTL 进待复核;观察与推导分栏
- 证明有用:命中率、矛盾检出、回归集、A/B(看“被纠正次数”)
本文案例来自作者自建的文件式记忆系统(索引 + 单事实文件 + 链接图,git 版本化),以及同源 Agent 工程中的降级链、时间线日志与可回滚写入实践。四枚小样为原生 DOM 实现,参数可控,逻辑即文中所述。