博客 • AI / API
Claude API 2026 新变化:Conversation Compaction 是什么?AI Agent 如何使用 JSON 保存长上下文与压缩后的状态?
Agent 跑得越久,Messages 数组就越长。工具结果、思考块、用户来回,都会把上下文窗口顶满。2026 年 Messages API 把这件事收成官方能力:Conversation Compaction。
Compaction 不是你在客户端自己写一段摘要再贴回去。API 会签发一块type: "compaction"的 JSON。下一次请求必须把这块原样放回messages,会话才从压缩后的状态继续。
本文将介绍:
- 阈值压缩和 9 月按需压缩各走哪条 beta
- 压缩前后
messages长什么样 - 哪些字段必须落盘,缺了就恢复不了 session
- 后台压缩、保留最近几轮时,JSON 怎么换位
- 怎么用 JSONNote 核对块的位置和字段
先记住这一点:落盘的不是「对话全文」,而是最新的 compaction 块,加上块后面还没被吃掉的轮次。块里的content、signature、encrypted_content是契约,不是注释。下面按「两种压缩 → 块长什么样 → 落盘清单 → 换位规则」拆开。
Compaction 压缩的是哪一段
官方定位很明确:服务器端压缩是长对话和 Agent 工作流的推荐做法,不必自己在客户端写摘要代码。窗口被旧内容填满时,响应质量会掉;压缩用一段摘要换掉更早的内容,让有效上下文还能往前走。说明见
阈值压缩打开后,API 做四步:输入 token 碰到触发值;生成摘要;写出compaction块;用压缩后的上下文继续回答。你下次把整段 assistant 响应追加进messages,API 会丢掉这块前面的内容,从摘要接着聊。
它是有损的。具体用过的工具参数、原话、早期思考,不会逐字留下。留下的是目标、决定和后面还用得上的事实。这和「把整个 messages 数组存进数据库」不是同一件事。Agent 技术栈里上下文怎么涨,见
两条路:阈值压缩和按需压缩
2026 年有两套 beta,不要混在同一次请求里。官方写明:compaction参数不能和context_management一起发。
| 路径 | Beta 头 | 你在 JSON 里写什么 | 块出现在哪 |
|---|---|---|---|
| 阈值压缩 | compact-2026-01-12 |
context_management.edits里加compact_20260112 |
普通回复开头;后面还可以跟 text |
| 按需压缩 | compact-2026-09-04 |
顶层compaction: {"type":"summarize"} |
单独一条,stop_reason为compaction,没有普通回复 |
阈值压缩的触发默认是input_tokens150000,最低 50000。可选pause_after_compaction:摘要写完先停,让你把最近几轮或指令性消息插回块后面,再继续生成。自定义instructions会整段替换默认摘要提示,最长 16384 字符。
按需压缩是 9 月这条线的重点。你决定何时压;摘要请求可以在后台跑,对话仍用完整历史继续;也可以把最近几轮原文留在摘要后面。签名块必须放在messages最前面,被摘要过的消息要从块前面拿掉,否则会 400,错误名是compaction_block_misplaced。Bedrock 和 Google Cloud 没有按需这条。
压缩前后的 Messages JSON
先看压缩前:就是普通的 user / assistant 轮次。
{
"model": "claude-opus-5",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": "I am building a recipe app. Help me name the main entities in the data model."
},
{
"role": "assistant",
"content": "Start with Recipe, Ingredient, and Step."
},
{
"role": "user",
"content": "Good. Now suggest field names for Recipe."
}
],
"compaction": { "type": "summarize" }
}
返回只有一块签名过的 compaction,stop_reason是compaction。之后每次请求,这块必须在最前面,后面只跟「没被这次摘要吃掉」的轮次。
{
"model": "claude-opus-5",
"max_tokens": 2048,
"messages": [
{
"role": "assistant",
"content": [
{
"type": "compaction",
"content": "Summary of the conversation: the user is designing the data model for a recipe app...",
"signature": "EuYBCkQY..."
}
]
},
{
"role": "assistant",
"content": "For Recipe, use title, description, servings..."
},
{
"role": "user",
"content": "Now do the same for Ingredient."
}
]
}
阈值压缩的块长得更「夹在回复里」:assistant 的content数组先是 compaction,再是 text。多次压缩时,以最后一块为准,它代表当前提示的最终状态。API 见到这块,会忽略它前面的内容块。你可以整段回传,也可以自己丢掉旧消息、只从这块往后发。
| 字段 | 它是什么 | 能不能改 |
|---|---|---|
type |
固定compaction |
不能 |
content |
摘要文本;失败时为null |
不能改字;空字符串不合法 |
signature |
按需压缩返回的签名 | 必须原样回传 |
encrypted_content |
不透明元数据,SDK 类型要求 verbatim 回传 | 有就原样存,不要解析 |
cache_control |
可选,给摘要单独做 cache 断点 | 你自己加,不是签发字段 |
SDK 把encrypted_content写成「先前压缩留下的不透明元数据,必须原样 round-trip」。客户端不要拆它,也不要当摘要正文展示。失败块的content为null时,服务器当空操作;仍然可以回传,但不会当有效摘要用。
恢复 session 必须落盘的字段
Agent 一重启,内存里的messages就没了。你要恢复的不是「压缩前那一整本聊天记录」,而是压缩之后还能继续请求的状态。
-
1
最新的 compaction 块
整块 JSON,包括
content、signature、encrypted_content。有多块时只需要最新一块:再压一次会把旧摘要和后面的内容一并吃掉。 -
2
块后面的 messages
没被这次摘要覆盖的 user / assistant 轮次,按原顺序。后台压缩期间新长出来的轮次也在这里。
-
3
当时的 system 和 tools
按需压缩时,摘要请求要带上对话其余部分同一套
system和tools。带保留思考的模型上,块后面那些轮次的 thinking 能不能不失效,取决于这两项有没有改,以及尾部是否紧挨着被摘要的消息。 -
4
你走的是哪条 beta
阈值压缩继续带
compact-2026-01-12。按需压缩的块,之后每次请求都要带compact-2026-09-04。头不对,块可能不被认。
{
"beta": "compact-2026-09-04",
"model": "claude-opus-5",
"system": "You are a coding agent.",
"tools": [],
"compaction_block": {
"type": "compaction",
"content": "Summary of the conversation: ...",
"signature": "EuYBCkQY...",
"encrypted_content": "..."
},
"tail": [
{
"role": "assistant",
"content": "For Recipe, use title, description..."
},
{
"role": "user",
"content": "Now do the same for Ingredient."
}
]
}
重建请求时:messages[0]放带这块的 assistant(或把块放进第一条消息的第一个 content),然后拼接tail。不要把压缩前的旧 user 消息再塞到块前面。
这和权限评估不是一层。那边管 tool call 能不能跑;这边管窗口被吃掉之后,状态还在不在。权限那篇见
Claude Managed Agents 如何自动判断 Tool Call。长流程里 Agent 怎么协作,见
后台压缩和保留尾部时怎么换
按需压缩的请求只摘要它收到的消息。所以你可以只把旧轮次送去压,最近几轮原文留在本地,压完把块插到这些轮次前面。官方叫 keep-tail。
后台压缩(async)更进一步:摘要还在跑时,对话继续用完整历史往下走。回来之后不能把整段历史换成一块,只能从队头去掉「当时送去压的那几条」,把返回的 assistant 消息放进去,后面新长出来的轮次原样留下。
if response.stop_reason == "compaction":
compaction_message = {
"role": "assistant",
"content": response.content,
}
history = [compaction_message] + history[sent_count:]
# sent_count = number of messages sent in the compaction request
中间不要改已经在历史里的消息。换位要发生在块到达后的第一次请求上,这样摘要期间产生的 thinking 才还有效。没有块回来就保持完整历史,稍后再试。
阈值压缩若打开pause_after_compaction,响应里往往只有这块,stop_reason也是compaction。你把要保留的原文接在块后面,再发下一次请求。注意:在 Claude Fable 5.1 上,如果把旧 assistant 轮次重新插回块后,要去掉其中的thinking / redacted_thinking,或带thinking-binding-controls-2026-08-01并设置prefix_mismatch_behavior: "drop_block",否则可能 400。那些思考块是对着完整历史生成的,对不上压缩后的对话。
和 prompt cache 怎么叠
压缩和 prompt caching 可以一起用。可以在 compaction 块上加cache_control: {"type":"ephemeral"},让摘要单独进缓存。系统提示末尾再加一个断点,压缩发生时系统提示的缓存还能命中,只用重写摘要这一段。
计费时不要只看顶层usage。压缩迭代的 token 可能记在usage.iterations里。落盘侧仍然只关心块和尾部,不必把 usage 当成恢复会话的契约。
用 JSONNote 核对块和历史
压缩前后的 JSON 体积差很大,字段又不能靠眼睛扫。调试时对位置,不对文采。
-
1
先格式化落盘文件
把 session JSON 丢进JSON 格式化,确认
compaction_block.type是compaction,signature还在。 -
2
再校验块的形状
用JSON Schema写一份最小 schema:
type枚举只有compaction;content允许 string 或 null;signature/encrypted_content有就当 string。禁止空字符串。 -
3
对比压缩前后的 messages
用JSON Diff看队头少了哪些轮次、块后面多了什么。按需压缩后,块前面不应该再出现被摘要过的 user 消息。
-
4
复盘时打码签名
摘要正文可以给人看。签名和不透明字段用Hash 分享前先打码。它们能续上会话,不要当普通文本发出去。
常见问题
Conversation Compaction 是把整段对话删掉吗?
不是。API 用一段摘要替换已经被压缩的旧消息。摘要装在 type 为 compaction 的内容块里。你必须把这块原样回传,会话才能从摘要继续。
恢复 session 最少要存哪些 JSON?
至少存最新的 compaction 块(含 content、signature、encrypted_content)、块后面的全部 messages,以及当时的 system 和 tools。少存签名或不透明字段,下一次请求接不上。
阈值压缩和 9 月的按需压缩有何不同?
阈值压缩走 compact-2026-01-12,在普通请求里按 token 触发。按需压缩走 compact-2026-09-04,单独发 compaction: {type: summarize},返回带签名的块,必须放在 messages 最前面。
自己写一份摘要文本能不能代替 compaction 块?
不能当同一条契约用。平台认的是它签发的块。自己写的 user 文本只是另一条消息,没有 signature,也不能让 API 丢掉块前面的旧内容。
compaction 块能改 content 再发回去吗?
不要改。官方要求原样回传。content 为空字符串不合法;content 为 null 表示压缩失败,服务器当空操作。改签名或 encrypted_content 会让后续请求失败或丢掉摘要。
小结
Conversation Compaction 把「窗口要爆了」收成一块可回传的 JSON。阈值压缩在普通请求里自动发生;9 月的按需压缩让你决定何时压、能不能后台压、最近几轮要不要留原文。
恢复 session 靠的是最新 compaction 块加它后面的 tail,不是压缩前的全文。content、signature、encrypted_content 原样存、原样发。块的位置错了,API 不会假装你还带着那段历史。
把块和 tail 当成两份契约来读。需要人盯的长任务,摘要instructions里写清必须留下的实体和未决问题;需要少打断的 Agent,用按需压缩在后台换块,不要在内存里只留一段自己写的说明。
把 compaction 块和 tail 拆开看