
上周在一个重构分支上和 Agent 结对写代码。
前面二十多轮对话都很顺畅:我们一起排查了旧架构里的并发锁问题,推翻了用全局 Mutex 的做法,明确把状态收敛到了独立的 Actor 结构里,并且把几个关键边界条件测得干干净净。
到了第 28 轮,屏幕上跳出了一行轻微的提示:Context compressed (compaction triggered)。
紧接着,当我让它继续补全下一个接口的持久化逻辑时,它非常自信地给出了一个全新的补丁——直接在全局作用域里新建了一个 Mutex,把我们两个小时前花大把时间否决并删掉的代码,一字不差地又写了回来。
很多人以为这是大模型的注意力衰减。只要看一眼它在压缩那一刻到底干了什么,就会发现根源很纯粹:它把至关重要的技术决策,当成普通对话摘要给抹平了。
为什么普通的“聊天摘要”救不了长会话?
在很多简易的 Agent 实现里,上下文超标后的处理逻辑通常很粗暴:
把之前的几十轮对话打包,调用一次大模型,附上一句提示词:“请总结以上对话的主要内容,保留重要信息”。
这种做法用来处理客服聊天或者日常问答或许够用,但在严肃的写代码场景里,它几乎是一场灾难。
一段 30 轮的 Coding 会话中,通常包含两类完全不同的信息:
- 高体积、低价值的即时噪音:用
grep扫出的两百行搜索结果、测试失败抛出的大段栈追踪、读取配置返回的冗长文本。 - 低体积、高致命度的决策断言:明确否决的死胡同、特定依赖的规避要求、统一的接口契约约束。
大模型做平铺摘要时,往往花大段篇幅描述“排查了哪些文件、跑了什么测试”,而把那些一两句话的硬性约束稀释成轻描淡写的“讨论了重构思路”。
等新的轮次开始时,Agent 看到的是被磨平棱角的概括。被验证并明确否决的坑,在它眼里重新变成了“未探索的合理方案”。
生产级 Compaction 的本质是“换班交接”
成熟的 Coding Agent 处理上下文压缩时,早已抛弃全文缩印思路。
生产级 Compaction 必须是一份严谨的工程交接简报(Handoff Briefing)。

接班的系统不需要关心上一班执行了多少次临时命令、翻查了哪些无关文件,它必须立刻接管的核心事实只有三样:当前系统的稳定状态是什么、哪些已被证伪的方案绝对不能碰、下一步待办的具体边界在哪里。
在工程实现上,成熟的交接机制包含四项设计:
1. 独立的 Summarizer 身份与调用
压缩上下文不能在原有的会话流里顺手完成。
在触发 Compaction 时,Harness 会向模型发起一次完全独立的、无状态的单次请求。这次请求不继承前面臃肿的历史,而且它的 System Prompt 会被切换为专用的总结指令:
“你是一个上下文精简与交接助手。你的任务是从以下历史中提取出供下一位工程师接手时必须掌握的硬性上下文。”
通过剥离原有的 Coding 角色,模型才能站在第三方视角客观评估哪些是临时操作,哪些是长期资产。
2. 结构化的交接分区模板
摘要绝不能是一段自由发挥的散文。一份合格的交接简报必须被严格约束在结构化的 Markdown 字段中:
[当前核心目标 Goal]
- 本次会话正在解决的核心问题与交付定义。
[架构决策与硬性约束 Key Decisions & Constraints]
- 已经确定采纳的设计模式与状态流转规则。
- 已被证明不可行并明确放弃的路径(Negative Constraints)。
- 必须遵守的命名、类型与安全约定。
[已完成改动与现状 State & Progress]
- 哪些文件已被修改/新增(附带关键导出符号)。
- 当前代码库的可用状态(哪些测试已通过)。
[未解决工作与下一步 Next Steps]
- 尚未处理的分支与待办清单。
其中最关键的一栏就是被放弃的路径。只有把“什么不能做”显式固化下来,Agent 才不会在失忆后把旧坑重新踩一遍。
3. 保留最近 N 轮的原始上下文
交接时必须在末尾保留局部缓冲区。
在实际执行中,Harness 会在当前对话末尾划定保留区(通常是最近 5 到 15 轮对话,约 2 万 Token 预算)。
[系统提示词 + 工具定义]
[Compaction 生成的结构化交接简报]
[保留下来的最近 10 轮完整交互原始记录]
[用户刚刚发出的新指令]

结构化简报负责锁定全局宏观目标与架构约束,未经压缩的最近几轮记录则保证当前修改的函数细节不会发生语境断裂。
4. 彻底剥离工具调用的垃圾输出
在长会话中,占据最多 Token 的从来不是人类和 AI 说的自然语言,而是各种工具的执行结果——动辄几千字符的 cat 源码、冗长的编译日志、复杂的 JSON 响应。
在准备交接数据时,底层的序列化模块应该主动过滤掉这些已经被消费过的中间产物,只留下“调用了什么工具、得出了什么关键结论”,把高熵的垃圾信息直接物理清除。
日常开发中,你可以怎么防范?
日常开发中,有三条低成本习惯能减少压缩失忆:
- 把关键决策写在文件里,不要只留在对话框里。如果某个技术约束非常重要(比如“某函数不要用多线程”),把它作为一条注释写在代码上方,或者记在仓库的规范文件里。因为代码文件是 Agent 随时可以通过工具读取的物理事实,它永远不会被 Compaction 冲掉。
- 在会话开始时声明不可逾越的边界。给任务设定清晰的 Scope,让 Agent 明确知道哪些模块不能动。
- 当长任务跨越阶段时,主动开新会话。如果一个 Feature 完成了接口定义并通过了测试,最稳妥的做法是在 Git 里提交一次干净的 Commit,然后开一个全新的会话接力。拿着明确的代码变更作为新起点,比守着一个聊了 50 轮的臃肿会话可靠得多。
上下文窗口再大,也敌不过无序的遗忘;在长程工程里,精确的交接永远比盲目的堆积更管用。