--- name: context-compression description: 上下文膨胀、工具结果撑爆窗口、Agent 决策质量随轮次下降时使用——上下文压缩的三个动机(长度成本、思考质量、上下文焦虑)、压缩即检索的机制、六种压缩策略实测数据、生产级五层分层压缩、四条设计原则,以及用子 Agent 隔离代替压缩。 --- # 上下文压缩策略 压缩解决的是相反方向的问题:**如何为上下文做减法**——什么时候压缩、怎么压缩、为什么即使上下文没满也应该压缩。核心认知:**上下文学习本质上是检索而非推理**,压缩就是把需要思考才能得到的结论,变成可以直接检索的知识。 ## 何时使用 - 工具调用结果动辄数万字符,几轮交互就撑满 128K 窗口 - Agent 在长任务中「明明窗口没满,却找不到关键信息」或反复纠结已解决的问题 - 模型在任务尚未完成前提前收尾、草率下结论 - 设计压缩策略(何时触发、压缩什么、保留什么)或排查压缩副作用 - 评估「把大任务委派给子 Agent」与「主 Agent 自己做完再压缩」的取舍 ## 核心原则 - **三个动机,层次不同**:① 长度与成本约束(窗口有限、token 越贵、延迟越高);② 提升思考质量——总结后的知识比原始形式更利于模型使用,十几轮搜索的原始结果散落各处,模型每次决策都要在数万 token 中反复检索;③ 缓解**上下文焦虑(Context Anxiety)**——模型认为窗口即将耗尽时可能在任务完成前提前收尾,在未接近耗尽时就提前压缩可能提升决策质量。 - **上下文窗口是一台只有一半的检索引擎**:检索这一半极强(相当于每次前向传播都内置了 RAG),但缺「提炼层」——上下文里的东西从来不会被自动数一遍、建索引或就地总结。任何「关于这些内容的结论」都要从原始记录现算一遍,代价随内容量 N 上涨。 - **压缩与状态栏是同一枚硬币的两面**:状态栏把算好的结论**加**进上下文(由代码确定性维护),压缩把臃肿的原始记录**换**成算好的结论(多用一次 LLM 调用蒸馏)。 - **上下文腐化(Context Rot)≠ 溢出**:溢出是「装不下了」,腐化是「装得下但找不到了」——后者更隐蔽,Agent 表面正常工作,决策质量悄然下降。注意力权重被分散到更多 token 上,无关内容一旦占大头,决策质量明显下滑。 - **主动显式提炼,不要期望模型自动学习**:与其让模型被动在海量信息中检索,不如主动提供经过提炼的高密度结构化知识。 - **压缩最容易丢失**:早期的架构决策、约束背后的理由、失败的路径。因此 Agent 需要定期把进展记录到文档,而不是把所有信息零散堆在执行历史里。 - **隔离优于压缩**:压缩是信息已进入上下文后的事后有损补救;隔离让大体积中间信息根本不进入主上下文。 ## 实践模式 ### 1. 六种压缩策略实测对比(实验 2-10) 任务:识别并追踪 OpenAI 联合创始人的职业状态;Kimi K3(原生约 1M 窗口,实验刻意限制在 128K 预算以触发压缩)。 | 策略 | 做法 | 迭代 | Token | 压缩率 | 问题 | | --- | --- | --- | --- | --- | --- | | 无压缩 | 完整保留原始结果 | 5 次即溢出失败 | ~165,000 | — | 7 次搜索累计约 367,000 字符(平均 52,000/次),数次搜索即耗尽 128K | | 个体摘要 | 每个结果独立生成 2-3 段摘要 | 12 | 276,608 | 10.9% | 信息碎片化,多页重复描述同一事件 | | 组合摘要 | 所有结果合并后一份综合摘要 | 10 | 93,449 | 4.3% | 输入超长必须截断,可能丢失末尾信息 | | **上下文感知** | 压缩提示中带入查询意图与已积累信息 | 7 | 40,157 | ~3.0% | 最优平衡点 | | 带引用的上下文感知 | 智能压缩 + 每条事实附带来源 URL | 7 | — | — | 内容有损、索引无损,可回溯原文 | | 自适应窗口化 | 阈值触发 + 批量压缩 + 防重复 | — | 174,601 | — | 初期保留完整原始信息,灵活性最大 | > 压缩率定义为「压缩后体积 / 原文体积」,数值越小表示压得越狠。上下文感知压缩将 token 使用量减少 75% 以上。 **上下文感知的提示词写法**:在压缩提示中指定 `Given the search query: {query}` 和 `Current context: {context}`,引导模型生成针对性摘要。实测把约 150K 字符压到 2K 字符时,仍保留创始人姓名与职位变动等后续任务需要的关键信息。 ### 2. 自适应窗口化三机制(推荐默认) - **阈值触发**:持续监控上下文使用率,prompt token 超过窗口 80% 时才激活压缩(如 102,400 / 128K)。 - **批量压缩**:触发时一次性压缩所有未标记的工具结果,而不是每轮零碎压。 - **防重复保护**:添加 `[COMPRESSED]` 标记,确保已压缩内容永不被重复处理。 ### 3. 压缩与 KV Cache 的共存 压缩不是在单次 API 调用中修改上下文,而是在**两次 API 调用之间**由框架预处理消息列表: 1. **System Prompt 和 Tool Definitions 永远不动**——静态前缀持续缓存。 2. **压缩对象是对话历史中的 tool results**——替换位置之后的缓存失效,之前的仍有效。 3. **有意识的权衡**:不压缩则溢出失败;压缩损失部分缓存但长度可控、信息密度更高。**压缩频次需要权衡——频繁压缩频繁破坏缓存,最好在接近阈值时批量压缩,而不是每轮都压。** ### 4. 生产级五层分层压缩(Claude Code 参照) 不同信息有不同保质期,压缩策略应与预期生命周期匹配,按代价从低到高排列: 1. **工具结果预算控制**:大体积输出存磁盘,模型只看摘要预览;替换决策一旦做出就冻结,保证缓存一致性。 2. **噪声直接删除**:低价值内容(如大量搜索结果中只被用了几行的部分)直接移除,不做摘要——对噪声做摘要只是浪费 token。 3. **API 层微压缩**:通过上下文编辑能力指示服务端从前缀移除指定工具结果,本地消息不变;零本地实现成本,但移除点之后缓存同样失效,产生一次缓存重建——适合上下文即将溢出、反正要付这次代价时用,不要频繁触发。 4. **归档式摘要**:逐轮结构化摘要,像 git log 保留每轮独立记录,而非 git squash 合并成一条,保留对话逻辑脉络。 5. **全量压缩**:LLM 驱动的完整压缩,作为最后手段;分两阶段(先压会话记忆,不行再全量),并配**连续失败熔断器**——生产数据显示大量会话被困在反复压缩失败的循环中,熔断器避免持续烧钱。 ### 5. 四条压缩设计原则 - **信息价值的非均匀分布**:关键决策点(如人员名单)> 支撑性证据(如新闻细节)> 冗余噪声(网页导航栏、页脚广告)。 - **语义完整性**:`Sutskever 于 2024 年 5 月离开 OpenAI` 不能压成 `Sutskever 离开`——时间和公司名是不可丢失的关键信息。 - **任务相关性**:同样内容在「查找创始人名单」和「了解个人背景」两个任务下应产生不同压缩结果。检索类任务保广度,分析类任务保深度,创作类任务保灵感触发点;理想的 Agent 应按任务类型自适应选择压缩策略。 - **压缩即理解**:有效压缩需要深层语义理解,负责压缩的模块本身要接近主模型能力,形成「模型调用模型」的递归架构;好处是显式压缩的结果可审查、可跨会话复用。 ### 6. 必须保留的四类信息 无论哪种策略,压缩时都要显式 preserve:**decisions(决策)、constraints(约束)、failures(失败路径)、citations(来源引用)**。 ### 7. 隔离优于压缩:子 Agent 上下文隔离 对比同一个任务「在代码库中找到处理支付回调的函数」: - **主 Agent 亲自搜索**:可能把十几个文件数万 token 原始代码纳入主上下文,找到目标后绝大部分沦为永久噪声,还得靠后续压缩清理。 - **委派给搜索子 Agent**:主上下文只增加两条消息——一条任务描述,一条结论(「函数位于 `src/payment/callbacks.py` 的 `handle_callback`,另有两处调用点」);中间过程的数万 token 随子 Agent 上下文一起丢弃。 代价是子 Agent 看不到主 Agent 完整上下文,**任务描述必须自包含、目标明确**。Claude Code 的 Task 工具、各类 Deep Research 的检索子 Agent 都是这一模式的生产实现。 ## 常见陷阱 - **每轮都压缩**:频繁破坏 KV Cache 前缀,应阈值触发 + 批量压缩。 - **对噪声做摘要**:把只用了几行的搜索结果压成摘要,纯浪费 token,应直接删除。 - **压缩时丢掉时间和主体**:语义不完整比不压缩更糟——模型会基于错误事实继续推理。 - **用远弱于主模型的模块做压缩**:压缩即理解,能力差距大会丢关键信息。 - **把压缩当成长度问题的唯一解**:即使窗口够大,压缩仍能提升思考质量、缓解上下文焦虑。 - **让模型自己数数和统计**:查找是注意力强项,统计要遍历全部记录并维护计数状态;应提前把结论写进上下文。 - **把所有进展只留在执行历史里**:早期架构决策、约束理由、失败路径最容易在压缩中丢失,必须定期落到文档。 - **给子 Agent 派发含糊任务**:子 Agent 上下文隔离的代价就是任务描述必须自包含,否则结论质量无法保证。 ## 配套代码 - `chapter2/context-compression/` — 实验 2-10:实现并对比 6 种压缩策略(`no_compression` / `individual` / `combined` / `context_aware` / `citations` / `windowed`),输出成功率、耗时、token、压缩率、溢出次数对比表;用 `python experiment.py -s context_aware` 单跑,`-m moonshot-v1-128k` 配合 `CONTEXT_WINDOW_SIZE=128K` 复现溢出。 ## 深度阅读 - `book/chapter2.md`「上下文压缩策略」