Agent 工程对照
← 返回框架对照

上下文 · 第 4 章

上下文压缩

上下文快满了,谁在什么阈值上、用什么策略腾出空间?

压缩机制的复杂度差别很大:Pi 只有一条全局公式,Codex 并行维护三条压缩路径。

压缩机制的复杂度

  1. 一条全局公式
  2. Pi
  3. openJiuwen
  4. DeepSeek
  5. Codex
  6. 多条并行路径

定性示意:只表示先后,间距没有数值含义。

场景建议

编辑判断

按你的需求,倾向哪种做法

借鉴的是那一家的设计,不等于选用那个框架。

场景

要接很多家模型

DeepSeek 按模型给阈值和保留量;openJiuwen 用预算比例,换模型不必改配置。

场景

厂商已经报了上下文超限,还要救回来

它把 context-overflow 单列为一种触发,绕过阈值和保留策略、强制做一次有效削减。

场景

压缩后不想丢掉 hooks 与事件的一致性

连「不摘要直接换窗口」这条路径都走完整的压缩生命周期,compact hooks 看到的事件是一致的。

容易踩的坑

  • 阈值放在哪一层,决定了 Harness 能服务多少种模型——先想清楚要支持几个模型,再选复杂度。

四家怎么做

差异与源码

openJiuwen

按上下文预算比例触发,挂多档压缩器

阈值是「有效上下文预算 × trigger_context_ratio」,即按比例而非绝对 token 数,所以换模型时不必改配置。压缩器不止一个:round_level(轮级)、current_round(当轮)、micro_compact(工具结果微压缩)、full_compact(整体压缩)、dialogue(对话压缩)各自实现 trigger 判断,组成一条处理器链。

适合
要换模型却不想改配置:阈值是预算的比例,不是绝对 token 数。
代价
五种压缩器组成处理器链,谁在什么时候触发要逐个读 trigger 才搞得清。
Codex

三条并行路径:本地摘要 / 远端摘要 / 直接换新窗口

最复杂的一份。除了常规的本地摘要压缩(compact.rs)和远端压缩(compact_remote.rs 及 v2),还有一条 token-budget 路径:它跳过模型摘要,直接装上一个全新的上下文窗口,但仍然走完整的压缩生命周期,让 compact hooks 和 ContextCompaction 回合项看到一致的事件。配置层还能指定阈值作用于「整个活动上下文」还是「压缩窗口前缀之后的部分」。

适合
对长会话质量要求极高、并且愿意为此维护多条路径的产品。
代价
本地摘要、远端摘要、直接换窗口三条路径都要走同一套生命周期,测试面最大。

此后的变化

  • 2026-W41 →

    新增默认关闭的 retain_client_tool_outputs:调用方标记 retain 的工具输出在远端压缩 v2 与换新窗口时整条保留,紧急裁剪停在它前面,与既有保留内容共用 64000 token 预算,超出时整条丢弃而不截断。

  • 2026-W38 →

    新增 model_post_turn_compact_threshold_percent 配置项,在最终响应之后、上下文占用达到配置百分比时触发一次性压缩(#46541),有输入待处理、回合被取消或开启 token 预算模式时跳过,审查者(approval reviewers)角色也跳过以免拖慢审批完成。本格此前的三条并行摘要路径(本地摘要/远端摘要/直接换窗口)之外,新增了一个与压缩策略本身解耦的「何时触发」开关。

  • 2026-W35 →

    压缩不再使已有的 Guardian 授权失效:授权挂在独立的「宿主用户消息修订号」上,压缩与内部上下文注入不推进该修订号。本格此前只描述压缩对上下文的影响,未涉及它对授权状态的副作用。

DeepSeek Harness

阈值细分到每个 provider + model

压缩策略是一份按「厂商 + 模型」索引的配置表:每个模型各有自己的 thresholdRatio(何时触发)、retainRatio 与 retainTokens(保留多少尾部)、专用的摘要模型,以及重试与溢出重试次数。触发分两种:pressure(步骤边界的正常压力)和 context-overflow(厂商已确认超限的补救);后者绕过阈值和保留策略,强制做一次有效削减。

适合
同时服务多家厂商、多个模型,每个模型窗口和摘要成本差异很大。
代价
要维护一张按厂商 + 模型索引的策略表,新增模型就要补一行配置。
Pi

一行公式,全局单一设置

整个判断只有一个减法:当前上下文 token 超过「窗口大小减去预留」就压缩。预留 16384、保留最近 20000 token,全局固定,不区分模型。对照 DeepSeek 的按模型策略表,可以清楚看到同一个问题在「服务一种用法」和「服务任意厂商任意模型」两种目标下的复杂度差距。

适合
只跑一种用法、模型固定,压缩规则一眼能看懂最重要。
代价
全局一条公式,不区分模型;预留与保留量对不同窗口不一定合适。
横向读这一行

复杂度呈明显阶梯:Pi 只有一条全局公式,openJiuwen 按比例触发并挂多档压缩器,DeepSeek 把阈值细分到每个 provider+model,Codex 则并行维护本地摘要、远端摘要和「不摘要直接换新窗口」三条路径。阈值放在哪一层,直接反映了这个 Harness 预期服务多少种模型。

设计空间分析

四家在「什么时候压缩」上给出了从全局一行公式到按厂商+模型建配置表的粒度光谱,在「压缩这件事本身该有几条路径」上又分出另一条轴,两条轴并不重合。

阈值粒度这条轴上,pi 在一端:shouldCompact 只做一次减法,contextTokens > contextWindow - reserveTokens,reserveTokens 与 keepRecentTokens 分别固定在 16384 和 20000,全局单一设置,不区分模型。openJiuwen 往前走了一步——阈值不是绝对 token 数,而是 effective_context_budget(context) × trigger_context_ratio,按预算比例算,换模型时数值自动跟着预算变。deepseek harness 在另一端:ModelCompactPolicyConfig 是一份按 provider + model 索引的表,每个模型各自维护 thresholdRatio、retainRatio、retainTokens、专用的 summarizationModel,以及 compactionRetries 与 maxOverflowRetries。

压缩路径的复数化是另一条轴,与阈值粒度无关。openJiuwen 把压缩拆成一条处理器链:round_level(轮级)、current_round(当轮)、micro_compact(工具结果微压缩)、full_compact(整体压缩)、dialogue(对话压缩)各自实现 trigger 判断。codex 有三条并行路径:本地摘要(compact.rs)、远端摘要(compact_remote.rs 及 v2),以及一条 token-budget 路径——它跳过模型摘要、直接装上全新上下文窗口,但源码注释明确写着这条路径仍然「modeled as compaction」,走完整压缩生命周期,让 compact hooks 和 ContextCompaction 回合项看到与前两条路径一致的事件;配置层的 model_auto_compact_token_limit_scope 还能指定阈值作用于「整个活动上下文」还是「压缩窗口前缀之后的部分」。deepseek harness 在路径这条轴上只分两种触发类型而非两条压缩路径:pressure(步骤边界的正常压力)和 context-overflow(厂商已确认超限的补救),后者绕过阈值和保留策略,强制做一次有效削减。pi 在这条轴上没有分支,只有前面那一行公式。

编辑判断

两条轴分开看,能看出不同框架把复杂度放在了不同的地方:deepseek harness 把复杂度放进「阈值表」,服务多厂商多模型场景下每个模型的窗口大小和摘要成本都不一样,逐个配置是代价;codex 把复杂度放进「路径」,token-budget 这条路径的存在理由(注释直接点明)是让上层看到的生命周期事件保持一致,不必关心底层到底是模型摘要还是换窗口;openJiuwen 把复杂度放进「压缩粒度的处理器链」,轮级、当轮、工具结果、整体、对话分别处理,是另一种切分问题的方式;pi 两条轴都不做,用一个固定公式把问题留给使用者去调参数而不是去读多套机制。

还没有定论:比例阈值、按模型配置表、多路径生命周期统一、单一全局公式这四种设计在真实长会话里对压缩质量(信息丢失量、摘要成本、触发频率)的实际影响,目前没有一手测试数据,以上是结构层面的比较。

这个问题为什么存在

第 4 章

上下文工程

上下文是稀缺资源:腐化、焦虑、压缩与缓存