上下文压缩 compaction
压缩是把接近窗口上限的历史摘要成一段高保真摘要,然后从摘要继续跑。必须保真的是当前目标、已做出的决定、未完成的待办与关键约束;工具结果清理、压缩、缓存三者互相牵制 —— 改写前缀会击穿缓存,少次大压比频繁小压更省也更稳。
也叫:compaction · 上下文压缩 · 历史压缩 · 摘要压缩 · 工具结果清理
原理拆解出自 T1-4
缓存层级
tools → system → messages,改上层废下层;两把刀都动最下层缓存前缀 ①
tools · 工具定义顺序一变就失效
② 末尾单独一个断点
system · 系统提示词让它能在压缩中幸存
③ 清理与压缩都动这里
messages · 历史对话 + 工具结果
压缩发生时
会话前缀作废新摘要要重新写入一次
system 那段照常读它自己有断点,活下来了
缓存的四个数(记不住就抄)
| 项 | 数值 | 读法 |
|---|---|---|
| 写入溢价 | 5 分钟约 1.25 倍原价;1 小时约 2 倍 | 写进去要先贵一次 |
| 命中 | 约 0.1 倍原价,并免费续期 | 续期从请求开始时刻算起 |
| 最小可缓存长度 | 512 / 1024 / 2048 / 4096 token,按模型不同 | 不到门槛不缓存,且不报错 |
| 回溯窗口 | 20 个 block | 一轮加太多 block,会把上次写入推出去 |
压缩流程:输入 token 数达阈值触发 → 生成一段摘要块 → 摘要块之前的内容全部作废 → 从摘要继续。阈值默认 15 万,最低只能设到 5 万 —— 这个下限本身就是官方在说:不要频繁小压。用量要跨所有迭代汇总,只看顶层字段会算漏账。
和解只需要一个断点:在系统提示词末尾单独打一个缓存断点,压缩发生时那段缓存仍然有效、照常读取,只有新生成的摘要需要作为新内容写入一次 —— 长系统提示词因此可以跨多次压缩一直活着。
隐形失效项才是排查噩梦:工具定义顺序变了、某些语言序列化时 JSON 键顺序不稳定、图片增删、思考或 effort 配置变动、
tool_choice 变化 —— 内容看着一模一样,缓存就是不命中。 ┌──────────── 上下文窗口 ────────────┐
│ tools │ system │ 历史 + 工具结果 │
└───┬───────┬────────────┬───────────┘
│ │ │
改这里 │ 改这里 │ 改这里 │
▼ ▼ ▼
┌───────────────────────────────────┐
│ 缓存前缀层级:tools → system → messages │
│ 改上层 → 下层全部失效 │
└───────────────────────────────────┘
▲ ▲
│ │
清理会动这里 压缩会动这里压缩的机制(截至 2026-08 已经是一等公民 API)
流程是:按输入 token 数达到阈值触发 → 生成一段摘要块 → 摘要块之前的内容全部作废 → 从摘要继续。几个必须知道的细节:
- 阈值默认 15 万 token,最低只能设到 5 万。 这个下限本身就是官方在告诉你:不要频繁小压。
- 压缩是一次额外采样,单独计费。 用量要跨所有迭代汇总,只看顶层的输入输出 token 字段会算漏账。
- 自定义摘要指令是「完全替换」,不是追加。 这是最容易踩的坑——你只写了「重点保留代码片段」,就等于把默认提示里「保留状态、下一步、已有结论」全删了。
- 可以在压缩后暂停,让你把最近几条消息原样接在摘要后面,避免刚说完的关键信息立刻被摘要走样。
- 带工具时压缩可能失败:模型在摘要那一步跑去调工具了,返回一个空摘要。解法是在指令里明确写「这一步只写文本,不要调用任何工具」。
- 摘要用的是同一个模型,不能换个便宜的来做。
缓存的机制
- 层级
tools → system → messages,改上层废下层; - 写入只发生在断点处,读取时向前回溯,回溯窗口是 20 个 block。这意味着一轮加太多 block,会把上一次的写入推出窗口,明明内容没变也命中不了;
- 有最小可缓存长度门槛,按模型不同为 512 / 1024 / 2048 / 4096 token 不等。不到门槛不缓存,且不报错——两个缓存字段都是 0 就是这种情况;
- 计费:5 分钟写入约 1.25 倍原价、1 小时写入约 2 倍,命中约 0.1 倍;命中会免费续期,续期从请求开始时刻算起(生成耗时也算在寿命里);
- 常见的隐形失效项:工具定义顺序变了、某些语言序列化时 JSON 键顺序不稳定、图片增删、思考或 effort 配置变动、
tool_choice变化。
压缩与缓存怎么和解(这是本篇最值钱的一段)
「压缩会击穿缓存」这个说法要说得更精确:击穿的是会话前缀,不是全部。
官方给的解法是:在系统提示词末尾单独打一个缓存断点。这样压缩发生时,系统提示词那段缓存仍然有效、照常读取,只有新生成的摘要需要作为新内容写入一次。长系统提示词因此可以跨多次压缩一直活着。
再配合「少次大压」,整笔账就清楚了:
一次压缩的固定开销 = 额外采样(读入约 T 个 token)
+ 摘要写入缓存
+ 会话前缀重建
压缩次数 ↑ → 固定开销 × 次数 ↑,同时信息经多次转述失真 ↑
所以阈值应尽量贴近窗口上限,而不是"勤快地小压"。以上节选自T1-4 上下文预算:压缩、清理与 prompt 缓存的三方博弈,读全文能看到前后语境。
延伸阅读
考这个知识点的题1 道
会连带问到12 道
- Q1-11什么是上下文工程?它和提示词工程是什么关系?
- Q1-12什么是 context rot?为什么上下文越长模型反而变笨?
- Q1-14Prompt 缓存是什么原理?怎么设计 prompt 才能「缓存友好」?
- Q3-12Agent 的记忆系统怎么设计?短期、长期记忆分别放哪?
- Q3-13上下文快满了:滑动窗口 / 摘要压缩 / 重要性过滤怎么选?压缩丢了关键决定怎么办?
- Q5-05Agent 的轨迹评估和单轮问答评估有什么不同?
- Q5-06一次多轮 Agent 请求的 trace 里应该记哪些东西?
- Q6-03KV Cache 是什么?为什么长上下文推理显存爆炸?
- Q6-05实时语音链路怎么搭?级联 ASR→LLM→TTS 和端到端语音模型的取舍?
- Q6-09应用 token 成本突然涨了 3 倍,怎么排查和治理?
- Q7-02给 AI 编程工具喂上下文有什么讲究?为什么整仓塞进去反而更差?
- Q7-03复杂任务怎么拆给 AI?先写 spec 再让 AI 实现是什么流程?