Agent Harness 学习
← 返回对照矩阵

context compaction

上下文

上下文压缩

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

横向读这一行

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

对比
openJiuwen带代码

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

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

def _trigger_token_threshold(self, context: ModelContext) -> int:
    budget = effective_context_budget(context, model_config=self._round_config.model)
    return max(int(budget * self._trigger_context_ratio), 1)

# —— 调用处(同文件 222-228 行)——
trigger_threshold = self._trigger_token_threshold(context)
if total_tokens >= trigger_threshold:
    logger.info(
        f"[{self.processor_type()} triggered] estimated context window tokens {total_tokens} "
        f"reaches trigger_context_ratio {self._trigger_context_ratio} threshold {trigger_threshold}"
    )
    return True
openjiuwen/core/context_engine/processor/compressor/round_level_compressor.py:1159-1161@ fd6c47854201
Codex带代码

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

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

/// Runs token-budget inline auto-compaction as a normal compaction lifecycle.
///
/// Token-budget compaction skips model/server summarization and installs a fresh context window
/// instead. It is still modeled as compaction so compact hooks and `ContextCompaction` turn items
/// observe the same lifecycle as local or remote compaction.
pub(crate) async fn run_inline_auto_compact_task(

// —— 配置层(core/src/config/mod.rs 621-627 行)——
/// Token usage threshold triggering auto-compaction of conversation history.
pub model_auto_compact_token_limit: Option<i64>,
/// Controls whether `model_auto_compact_token_limit` applies to the full
/// active context or only tokens after the carried compaction-window prefix.
pub model_auto_compact_token_limit_scope: AutoCompactTokenLimitScope,
codex-rs/core/src/compact_token_budget.rs:47-52@ 4beea50e26dd

阈值细分到每个 provider + model

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

const modelPolicy: z<ModelCompactPolicyConfig> = z.object({
  provider: z.string().required(),
  model: z.string().required(),
  thresholdRatio: thresholdRatioSchema,
  retainRatio: retainRatioSchema,
  retainTokens: retainTokensSchema,
  summarizationProvider: summarizationProviderSchema,
  summarizationModel: summarizationModelSchema,
  maxTokens: maxTokensSchema,
  compactionRetries: compactionRetriesSchema,
  maxOverflowRetries: maxOverflowRetriesSchema,
packages/compaction/compaction-basic/src/index.ts:82-92@ b150a551b8d4
Pi带代码

一行公式,全局单一设置

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

/** Return whether context usage exceeds the configured compaction threshold. */
export function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean {
	if (!settings.enabled) return false;
	return contextTokens > contextWindow - settings.reserveTokens;
}

/** Default compaction settings used by the harness.(同文件 157-162 行)*/
export const DEFAULT_COMPACTION_SETTINGS: CompactionSettings = {
	enabled: true,
	reserveTokens: 16384,
	keepRecentTokens: 20000,
};
packages/agent/src/harness/compaction/compaction.ts:246-250@ bfb004d4418f

design space / 设计空间分析

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

阈值粒度这条轴上,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 两条轴都不做,用一个固定公式把问题留给使用者去调参数而不是去读多套机制。这段是本站的编辑推断,不是各项目的官方表述。

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

back to course / 回到课程

矩阵展示的是「各家怎么做」。这个问题本身为什么存在、有哪些经典权衡,在课程里讲:

4

上下文工程

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