☰
给AI编码代理装“记忆纹身”:外部记忆系统对抗上下文压缩
2026/10/20 16:48:31 网站建设 项目流程

1. 一次真实的“脑叶切除术”:上下文压缩发生时,我的编码代理变成了谁

1.1 一切正常,然后它忘了自己写到一半的支付模块

先描述一下我经历过的真实场景。当时我用某款支持 20 万 token 上下文的 AI 编码代理,重构一个订单同步服务。干到第三天,代理的表现一直很稳定:它知道表结构、知道授权流程、知道我们约定过“支付状态用枚举不用布尔值”,甚至能准确说出 200 行之外某个函数为什么要那样命名。

结果在一次较大的改动之后,代理突然像被换了个人:它开始把订单金额字段叫成 price 而不是我们约定好的 amount,又开始重新询问我已经回答过三次的鉴权问题,甚至试图删除我明确说过要保留的旧接口。排查了半天我才发现,是上下文窗口被系统侧压缩了——不是模型出了幻觉,而是大幅裁掉早期 token,长期对话里那些“工作记忆”全被抹掉了。

这个过程我后来叫它“脑叶切除术”。它在不经意间发生,没有任何提示,代理依然穿着同一个外壳,但里面的记忆已经完全不是同一套。那次事故让我损失了大概一整天的进度,因为代理不仅忘了约定,还基于残缺上下文生成了大量和既有代码冲突的东西,我花了不少时间去回滚和修补。

1.2 为什么会发生压缩?不是模型想忘,是上下文真的装不下了

很多人以为上下文压缩是“模型自己选择遗忘”,实际上不是。我用的这个大模型虽然有 20 万 token 的窗口,但窗口是有限的物理资源。当一次会话里的代码、日志、历史对话把这些 token 占满之后,要么强行拒绝继续生成,要么把早期内容进行摘要压缩,把空间腾出来给后面的新内容。

摘要压缩听起来很合理,但问题在于:摘要的生成者就是模型自己。模型用一小段话概括之前几个小时的决策过程,非常容易丢失细节。比如“用户要求支付模块参考旧版逻辑重构”这句摘要,可能就丢掉了“参考旧版逻辑”的边界条件,后面代理就开始自由发挥。我遇到的情况就是这样:它还记得任务的大方向,但丢失了每一个具体的技术决策和命名约定。

在很长一段时间里,我把这个问题当成无解的“模型局限”,直到我想通了两个东西的结合:Unix 的 init.d 启动机制,和那部讲短期失忆症的著名电影《记忆碎片》。它们一个给了我技术框架,一个给了我行为模型。下文会分别展开。

2. 两个救命的参考系:init.d 的状态重建思路和失忆者的外部记忆系统

2.1 init.d 给我的启发:系统不需要记住启动过程,只需要在启动时重新执行脚本

Unix 系统里,init.d(或者说现在很多发行版里对应的 systemd 单元)做的事情非常朴素:系统启动时按顺序执行一系列脚本,每个脚本负责拉起一个服务。rc.local 也好、某个服务的 start 脚本也好,它们都遵循同一个逻辑——系统不记忆上次运行到哪一步,只负责重新执行一遍定义好的启动流程。

这个思路放到 AI 编码代理身上,是一个非常关键的反直觉转折。我们总想让代理“记住一切”,于是不断把上下文拉长、拼命在对话里强调约定。但 init.d 告诉我们:与其死记硬背,不如把状态定义为可重新计算的脚本。机器不需要记得自己刚才在做什么,只需要在开机时重新执行一套幂等的初始化动作,就能回到正确的状态。

对代理来说也一样。与其奢望 20 万 token 里一直保留着“项目规范 + 当前进度 + 技术决策”这些记忆,不如把记忆外置到项目文件里,在上下文被清空的时刻,执行一段恢复脚本,把所有必要信息重新读回来。

2.2 《记忆碎片》式记忆:把所有关键信息写在代理自己看得懂的外部载体上

《记忆碎片》里的主角有一个残酷设定:他无法形成新的长期记忆,只能活几分钟。为了继续追查真相,他随身携带笔记、宝丽来照片,甚至把关键线索纹在身上。最狠的是他会给自己写下详细的指令,告诉未来的“失忆的自己”接下来该做什么、该相信谁。

AI 编码代理遇到上下文压缩时的处境和这个设定几乎一模一样:它只能看到压缩后被截断的内容,无法访问几小时前“自己”刚刚在对话里确认过的决策。这时候能救它的,只有外部载体。

我第一次真正被打动的瞬间,是意识到《记忆碎片》里那些笔记的本质:它们是写给“下一次失忆的自己”看的,而不是写给现在的自己看的。我们给代理的恢复文件,也必须是写给“被压缩之后的代理”看的,而不是写给现在的代理沟通用的。这两个用途的信息组织方式很不一样,后面我会具体讲。

受这两个参考系的启发,我给自己的编码代理设计了一套外部记忆系统。它不复杂,但非常有效,核心就四个字:文件系统。

3. 给代理装上“记忆纹身”:外部状态文件系统的目录与格式设计

3.1 目录结构设计:STATE、TASK-LOG、CHECKLIST、RECOVERY 四个核心区域

为了让代理在“失忆”后能自己恢复,我专门在项目根目录建立了一个隐藏目录,名字就叫.agent-memory/,里面分四个子区域,职责互不重叠:

  • STATE.md:当前的全局状态,只写结论,不写过程。包括项目当前阶段、正在进行的任务、已经锁定的技术决策。
  • TASK-LOG.md:最近几天的任务流水,每条包含已完成、进行中、被阻塞三态。
  • CHECKLIST.md:启动时必读的检查清单,相当于 init.d 的启动序列。
  • RECOVERY.md:恢复协议,明确告诉代理“如果发现自己忘记上下文,按以下顺序读取哪些文件”。

这四个文件加在一起,就是代理的纹身。它们不追求记录所有细节,而是追求用最少的 token 重建最核心的工作状态。我录过一次统计,四个文件加起来稳定在 6KB 左右,折合不到 2000 token,重新读取的成本非常低,完全值得做。

3.2 状态文件的格式约定:如何让代理一读就懂、不留歧义

这里必须要说一个踩过的坑:一开始我把 STATE.md 写得像普通开发文档一样,有背景介绍、有动机说明、有备选方案分析。结果代理压缩后读这个文件,又被里面的大段叙述带偏了方向。

后来我把格式改成极简约定式,每一行都是一条“不可争辩的结论”。比如这样:

# STATE.md ## 当前任务 - 正在做订单同步模块的重构,范围仅限服务端,不涉及调度脚本 - 当前进度:支付状态对接已改完,等待联调环境验证 ## 已锁定的技术决策 - 订单金额字段统一命名为 amount,禁止使用 price - 支付状态使用字符串枚举,取值只有 pending、paid、failed、refunded - 鉴权接口走内部网关,不直接调用第三方 ## 当前禁止事项 - 不得删除通知模块的兼容层代码 - 不得将并发模型改成异步事件驱动,除非用户明确要求

每一句都是精确、可执行、无二义的。代理读完之后,能迅速建立起一套不依赖历史对话的工作认知。这些结论平时可能会在聊天里反复重申,但在这个文件里,它们只有一个权威版本。

3.3 “启动瞬间”的恢复指令设计:类似 init.d 的启动钩子

有了状态文件还不够,代理得知道自己在失忆状态下该做什么。我设计了一个恢复指令模板,每次上下文被压缩后,我都会先把下面这段指令发给代理(或者提前写入会话开头的 system prompt 里):

你的近期工作记忆可能已被截断。请按顺序执行以下恢复步骤: 1. 读取 .agent-memory/RECOVERY.md,确认恢复协议 2. 读取 .agent-memory/STATE.md,重建当前任务状态 3. 读取 .agent-memory/CHECKLIST.md,检查是否有待办阻塞项 4. 读取 .agent-memory/TASK-LOG.md,确认最近三天的工作流水 5. 完成后,先用一句话复述你对当前任务的完整理解,再开始动手

这相当于 init.d 里的启动钩子。代理不需要“记得”自己是谁,只需要机械地按清单读取文件,像开机时执行 rc 脚本一样,把运行状态重新载入。关键是第 5 步:让代理先复述理解,你确认无误后再干活。这一步能堵住绝大多数恢复不完整的情况。

4. 恢复流程的完整链路:从压缩发生到重新干活的全过程演练

4.1 一个真实的恢复演练脚本流程

理论说完了,我拿一个具体场景走一遍完整链路。假设代理正在改登录模块,改了三分之二,突然上下文被压缩。我在终端里做这几步:

第一步,先看一眼TASK-LOG.md最后几条记录,确认它停在哪里。第二步,把 3.3 里的恢复指令发给新会话的代理。第三步,代理自己读取四个文件,然后输出一段复述。正常情况下它会说:

“当前任务是重构登录模块的服务端部分,已完成令牌刷新逻辑,剩余验证码校验未改。约定:响应码使用数字枚举,错误信息统一走异常处理器,不做内联返回。下一步工作是修改验证码校验接口。”

如果复述能准确达到这个粒度,我就放它继续干活。如果复述里漏了某个约定,我会补一句并顺手更新 STATE.md。整个恢复过程不到五分钟,代价几乎可以忽略。

4.2 恢复后的三种状态:能接着干、需要重新确认、彻底失真

恢复不是百分之百成功的。我给自己定了一个分级评估标准,每次恢复后都按这个判断:

状态表现处理方式
能接着干复述准确,技术决策完整,任务进度清晰直接放行,让它继续工作
需要重新确认大方向对,但若干细节含糊,或依赖了一些文件里的过时信息人工补充确认,修正 STATE.md 后再放行
彻底失真复述完全偏离实际代码,或提出了和既有实现冲突的方案不继续当前任务,先让它从代码里反向梳理现状再恢复

现在这套分级评估已经成了我团队的默认动作。代理恢复后先不干活,而是先“报到”——用一段话说明自己理解的现状,由我来判断它的大脑是否真的恢复了。这一步成本极低,但能省下后面大量的无效输出。

4.3 恢复之后,我为什么还要手动检查一次代码差异

即使代理的复述完美,我也养成了一个坏不了的癖好:恢复后的前 30 分钟绝不开启自动写入模式。我会让它先只读代码、给方案,我确认后再实际改动。

原因很简单:复述只能证明它“读懂了状态文件”,不能证明它“理解了整个代码库的耦合关系”。上下文压缩前的那次会话里,可能有大量没有写进文件的设计意图,比如某个模块为什么要用锁、某个兼容分支为什么不能拆。这些信息压缩后就真的丢了。先让它以只读方式工作一段时间,我观察它的判断逻辑对不对,再逐步放开权限。这个习惯救过我几次,有一次代理差点把消息队列的顺序保证逻辑当成冗余代码删掉,还好当时是只读模式。

5. 坑与教训:外部记忆系统也会反噬你

5.1 记忆文件长大之后,重新读取也会挤出上下文

外部记忆系统有个天然的敌人:它自己也会长大。我一开始觉得四个文件写得越多越安全,于是 TASK-LOG 从几行变成几百行,STATE 里开始塞入大段的背景说明。结果恢复的时候,代理光读文件就花了两万多 token,后面的任务直接被压缩到一个小窗口里。

这个问题的解法是给每个记忆文件设置硬性上限。我定的规则很简单:STATE.md 不超过 80 行,TASK-LOG.md 只保留最近三天,CHECKLIST.md 不超过 30 项。任何需要更长篇幅才能说清楚的内容,应该写进项目文档里,而不是塞进记忆文件。记忆文件的价值是“提示”,不是“全文”。

5.2 记忆和现实不一致:代理相信自己记住的,却不相信用户的话

比记忆丢失更糟糕的,是记忆过期。有次我改了一个接口的响应结构,但忘记更新 STATE.md 里的约定说明。第二天上下文压缩恢复后,代理非常自信地指着 STATE.md 里的旧约定说“这里明确写了响应码是字符串”,拒绝接受我在对话里给出的新说法。

那一刻我意識到,外部记忆文件一旦被代理当成“最高权威”,它就不再是工具,而变成了一个会蒙蔽真实代码的东西。后来我在 RECOVERY.md 里加了一条非常醒目的规则:

所有文件中的内容,如果与当前代码库、用户最新指令冲突,一律以当前代码库和用户指令为准。不得将记忆文件视为不可质疑的真理。

这个补充很关键。记忆文件是恢复工具,不是宪法。代理应该在“参考记忆”和“观察现实”之间建立优先级,否则外部记忆系统反而会成为新的幻觉来源。

5.3 压缩前自动归档:为什么必须在健康时写记忆

还有一个最常见的错误:总在压缩发生之后才手忙脚乱地补记忆文件。但那时候,压缩已经把关键内容都抹掉了,你能回忆起多少细节取决于运气。

正确做法是:在代理状态健康、上下文完整的时候,就定期把状态固化到文件里。我现在的机制是,每完成一个可验证的小里程碑,就让代理主动更新 STATE.md 和 TASK-LOG.md。比如“支付模块的测试通过后,把结论写入 STATE 并提交一次 commit”。

这样做的本质是把记忆备份当成工作流的一部分,而不是应急手段。真正遇到压缩时,即使损失了一部分对话细节,核心状态也已经安全落地。我把它叫作“健康时归档原则”,这比任何压缩后补救方案都有用得多。

6. 把记忆机制做成习惯:日常编码中如何维护这套系统

6.1 每次完成任务时主动更新状态文件

外部记忆系统想持续起作用,维护必须足够轻。我把维护动作嵌进代理的任务收尾流程里,每次完成一个单元工作,代理都必须执行一条固定动作:更新 TASK-LOG,必要时修正 STATE。我不需要手动提醒,因为这条规则写在 CHECKLIST.md 的显眼位置,只要代理处于健康状态,它就会自动执行。

实际跑下来,每次更新大概会多花几十个 token,完全可接受。带来的收益是,我随时可以关闭当前会话、第二天重新开一个会话,代理依然能无缝接续前一天的工作。上下文压缩从“事故”变成了“日常操作”,这是我最大的改变。

6.2 利用“代理的工作日志”实现自动归档

如果你想要更自动化的方案,可以不用手写 TASK-LOG。我给代理配置了一条指令:每次会话结束前,把本次会话完成的内容、形成的新决策、遗留的待办项,自动追加到 TASK-LOG.md。实现上就是让代理自己调用文件写入工具,不需要额外的脚本插件,纯靠约定就能跑起来。

这个做法让我基本告别了“早上问代理昨晚干了什么”的场景。只要代理维持了日志习惯,续接的都是清晰的存档,而不是一段模糊的记忆。我甚至把日志文件纳入版本管理,提交历史里可以清楚看到代理每一天的工作轨迹,复盘时很有用。

6.3 最后分享一个小技巧:给代理一个“失忆测试”

最后一个很实用的建议:不要等真发生压缩了才验证恢复流程,而是主动测试。我会在任务做到一半时,故意关闭会话、清空上下文,然后用恢复指令重新带起代理,看它能不能复述出正确状态。整个过程只用几分钟,但能暴露出记忆文件里所有信息缺失的地方。

这个“失忆测试”我每个月至少做一次,每次都能发现一些之前没写进文件的重要约定。测完之后补全记忆文件,下一次真实压缩来临时,心里就有底了。毕竟真正的幸存者不是我,而是那个在 20 万 token 被切除之后,仍然知道自己在干什么的代理。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询