☰
AI编码代理上下文压缩后断片问题与接续方案实测
2026/10/28 5:30:32 网站建设 项目流程

1. 为什么"压缩完就断片"是编码代理绕不开的坎

先说结论:上下文压缩本身不是问题,问题是压缩之后代理"接不上"。我花了 10 天时间,跑了 430 条公开可查的记录,专门盯这件事——AI 编码代理在上下文被压缩、裁剪、摘要之后,到底还能不能继续把活干完。答案比想象中微妙:能,但前提是你得给它留一条"记忆的接缝"。

先把场景摆清楚。你在用一个编码代理(比如基于 Codex 这类命令行代理形态的工具)改一个中等规模的项目,聊到第 40 轮的时候,对话历史已经堆到几十万 token。这时候系统会触发上下文压缩:把前面的历史摘要成一段短文本,或者按重要性裁剪掉一部分,腾出窗口继续往下走。压缩动作本身很成熟,摘要模型也够聪明,但真正翻车的地方在后面——代理重新开始工作时,它不知道"我刚才改到哪了""哪个文件已经动过""上一步的测试为什么失败"。于是它开始重复劳动、改错文件、把已经修好的 bug 又改回去。

这个现象我给它起了个名字叫压缩断片。它不是模型能力问题,而是状态交接问题。压缩把"对话内容"压没了,但代理真正依赖的是"任务状态"——当前改到哪个文件、哪些假设已经验证过、下一步计划是什么。这两者不是一回事。对话历史是流水账,任务状态是账本余额。你把流水账撕了,余额还在,但代理看不到余额,只能从头翻流水。

430 条记录里,我统计了一下断片的表现形式,大致分四类:

断片类型典型表现占比
重复劳动重新读取已读文件、重跑已通过的测试约 38%
状态回退把已修复的代码改回旧版本约 24%
目标漂移忘记原始需求,开始做无关优化约 21%
工具误用重复调用同一命令、参数记错约 17%

这四类里,状态回退最危险,因为它会静默地破坏已经正确的工作成果,而且往往要等到最后跑测试才暴露。我遇到过最离谱的一次:代理在第 12 轮修好了一个边界条件,第 35 轮压缩之后,它"回忆"起这个函数"好像有问题",又给改了回去,来回折腾了三轮。

所以这篇东西要解决的核心问题就一个:压缩之后,怎么让代理无缝接上。关键词里的 long_mem、engram 这些概念,本质上都是在回答这个问题——怎么在压缩的缝隙里,保留一份代理能读懂的"任务记忆"。下面我会把 10 天实验里验证过的做法、踩过的坑、以及为什么某些方案看着美好实际没用,一条条拆开讲。

2. 实验设计:430 条记录是怎么攒出来的

2.1 任务选取与压缩触发点的设定

要让实验有说服力,任务不能太简单也不能太发散。我选了 6 类编码任务,每类都要求跨多个文件、有明确的验收标准(能跑通测试或能编译通过):

  • 修 bug:给定一个失败测试,要求定位并修复
  • 加功能:在现有模块上增加一个小特性,需要改 2-3 个文件
  • 重构:把一个长函数拆成多个,保持行为不变
  • 补测试:给未覆盖的分支补单元测试
  • 依赖升级:升级一个库并修复破坏性变更
  • 文档同步:代码改了之后同步更新注释和 README

每类任务跑多个变体,总共凑出 430 条有效记录。压缩触发点我设了两档:一档是固定阈值(历史达到窗口的 70% 就压),一档是任务边界触发(每完成一个子任务就主动压一次)。后者是我后来加的,因为发现固定阈值经常在任务做到一半时突然压缩,断片最严重。

提示:压缩触发时机比压缩算法本身更影响接续质量。在任务边界压缩,代理的"心理状态"是干净的,接续成功率明显更高。

2.2 记录什么:从 token 数到"接续失败率"

每条记录我采集了这些字段:压缩前的历史长度、压缩比、压缩后代理第一次工具调用的类型、是否重复了已完成的动作、最终任务是否通过验收、以及一个我自定义的指标——接续失败率。

接续失败率的定义是:压缩后代理的前 3 次工具调用中,只要有一次是"重复已完成动作"或"基于错误状态的操作",就算一次接续失败。这个指标比最终成功率更敏感,因为很多断片会被代理自己"绕回来",最终任务还是过了,但中间浪费了大量 token 和时间。

实测下来,不做任何干预的基线组,接续失败率是61%。也就是说,超过一半的压缩事件之后,代理都会先犯一次迷糊。这个数字比我预想的高很多,也说明这个问题值得认真对待。

2.3 一个反直觉的发现:摘要越长,接续越差

实验早期我有个想当然的假设:压缩时摘要写得越详细,代理接续越好。于是我让摘要模型尽量保留细节,压缩比控制在 3:1 左右。结果接续失败率反而升到了68%,比激进压缩(10:1)还差。

原因想通了其实很简单:长摘要里混入了大量"已完成"的细节,代理读到这些细节,会误以为这些事还没做。比如摘要里写"用户要求修复登录超时问题",代理读完就去重新查登录超时,完全没注意到后面还有一句"该问题已在第 8 轮修复"。摘要越长,这种"已完成/待完成"的混淆越多。

这个发现直接改变了我的方案方向:压缩不该追求"保留更多信息",而该追求"保留正确的状态标记"。信息可以丢,状态不能丢。后面讲的 long_mem 和 engram 思路,核心都是围绕"状态标记"而不是"信息保真"。

3. 三种接续方案实测:谁在真干活,谁在自嗨

3.1 方案一:纯摘要压缩(基线,失败率 61%)

这是最朴素的做法,也是大多数工具默认的行为:把历史丢给摘要模型,生成一段自然语言摘要,替换掉原始历史。优点是实现简单、token 省得多;缺点是摘要模型根本不知道"哪些是任务状态、哪些是闲聊",它按语言重要性摘要,而语言上重要的内容未必是任务上关键的。

我举个真实例子。某条记录里,代理花了 5 轮讨论一个命名规范,又花了 3 轮实际改代码。摘要模型觉得命名规范讨论"信息密度高",大段保留了;实际改代码的部分因为都是工具调用和文件 diff,被压成了一句"修改了相关文件"。结果压缩后代理接着讨论命名规范,完全忘了代码已经改完。

纯摘要方案不是不能用,但它必须配合外部状态记录,否则就是赌博——赌摘要模型恰好把关键状态留下了。

3.2 方案二:结构化状态快照(失败率降到 29%)

这个方案是我实验里性价比最高的。核心思路:压缩对话历史的同时,单独维护一份结构化的任务状态,不参与摘要,原样保留。这份状态用固定 schema,大概长这样:

{ "task_goal": "修复登录超时", "current_step": "已定位到 token 刷新逻辑", "files_touched": ["auth/session.py", "auth/token.py"], "verified_assumptions": ["超时阈值是 30s", "刷新接口返回 401 时需重试"], "failed_attempts": ["直接改超时阈值无效"], "next_action": "检查刷新接口的重试逻辑" }

这份快照只有几百 token,但它是状态不是内容。压缩后把它放在系统提示或首条消息里,代理一眼就知道自己站在哪。实测接续失败率从 61% 降到 29%,而且失败的类型也变了——从"状态回退"变成了"细节遗忘",后者危害小得多,代理问一句就能补回来。

维护这份快照的关键是谁来更新。我试过让代理自己更新,结果它经常忘记;后来改成在每次工具调用后由外层逻辑自动抽取(比如文件路径从工具参数里拿,验证过的假设从测试结果里拿),可靠性高很多。这也是 long_mem 这类思路的工程化落点:记忆不是让模型自己记,而是让系统帮它记。

3.3 方案三:engram 式关键片段锚定(失败率 22%,但有代价)

engram 这个词借的是神经科学里"记忆痕迹"的概念,放到这里指的是:不压缩某些关键片段,把它们原样锚定在上下文里。哪些算关键片段?我的判定规则是:

  • 包含最终决策的对话轮次("就用方案 B")
  • 包含失败结论的轮次("这个方向走不通,因为...")
  • 包含验收标准的轮次("测试必须全绿")

这些片段通常只占历史总量的 10%-15%,但承载了 80% 的状态信息。把它们锚定住,其余部分激进压缩,接续失败率能压到 22%。

但代价也很明显:锚定片段会随任务推进不断累积,跑到后期,锚定内容本身就可能撑爆窗口。我遇到过跑到第 60 轮时,锚定片段已经占了窗口的 40%,压缩等于没压。所以 engram 式方案必须配一个"锚定片段的再压缩"机制——把老的锚定片段合并成状态快照,也就是方案二的形态。三者其实是递进关系,不是互斥选项。

方案接续失败率token 开销实现复杂度适用阶段
纯摘要61%最低低短任务
状态快照29%低中通用
engram 锚定22%高高长任务前期
快照+锚定混合18%中高长任务全程

4. 把方案落地:一份可抄的接续协议

4.1 压缩前后的固定动作清单

光有方案不够,得有可执行的协议。我在实验后期固化了一套动作,压缩前后各三步,跑下来最稳:

压缩前:

  1. 冻结当前任务状态,写入快照(用上面的 schema)
  2. 标记本轮的关键决策片段,加入锚定池
  3. 记录"最后完成的动作"和"下一个待做动作"

压缩后:

  1. 首条消息注入状态快照,明确告诉代理"这是你之前的状态"
  2. 注入锚定池里的关键片段
  3. 显式声明"以下动作已完成,不要重复",列出 files_touched 和 verified_assumptions

第 3 步是很多人会漏的。代理重复劳动,往往不是因为它忘了,而是因为没人明确告诉它"这些别做了"。一句"auth/session.py 已修改并通过测试,无需再次读取",能省掉大量无效工具调用。

4.2 状态快照的字段设计:哪些必须有,哪些是坑

字段设计我迭代了 4 版,最后稳定下来的字段和踩过的坑:

  • task_goal:必须有,且要短。写长了代理会纠结措辞。
  • current_step:必须有。这是代理"我在哪"的唯一坐标。
  • files_touched:必须有,用路径列表。坑在于要区分"改过"和"改对了",我后来加了files_verified单独记录已验证的。
  • verified_assumptions:必须有。这是防止代理推翻已确认结论的关键。
  • failed_attempts:强烈建议有。没有它,代理会重走死路。我统计过,带这个字段的方案,重复失败尝试减少了 70%。
  • next_action:必须有,但只能写一个。写多个代理会犹豫,实测写单个动作时接续最顺。

注意:快照字段不是越多越好。我试过加conversation_summary字段,结果代理开始依赖摘要而不是快照,接续质量反而下降。快照要的是"状态",不是"内容"。

4.3 一个容易忽略的细节:压缩后的第一条消息怎么措辞

措辞对结果影响巨大,这点我反复验证过。对比两种写法:

写法 A:"以下是之前的对话摘要:[摘要内容]。请继续完成任务。"

写法 B:"你正在执行任务:修复登录超时。当前进度:已定位到 token 刷新逻辑。已修改文件:auth/session.py(已验证)、auth/token.py(未验证)。已确认:超时阈值 30s。已失败:直接改阈值无效。下一步:检查刷新接口重试逻辑。请从下一步继续。"

写法 B 的接续失败率比 A 低 40% 以上。差别在于:B 用的是第二人称的进度陈述,代理读起来像"自己的记忆";A 用的是"别人的摘要",代理读起来像"参考资料",需要二次理解,理解过程就容易出错。

这个细节看着小,但它解释了为什么很多工具"明明做了摘要,代理还是断片"——摘要的人称和视角错了。记忆要以第一人称、进行时的口吻注入。

5. 那些看着美好、实测翻车的做法

5.1 让代理"自己记住":为什么不可靠

最直觉的做法是:在系统提示里写一句"请记住你的任务状态,压缩后自行恢复"。我试了,失败率 74%,比什么都不做还差。原因是模型没有真正的持久记忆,它所谓的"记住"只是在当前上下文里保持,一旦上下文被压缩,这个"记住"的指令本身也可能被压掉,或者被压成一句无意义的"请记住状态"。

更麻烦的是,这种指令会让代理产生虚假的自信。它会声称"我记得我改过这个文件",但实际上记错了。这比明确说"我忘了"更危险,因为你会信它。

结论:记忆必须外置,由系统维护,不能指望模型自省。long_mem 这类方案的价值就在这——把记忆从模型内部搬到外部存储,压缩只影响对话,不影响记忆。

5.2 全量保留工具调用记录:token 黑洞

另一个极端是:压缩时把工具调用记录全留着,只压自然语言对话。想法是"工具调用是事实,事实不能丢"。实测 token 消耗爆炸,而且接续质量没提升——因为工具调用记录里 90% 是重复的读文件、跑测试,真正有状态价值的没几条。

正确做法是对工具调用做聚合:把"读取了 a.py、b.py、c.py"聚合成一条"已读取 3 个文件",把"测试通过"聚合成一条状态标记。聚合后的记录既保留了事实,又不占 token。我在方案二里就是这么做的,效果比全量保留好得多。

5.3 频繁压缩:越压越乱

有人觉得"小步压缩"更安全,于是每几轮就压一次。实测这是灾难。频繁压缩会让代理不断重新理解状态,每次理解都有微小偏差,偏差累积起来就是目标漂移。我有一组记录,每 5 轮压一次,跑到第 40 轮时,代理已经在做一个完全无关的优化了。

压缩频率的经验值是:在任务边界压缩,而不是按轮次压缩。一个子任务做完,状态干净,这时候压最安全。如果任务很长没有明显边界,就按"完成一个可验证动作"来切分边界。

6. 10 天实验后,我留下的几条硬经验

跑完 430 条记录,最大的体会是:上下文压缩的接续问题,本质是状态管理问题,不是压缩算法问题。你把压缩算法换得再先进,只要状态没交接好,代理照样断片。反过来,状态交接做好了,用最朴素的摘要压缩也能跑得不错。

几条可以直接拿去用的经验:

第一,状态快照要外置、结构化、第一人称注入。这三条缺一不可。外置保证它不被压缩,结构化保证代理能解析,第一人称保证代理把它当自己的记忆而不是参考资料。

第二,压缩时机选在任务边界。宁可多等几轮,也不要在任务做到一半时压缩。边界压缩的接续失败率比随机压缩低一半以上。

第三,明确告诉代理"什么别做"。已完成文件、已验证假设、已失败尝试,这三样列清楚,能省掉大量重复劳动。代理不是不聪明,是没人告诉它边界在哪。

第四,别追求摘要的信息保真,追求状态保真。摘要写得再详细,只要状态标记错了,就是负资产。我现在的做法是摘要尽量短,状态快照尽量准。

第五,engram 式锚定要配再压缩。锚定片段会累积,不处理迟早撑爆窗口。定期把老锚定片段合并进状态快照,是长任务能跑下去的关键。

最后分享一个我实验里的小技巧:在状态快照里加一个confidence字段,记录代理对当前状态的置信度(高/中/低)。当置信度是"低"时,压缩后第一条消息里加一句"如果不确定,先读取相关文件确认,不要直接动手"。这一句话让低置信场景下的接续失败率又降了一截。代理在不确定时最怕的就是硬着头皮干,给它一个"可以先确认"的许可,它反而更稳。

这套东西我还在继续跑,后面想试试把状态快照做成可查询的存储,让代理在需要时主动"回忆"某个片段,而不是每次压缩都全量注入。等有新结果再聊。

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

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

立即咨询