章节导航 第 15 章 / 共 21 章
上下文管理与 Compaction
为什么需要 Compaction、三层架构、内置策略、训练一致性、自定义策略开发
问题
Agent 的多轮对话会不断增长。当对话超过 LLM 的上下文窗口(如 128K tokens),推理会失败。
简单的截断会丢失关键信息。Nexus 的 Compaction 系统提供了更智能的方案。
这里需要先分清一件事:压缩的代价至少有四种。
- 信息损失——丢掉的内容让 Agent 答错;
- 重获取成本——丢掉的内容让 Agent 再调一次工具去取回来,省下的 token 被重新花掉;
- 前缀缓存失效——压缩改写的是提示词前缀,而 agent 流量里绝大部分 prompt token 是重读;前缀一改,原本命中缓存的重读部分要按全价重新 prefill;
- 来源摧毁——压缩把带类型的对话压成扁平文本,“这句话是谁说的”这一信息随之消失;而它正是所有授权判断的依据。
第四种与前三种不同质:前三种是效率与正确性代价,第四种是安全代价。arXiv 2610.29869(2026-10-21,《Compaction Laundering: Provenance Destruction in Context-Compacting Coding Agents》)把它定性为可利用的信任边界:harness 把带类型的对话扁平化成单个字符串、用 [User]: 之类的文本标签标注每条消息,这一步在摘要器运行之前就在代码层面摧毁了消息来源(provenance)。于是”能控制 agent 所读到的任何内容”的攻击者可以伪造一条 user turn,且它在压缩后作为真实指令存活。实测规模:跨 5 个扁平化 harness(pi、opencode、Cline、Aider、OpenHands),伪造的批准洗进 agent 记忆的成功率是 87–100%,上下文窗口大到约 908,000 token;跨 4 个角色化 harness(Codex、Gemini CLI、Qwen Code、Continue),同一攻击成功率 0%。论文还把攻击追到了执行后果:一条被洗白的长期批准驱动真实的工具运行 agent 在沙箱里执行了攻击者控制的 curl | sh;在 live pi TUI 上端到端约 50%(对照的干净臂 0%)。
决定性因素不是压缩算法,而是 harness 是否保留角色类型——这条判据可以直接用。对 Nexus 的含义:CompactionStrategy.compact() 的入参是 messages(本章 §自定义策略 给出了签名),只要压缩前后保持结构化 role 而不是拼成字符串,就站在 0% 那一侧。这是一条零成本的架构约束,且必须在写压缩器之前定下来——事后改要动所有已落地的策略。另注意 OpenHands 在 87–100% 那一组,而它正是 Ch12 的对标对象。(数字均为 abs 页摘要级,未回 HTML 正文主表 📌。)
第二种代价容易被忽略,却在最坏情况下能抵消甚至超过压缩本身省下的量。对工具密集的 Agent(Nexus 的 ToolExecutor 每轮可能产生多个大体积 tool message)尤其如此。本章后面「第四个维度:压缩的下游代价」一节会给出它的量化形式。第三种代价不是纸面推论:Claude Code 在 v2.1.275(2026-09-17)修的正是这一条——官方原文是「a restored memory file’s age note changing between requests after a compaction or resume, which caused prompt cache misses」。这是生产环境里真实发生过的 cache miss,不是论文模拟。
还要先限定一句,它会影响全章的论证走向:避免上下文溢出只是压缩的一部分理由。AutoCompact(arXiv:2610.02163,2026-10-01)在 256K 窗口下取得 +9.2 个绝对百分点的增益,而论文明确写这个窗口 never overflows——压不压都不是为了装得下。它的解释是:过期的探索历史本身会伤害后续决策,哪怕它装得下。换句话说,压缩同时是思维工具,不只是内存管理。本章后文凡是出现「为避免溢出而压缩」的表述,都应在这个更宽的口径下读。
三层架构
CompactionManager(编排层)
│ "什么时候做 compaction"
▼
CompactionTrigger(触发层)
│ "当前对话是否需要 compaction"
▼
CompactionStrategy(策略层)
│ "怎么压缩对话"
▼
压缩后的 messages
CompactionTrigger
监控 token 用量,决定是否触发:
class CompactionTrigger:
def should_compact(self, messages, tools, token_count) -> bool:
# 当前 token 数 > 阈值 → 触发
return token_count > self.threshold
这一行是全章最容易被读轻的地方:它把「要不要压缩」压成了一个标量与一个常量的比较。生产级实现里,触发至少有四个判据,而且每个判据都有自己独立的失效模式:
| 判据 | 平台侧实证 | 失效模式 |
|---|---|---|
| token 数 > 阈值 | 通用 | 阈值必须是 per-model 参数而非全局常量 + 规范化键 + 失配告警(三件套,见下)(v2.1.288,2026-10-02:/autocompact 改为按模型保存自动压缩窗口);token 估算误差会被直接放大到触发时机(v2.1.273,2026-09-15:context meter 与 auto-compact 把 advisor-tool turns 按约 2 倍计,导致在真实窗口约一半处就触发) |
| 还能不能被客户端安全分类器审查 | v2.1.288(2026-10-02) | 分类器的容量上限是独立于上下文窗口的第二条触发线:auto mode 下对话长到分类器无法审查时改为压缩,而不是每个工具调用都去问一遍或直接失败 |
| 最近一次回复上报的 token usage | v2.1.288(2026-10-02) | 上报为 0 会静默关掉触发,长对话直接报 Prompt is too long 而不是自动压缩 |
| 是否存在可完整摘要的 exchange | v2.1.269(2026-09-11) | 没有可完整摘要的前序 exchange 时,会话永久卡在 Prompt is too long |
per-model 只是三件套的第一件。 上面”阈值必须是 per-model 参数”这条主张是对的,但它自带一个失效模式:一旦按模型名做键,模型名的规范化就成了正确性前提。两条独立实证:
- v2.1.305(2026-10-20):“Fixed
/effortand/autocompactsettings not applying when the model name differed from amodelOverridesvalue by letter case or a[1m]tag”——模型名与modelOverrides值仅大小写不同或带[1m]标签时,设置直接不生效。 - v2.1.302(2026-10-16):“Fixed
/autocompactand/effortsaving to the wrong model when a gateway names Opus 4 or 4.1 with a prefix, such asanthropic/claude-opus-4-1”——网关用前缀命名模型时,设置被存到了错误的模型下。
因此本章的建议应写成三件套:per-model 存储 + 规范化键(大小写 / 标签 / 网关前缀统一处理)+ 失配时显式告警。只做 per-model 会把”全局常量错”换成”静默存错地方”——后者更难发现,因为配置看起来是对的、也在生效,只是生效在别的模型上。
还有一条边界情况:新环境的首次请求、或换模型之后的那一次请求,用的是内置窗口与输出上限而不是服务端下发的(v2.1.288,2026-10-02),该请求最多等待 1.5 秒——窗口未知时的引导问题也要算进触发器的健壮性。
把这些列出来的意义不在照抄,而在指出一个方向:触发失败不是「那就先不压」,而是直接变成硬错误。上表四条里有三条的失效结局都是 Prompt is too long。触发器的健壮性与压缩策略本身同等重要,而 §CompactionStrategy 那 968 行里没有一行是为它写的。
CompactionStrategy
实际的压缩逻辑。compaction_strategy.py 有 968 行——这是整个 compaction 目录里最大的文件。
内置策略:
| 策略 | 注册名 | 方法 | 适合场景 |
|---|---|---|---|
| Keep Recent | keep_recent | 保留 system + 最近 N 条消息 | 简单快速,信息损失大 |
| Summary | summary | 用 LLM 总结历史,替换为摘要 | 信息保留好,但增加 LLM 调用 |
| Rolling Summary | rolling_summary | 增量式 LLM 总结 | 在 summary 基础上避免重复总结 |
| RL-learned | (实验性) | 通过 RL 训练 compaction 策略模型 | 高保真压缩,但训练成本高 |
这张表有一个空格:keep_recent / summary / rolling_summary 三条手写策略都不做因果判断,而 RL-learned 那一条要求训练。对 Nexus 这种「不重新训练策略模型」的框架,中间那一格——不训练也能超过手写策略——此前是空的。FOCUS(arXiv:2609.37590,2026-09-29,Microsoft)填的就是这一格:它不问「压缩到多少比例」,而是问「下一步决策因果依赖于哪些过去的交互单元」,保留那些、丢掉其余;完全在测试时进行,无需离线数据采集或微调,架构无关,可以作为模块化压缩层挂在闭源 API 前面。官方摘要报的口径是峰值上下文削减最多 48%、依赖削减 73%,相对不压缩的完整执行任务成功率提升最多 8.9 个百分点。注意最后这个分母:对照是「不做压缩」,不是「别的压缩器」。训练无关是它在工程上最重要的属性——本节其余方案要么靠手写规则、要么靠训练,FOCUS 是唯一可以直接接进闭源链路的一条。
这张表还有第二格此前是空的,FOCUS 填的是”训练无关”,另一格是确定性与可审计性。现有三条手写策略里,summary 与 rolling_summary 都用 LLM 重写内容,keep_recent 直接丢弃——没有任何一条保证”原文不变,且明确告知删了什么”。2610.20116(2026-10-14)的 transparent-compact 填的就是这一格:为开源 Pi coding agent 设计的简单确定性方法,面向轻量 / 本地 / 高吞吐模型,它保证上下文里的 user 与 assistant 原文不被模型或算法改写,并显式标注压缩剥掉了哪些信息。论文对照了 Pi 默认的模型写摘要与 pi-vcc(同为确定性但不逐字保留 prose 的流行方法),基准跑在四个不同性能档的模型上,重点是 Qwen3.8-27B。
把它与上面两条(角色化 harness 攻击成功率 0%、无来源摘要条目驱动错误动作)放在一起读,会得出一条本章此前没写的原则:保留原文结构不只是保真度问题,它同时是防御。三条证据指向同一件事——一旦压缩器重写内容,“谁说的""有没有依据""删了什么”就同时失去了可核对性。
因此对 Nexus 的建议可以写得很具体:keep_recent 之外的策略都应提供”可关闭重写”的选项,并在压缩结果里显式标注被剥掉的部分(本章已有的 metadata / segment 机制可承载该标注)。
RL-learned 策略是新兴的研究方向,不再由人工设计压缩规则和 prompt,而是通过 RL 训练一个策略模型来决定何时压缩、保留什么:
- FoldAct:分离损失计算(summary token 和 action token 分别计算 PPO surrogate loss)+ 全上下文一致性损失,解决梯度稀释和自条件崩溃问题。量化数据:无一致性损失版本 49.6× 加速 + 80.7% 内存削减;完整版 5.19× 加速;长轨迹(10+ 轮)压缩至约 25% 初始长度
- SUPO(ICLR 2026 提交):推导出支持端到端优化 tool-use 行为和 summarization 策略的策略梯度公式,通过 advantage broadcasting 实现长程信用分配
- ActiveContext:解耦式架构——轻量 ContextCurator(7B)通过 RL 学习主动信息熵减,为冻结的 TaskExecutor 策展工作记忆
- CompactionRL(Yujiang Li、Zhenyu Hou、Yi Jing、Jie Tang、Yuxiao Dong;清华大学,前三位作者在 Z.AI 实习期间完成,2026-07-06,arXiv:2607.05378):把 context compaction 纳入 RL 训练,联合优化任务执行与摘要生成,用 token-level loss normalization(解决 summary token 梯度稀释)+ cross-trajectory GAE(跨压缩边界的信用分配)。GLM-4.5-Air(106B-A30B)达 SWE-bench Verified 66.8%(+7.0)、Terminal-Bench 2.0 24.5%(+3.1);GLM-4.7-Flash(30B-A3B)达 56.0%(+5.5)、20.2%(+6.8)。已部署到 GLM-5.2(750B-A40B)的 RL pipeline。
- ContextPilot(Zhuoshi Pan、Qizhi Pei 等 7 位作者;脚注区一级证据
Affiliation: 1 Tsinghua University 2 Tencent Youtu Lab 3 Shanghai AI Lab,2026-08-28,arXiv:2608.28476;代码github.com/Tencent/ContextPilot,权重huggingface.co/collections/panzs19/contextpilot):把上下文管理做成模型习得的行为,而不是框架里的固定策略。三件事——① 扩展工具集(论文工具语义含readChunk/searchChunk/writeMemory,覆盖 global planning、structured long-term memory、soft context offloading);② Context-Aware Partial Rollout:用 context 与 entropy 的变化定位高影响的上下文编辑决策,把探索预算集中到这些分支上采样;③ Snapshot-level Credit Assignment:把所有下游分支轨迹的结果并入动作级 advantage。基座为 Qwen3-8B / Qwen3-14B / Gemma4-E4B-it(长上下文 QA)与 WebSailor-7B / WebExplorer-8B(深度搜索)。主表与 5.2 节数字:仅用 32K 上下文窗口就超过 128K 窗口的基座;ContextPilot-8B-RL 在四个长上下文 QA 基准上平均超过 StateLM-8B-RL 3.55 分,比自身 SFT 版本再涨 3.62 分,在最难的 BrowseComp+ 上 RL 增益达 5.34 分(该基准平均输入约 552K token,NovelQA 约 119K——论文据此指出 RL 收益随任务难度增大);深度搜索上在两个基座上平均超过 SUPO 1.51 分。token 效率:取 ≥15 轮的轨迹看每轮输入,WebExplorer-8B 在 BrowseComp 上近线性增长到约 30K,ContextPilot-8B 稳定在约 8K–10K
- AutoCompact(arXiv:2610.02163,2026-10-01):把
compact()做成 agent 动作空间里的一个动作,训练它决定何时压、保留什么工作态、压完之后怎么继续。训练数据的构造方式值得单独记一笔——先跑基座 agent,用一个 judge 在线审阅它的压缩决策、摘要与压缩后的动作,把有问题的替换成修正版再放进环境里执行,于是每条轨迹都从被修正过的决策继续;这些轨迹先做 SFT,再用二元任务成功奖励做 RL,奖励里没有任何 compaction-specific 项。基座 Qwen3-Coder-30B-A3B-Instruct:SWE-bench Verified 绝对 +9.2 个百分点、SWE-PolyBench Verified 绝对 +5.0。行为侧的读数更能说明问题:SFT 后 agent 已在 44.3% 的任务上主动调用compact(),RL 后升到 58.5%,同时摘要遗漏关键状态的比率从 3.1% 降到 0.2%、遗漏下一步动作的比率从 8.2% 降到 2.2%。两个方向同时改善——用得更多,错得更少。
需要把一处可能的误读挡在前面。AutoCompact 的 Introduction 把 CompactionRL 明确归为 length-triggered 一类并点名批评,原文是:这类方法只在剩余上下文预算跌破固定阈值时压缩,“ties compaction to context length rather than task progress: stale exploration accumulates until the threshold is reached, and compaction may then occur in the middle of an unresolved stage whose evidence is still needed”。本章上一条刚把 CompactionRL 作为工业部署案例写入,两处看似打架,其实是触发时机归谁决定这一件事的两种答案:归框架(阈值,与任务进度无关)还是归策略(学出来的,随任务进度走)。CompactionRL 的价值在于它把压缩接进了 RL 的目标函数并已落地到 GLM-5.2;AutoCompact 的价值在于它把「何时压」本身也交给了策略。两者不是谁推翻谁,而是同一个坐标轴上的两个位置。顺着这条轴,本章后文「手写规则是框架决定何时压,ACM 是 Agent 决定何时压」那条二方对照可以升级成三方:框架阈值 / 可学习策略 / 固定 rubric。
RL-learned 策略已开始进入工业部署(CompactionRL 已集成到 GLM-5.2 的 RL pipeline),但尚未成为通用可配置的 compaction strategy。但其对训练一致性的影响深远——FoldAct 的分离损失计算直接解决了 policy-dependent compression 的 RL 训练不稳定问题(详见下文训练一致性扩充)。
RL-learned compaction 的应用场景也正从”对话历史压缩”扩展到更广的维度:
- 检索观察压缩:RECON(arXiv:2510.10448)在 RL-trained search agent 的每次检索后插入 learned summarizer,压缩器在 RL 训练期间保持冻结以维持策略稳定。集成进 Search-R1 流水线后,总上下文长度减少 35%,训练速度提升 5.4%,推理延迟降低 30.9%,平均 exact-match 在 3B agent 上 +14.5%、7B agent 上 +3.0%
- 记忆构建:LazyMem(arXiv:2607.22690)提出 query-time memory construction——写时存原始消息,查询时才用 4B 小模型做 KEEP/DROP + 压缩决策。在 LongMemEval 上以 213 个 answer-context memory token 达到 0.85 的 LLM-judge 准确率,相比 retrieval-only 少 68.7×、相比最强非 oracle 基线少 21.0×
- 记忆管理 RL 化的两条同期线索(📌 仅登记,未抓全文核数字):Hindsight Memory-PRM(arXiv:2608.29605,2026-08-30,17 位作者)用可审计的 hindsight credit 监督记忆管理;Selective Forgetting(arXiv:2608.28978,2026-08-29,3 位作者)用图基遗忘做长期 Agent 记忆修剪。两者与 RECON / LazyMem 同属”把记忆的留弃交给学习”这一方向
summary 策略的 prompt:
Please summarize the above history concisely.
Target length: approximately {ratio}% of original length.
Preserve key facts, decisions, tool call results, and context
needed for continuing the conversation.
Focus on tool call and tool response.
这段 prompt 缺一类东西,而补它的成本是零。 上面列的 key facts, decisions, tool call results 并没有把约束、禁令与权限声明单独点出来。2606.22528 给出的因果非常直接——约束在摘要中存活时违规率仍为 0%,被丢弃时达 38%,且论文的免训练缓解 Constraint Pinning 做的事就是把治理约束从有损压缩里隔离出来。因此最低成本的改动是在压缩 prompt 里显式加一条”保留所有约束、禁令与权限声明”:不改架构、不改策略,只改一句提示词,就把合规类内容从”可能被当成普通事实丢掉”变成”被点名要求保留”。
(更硬的做法是像 Constraint Pinning 那样把这类内容隔离出压缩流程,而不是指望 prompt 记住它——本章 §自定义策略 的 register_compaction_strategy 足以承载这一层。)
训练一致性
Compaction 对训练数据有重大影响——压缩后的对话和原始对话不同。如果训练时的 compaction 行为和推理时不一致,模型会学到错误的分布。
基础保障
⚠️ 先写一条前提:下面两条保障都依赖”压缩产物是一段可枚举的离散 token 序列”。 2610.11287(2026-10-08)的 REMORY 代表的正是不满足这个前提的另一条路:它在文本摘要之外补一段有界的软记忆 token 序列——网络学会生成让冻结的 LLM 逼近”用完整历史时的续写”的 token,这些 token 条件化于摘要并接在摘要之后,形成序列维度上的残差连接。实测:SummHay 上在洞察覆盖率几乎不变的前提下改善来源归因,用 5.2% 的输入位置逼近全上下文联合分数;Qwen3.8-27B 与 GLM-5.3-Flash 在长程 agent 基准上有稳定增益,且两者在 BrowseComp 与 Terminal-Bench 2.1 上重复工具输出与工具错误都显著更少。
这类产物无法回放到文本里——本节下面”HyTuner 记录压缩前后 token 序列、确保训练时能精确复现”的方案对它不适用,因为
prompt_ids/response_ids/response_mask/logprobs记录的是离散 token 序列。因此本章应在训练一致性上留一个边界:一旦压缩产物不再是离散 token 序列,“精确复现压缩前后 token 序列”这条保障即失效,需要另找可复现口径(例如把软 token 的生成网络与其输入一起版本化)。
- HyTuner 集成:CompactionManager 在 compaction 发生时产生新的 segment。HyTuner Processor 记录 compaction 前后的 token 序列,确保训练时能精确复现
- 确定性:相同输入 + 相同 compaction 配置 → 相同输出。Cache 系统也包括 compaction 步骤的缓存
Policy-Dependent Compression 的深层挑战
基础保障解决了”可复现性”问题,但未触及更根本的 RL 训练不稳定性问题——compaction 本身是 policy-dependent 的(摘要内容由当前策略决定),这引入了三个系统性挑战:
1. 分布偏移(自条件崩溃)
Compaction 后的摘要成为下一步推理的输入,而摘要内容取决于当前策略。策略更新 → 摘要变化 → 输入分布偏移 → 策略进一步更新,形成自条件循环。FoldAct 将其形式化为”自条件崩溃”——策略的微小变化通过 compaction 放大为输入分布的巨大偏移。
2. 梯度稀释
在 PPO 训练中,summary token 在整条轨迹中占比很小(通常 < 5%),但其质量直接影响后续所有 action token 的输出。标准 PPO 的 surrogate loss 对所有 token 一视同仁,导致 summary token 的梯度信号被 action token 稀释。FoldAct 的解法是分离损失——summary token 和 action token 分别计算 surrogate loss。
3. 信用分配
多轮 compaction 后,最终结果的奖励如何分配给早期 compaction 决策?这是长程信用分配问题。SUPO 通过端到端策略梯度 + advantage broadcasting 解决:compaction 决策的 advantage 不仅来自当前轮的即时反馈,还传播后续轮次的累积奖励。
解耦方案:ContextCurator + TaskExecutor
ActiveContext 提出了一种架构解耦:轻量 ContextCurator(7B)通过 RL 学习主动信息熵减,为冻结的 TaskExecutor 策展工作记忆。这种解耦的优势是:
- TaskExecutor 的策略保持稳定(不受 compaction 干扰)
- ContextCurator 可以独立升级(不影响主策略)
- 7B ContextCurator 即可匹配 GPT-4o 的上下文管理性能
与 Nexus 的关系:Nexus 的 CompactionManager + Agent 分离与此思路类似,但 CompactionManager 使用手写规则而非 RL 训练。理论上,CompactionManager 可以被替换为一个 RL-learned 的 ContextCurator,通过 register_compaction_strategy 注册为自定义策略。
Agent 自主管理压缩时机(ACM)
与”RL-learned ContextCurator”(训练策略模型)不同的另一条路线是 ACM(Agent-managed Context)——把压缩时机与内容纳入 agent 的 action space,通过 manage_context + query_memory 两个工具让 Agent 自己决定”何时压缩、保留什么”。压缩摘要携带唯一标识关联外部存储的原始消息,可随时回查,声称”无损上下文管理”——原始消息不被永久丢弃。
与 Nexus 的关系:这是比 RL-learned 更轻的落地路径——无需训练策略模型,只需给 Agent 增加两个管理工具,通过 register_compaction_strategy 即可接入。它与 CompactionManager 手写规则形成对照:手写规则是”框架决定何时压”,ACM 是”Agent 决定何时压”。
需要补一句:轻不等于终态。ContextPilot(见上文 RL-learned 策略)表明工具接口可以先定下来,之后要不要用 RL 训练模型来使用这些工具,是独立的第二步——先有 manage_context 式的动作空间,才谈得上训练;工具集设计得越完整(规划 / 长期记忆 / 软卸载),后续训练的上限越高。在 Nexus 里先把工具接口开出来,与日后的 RL 路线并不冲突,反倒是其前置条件。
📌 同期线索登记(2026-10-06,未回源,不作断言):Context Language Models(CLM,2026-10-01)让模型把上下文当文件来编辑,而不只是压缩,并配套 Suffix Cache Reuse 处理「编辑破坏前缀缓存」的问题。它是「agent 自主管理上下文」这一方向更强的形态,与本节 ACM 同源但走得更远。此处只登记方向与来源,不写具体数字,ACM 本身的待验证状态不因这条线索而改变。
自定义策略
from nexus.extensions.compaction.compaction_strategy import (
CompactionStrategy, register_compaction_strategy,
)
@register_compaction_strategy('my_custom')
class MyCustomCompaction(CompactionStrategy):
def compact(self, messages, tools, target_tokens, token_count_fn):
# 你的压缩逻辑
# 返回压缩后的 messages 列表
...
然后在配置中使用:
agent_config:
compaction_strategy: "my_custom"
压缩提示优化
summary 策略的 prompt 是固定的,但压缩质量高度依赖 prompt 的表达。ACON(Agent Context Optimization)提供了一种不修改框架代码、仅优化压缩提示的方法:
ACON 迭代优化流程
- 收集失败案例:找到全上下文成功但压缩上下文失败的案例
- 分析丢失内容:LLM 分析压缩过程中丢失了什么关键信息
- 更新压缩提示:修改 summary prompt,要求保留该类信息
- 迭代直到收敛:重复 1-3,直到压缩准确率达标
- 可选蒸馏:将优化后的提示策略蒸馏到小模型
ACON 实验结果:26-54% 峰值 token 减少,95%+ 任务准确率保留。值得注意的是,ACON 蒸馏到小模型后仍保留 >95% 准确率,且显著增强小模型作为长程 agent 的能力——AppWorld +32%、OfficeBench +20%、Multi-objective QA +46%。这意味着 ACON 蒸馏后的小模型可作为 Nexus ContextCurator 的轻量实现路径:用 7B 级蒸馏模型替代 GPT-4o 级摘要模型,在不牺牲压缩质量的前提下大幅降低成本。
Factory Anchored Iterative Summarization
Factory 的 anchored iterative summarization 在评估中最优(4.04 分 vs Anthropic compact API 3.74 vs OpenAI 3.43)。核心差异:增量合并到持久锚定状态而非每次全量重建摘要。
与 Nexus 的 rolling_summary 对比:
rolling_summary是增量式的,但每次重新生成完整摘要(非锚定)- Anchored 方法维护一个持久状态,每次只追加增量变更,减少信息损失
实践建议:直接在 summary 策略的 prompt 基础上应用 ACON 优化,是最小成本提升压缩质量的方式。无需修改 Nexus 框架代码。
压缩比量化对比
| 策略 | 预期压缩比 | 信息保真度 | 适用场景 |
|---|---|---|---|
| keep_recent | ~10× | 低(丢弃历史) | 短任务、对历史不敏感 |
| summary | ~3-5× | 中等 | 通用场景 |
| rolling_summary | ~3-5× | 中等偏上 | 长对话、需要历史连贯性 |
| RL-learned | ~16× | 高(98%+ QA 保真) | 实验性,Near-Lossless 论文报告 |
注:RL-learned 的 16× 压缩比来自 Near-Lossless Context Compression via RL 论文(ACL ARR 2026 提交),7B/32B 模型在 120k-token 上下文上匹配全上下文性能。手写策略的数据基于经验估计。
第四个维度:压缩的下游代价
上表比较的是”压缩掉多少”与”保真度如何”,隐含假设是省下的 token 就是净收益。TRACER(arXiv:2608.29363,2026-08-29)指出这个假设不成立,并把缺口命名为 compression–consequence gap:分配保留预算时不考虑”删掉某个工具输出”的下游后果,而激进压缩可能触发该工具被重新调用,重调用的开销反过来抵消、甚至超过省下的 token。论文给的量级感很直接——单次 SQL 结果就可以超过一万 token,而一个典型任务要调五到十次工具。
它的做法是把压缩重定义为逐工具的序列决策:轻量 REINFORCE 策略输出 query-conditioned 的保留比例(Beta 分布参数化),奖励一并计入任务成功、总 token 消耗与压缩后的工具重调用次数;再用一个学到的 outcome model 做单工具反事实(“如果全部保留这个工具输出会怎样”)来稠密化信用分配。
⚠️ “为当前步骤优化”不等于”对后续步骤更好”——query-conditioned 这一条要带边界读。 2610.27301(2026-10-19,《Evaluating Context Compaction on the Questions That Come Later》)做的正是这件事:用四个语料(每个文本标注多个问题的证据 + 记录 agent 轨迹),并把每个方法都与同预算下的随机删除对照。结果分两半:
- 当前问题:在 MultiRC 上,保留
gpt-6-luna(经 OpenAI Decisions API)判定为当前问题所必需的句子,能把当前问题准确率提 11.4 个点(用 25% 的 token)。- 后面的问题:没有任何”为当前问题选句子”的方法显著优于随机删除。反而是不看问题的 LLMLingua-2(删词而非删句子)在同 token 数下比随机删句子高 12.5 个点;它保留的内容词约为随机删除的两倍,而在内容词保留量拉平后增益降到 7.3 个点(论文标为探索性)。
- 另有一处方法学提醒:在 FEVER 上,标签准确率会高估保留文本实际支持的结论——按保留证据重新打分后,句子选择的增益不再被检出。
这不推翻上表给 TRACER 的 ✅(它的奖励里还有任务成功与总 token,不只是当前步骤),但“query-conditioned”应被理解为”对当前步骤”的优化:凡把压缩预算绑定在单一步骤上的方法,都要回答”下一步会不会被这一步的剪枝伤到”。对 Nexus 的落点很具体——本章建议”给 tool message 打’该工具后续是否被重调用过’的标签”,那个标签应当跨后续步骤统计,而不只看下一次调用。
| 策略 | 是否计入下游代价 |
|---|---|
| keep_recent | ❌ 只按新旧丢弃 |
| summary / rolling_summary | ❌ 只看压缩比与信息保真 |
| RL-learned(FoldAct · SUPO · CompactionRL) | 部分——奖励计入任务成功,但不含重调用惩罚 |
| TRACER | ✅ 奖励显式含”压缩后的工具重调用次数” |
但「下游代价」这个词在本章里只开了一格,而它其实至少有三格。上表问的是「谁在计重调用」,下面这张表问的是「代价有几种类型」:
| 代价类型 | 谁在计 | 实证 |
|---|---|---|
| 工具重调用 | TRACER ✅;其余 ❌ | TRACER 论文(见上) |
| 前缀缓存失效 | 全部 ❌ | v2.1.275(2026-09-17)生产环境 cache miss;agent 流量里重读占比高,压缩改写前缀后这部分要全价重算。量级:KV-streams(2609.35750,2026-09-28)实测兼容三种不同压缩策略、训练 2.6–5× wall-clock 加速——它不改变压缩策略,只把 KV cache 向前流式传递而不是每次压缩后清空,反证了”反复 prefill”正是这一行的成本主体 |
| 副作用被重复触发 | 全部 ❌ | v2.1.271(2026-09-14):压缩后仍在运行的后台命令(watch 任务、dev server)被启动了第二份 |
| 来源摧毁(安全) | 全部 ❌ | 2610.29869(2026-10-21):5 个扁平化 harness 上伪造 user turn 洗进记忆的成功率 87–100%(窗口大到约 908K token);4 个角色化 harness 上 0%。判据是是否保留角色类型,不是压缩算法 |
| 安全约束被抹除(合规) | 全部 ❌ | 2606.22528:跨 7 个模型家族、1,323 个 episode,违规率从”策略在完整上下文中”的 0% 升到压缩后的 30%,某些模型达 59%;约束在摘要中存活时违规率仍为 0%,被丢弃时达 38% |
| 摘要里凭空多出的条目 | 全部 ❌ | 2610.20927:某广泛使用的结构化压缩器上,83% 的失败运行可追溯到一条执行了某摘要条目的动作,而这些条目大多没有任何环境输出支持 |
后三行是 2026-10-24 补的,它们把”代价”这个词从效率扩展到了安全与合规。 前三行(工具重调用 / 前缀缓存 / 副作用重复)都是”丢了什么”,后三行里新增了两种不同性质:
- 来源摧毁(第四行)不是丢内容,是丢归属——内容还在,但”谁说的”没了,于是伪造的指令与真实的指令不可区分。
- 安全约束被抹除(第五行)的量级比前三行大得多:不是多跑一次命令,而是违规率 0% → 30%(某些模型 59%)。2606.22528 把这一失效模式命名为 Governance Decay,并给出 ConstraintRot 基准(长程 agent 场景 + 确定性工具调用评分)。最可利用的一条因果是”存活即 0%“:约束在摘要中存活时违规率仍为 0%,被丢弃时达 38%。论文给出的缓解叫 Constraint Pinning——把治理约束从有损压缩中隔离出来(免训练),在基准上把违规率恢复到 0%。这条与 Ch12 的 OWASP 治理层、Ch14 的
classifier_category是同一条线,可互引。 - 摘要凭空多出的条目(第六行)方向相反,是”多出什么”。机制:压缩 prompt 要求写”决策与下一步”,于是每条摘要混入了环境确立的事实与压缩器自己写的推理,agent 事后无法区分二者。⚠️ 引用口径要准:83% 的分母是失败运行,不是全部运行,也不是摘要条目总数——不得写成”83% 的摘要条目无来源”。
第三行尤其值得单独说。TRACER 的代价模型是「被删掉的工具输出会不会被重新取回」,它长在工具调用的账本上;而 v2.1.271 的代价是「压完之后某个还活着的进程被复制了一份」——这件事根本不在工具调用的账本上,用再好的工具级信用分配也看不见。同理第一行与第二行也不共用账本:一个是「重跑一次工具」的显式开销,一个是「缓存不命中」的隐式开销。把三格摆在一起的含义是:「是否计入下游代价」不是一个是非题,而是一张清单,任何只填了一格的压缩器都还可以被继续追问。(上表现已扩为六格。)
第六行有一条极轻的落地路径:Source-Judged Compaction(SJC)。 2610.20927 提出的解法是免训练的后处理步骤——给每个摘要条目打”它在环境输出或上一轮摘要中有没有来源”的分,删掉没有来源的条目;既不改 agent,也不改压缩器,每次压缩只多一次廉价的 judge 请求(论文用的 judge 是 Jev,一个非生成式决策模型,用校准概率而非文本回答”有没有来源”)。实测在 AppWorld 与 OfficeBench 上、两个 agent 模型下,相对底层压缩器把跨运行可靠性提升 2.1%–8.9%,且不增加 agent 的 token 用量。
对 Nexus 的落点几乎是免费的:本章 §自定义策略 的 register_compaction_strategy 已是现成接口,SJC 可以作为一层过滤器挂在现有 summary 策略之后,不必换压缩器。(SJC 与下文的 transparent-compact 均只读 abs 页,未读实现细节,“可直接挂进 Nexus”为编辑推断 📌。)
数字(均回 arxiv.org/html/2608.29363v1 正文与表 3 核对):在真实生产查询、三种压缩器后端上,相对”全保留”省下 29–46% token 且任务成功率持平或更高——摘要式压缩收益最高(46%),截断 31%、AGORA 式后端 34%、Self-Info 29%;相对”按工具类型固定比例”的静态策略再省 15–18%(原文是 token 节省的百分比,不是百分点);跨 agent backbone 与压缩器架构迁移仍为正收益;5 个 held-out LOCA-bench 环境上省 18–25%。学到的 per-tool 信用分与实测的单工具后果相关:Spearman ρ = 0.66、top-3 命中 82%、方向符号正确率 84%。另有一个容易与前者串台的数——outcome model 自身在留出 episode 上的 Spearman ρ = 0.72(Brier 0.07),那是模型精度,不是反事实验证的相关性。
限制必须随数字一起读:验证集只有 120 条生产查询,按难度分三档——25 条简单查询、55 条”schema 检索 + SQL”多步查询、40 条多表权限查询;训练/留出划分为 80/40,留出集按 7/19/14 分层。查询来自单一企业的单一 Agent 栈。补充材料明确不含生产 agent、数仓与知识库内容、原始 prompt 与工具输出、事件日志与轨迹、训练 checkpoint,也不含 LOCA-bench 与商业模型端点,外部无法复现。部署门槛同样实在:训练前需先录一份”全保留”的重放基线,即每查询 5 次参考运行——对 Nexus 用户来说,这条是可操作性上的硬门槛,不是论文里的脚注。
对 Nexus 的含义:ToolExecutor 天然知道每次工具调用的来源与体量,Ch17 的 tool_output_truncation: 10000 已经是逐工具的粗粒度版本(按字符截断),TRACER 给的是”按工具输出的后验价值决定保留比例”的升级版。最小落地可以很轻——在 post_iteration_hook 或自定义 CompactionStrategy 里给 tool message 打上”该工具后续是否被重调用过”的标签,先把这个特征攒下来,再谈策略。
平台级 Compaction 与自建 Compaction 的关系
OpenAI 于 2026.02 在 Responses API 中发布 server-side compaction,配合 GPT-5.4 的 1M token 上下文窗口。这标志着压缩从”框架级策略”升级为”平台级能力”。对 Nexus 的影响需分短期和长期分析:
短期影响:使用 OpenAI 模型的 Nexus Agent 可受益于平台 compaction,减少触发自建 CompactionManager 的频率。1M 上下文窗口改变了压缩触发阈值——许多当前需要主动压缩的场景可能被更大窗口直接容纳。
机制层事实:平台侧的 compaction 已经是一等可观测事件,而不只是一个内部动作。以 Claude Code 的 OTel 事件为例,claude_code.compaction 带 trigger(auto / manual)、success、duration_ms、pre_tokens、post_tokens、error,以及 precompute_reuse——自动 compaction 会在上下文窗口填满之前于后台预先生成一份摘要,手动触发时记录是否复用:命中为 hit;未命中给出三种理由 miss_custom_instructions(自定义指令变了)、miss_hook(hook 干预)、miss_not_ready(还没算完)。该属性要求 v2.1.153+。
这条记录揭示平台还做了第三件事:预计算 + 可失效复用。于是 §Factory Anchored Iterative Summarization 之后那条”两条路”的对比可以扩成三条:
| 方案 | 何时算 | 失效模式 |
|---|---|---|
rolling_summary | 每次触发时全量重算 | 重复消耗 + 信息损失累积 |
| Factory Anchored | 增量合并到持久锚定状态 | 锚定状态污染 |
| 平台 precompute | 窗口填满前于后台先算好,用不上就作废 | 三种 miss(自定义指令 / hook 干预 / 尚未就绪) |
| 迭代式再压缩 | 压完仍超长就再压一次,每次保留更少的最近对话 | 抖动:压了没用、再压还没用,直到把上下文压到不可用 |
第四条路来自 v2.1.284(2026-09-28):官方原文是「Fixed “Prompt is too long” errors that persisted after compacting: when the compacted request is still too long, Claude Code now compacts once more, keeping less of the recent conversation」。它和前三条不在同一个维度上——前三条是「一次压缩怎么算」,第四条是「一次不够怎么办」,因此它可以和前三条任意一条叠加。
它的失效模式在生产里已经有名字了。v2.1.286(2026-09-30)的官方条目里出现了 autocompact-thrashing 这个错误名,并说明模型 fallback 会把上下文窗口从 1M 掉到 200K——窗口骤降会诱发抖动,抖动又会把上下文压到不可用,两者互为因果。对 Nexus 的直接含义是:CompactionManager 需要一个重试上限 + 抖动检测,判据可以很轻——连续 N 次压缩后 token 数下降比例低于某个阈值时,报 COMPACTION_THRASHING 而不是继续压。没有这个分支的话,“再压一次”这条恢复路径本身就会变成新的死循环。
抖动的触发源已经查明,可以把它们做成输入特征。 上面给的判据(连续 N 次压缩后 token 下降比例低于阈值)是形态判据,平台侧随后定位到了两类具体来源:
- v2.1.307(2026-10-21):“Fixed some ‘Autocompact is thrashing’ errors in turns that read many PDF pages, images or other large tool results”
- v2.1.309(2026-10-22):“Fixed sessions compacting repeatedly after reading files under a very large nested CLAUDE.md”
两者都是大体积输入,与本章开篇”工具密集的 Agent(ToolExecutor 每轮可能产生多个大体积 tool message)“的预判吻合。因此抖动检测可以把”本轮工具结果体积”作为一个输入特征,而不只是看压缩后的 token 下降比例——前者是原因,后者是症状。
“压完仍失败”还有一个此前没列的原因:压掉了消息但没压掉 token。 v2.1.310(2026-10-23)修的是 “Fixed compaction after a ‘Prompt is too long’ error keeping too much of a conversation with dense text, such as base64 or SVG, so the next request could fail again”——高密度文本(base64、SVG)按消息条数看是被压掉了,但 token 几乎没降。因此上面”迭代式再压缩”的失效模式要补这一格:再压一次对高密度文本无效,判据不能只看消息条数,要看 token 下降量。
触发信号本身也可能是假的。 v2.1.306(2026-10-20)修的是 “Fixed an API error being replaced by ‘prompt is too long’, and an unneeded compaction, in sessions with many MCP tools”——一个真实的 API 错误被替换成”prompt is too long”,并因此触发了一次不必要的压缩。这与 §CompactionTrigger 那句”触发失败不是’那就先不压’,而是直接变成硬错误”是同一个问题的另一面:不只是压缩失败会变成硬错误,别的错误也会被误报成压缩信号。触发器的输入应先做错误类型甄别。
还有一条形态限定本章此前完全没写:非交互形态的压缩是一个独立的内存开销面。 v2.1.310(2026-10-23):“Improved memory use of non-interactive sessions (-p, Agent SDK) during compaction when the transcript file is very large”。本章的实证几乎全部来自交互式 CLI 形态,而 Nexus 的 RemoteRuntime 正运行在该形态(Ch12),批量执行(Ch13)还会把单次开销乘以并发数。因此给 Nexus 的建议要补一条:压缩的内存占用应按”最大 transcript × 并发数”估算,而不是按单会话——否则 Batch Runner 在长跑批次上的 OOM 会发生在压缩这一步,而不是在执行这一步。
三条路各有各的失效模式,选哪条取决于你更怕哪一种。
压缩的第三种角色:窗口强制
前面把压缩定位成两件事:溢出前的补救,以及训练数据的采集口径。平台侧还有第三个定位——它是执行窗口策略的手段,压缩不是窗口快满了才来救火,而是被拿去主动把会话压回某个目标窗口。
两条官方事实可以钉住这件事。CLAUDE_CODE_DISABLE_1M_CONTEXT 在 v2.1.223(2026-08-05)被改成:对所有原生 1M 窗口的 Claude 模型,通过 auto-compaction 把它们压回 200K,而不只是作用于一份固定名单;启动时若发现 auto-compaction 没能把会话压在 200K,会给出警告。更早的 v2.1.172(2026-06-10)是同一套逻辑的雏形:使用 1M 上下文但没有额度的会话会永久卡住,修法是让会话自动压回标准上下文限制以内。同一版本还加了另一条:auto-compact 会把使用未知 model ID 的会话维持在假定窗口之内,而不是任其膨胀。
这也解释了为什么窗口一变,压缩触发点就整体跟着动:v2.1.287(2026-10-02)把 Opus 4.7+ 与 Fable 在 Bedrock / Vertex / Foundry / Claude apps gateway 上默认切成 1M 窗口后,v2.1.247(2026-08-26)已经先把 Sonnet 5 的默认自动压缩窗口从约 934K 提到约 967K——窗口上限与压缩策略在平台侧是同一个旋钮的两面。
对 Nexus 的含义是一条具体的设计要求:CompactionManager 应当支持目标窗口而不只是触发阈值。两者的差别是方向性的——阈值只能被动响应(超了才压),目标窗口可以主动下压(为了把会话维持在某个尺寸而压)。只有阈值这一个旋钮时,「给这个 agent 限定 200K 工作集」这类需求是表达不出来的。
但有了这个旋钮还不够——它必须可回读。 v2.1.304(2026-10-19)修的就是这个旋钮本身的一次静默失效(官方 changelog 原文):
“Fixed
CLAUDE_CODE_AUTO_COMPACT_WINDOW=200ksilently setting a 100K window: the variable now acceptskandMsuffixes like--autocompact, and warns when a value is out of range or ignored”
即:环境变量写了 200k,实际生效的是 100K,且不报错;修复后才接受 k / M 后缀,并在值越界或被忽略时告警。也就是说,平台侧这个旋钮刚刚被证实会”写了不生效且不报错”——而它正是本节主张 Nexus 要新增的那一个。
对 Nexus 的落点:目标窗口这类配置应提供生效值回读(或至少启动期校验告警),否则”为了把会话维持在某个尺寸而压”这条能力会在配置层被静默打折。此条与 §CompactionTrigger 表里 v2.1.273 那条(advisor-tool turns 按约 2 倍计,导致在真实窗口约一半处就触发)是同一族——阈值/窗口类配置的失效都表现为静默偏移,而不是报错。
粒度还要再推一层:不是每会话一个目标窗口,而是每个 agent 一个。 v2.1.296(2026-10-11)给 subagent frontmatter 与 --agents 定义加了 autoCompactWindow,官方原文 “so a subagent can auto-compact earlier than the main conversation’s window”——子 agent 可以比主会话更早压。对 Nexus 的落点很直接:本章 §自定义策略 的 YAML 示例现在只有 agent_config: compaction_strategy 一个键,应再允许 compaction_window_ratio(或目标窗口)按 agent 覆写。这与 Ch13 的 subagent / fork 形态、Ch17 的 per-agent 配置是同一条线。
第三个平台:Gemini CLI 把压缩触发开放成配置项。 前面两家各有各的形态——OpenAI 是服务端 compaction 加 1M 窗口,Claude Code 是 OTel 一等事件加后台预计算。Gemini CLI 补上的是第三种形态:它把压缩触发阈值直接开放成一个可写进配置的数。model.compressionThreshold 的官方定义是「触发上下文压缩的上下文使用占比(例如 0.2、0.3)」,默认 0.5;配套的 model.maxSessionTurns 限制单会话轮数,默认 -1(不限)。
要点不在那个默认值,而在参数化方式:它用的是占比,不是绝对 token 数。这个差别在窗口会变的年代是实的——占比口径天然随窗口缩放,绝对 token 口径在窗口从 200K 变成 1M 时必须手工改一次。本章已经有现成的例子:Sonnet 5 的自动压缩点从约 934K 被提到约 967K(v2.1.247),正是绝对口径下的手工维护。建议 Nexus 自建路径采用占比口径,把阈值写成窗口的分数而不是 token 常数。
长期影响:自建 compaction 的核心价值仍在于训练数据采集。HyTuner Processor 需要精确的压缩前后 token 序列来构建训练数据,平台 compaction 对客户端透明,无法提供此能力。此外,token 成本仍随长度线性增长——即使 1M 窗口不触发截断,长上下文的推理成本也随 token 数增加,压缩的经济价值不减。
结论:平台 compaction 是 Nexus compaction 的补充而非替代——平台处理”能容纳多少”,Nexus 处理”应该保留什么”以及”训练数据怎么采”。再补一层:平台还不只是”能容纳多少”,它在用预测性预计算掩盖压缩延迟。Nexus 若要在自建路径上对齐,最小成本是给 CompactionManager 增加”预算 + 复用”分支——token 接近阈值时先生成摘要,真正触发时直接复用,并把命中率记成一个可观测指标(对应上表的 hit 与三种 miss)。
本轮未采纳与待核实(2026-10-06 编辑)
KV-streams(vLLM 0.19 fork,作者自述近 2× 加速):无正式论文,本轮未取到一手源,不引用此数字。已于 2026-10-24 解除:该工作已有正式论文——arXiv 2609.35750《KV-streams for Efficient Compaction in Agentic Reinforcement Learning》(2026-09-28)。它是 plug-and-play、兼容任何压缩策略的方案,机制是把 KV cache 向前流式传递而不是每次压缩后清空;实测支持三种不同压缩策略,训练 2.6–5× wall-clock 加速,且”no evidence of hindering performance”。论文还发现流式 KV cache 可以充当一种循环状态,把早已从上下文中消失的信息继续往前带(受控实验显示这一行为只需 RL 即可涌现)。数字已写入 §第四个维度 的”前缀缓存失效”行作为量级证据。- ⚠️ 两处须限定:① 数字用 2.6–5×,不用本章原登记的”近 2ד;② “vLLM 0.19 fork”这一归属在 abs 页未出现,本章不写归属,留 📌。另:2.6–5× 为摘要级,未回 HTML 正文主表。
- 本章此前只登记”作者自述近 2ד而拒绝引用,是因为当时没有一手源;现在有了一手源,数字反而更大——这说明”未采纳”登记的正确处理是回源,而不是永久搁置。
- Galahad(2026-10-02,测得 prompt token 中 98.7% 是重读):同样未回源。本章开篇已经用「agent 流量里绝大部分 prompt token 是重读」的非数字表述保留了这一论点,等拿到一手源再补百分比。
- CLM 的具体数字(BrowseComp-Plus +11.4%、21.5% 更少 FLOPs、Suffix Cache Reuse 服务端算力 −35%):按低置信度纪律只登记方向不写数字,见 §ACM。
- Claude Code 版本锚点:本轮全部版本事实回
code.claude.com/docs/en/changelog.md一手核对,版本日期取 npm registry 的time字段,最新为 2.1.289(2026-10-03)。其中 v2.1.223 的提案标注日期为 08-06,npm 实际为 2026-08-05T22:51Z,本章按 npm 写。