AI编码代理上下文压缩恢复:430条记录揭示结构化状态块方案
发布时间:2026/10/28 21:57:01 锦皓数字建站

1. 为什么压缩完就断片是编码代理绕不过去的坎做过 AI 编码代理的人大概率都经历过这个场景一个任务跑到一半上下文窗口快满了系统自动触发压缩把前面几十轮对话揉成一段摘要。压缩本身没问题问题是压缩完之后代理就像换了个人——刚才还在改src/auth/session.ts的第 47 行压缩后它开始重新问你想让我做什么或者更糟它自信满满地去改一个根本不存在的文件。我这次做的事情就是拿一个真实的编码代理连续跑 10 天把每一次压缩—恢复的过程完整记录下来最后攒了 430 条公开可查的记录。目的只有一个搞清楚压缩之后代理到底丢了什么以及怎么让它接得上。先说结论免得你看到一半才发现方向不对上下文压缩丢掉的从来不是信息量而是结构。摘要能把我们在做一个登录重构这件事保留下来但它保不住当前正在编辑的文件路径 光标附近的代码 上一步刚跑完的测试结果 下一步打算做什么这一整套工作状态。前者是叙事后者是状态机。代理恢复靠的是状态机不是叙事。这也是为什么很多团队觉得我摘要写得挺全的啊怎么还是接不上。因为你补的是故事它缺的是现场。关键词里出现的long_mem、engram这些词本质上都在试图回答同一个问题怎么把现场用一种比原始 token 更省、但比自然语言摘要更结构化的方式存下来。engram这个词借的是神经科学里记忆痕迹的概念挺贴切——它不是完整记忆是能触发完整记忆的那一小块钩子。这个思路后面会展开讲。至于Codex相关的那些热搜词比如codex cli、codex 安装、codex 使用教程说明大量人正在把这类代理往本地工作流里塞。而一旦你真的天天用它干活压缩恢复这个问题就会从理论问题变成每天都要骂一次的现实问题。我这 430 条记录里超过六成的失败恢复都跟这个直接相关。这篇文章适合三类人正在自建编码代理的工程师、在用现成代理但总觉得它记性差的重度用户、以及想理解长上下文管理到底难在哪的技术负责人。我会把实验设计、观察到的失败模式、我试过的几种接续方案、以及最终跑通的那套做法全部摊开讲。不吹方案只讲哪些真的管用、哪些看着优雅实际一跑就崩。2. 430 条记录是怎么攒出来的实验设计与记录口径2.1 代理配置与任务选取我用的代理是一个基于 CLI 的编码助手跑在本地仓库上工具集包括文件读写、shell 执行、代码搜索这几类基础能力。任务不是随便挑的我刻意选了三类难度递进的工作单文件小改改一个函数签名、修一个类型错误。这类任务通常 5 到 15 轮就结束压缩很少触发主要用来做基线对照。跨文件重构比如把一个散落在 6 个文件里的配置读取逻辑收敛到一个模块。这类任务动辄 40 轮以上压缩必然触发是观察重点。带验证的修复改完必须跑测试测试挂了要回读日志再改。这类任务的状态最复杂因为它同时涉及代码状态和验证状态两条线。10 天里我总共跑了 87 个任务平均每个任务触发 4 到 5 次压缩累计下来就是 430 条左右的压缩—恢复记录。每条记录我固定采集这几个字段压缩前的最后 3 轮动作、压缩摘要的实际内容、压缩后代理的第一个动作、这个动作是否正确、以及如果错了错在哪。提示记录压缩后第一个动作是关键。因为恢复是否成功几乎在第一个动作就能看出来。如果它第一件事是重新读文件、重新确认路径那基本是没接上如果它直接去改正确的行那就是接上了。2.2 什么叫接得上我给恢复成功下的定义这里必须先把标准定死否则 430 条记录没法统计。我定义的恢复成功要同时满足三条目标一致压缩后代理执行的动作和压缩前它正在推进的目标是同一个没有跑偏到别的子任务。位置正确它操作的文件和代码位置是对的不需要人类再指一遍。无需重述我没有手动补一句你刚才在改 XX它自己就接上了。三条里任意一条不满足我就记为一次失败恢复。按这个口径统计430 条里成功接上的只有 158 条成功率大概 37%。剩下 272 条失败里我进一步分了类这个分类是后面所有分析的骨架。失败类型占比典型表现目标漂移34%跑去处理摘要里提到的次要话题位置丢失29%知道要改什么但改错文件或改错函数状态错乱22%以为测试已经过了其实没跑完全重置15%重新问你想做什么这张表值得盯着看一会儿。目标漂移排第一说明摘要的叙事性反而是个陷阱——摘要里但凡提到一个次要的、没做完的小事代理恢复后很容易被它勾走。位置丢失排第二印证了我开头说的摘要保不住现场坐标。2.3 记录过程中踩的第一个坑摘要本身不可见实验刚开始时我犯了个错我以为能直接拿到压缩摘要的原文。结果发现很多代理的压缩是黑盒的摘要生成后直接进上下文不落盘、不打印。我不得不改用一个笨办法——在压缩触发前后各打一次完整的上下文快照然后 diff 出摘要那一段。这个坑值得单独说因为它影响了很多人的调试。你想优化恢复第一步得能看见摘要长什么样。如果连摘要都拿不到你根本不知道代理恢复时手里攥的是什么牌。我后来在代理外面套了一层日志把每次请求的完整 messages 数组 dump 下来才拿到干净的摘要文本。3. 摘要为什么保不住现场三种失败模式的根因拆解3.1 目标漂移摘要的平权问题摘要生成有个天然倾向它会把所有提到过的事情按重要性排个序然后平等地写进去。但代理的工作记忆不是平权的——当前正在推进的那一条权重应该远高于其他所有条目。我举个记录里的真实例子。一个重构任务进行到第 30 轮代理正在把config/loader.ts里的读取逻辑往config/index.ts搬。摘要里是这么写的用户要求重构配置模块已识别 6 个相关文件loader.ts的读取逻辑待迁移另外用户提到想顺便加个环境变量校验。压缩后代理的第一个动作是什么它去加环境变量校验了。因为摘要里另外那半句和主线任务在文本上权重差不多代理没有理由认为主线更重要。这就是根因自然语言摘要没有当前焦点这个字段。它是一份静态清单不是一份带指针的状态。人读摘要能靠常识判断哪条是主线代理不能它只能按文本显著性来。3.2 位置丢失坐标是结构不是语义第二个高频失败是位置丢失。摘要能写正在修改 session 相关代码但写不出src/auth/session.ts第 47 行refreshToken函数体内刚把expiresIn从硬编码改成读配置。有人会说那我把路径写进摘要不就行了我试过效果有限。因为路径只是坐标的一半另一半是在这个位置上的具体意图。你写正在改session.ts代理恢复后打开文件面对 300 行代码它还是不知道该动哪。它需要的是改到哪了 下一步动哪这是一个带方向的坐标不是静态地址。我在记录里做过一个对比给摘要加上文件路径位置丢失率从 29% 降到 21%再加上当前函数名 下一步意图降到 9%。这说明位置信息必须成组出现才有用单给路径等于没给。3.3 状态错乱验证结果是最容易被摘要吃掉的东西第三类失败最隐蔽也最危险。代理改完代码、跑了测试、测试挂了、它正准备根据日志再改一轮——这时候压缩触发。摘要里通常会写已修改 XX 文件但很少写测试当前是失败状态失败原因是 YY。结果就是代理恢复后以为测试已经过了直接进入总结陈词模式跟你说任务已完成。你一看测试还是红的。这个问题的根因在于摘要生成器倾向于记录动作不倾向记录动作的结果状态。改文件是动作测试失败是状态。动作好写状态难写于是状态就被省略了。但对代理来说测试失败这个状态比改了文件这个动作重要得多因为它直接决定下一步该干嘛。注意如果你在自建代理压缩时务必把最近一次验证的结果作为强制字段保留哪怕它只是一句测试失败错误在 X 文件 Y 行。这一条能砍掉相当一部分状态错乱。4. 我试过的四种接续方案哪些真能用4.1 方案一加大摘要预算——治标不治本最直觉的做法是把摘要写长一点多塞点信息。我试了把摘要从 200 字扩到 800 字。结果是成功率从 37% 提到 44%提升有限而且带来新问题摘要本身开始占用可观的上下文压缩的收益被吃掉一部分。更麻烦的是摘要变长后目标漂移反而更严重了。因为摘要里提到的次要事项更多了代理被勾走的概率上升。这印证了 3.1 的结论问题不在信息量在结构。你往一个没有焦点的清单里塞更多条目只会让焦点更模糊。4.2 方案二结构化状态块——目前最稳的一招真正有效的是把摘要拆成叙事部分和状态块两块。叙事部分随便写状态块用固定字段{ current_goal: 把 loader.ts 的读取逻辑迁移到 index.ts, current_file: src/config/loader.ts, current_symbol: loadConfig, last_action: 已复制 loadConfig 函数体到 index.ts, next_intent: 删除 loader.ts 中的原函数并更新引用, last_verification: { command: npm test -- config, result: failed, reason: index.ts 缺少 ConfigError 导入 } }这个状态块跟着摘要一起进上下文。实测下来恢复成功率从 44% 直接跳到 71%。原因很简单它把现场用结构化的方式保住了代理不需要从叙事里猜焦点直接读字段就行。long_mem这类思路的核心其实就在这里——不是存更多文本是存更结构化的状态。engram那个记忆痕迹的比喻也贴切状态块就是那个钩子代理看到current_file和next_intent就能把整个现场重新拉起来。4.3 方案三让代理自己写交接笔记——好用但要看时机第三个方案是让代理在压缩前主动写一段交接笔记明确告诉未来的自己该干嘛。这个思路来自人类交接班——你下班前给接班的同事留张条。我试下来效果取决于触发时机。如果是代理自己判断快满了然后写笔记质量参差不齐有时候它写得敷衍。如果是外部强制在压缩前插入一个写交接笔记的步骤质量稳定很多成功率能到 68% 左右略低于结构化状态块但胜在实现简单不用改摘要生成器。它的短板是交接笔记仍然是自然语言仍然有目标漂移的风险。我记录里有几例代理在笔记里写了三件事恢复后挑了最不重要的那件干。4.4 方案四外部记忆文件——适合长任务不适合快节奏最后一个方案是把状态写到仓库里的一个文件比如.agent/memory.md压缩后代理第一件事就是读这个文件。这个方案在超长任务里很香因为记忆可以跨会话保留。但在快节奏的短任务里每次读写文件都是额外开销而且代理有时候会忘了读。我最终的方案是方案二为主、方案四为辅结构化状态块负责单次压缩的接续外部记忆文件负责跨会话的长期上下文。两者配合10 天实验后半段的恢复成功率稳定在 75% 以上。方案成功率实现成本适用场景加大摘要预算44%低不推荐单独用结构化状态块71%中单次压缩接续交接笔记68%低快速实现外部记忆文件视情况中跨会话长任务5. 把状态块真正跑起来字段设计与生成时机5.1 六个必填字段一个都不能少上面那个 JSON 不是随便写的每个字段都对应一种失败模式。我把字段和它解决的问题列一下你就明白为什么不能省current_goal解决目标漂移。它强制把主线单独拎出来和次要事项隔离。current_filecurrent_symbol解决位置丢失。两个一起给代理才能定位到具体函数。last_action告诉代理上一步干了啥避免重复劳动。next_intent这是最关键的一个。它把下一步显式写出来代理恢复后不用自己推理该干嘛。last_verification解决状态错乱。把最近一次验证的命令、结果、原因全带上。我试过删字段删next_intent成功率掉 15 个点删last_verification掉 12 个点。这两个是性价比最高的如果只能保留两个字段就留这俩。5.2 生成时机别等压缩触发才想起来状态块什么时候生成我踩过的坑是等压缩触发了才让代理去总结状态这时候它已经在准备被压缩的状态里写出来的东西质量差。正确做法是滚动更新。每完成一个有意义的最小步骤——比如改完一个函数、跑完一次测试——就更新一次状态块。这样压缩触发时状态块已经是最新的直接拿来用就行不需要临时生成。这个思路和数据库的 WAL 日志有点像不是等崩溃了才记录是边做边记。代价是每步多一次小开销但换来的是压缩时零延迟、零质量损失。def update_state_block(agent, step_result): state agent.current_state state[last_action] step_result.action state[next_intent] step_result.next_plan if step_result.verification: state[last_verification] { command: step_result.verification.cmd, result: step_result.verification.status, reason: step_result.verification.reason } agent.persist_state(state)5.3 恢复时的第一动作先读状态块再动手光有状态块还不够代理恢复后得知道要先读它。我在系统提示里加了一条硬规则压缩恢复后的第一个动作必须是读取状态块并复述current_goal和next_intent。这条规则看着笨但极其有效。因为复述这个动作强迫代理把焦点重新加载一遍而不是直接凭摘要的模糊印象开干。我记录里加了这条规则后目标漂移率从 34% 降到 11%。提示复述不要让它自由发挥要求它原样引用next_intent字段。自由发挥又会引入漂移。6. 那些文档不会告诉你的实操细节6.1 压缩阈值别卡太死很多人把压缩阈值设成窗口的 95%觉得这样最省。实测下来这是自找麻烦。因为压缩本身要消耗 token 来生成摘要和状态块你卡到 95%压缩过程本身就可能溢出导致压缩失败或者摘要被截断。我的经验值是80% 到 85%触发压缩。留出的空间刚好够生成状态块和摘要还能给恢复后的第一轮动作留点余量。这个数字不是拍脑袋是我对比了 70%、80%、90% 三档之后的结论80% 档的综合成功率最高90% 档因为压缩质量下降反而更差。6.2 状态块要防过期滚动更新有个副作用如果代理卡在某个步骤很久状态块可能停留在旧状态。我遇到过代理在等一个长时间命令状态块还写着准备运行测试结果命令早就跑完了。解决办法是给状态块加个时间戳和最后更新步骤号恢复时如果发现状态块太旧就先做一次轻量的状态重建——重新读一下当前文件、重新跑一次快速检查。这个兜底逻辑救过我好几次。6.3 别让摘要和状态块打架摘要和状态块是两份东西如果它们说的不一致代理会懵。我记录里有几例摘要说正在改 A 文件状态块说current_file是 B 文件代理恢复后两个都试了一遍。所以生成顺序很重要先生成状态块再基于状态块生成摘要。这样摘要天然和状态块一致。反过来做摘要先写好状态块再补就容易对不上。6.4 测试失败的状态要写得具体last_verification里的reason字段千万别写测试失败就完事。要写到哪个测试、什么错误、大概在哪。我对比过reason写具体的情况下代理恢复后一次修对的概率是 63%只写测试失败的情况下只有 28%。原因还是那个代理需要的是能直接指导下一步动作的信息。测试失败是状态ConfigError 未导入在 index.ts 第 12 行才是可执行的线索。7. 10 天实验下来我对接得上这件事的理解跑完这 430 条记录最大的收获不是某个具体方案而是对问题性质的重新认识。上下文压缩之后的接续本质上不是记忆问题是状态管理问题。你不需要让代理记住所有事你需要让它在任何时刻都能回答三个问题我在干嘛、我在哪、下一步是什么。这三个问题自然语言摘要答不好结构化状态块答得好。这不是说摘要没用摘要负责叙事连续性状态块负责操作连续性两者分工明确。我早期把所有希望寄托在摘要上就是没分清这两件事。long_mem、engram这些方向之所以值得关注也是因为它们都在往结构化状态这个方向走而不是单纯地存更多文本。存更多文本是死路因为上下文窗口再大也有上限而任务复杂度是没有上限的。至于Codex这类工具在国内的落地我个人的观察是安装、配置这些一次性问题都好解决真正长期折磨人的就是这种跑着跑着接不上的体验问题。谁能把压缩恢复做扎实谁的工具就能真正进入日常workflow而不是停留在demo阶段。最后分享一个我一直在用的小技巧每次压缩恢复后如果代理的第一个动作对了我会在日志里给它打个正样本标记如果错了打个负样本。攒够几百条之后这些标记能反过来指导你调状态块的字段权重——哪些字段真正在起作用数据会告诉你。我这 430 条记录就是这么一点点攒出来的没什么捷径但每一条都算数。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。