上下文压缩
上下文压缩在减少每次调用时输入给模型的 token 数量的同时,保留了模型实际执行操作所需的信息。在长期运行的智能体和长对话中使用它,可以降低成本和延迟,并保持在上下文窗口限制之内。其三大手段是总结历史记录、剪裁无关上下文以及压缩提示词。核心风险是信息有损:遗漏了那一个至关重要的细节。应衡量保留的信息量,而不仅仅是节省的 token 数量。
问题
长期运行的智能体(Agent)和多轮对话会不断累积上下文:每次工具调用结果、先前的消息以及检索到的文档都会在下一次调用中被重新传入。Token 数量随着交互呈近似线性增长,导致单次调用的成本和延迟不断攀升,最终导致窗口溢出,而最旧的(有时也是最重要的)内容会被静默截断。简单的解决方法——如扩大窗口、更激进地截断——要么会增加成本,要么会破坏模型保持连贯性所需的信息。
适用场景
适用于上下文增长无界且远超单步执行所需的情况:具有漫长历史记录的对话助手、循环执行多次工具调用的自主智能体、检索过度的 RAG 流水线,以及 Prompt 大小主导成本的批处理任务。当累积的大部分上下文是冗余或过时的、你可以控制 Prompt 的组装,并且可以容忍一定的重构误差时,该模式非常适用。如果每个 Token 都至关重要(如法律、审计、精确召回任务),或者交互足够短以至于窗口从未面临压力,则不适用此模式。
解决方案
将实时上下文视为需要主动管理的预算,而不是一个只能追加的日志。存在三种互补的手段。摘要(Summarization)用更短的梗概替换一段历史记录——通常是定期更新的旧轮次滚动摘要,而最近的轮次则保持原样。剪枝(Pruning)移除与当前步骤无关的上下文:去重、丢弃过时的工具输出,并仅选择评分高于相关性阈值的检索分块。Prompt 压缩(Prompt compression)(例如 LLMLingua)在发送 Prompt 之前,使用较小的模型删除或改写低信息量的 Token,以微小的准确率损失换取大量的 Token 减少。\n\n将这些组合成一个具有明确边界的流水线:保留一个原样呈现的近期窗口、一个旧历史记录的滚动摘要,以及一个按需填充的检索插槽。保护一个“固定(pinned)”区域,用于存放绝不能被压缩的事实——如标识符、约束条件、当前目标。至关重要的是,对结果进行检测:运行评估集来对比启用和未启用压缩时的回答,以便观察质量何时下降,并针对每个工作负载(而非全局)调整压缩强度。压缩是质量与成本之间的权衡拨盘,而不是无代价的获益。
组件
优势
- 发送更少的 Token 可以直接降低每次调用的输入成本,这在长周期的智能体循环和高并发流量中会产生累积效应。
- 更小的 Prompt 意味着更少的编码工作量和更短的首字延迟(Time-to-First-Token),从而提高交互式和智能体工作流中的响应速度。
- 限制实时上下文的边界可以让长对话和多步骤智能体持续运行,而不会导致窗口溢出或静默截断。
- 移除冗余和过时的上下文可以通过减少干扰来提高质量,帮助模型专注于当前重要的内容。
风险
- 摘要和剪枝可能会丢弃事后证明起决定性作用的单一细节,从而导致模型给出看似笃定却完全错误的回答。
- 滚动摘要是对先前的摘要进行再摘要;微小的遗漏会在多个循环中累积,直到对话主题在不知不觉中偏离。
- 运行摘要器或压缩器会增加其自身的延迟、成本和故障点,这可能会抵消在短期交互中节省的开销。
- 激进的剔除可能会静默移除模型仍依赖的约束或指令,且不会发出明显的错误信号。
不适用场景
- 当每个 Token 都至关重要时(如法律、审计、合规或精确数据提取),有损压缩是不可接受的。
- 如果对话极少对窗口造成压力,压缩带来的开销将超过其节省的成本,并增加不必要的复杂性。
- 在没有保留评估框架的情况下,不要部署任何内容:因为你无法判断压缩是否在静默降低回答的质量。
技术
示例
- 在大型代码库上进行迭代的智能体保留了原样呈现的近期窗口以及早期步骤的滚动摘要,同时固定了任务规范和文件路径,以确保不会偏离目标。
- 多会话支持机器人将先前的轮次总结为紧凑的案例摘要,剪枝已解决的子问题,同时固定客户的账户限制条件。
- 获取多个分块的检索流水线应用相关性剪枝和 Prompt 压缩,以仅发送高信号段落,在不丢失答案的情况下减少 Token 数量。
生产实践证据
- 上下文
- 在 57 天内(161 个会话 / 2,776 轮次)观察到的单操作员、本地优先的 OpenClaw 部署,数据从智能体自身的轨迹追踪中聚合而来。
- 场景
- 预先压缩长篇自主运行记录,并截断工具调用结果,以保持在 Prompt 预算之内。
- 技术
- 具有安全余量的预先压缩、工具结果截断、上下文剪枝钩子(Hook)以及轮次中途溢出预检。
- 负载
- 在 57 天内的 2,776 轮次中,共发生 2,810 次上下文编译事件。
- 结果
- 压缩在整个窗口中内联运行,使多步自主轮次保持在预算范围内(p95 为每轮 87.6 秒),且未出现上下文溢出故障。单操作员、本地优先部署。
KPI
- 单次调用 Token 数(输入)
- 主要的成本驱动因素。追踪压缩前后的分布情况;健康的结果是 Token 数量明显减少,且下游错误没有增加。
- 信息保留度 / 任务质量
- 在评估集上对比启用和未启用压缩时的回答。理想的情况是,随着 Token 数量下降,质量在可接受的容差范围内保持稳定。
- 端到端延迟
- 扣除压缩开销后的净延迟。理想情况是降低总延迟;需注意避免摘要器或压缩器的调用开销抵消了节省的延迟。
- 上下文溢出 / 截断率
- 交互触及窗口限制的频率。理想情况是将此概率降至接近零,且无需丢弃固定内容。
已观测到的失效模式
- 摘要遗漏了早期提到的约束条件;许多轮次后,智能体违反了该约束,因为该事实已完全从上下文中消失。
- 重复的重新摘要会放大改写错误和遗漏,直到运行中的摘要不再反映实际发生的情况。
- 预算配置错误导致本应受到保护的标识符或指令被压缩,从而静默破坏了正确性。
- 激进的相关性阈值过滤掉了对边缘情况至关重要的上下文,导致质量在测试中看起来很好,但在实际应用中却出现失败。
经验教训
- 最大化减少 Token 数量非常简单,但孤立来看毫无意义;真正的衡量标准是模型是否仍能正确回答。
- 显式保护标识符、约束条件和当前目标,确保任何压缩阶段都无法将其剔除。
- 压缩旧的历史记录,而不是当前的活跃上下文;最近的交流承载着与决策最相关的信号。
- 针对评估集调整每个工作负载的压缩强度;对闲聊安全的操作在审计任务中可能是鲁莽的。
常见问题
- 这与长期记忆有什么不同?
- 长期记忆将事实持久化保存在 Prompt 之外,并根据需要进行检索;而上下文压缩则缩小了每次调用时发送的实时上下文。两者是互补的:记忆决定召回什么内容,压缩决定这些内容在窗口中以多紧凑的方式呈现。
- 摘要、剪枝还是压缩——我应该使用哪一种?
- 首先进行剪枝(免费,且在移除真正的冗余时是无损的),当旧历史记录无界增长时对其进行摘要,并且只有在仍需要更多空间且能够验证质量损失时才加入 Prompt 压缩。大多数系统会结合使用这三者。
- 我该如何知道压缩是否损害了质量?
- 在启用和禁用压缩的情况下运行评估集,并对比任务结果,而不仅仅是 Token 数量。留意看似笃定却完全错误的回答以及丢失的约束条件——这些都是有损压缩过度时的典型特征。