资讯动态

对抗AI编码代理上下文压缩:用init.d式外部记忆恢复失忆状态

发布时间:2026/10/13 21:52:09 来源:尧图企业网站定制
我真正意识到 AI 编码代理也会“失忆”是在一次接近 20 万 token 的上下文压缩之后。那天我的代理正在一个长周期项目里连续工作我对它讲过的每一轮细节都还历历在目但它突然像被动了手术一样再也想不起两小时前自己给出的重构方案甚至搞错了我反复强调过的目录结构。那种感觉就是一场“脑叶切除术”不是慢慢遗忘而是某次压缩触发后整段对话里的关键记忆被粗暴剪掉。后来我把代理从“靠对话记忆”改造成“靠外部文件记忆”核心思路来自两个地方Unix init.d 的启动恢复机制以及电影《记忆碎片》里那套写在纸条和照片上的外置笔记系统。这篇文章就把我完整的方法、文件结构、提示词写法和验证过程都放出来给同样在长上下文里做 AI 编码代理的人一个可复制的参考。1. “脑叶切除术”降临的现场20 万 token 压缩到底删掉了什么1.1 触发压缩的那一刻代理变成了另一个人先还原一下现场。当时我让代理处理一个连续迭代的任务大概涉及三个相互关联的模块中间还穿插了两次需求调整。对话刚开始很流畅代理能准确说出每个模块之间的依赖关系也知道我要求“不要动公共接口”这类约束。但上下文一到 20 万 token 附近压缩机制就被触发了。压缩之后代理虽然还活着却能明显感觉到它变成了另一个人它开始频繁用猜测的方式补全变量名对着一个已经讨论过“绝对不要用某方案”的功能又重新提了一遍方案甚至把项目入口文件都指错了。这其实不是代理变笨了而是它赖以工作的上下文被压缩成了少数几条摘要。原始对话里那些具体的推论过程、用户偏好、错误修正记录全都被合并成类似于“用户讨论了方案A和方案B最终选择方案B”这样高度浓缩的文本。摘要不是索引它没有保留你推理时的完整路径。你失去的不是某一个词而是一整套“为什么这么做”的因果链。1.2 被“切除”的信息往往是最有价值的我把压缩后丢失的信息分成了四类这四类正好对应了长上下文项目里最致命的东西丢失类型具体表现为什么难恢复全局导航不知道项目核心模块在哪、入口在哪代码还在但代理失去了“地图”编码约束用户说过的命名规范、接口红线散落在历史对话里摘要不会逐条保留决策背景为什么选方案B而不选方案A结论可能还在但原因已经被剪掉当前进度正改到哪个文件、试过哪些失败路径上下文压缩优先清掉“最旧”的信息其中最难恢复的是“决策背景”。代码文件只能展示结果不能展示讨论过程。比如我看到代码里某个模块没有做抽象但只有对话上下文里记录了“我们讨论过抽象因为当前业务变化太快强行抽象反而会锁死后续扩展”。这个信息一旦被压缩掉代理下次看到同一段代码很可能又提出一个已经被否决的重构方案。1.3 为什么“把代码重新读一遍”救不回来很多人会说压缩就压缩吧让代理重新扫一遍代码不就行了。我也试过效果非常差。代码是静态的但项目状态是动态的。代理重新读代码能知道文件里写了什么却不知道“我正在改哪个文件”“用户刚否掉了哪条路线”“下一个步骤应该做什么”。这些不是代码里能推出来的信息。更麻烦的是一次上下文的压缩往往不是只发生一次。长项目里代理会反复逼近 token 上限每压缩一轮它对外部状态的感知就更模糊一层。如果你每次都想靠“重新读代码”来补救那就等于每次都让代理从零开始探索一个熟悉但记不住的项目。这既浪费 token也让代理的判断质量断崖式下降。所以我的结论很直接别再指望代理用对话记忆扛过压缩你应该把关键状态放到上下文之外的文件系统里让代理每一次启动都能从外部恢复。这就引出了 Unix init.d 给我的第一个启示。2. Unix init.d 给我的第一个启示把代理从“记性好”改造成“可重启”2.1 init.d 到底在干什么凭什么重启后还能恢复服务类 Unix 系统里有一个我很早就接触但直到前几天才真正想通的设计init.d 启动脚本。系统启动时内核把控制权交给 init 进程init 再去执行/etc/init.d/下面的一系列脚本把网络、数据库、业务服务一个个拉起来。这里最关键的一点是开机之后的进程不需要记得“上一次运行到哪一行”它只要按照启动脚本里定义好的步骤重新初始化就能恢复到对外提供服务的状态。系统不是靠“内存里的记忆”来保证永不重启的而是靠“把状态写入磁盘、把恢复步骤固化成文件”来保证随时可以重启。对话上下文对 AI 代理来说就像服务器里易失的内存文件系统才是那块掉电不丢的磁盘。上下文压缩等于一次强制重启如果你没有准备任何“启动脚本”那重启之后自然一片空白。2.2 把 AI 编码代理当成守护进程来设计想通这一点之后我给代理换了一个心智模型。以前我把它当作一个“越聊越懂我的助手”现在我把每次对话都当作一个独立的进程实例这个实例注定会死亡死亡方式可能是上下文压缩、模型切换、断线重连甚至是我自己重置对话。那我真正需要保证的是这个实例无论什么时候消失下一个实例都能通过同样的启动流程恢复工作状态。这个启动流程必须包含项目导航知道代码在哪、入口在哪、测试命令是什么硬性原则用户定下的约束和偏好不能靠猜当前进度正在做什么、做到哪、下一步是什么决策日志关键选择为什么这么定这些东西都应该放在文件系统里而不是放在对话上下文里。代理每次进入新会话先执行初始化流程读取这些文件然后才开始干活。它记不记得上次对话不重要重要的是文件系统里始终保留着唯一权威的状态。2.3 对话记忆和文件记忆的边界到底怎么分有人可能会问那我还要不要上下文当然要。上下文适合放“瞬时状态”比如当前正在排查的一个小问题、刚粘贴进来的报错日志、正在讨论尚未定论的想法。这些信息本来就是临时性的压缩掉也不可惜。真正需要落盘的是那种“本次对话结束后依然有价值”的信息。我用一个很简单的问题来判断如果这个信息只能存在于当前上下文里而我的代理明天还要继续这个项目那它是不是必须被写进文件只要答案是“是”我就立刻写。比如某个接口规则、某个被否决的方案、某个即将开始的任务这些都不是临场讨论而是项目长期的“开机配置”。一旦你按这个边界来管理上下文压缩就不再是灾难而只是例行重启。这也是为什么我在真实项目里开始像管理服务器一样管理代理每台“代理服务器”都有一套 init.d每次上线先跑启动脚本。3. 《记忆碎片》式外部笔记让失忆后的代理有路可走3.1 记忆碎片里的核心设定正是失忆代理的写照电影《记忆碎片》讲的是一个无法形成新长期记忆的人靠拍立得照片、手写纸条甚至纹身来给自己留下线索。他每次醒来都不知道自己前一秒做过什么但通过外部记录他可以继续追踪那件对自己非常重要的事。代理被上下文压缩之后基因上是同一个模型但短期记忆已经被切掉了。它和我都非常需要一个“外部档案”让没有记忆的个体也能沿着笔记继续执行任务。电影里的主角不敢完全相信自己的大脑因为记忆会被篡改。代理也一样压缩后的摘要不可靠它可能把“讨论过但否掉了的方案”当成可选方案把“绝对不动的模块”当成普通模块。因此外部笔记必须比对话摘要更权威。它的存在不是为了备份语气而是为了在任何时刻都提供一份可以被重新读取的真相。3.2 笔记四要素导航、原则、进展、决策我给每份外部笔记拆成四个文件对应四类必须持久化的信息。首先是导航文件。它负责告诉一个完全失忆的代理项目是什么、入口文件在哪、核心模块在哪、测试怎么跑。很多代理失忆后的第一反应是“从头扫描整个仓库”这非常浪费 token。导航文件能让它在一秒内恢复方向感。其次是原则文件。它记录那些绝对不能违反的规则比如命名风格、接口红线、框架约束等。这些规则通常是在对话里多次强调才形成的一旦压缩被切掉代理很容易重新踩进旧坑里。原则文件写得越短越好让代理每次启动都能读一遍成本极低回报极高。第三是进展文件。它记录当前进行到哪一步。这个文件必须非常具体不能只写“正在重构”而要写“正在重构 src/core/scheduler.ts 的取消任务逻辑已完成状态机改造未开始队列去重”。这样即便是完全陌生的新会话读完也能直接接手。第四是决策日志。它记录关键选择的原因。我在项目里会专门留一个清单每条决策包括“当时的问题、可选方案、为什么选了现在这个”。这个文件解决的是代理重复提出已被否决方案的问题。如果代理知道“方案A已经被验证过性能不足详见决策日志第3条”就不会再浪费一堆 token 去走回头路。3.3 让代理形成“先写笔记再动手”的肌肉记忆笔记文件不可能靠人肉手工维护必须让代理自己养成更新习惯。我在提示词里明确要求每次完成一个里程碑、每次遇到需要做技术选择时先更新对应的笔记文件再继续进行接下来的任务。我还试过在项目提示词里写这样的规则“当你准备改动某个核心文件之前必须先在progress.md里写清你要改什么、为什么改。如果你发现一个方案被否定立即追加到decision-log.md。”这样做的效果比我想象中好代理在行动前会暂停一下把状态固化到磁盘而不是只在对话里留下痕迹。说实话让代理主动持续更新笔记并不容易。它本质上是在对抗模型“直接输出”的惯性。所以我会把“更新笔记”本身当成一个子任务甚至会在提示词里写出更新的格式让代理像填表一样执行。一旦这个习惯建立起来笔记文件就变成了项目里最可靠的技术文档。4. 落地实现一套 init.d 加 memo 双通道的代理恢复机制4.1 我的.agent目录长什么样最终我在项目根目录下建了一套非常简单但很管用的目录结构.agent/ ├── init.d/ │ ├── 01-navigation.md │ ├── 02-principles.md │ ├── 03-progress.md │ └── 04-decision-log.md ├── hooks/ │ └── pre-edit.sh ├── run-init.sh └── session.ctxinit.d里的四个 markdown 文件就是上面说的四类外部记忆。run-init.sh是启动入口它会按顺序读取并输出所有笔记文件。hooks/pre-edit.sh是我用来强制刷新进度的小钩子它可以和编辑器或工作流结合在代理修改关键文件前把当前任务重新登记一次。session.ctx记录当前会话的标识和时间用于切换会话时快速识别状态是否过期。run-init.sh本身极其简单核心逻辑就是把笔记文件打出来#!/usr/bin/env bash cd $(git rev-parse --show-toplevel 2/dev/null || true) for f in .agent/init.d/*.md; do echo $(basename $f) cat $f echo done它做的事情和 Unix init.d 一模一样不关心你之前状态如何只负责把预置脚本执行一遍让系统恢复到已知状态。代理不需要任何魔法记忆只需要在每次新任务开始时先跑一遍这个脚本。4.2 一次“冷启动”的完整流程我把代理从失忆状态恢复到可工作状态的过程总结成下面几步开启新的代理会话或者检测到上下文刚被压缩过。在会话里输入初始化指令让代理读取run-init.sh的输出内容。代理根据导航、原则、进展、决策四个文件简述自己对当前状态的理解。确认理解无误后直接开始推进进展文件里写的“下一步”。工作过程中如果完成任务或做出决策立即回写03-progress.md和04-decision-log.md。下次无论因为什么原因开启新会话再次重复第 2 步。这个流程里最关键的是第 2 步。很多人在新会话里直接丢给代理一个任务代理根本不会主动去读笔记。我最后是把“无论如何都要先跑初始化”写进了项目级提示词里才彻底解决这个问题。4.3 关键提示词模板我实际在用的版本为了方便你直接抄我把初始化指令贴在下面。注意这不是废话它决定了代理会不会真正依赖外部笔记初始化指令请先运行 bash .agent/run-init.sh读取其中所有笔记内容。 不要复述这些文件也不要向我确认“是否明白”。 请直接告诉我三件事本项目的核心目标、当前进度、下一步要做的事。 之后所有操作都以 .agent/init.d/03-progress.md 里的进展为准。 如果遇到新的技术选择完成阶段后先把结论追加到 04-decision-log.md。为什么要强调“不要复述”因为复述等于把笔记内容又重新加载进上下文白白消耗 token还让代理觉得自己已经“记住”了实际上只是多念了一遍。而明确要求它输出“核心目标、当前进度、下一步”这三件事是为了验证代理真的读懂了笔记而不是只做了文本扫描。在文件更新上我用的提示词同样很具体当你完成任务清单中的某一项时先在 03-progress.md 里把该项标记为已完成 然后写清楚“下一步任务”和“当前风险”。如果某方案经过验证被否决 必须把否决原因写入 04-decision-log.md否则视为本次操作无效。这是一条强约束能避免“活干完了但文件没更新”的偷懒行为。4.4 这套组合为什么能抵御上下文压缩道理说穿了非常简单。上下文压缩只能删除对话历史不能删除项目文件。你越是把关键知识塞进对话里就越容易在压缩时被连根拔起。反过来你把知识写成磁盘文件并且让代理养成“干活前先读文件、干完活先更新文件”的习惯那对话历史就只是临时缓冲区不再是唯一真相。每经历一次压缩代理都可以通过重新运行run-init.sh回到同样的状态。这就像是给服务器做了一次故障恢复演练虽然进程被杀掉了但只要启动脚本还在它就能重新拉起全部服务。这套机制的防丢能力取决于笔记文件的完整性和新鲜度而不是模型本身的记忆能力。5. 实测验证与藏在背后的几个坑5.1 我如何故意制造一次压缩事故为了验证这套系统不是心理安慰我做了一次比较硬核的压力测试。测试分成三个阶段第一阶段让代理在本项目的笔记指引下完成一个中等难度的模块改造同时确保进展文件和决策日志都更新到位。第二阶段故意把一段超长日志文件丢给代理并要求它做总结人为把对话上下文推高直到触发压缩。这一步我没有办法精确控制压缩时机但通过塞入大量无价值的文本可以很快逼近 token 上限压缩通常会在这个过程里被触发。第三阶段开启一个全新的代理会话只给出一条提示“请执行初始化指令然后继续开发”。然后观察它是否能准确说出当前进度、下一步任务以及之前定下的约束。5.2 恢复效果对照有笔记和没笔记是两个世界我对比了有笔记基线和没有笔记基线的差距验证项没有笔记基线有笔记基线项目入口和核心模块重新扫描仓库才能找到误差大秒级恢复路径准确编码约束大概率遗忘重新踩坑原则文件明确列出不会违反当前进度完全不知道从哪继续进展文件直接指向下一步被否决的技术方案可能再次提出决策日志里有一条明确否决记录实际测下来只要笔记更新得及时新会话里的代理可以做到“像没有失忆一样”继续干活。尤其是decision-log.md它解决了我最头疼的重复决策问题。新代理不再像以前那样对每个方案都重新评估一遍而是先看日志里有没有现成结论这既防止走回头路也显著节省了上下文和推理时间。5.3 有些状态确实无法做到完全免疫当然我不建议你把这套机制神化成“绝对免疫”。有些状态天生存在于上下文和当前环境中无法完全持久化。比如视觉状态。代理有时会基于某个输出内容进行视觉上的观察上下文压缩后它不会记得自己刚才盯着哪个区域。这类信息我只能妥协用一句话写进进展文件例如“我最后在查看 src/core/scheduler.ts 的第 120 行附近”能把损失降到很低但不可能完美还原。比如临时讨论里的灵感和猜测。如果一次对话里你随口说了一个想法但代理没有把它写进决策日志那这个想法就会彻底消失。压缩之后你再问它它也只会给出模糊的回复。所以我说这套体系针对的是“可文档化的状态”而不是所有人的临场思维。5.4 实操中我踩过的几个坑以及最后的处理办法这套体系并不是第一天就完全好用的。我开始时踩了不少坑最值得说的是下面这几个。第一个坑是笔记越写越长。最初的navigation.md写了三四十行结果代理每次启动读到一半就开始泛泛复述反而不如短文件有效。后来我强制要求每个文件控制在 50 行以内只保留“读完之后能继续干活”的最低必要信息。过长的内容会被拆到专项文档里不在初始化路径上。第二个坑是只更新进度不更新决策。代理往往愿意在完成任务后更新 progress但不会自觉去写“为什么放弃了方案A”。我发现必须用提示词把这件事变成硬性任务否则决策日志会一直停留在最初那几条。后来我把“否决方案必须写日志”当作完成任务的提交标准这个问题才解决。第三个坑是笔记文件也可能过期。老状态留在 progress 里会严重误导代理它可能会继续做一件已经做完的事。我的处理方式是要求代理在开始新任务前先看“当前任务状态”如果发现笔记和实际代码不一致第一件事不是动手而是先把 progress 修订正确。这个“先矫正再执行”的过程反而让我发现了很多本来会被掩盖的沟通误差。还有一个非常现实的坑新会话初始化之后如果代理没有强制被要求“以笔记为准”它还是会倾向于依赖自己刚看到的代码和对话。项目级提示词里加上“所有操作以.agent/init.d/03-progress.md为准”之后这个问题的发生频率才降下来。经过这一轮调整我现在面对上下文压缩已经不怎么慌了。我甚至开始主动把每一次新会话当作一次“服务器重启”来设计确保即使整个过程从头再来代理也能在几分钟内进入工作状态。这套组合拳的收益远超我最初预期不只是防止失忆它顺带让代理的决策一致性、token 利用率和项目文档质量都提升了。最后再多说一句我个人的感受别等到上下文已经 20 万 token 了才开始想“万一被压缩怎么办”。现在就把.agent/init.d建起来养成“每个重要结论都落盘”的习惯你会发现哪怕下次真的又来一场“脑叶切除术”你的代理也能稳稳地回到原来的位置上。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑