编码代理上下文压缩接续失败率研究:430条记录与状态固化实践

发布时间:2026/10/28 14:27:15
编码代理上下文压缩接续失败率研究:430条记录与状态固化实践

1. 上下文压缩这件事,为什么会让编码代理"断片"

先说结论:上下文压缩本身不是问题,问题是压缩之后代理还能不能"接得上"。我花了 10 天时间,跑了 430 条公开记录,专门盯这件事。所谓"接得上",指的是代理在被压缩掉一段历史之后,还能不能继续正确地理解当前任务、记住之前做过的决策、不重复踩同一个坑。这三点里任何一点崩了,整个编码流程就会开始漂移。

大多数人第一次遇到这个问题,是在长任务里。比如让代理改一个跨十几个文件的重构,前半小时它干得好好的,突然开始重复问你已经回答过的问题,或者把之前删掉的代码又加回来。你以为是模型变笨了,其实是上下文被压缩了,而压缩过程丢掉了它"接续"所需的关键锚点。

这里要先厘清一个概念:上下文压缩和上下文截断是两回事。截断是简单粗暴地丢掉最老的消息,压缩则是把一段历史总结成更短的表示。压缩理论上更聪明,因为它保留了语义,但代价是——总结是有损的,而编码任务对"精确性"的要求极高。一个变量名、一个函数签名、一个"我们决定不用某个库"的结论,丢了就是丢了,总结里未必留得住。

我这次实验的核心观察对象,就是压缩之后代理的"接续质量"。430 条记录里,我按任务类型分了类:单文件小改、跨文件重构、依赖升级、bug 定位、测试补全。结果很明确——任务越依赖历史决策,压缩后的接续失败率越高。单文件小改几乎不受影响,跨文件重构的失败率能到三成以上。

为什么会这样?因为编码代理的工作记忆不是线性的。它需要同时记住:当前文件的状态、之前改过哪些文件、为什么这么改、哪些方案被否决了。压缩算法通常按"时间远近"或"信息密度"来取舍,但编码任务里,一个两小时前"我们决定不改这个接口"的结论,可能比五分钟前的一句闲聊重要得多。压缩如果按时间衰减,就会把这个关键决策压没。

提示:判断你的代理有没有"断片",最简单的信号是它开始重复已经确认过的决策,或者对已经改过的文件表现出"第一次见"的反应。

我在实验里用了一个笨但有效的办法来量化:给每条记录打两个分,一个是"压缩前任务完成度",一个是"压缩后继续同一任务的完成度"。两者差值超过 20% 的,我就标记为"接续失败"。430 条里,标记为失败的有 137 条,接近三分之一。这个比例比我预想的高,也说明这个问题不是边缘 case,而是长任务里的常态。

2. 430 条记录里,接续失败到底长什么样

2.1 三类最典型的断片模式

把 137 条失败记录摊开看,模式其实很集中,主要就三类。

第一类是决策遗忘。代理在早期明确说过"这个模块我们用 A 方案,不用 B 方案",压缩之后它又开始推荐 B 方案,甚至直接按 B 方案改代码。这类占失败记录的 41%。它的危害在于,你如果没盯着,代码就被改回去了。

第二类是状态错乱。代理对"当前代码处于什么状态"判断错误,比如以为某个函数还没创建,其实早就创建了,于是重复创建导致冲突。这类占 33%。它通常表现为编译报错或者重复定义。

第三类是目标漂移。代理忘了最初的任务目标,开始做一件相关但不同的事。比如你让它"优化查询性能",它压缩后开始"重构查询的代码风格"。这类占 26%,最隐蔽,因为它的输出看起来是合理的,只是不对题。

2.2 一个具体的失败案例复盘

我挑一条最有代表性的记录展开。任务背景是给一个中型项目做依赖升级,涉及 8 个文件。前 40 分钟代理干得很顺,改了 5 个文件,并且在对话里明确记录了两条决策:一是"某个旧 API 在新版本里被移除,改用新 API",二是"某个配置文件不动,因为改动风险高"。

然后上下文触发了压缩。压缩后的第一条指令是"继续"。代理的反应是:它重新去读了那个"不动"的配置文件,并且开始改它。同时它对新 API 的用法也退化了,用回了一个已经废弃的写法。

我复盘了压缩前后的上下文,发现问题出在压缩的取舍逻辑上。那两条关键决策,在原始上下文里是分散在两段长对话里的,压缩算法把它们当成了"过程性描述"给压掉了,只保留了"改了哪些文件"这种结果性信息。结果就是——代理知道"改了什么",但不知道"为什么这么改"和"什么不能改"。

这个案例给我的启发是:压缩算法优化的目标,和编码任务需要的目标,是不一致的。压缩算法通常优化"信息熵"或"token 效率",而编码任务需要的是"决策可追溯性"。这两者经常冲突。

2.3 失败率和任务长度的关系

我把 430 条记录按任务时长分了桶,画出来的趋势很清晰:

任务时长记录数接续失败数失败率
小于 30 分钟1682112.5%
30 到 90 分钟1544831.2%
90 到 180 分钟764153.9%
大于 180 分钟322784.4%

这张表说明一件事:任务越长,压缩几乎必然发生,接续失败几乎必然出现。超过 3 小时的任务,失败率超过八成。这不是模型能力问题,是上下文管理机制的结构性问题。

注意:这张表里的"失败"是相对严格的判定,只要出现一次决策遗忘或状态错乱就算。如果你把标准放宽到"最终任务还是完成了",失败率会低一些,但返工成本会显著上升。

3. long_mem 和 engram 这两条路,各自解决了什么

3.1 long_mem 的思路:把记忆外置

long_mem 这类方案的核心思路很直接——既然上下文里放不下,那就把记忆放到上下文外面,需要的时候再取回来。它本质上是一个"外部记忆库",代理在关键节点把决策、状态、结论写进去,压缩之后从里面读回来。

我在实验里试了这种模式,效果是有的,但有个前提:写入和读取的时机必须由代理自己判断,而不是由外部规则强制。如果强制在每个压缩点都写入,会写进去大量噪音;如果完全不强制,代理经常忘了写。我最后用的折中方案是——在任务的关键决策点(比如"确定方案""否决方案""发现约束")触发写入,这些点用关键词识别。

long_mem 的优点是通用,不依赖具体模型。缺点是它引入了额外的读写开销,而且"什么时候该记"这件事本身就需要判断力,判断错了要么记太多要么记太少。

3.2 engram 的思路:把记忆做成结构化痕迹

engram 走的是另一条路。它不追求"记住所有东西",而是把关键信息做成结构化的"痕迹",类似给任务建立一个轻量的状态机。每条痕迹包含:当前阶段、已确认的决策、待验证的假设、已知的约束。

这个思路的好处是抗压缩。因为痕迹是结构化的、精简的,它本身就不容易被压缩算法当成冗余信息丢掉。我在实验里对比过,同样触发压缩,用 engram 模式的记录,接续失败率比裸奔低了大约一半。

但 engram 也有代价:它需要你在任务开始时就定义好"痕迹的结构"。如果结构定义得太粗,记不住细节;定义得太细,维护成本高。我试了几版结构,最后收敛到一个比较简单的四字段版本,够用。

3.3 两种方案的适用边界

把两种方案放一起对比:

维度long_memengram
记忆位置上下文外,按需取回上下文内,结构化精简
抗压缩能力中,依赖读取时机高,结构本身抗压
维护成本低,自动写入中,需定义结构
适合任务长周期、决策密集中长周期、状态明确
主要风险记太多或记太少结构定义不当

我的实际选择是两者混用:用 engram 维护核心状态,用 long_mem 兜底细节。核心状态保证代理不漂移,细节记忆保证它不用重复劳动。这个组合在 430 条记录的后半段实验里,把接续失败率压到了 15% 以下。

4. 让代理"接得上"的实操配置

4.1 压缩触发点的选择

大多数人用的是默认压缩策略,通常是按 token 数触发。这个策略的问题在于,它不管你当前在干什么,到点就压。我改成按任务阶段触发:在一个明确的子任务完成、且下一个子任务还没开始时,才允许压缩。这样压缩点落在"自然的断点"上,接续难度低很多。

具体怎么判断"自然断点"?我的做法是看代理最近几条输出里有没有"完成信号",比如"这个文件改完了""测试通过了""下一步是……"。有这些信号,就是断点。没有,就再等等,哪怕 token 已经超了,也先做一次轻量的状态固化再压。

4.2 状态固化的最小字段集

不管用哪种记忆方案,压缩前做一次状态固化都是值得的。我用的最小字段集是四个:

  • 当前目标:一句话,说清楚这次任务要达成什么。
  • 已确认决策:列表,每条是"做了什么决定 + 为什么"。
  • 已知约束:列表,每条是"什么不能做 + 为什么"。
  • 当前进度:哪些文件改了、哪些没改、下一步是什么。

这四个字段加起来通常不到 200 token,但能覆盖接续所需的核心信息。我试过更复杂的字段集,收益不明显,维护成本反而上去了。

4.3 一个可直接抄的固化模板

下面是我实际在用的固化模板,你可以直接拿去改:

[状态固化] 目标:<一句话> 已确认决策: - <决策1>,原因:<原因> - <决策2>,原因:<原因> 已知约束: - <约束1>,原因:<原因> 当前进度: - 已改:<文件列表> - 未改:<文件列表> - 下一步:<动作>

用法是在压缩前,让代理按这个模板输出一次,然后把这段内容作为压缩后的"锚点"放在上下文最前面。实测下来,这个动作能把接续失败率降低大约四成。

提示:模板里的"原因"字段别省。我对比过,带原因的固化比不带原因的,接续质量高出一截。因为原因本身就是决策的上下文,丢了原因,代理容易在类似场景下做出相反的决定。

5. 实验里踩过的坑和反直觉发现

5.1 压缩率不是越高越好

我一开始的直觉是,压缩得越狠,省下的 token 越多,代理能"看到"的近期上下文就越多,应该更好。实测打脸了。压缩率超过某个阈值后,接续失败率反而上升。原因是压得太狠,连状态固化的内容都被压没了。

我最后把压缩率控制在一个中等区间,具体数值因任务而异,但原则是:固化内容必须完整保留,其余部分可以压。也就是说,压缩前先把固化内容"钉"住,不让它参与压缩。

5.2 代理自己写的总结,比外部总结更管用

我试过用外部脚本生成压缩总结,也试过让代理自己生成。结果是代理自己生成的总结,接续效果更好。原因可能是代理自己知道"什么对它重要",它写的总结更贴合它后续的推理需求。外部脚本写的总结,往往更"客观",但不够"有用"。

所以我的做法是:压缩总结让代理自己写,但给它一个模板约束,避免它写得太随意。

5.3 短任务也会断片,只是不明显

前面那张表里,30 分钟以内的任务失败率也有 12.5%。这个数字一开始让我意外,后来想明白了——短任务如果决策密度高,一样会触发压缩,一样会断片。只是短任务你更容易发现和纠正,所以感知不强。

这提醒我,上下文管理不是长任务专属问题。任何决策密集的任务,都值得做状态固化。

5.4 反复压缩会累积误差

如果一个任务触发了多次压缩,每次压缩都丢一点信息,误差会累积。我在实验里见过一个极端案例,任务触发了 5 次压缩,到第 5 次时,代理对最初目标的记忆已经面目全非。

对策是每次压缩都基于"固化内容 + 上次固化内容"来重建,而不是基于"上次压缩结果"来二次压缩。这样误差不会累积,因为固化内容是稳定的锚点。

6. 把接续质量做成可观测指标

6.1 三个可量化的信号

光靠感觉判断"接得上接不上"不靠谱,我给自己定了三个可量化的信号:

  • 重复提问率:代理重复问已经回答过的问题的次数,除以总提问次数。
  • 决策回退率:代理做出与已确认决策相反的决定的次数。
  • 返工率:因为状态错乱导致的重复劳动次数。

这三个信号我每完成一个任务就记一次,攒够样本就能看出趋势。实测下来,做了状态固化之后,这三个指标都能明显下降。

6.2 一个简单的记录表

我用的是最朴素的表格,每行一个任务:

任务时长压缩次数重复提问决策回退返工是否固化
重构A95min2312是
升级B140min3745否

这张表攒到几十行之后,规律就自己浮出来了。我的经验是,别指望一次实验就得出结论,攒样本比单次分析重要。

6.3 指标怎么用

这些指标不是用来"考核"的,是用来"定位问题"的。比如你发现某个任务的决策回退率特别高,就去翻它的压缩记录,看是哪次压缩丢了关键决策。定位到了,下次同类任务就知道该在哪个环节加固。

我现在的习惯是,每个长任务结束后花两分钟填一下这张表。两分钟的成本,换来的是对上下文管理问题的持续可见性。

7. 关于 Codex 这类编码代理的接续实践

7.1 代理的"工作记忆"和人的不一样

用 Codex 这类编码代理时间长了会发现,它的工作记忆和人的工作记忆有个根本区别:人会主动"复习"重要信息,代理不会。它只会被动地使用上下文里现有的东西。所以一旦压缩丢了信息,它不会像人一样"想起来",而是直接基于残缺信息继续干。

这个区别决定了,对代理做上下文管理,必须比对人更主动。你不能指望它自己维护记忆,得靠机制。

7.2 把固化做成习惯动作

我在用 Codex 做长任务时,养成了一个习惯:每完成一个子任务,就让它按固化模板输出一次。哪怕还没触发压缩,也输出。这样做的额外好处是,固化内容本身就成了一个"检查点",我能快速扫一眼,确认它没跑偏。

这个习惯动作的成本很低,一次输出也就几秒钟,但收益很实在。我统计过,养成这个习惯之后,长任务的返工率下降了大约三分之一。

7.3 别让代理"自由发挥"记忆

有些代理支持自动记忆功能,会自动把一些内容存起来。我的建议是,自动记忆可以开,但别完全依赖它。自动记忆的取舍逻辑你不可控,它可能存了你不需要的,漏了你需要的。关键决策还是手动固化更稳。

我自己的配置是:自动记忆开着当兜底,关键决策手动固化当主力。两层保险,比单靠一层踏实。

8. 这套方法在什么场景下不划算

8.1 一次性小任务

如果你只是让代理改个错别字、调个格式,那这套方法纯属多余。状态固化的开销比任务本身还大。我的判断标准是:任务是否需要跨多个决策点。不需要,就别上这套。

8.2 探索性任务

有些任务本身就是探索性的,比如"帮我看看这个代码库能怎么优化",没有明确目标,决策也是边走边定。这种任务做状态固化意义不大,因为"已确认决策"这个字段经常是空的。这类任务更适合用 long_mem 兜底,而不是 engram 式的结构化固化。

8.3 短平快的批量任务

如果你在批量处理一堆相似的小任务,每个任务都很短,那逐个做固化不现实。这种情况更适合在批量层面做一次统一的上下文管理,而不是任务层面。

注意:判断划不划算,核心看"决策密度"和"任务时长"两个维度。两个都高,就值得上;有一个低,就可以简化。

9. 我个人在 10 天实验后的几点体会

这 10 天跑下来,最大的体会是:上下文压缩不是要解决的问题,而是要管理的约束。你没法让压缩不丢信息,但你可以让丢的信息不那么关键。状态固化、engram 结构、long_mem 兜底,本质上都是在做这件事——把关键信息从"可压缩区"挪到"不可压缩区"。

第二个体会是,接续质量是可以被观测和改善的。一开始我以为这是玄学,后来发现只要把信号量化,问题就变得可定位、可修复。重复提问率、决策回退率、返工率这三个指标,我现在每个任务都会扫一眼。

第三个体会是,别追求完美记忆,追求够用记忆。我试过把什么都记下来,结果是上下文被记忆本身撑爆了,反而更糟。够用的标准是:代理能接着干,不跑偏,不重复劳动。达到这个标准就够了,多记的都是负担。

最后一个实操建议:如果你刚开始管这件事,别一上来就搞复杂方案。先从"压缩前让代理按模板输出一次状态"这个动作开始,成本最低,收益最直接。跑顺了,再考虑上 long_mem 或 engram 这类更结构化的方案。我自己的路径就是这么走的,先解决有无,再解决好坏。