Agent Harness 源码拆解(四):上下文工程——窗口有限,历史无限,压缩又怕丢真相

上下文工程是 harness 里最拧巴的一块:模型的窗口是有限的,token 成本随历史长度上涨,可关键信息一条都不能丢。这三个约束互相打架,而打架的战场通常在一个叫 compaction(压缩)的机制上。

更麻烦的是,窗口变大不等于可以随便塞。Anthropic 给长上下文里的性能退化起了个名字叫 context rot(上下文腐烂):随着 token 数增加,模型从上下文里准确召回信息的能力会持续下降——这是 transformer 注意力的固有约束(每个 token 与其他 token 是 n² 关系),不是某个模型的 bug。所以上下文工程的核心不是”塞得越多越好”,而是在每个推理步,挑出最可能产生期望行为的那一小撮高信号 token。后面六家的所有机制,本质上都是在做这一个动作。

这一章看六家怎么处理”压缩又不丢真相”。答案分成了两派,而两派其实共享同一个地基。

那个地基:投影,不是改写

先说六家共同的地基,因为它决定了一切。发给模型的上下文是投影,磁盘上的事实日志永不动。压缩、上下文编辑,改的只是”模型可见的那份投影”,原始条目一条都不删。

这个地基带来一个漂亮的心理安全感,上一篇已经埋过:既然模型上下文只是有损投影,那压缩就可以放心大胆地做,甚至做错了也不要紧——真相永远在日志里,大不了重新投影一遍。

丢掉这个地基的反面模式是:图省事直接删消息。短期省了 token,但把一个会出错的摘要变成了不可校验的第二真相——从此再也无法回放审计,崩溃后恢复出来的也是失真的历史。Maka 把这条说得很重:checkpoint 是物化视图,不是 WAL 截断——它可以从源日志重建,但没资格宣布源日志过时。

pi:三级膨胀防线,加一个”增量 diff”的巧思

pi 把上下文膨胀的防线分三级:超长的先截断 → 超过阈值触发 compaction(老消息摘要 + 保留近期原样)→ 还不够就溢出恢复。

但真正有辨识度的是它的系统提示增量 diff。动态 harness 里系统提示会变(工具激活了、skill 列表变了、切了模型),如果每轮重发整串提示,provider 的前缀缓存立刻失效、成本翻倍;如果只改内存里一份”当前提示”,transcript 就不再自解释——回看一条旧消息,无法还原”当时模型看到的规则是什么”。

pi 的解法:系统提示不是字符串,而是 transcript 里的 system 消息序列。首轮写全量,之后每轮算出 patch(null 表示删除某 section),只把变化的 section 作为新 system 消息追加,按序重放即还原。一石二鸟:缓存友好的最小变更 + 历史可重放。

代价:这套增量 diff + 三级投影管线,是 pi 上下文代码里最复杂的一块。为了”既能激进压缩、又能完整审计”,它付出的复杂度都在投影层。

opencode:把 system prompt 当缓存经济学问题

opencode 做了一件同类没做的事:把 system prompt 当作 provider-cache 的经济学问题来设计。核心矛盾是——环境事实(工作目录、日期、AGENTS.md)会变;system prompt 必须放最前面才能当缓存前缀;每轮重算 system prompt 就打穿缓存。

它的解法叫 Context Epoch:基线在一个 epoch 内逐字节冻结并持久化,环境变化以对话中段的 system 消息追加。三条铁律:基线逐字节冻结(跨进程重启复用,前缀不变 = 缓存命中);环境变化追加 durable system message,而不是重渲染前缀;只说新值不说旧值(示例对话里明文规定)。

配套的还有一个通用模式:懒采样,绝不异步推送。”Context changes are sampled and admitted lazily at a Safe Provider-Turn Boundary, never pushed asynchronously.” 文件 watcher 不打断执行、不唤醒空闲会话,所有对账发生在本来就要调 provider 的时刻——避免”watcher 事件风暴打断正在跑的 turn”。

CodeWhale:缓存 miss 必须可命名

CodeWhale 把缓存从优化项升级成了系统级契约,写成一条不变量:

会话开始后,系统提示和工具目录是冻结字节。历史只增。缓存 miss 只允许发生在”能说出为什么”的时候。

它最狠的一招在不重新锚定:任何没声明原因就改变头部字节的行为,都算 drift——保持原来的 pin 不变(让同一个未声明的漂移继续被计为 miss,而不是悄悄变成新基线),/cache stats 显示 WARNING。

它明说拒绝过一个旧行为:”detect-and-report + re-pin on drift”——重新锚定后,/cache stats 看着恢复稳定了,但 provider 侧缓存其实已经死了。指标不能靠迁就现实来变绿。这条的迁移价值极高:缓存退化从玄学变成了可调试事件,关键就是”miss 必须可命名”。

代价:这套契约严格到”灵活性受限”——工具面中途变化、目标切换、会话恢复,每次变更都要记录原因。换来的是缓存这条命脉的可观测。

grok-build:压缩是一台带预燃室的机器

grok-build 的压缩是它工程密度最高的单点之一,核心是”两遍法 + 预燃”。

接近阈值时,它先在后台对早期历史做一遍摘要(pass-1,产出 NOTE₁),真触发压缩时把 NOTE₁ 和近期尾部一起再摘要(pass-2)。预燃的时机是阈值前 10 个百分点,给 pass-1 留出跑完的余量——那个预留值 32768 token 的注释写着”covers p99 of prod output”,是从线上输出分布量出来的,不是拍的。

但两遍法有个致命难题:pass-1 跑的时候对话还在长,等 pass-2 真要用 NOTE₁ 时,被摘要的前缀可能已经变了(用户 rewind、branch、edit)。解法是前缀指纹——pass-1 结束时给被摘要的前缀算一个廉价指纹,pass-2 用之前先比对,对不上就直接丢弃、宁可重算。

这个”后台计算必须配失效判据”的直觉,可以推广到任何”提前算了备用”的东西:预摘要、预渲染、预热缓存,都要回答——怎么知道它过期了。

Maka:压缩是投影,coverage 让摘要可审计

Maka 把问题重新表述为:”在保留完整事件事实的同时,如何为下一次模型决策计算一个更小的续传视图?”它用三个对象分清楚:

  • Runtime Events 日志:用户/模型/工具的语义事实——是真相,不丢细节;
  • Compact Checkpoint:续传摘要 + 已验证前缀的 coverage——是 durable 投影,会丢细节;
  • Provider Request Messages:本次调用消费的工作上下文——是临时投影。

Checkpoint 必须绑定”覆盖了哪些有序事件、through 边界在哪、源 digest 是什么”,这样摘要才可以被审计。压缩失败时退回源推导的安全上下文,而不是发明摘要(fail open 到源)。LLM 只生成摘要值,Runtime 拥有 coverage、durable、重放资格的全部决定权。

如果你要处理上下文

  • 小 harness:只要一级截断 + 一级压缩,别把三级防线全搬来。
  • 要省钱:必须做缓存前缀稳定——学 pi 的增量 diff 或 opencode 的 Context Epoch,稳定内容冻结、易变内容追加。
  • 要审计、要崩溃恢复:必须做投影与事实分离——学 Maka 的三对象模型。
  • 长会话、缓存是命脉:学 CodeWhale,把”miss 必须可命名”写进不变量。

四个最容易踩的翻车点:

一是删消息代替压缩。把摘要当真相落盘,短期毫无问题,直到你需要回放审计,发现原始数据早没了。

二是缓存前缀每轮重算。系统提示、工具表每轮重渲染,等于把最长的稳定前缀每轮重新计价。

三是用过期摘要。后台预计算的摘要没配失效判据,用户 rewind 之后还在用旧 NOTE₁(grok 用前缀指纹防这个)。

四是指标靠重锚定变绿。缓存漂移了就重新锚定,指标好看了,provider 侧缓存已死(CodeWhale 明说拒绝)。

下一篇讲 工具系统:工具集怎么管理、怎么不撑爆上下文、工具结果怎么给模型”下一步指引”而不是当日志。


本篇术语

  • compaction(压缩):把老对话历史摘要、保留近期原样,以控制上下文体量;投影层面操作,不删事实
  • 投影(projection):从事实日志现算出来的视图(模型上下文、UI、恢复状态都是投影)
  • prompt cache:provider 对请求前缀没变的部分复用算力,前缀变一个字节缓存失效
  • 增量 diff:系统提示只发变化的部分,而不是整串重发,保持缓存前缀稳定
  • Context Epoch:一个 epoch 内基线逐字节冻结,变化以中段 system 消息追加
  • 前缀指纹(prefix fingerprint):给被摘要的对话前缀算一个廉价指纹,用于判断后台预计算是否过期
  • coverage:checkpoint 声明”覆盖了哪些有序事件”,让摘要可审计

参考资料

参照系仓库(结论基于 2026-09 下旬至 10 月上旬各仓库 HEAD):

  1. earendil-works/pi —— system-prompt.ts 增量 diff、三级投影管线
  2. anomalyco/opencode —— Context Source / Context Epoch
  3. xai-org/grok-build —— session/compaction.rs 两遍压缩与 prefire
  4. zai-org/ZCode
  5. apache/maka —— history-compaction.ts 三对象模型
  6. Hmbown/CodeWhale —— docs/CACHE.md 缓存契约

Harness 设计方法论文献:

  1. Anthropic,《Agent Harness Design: 3 Patterns for Harnessing Claude’s Intelligence》
  2. Anthropic,《Effective context engineering for AI agents》(2025-09)——context rot、注意力预算、just-in-time 检索的官方论述,本篇”挑出高信号 token”框架出处
  3. OpenAI,《Prompt Caching》——前缀匹配与缓存折扣的官方口径,佐证稳定前缀冻结