博客 • 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 拆開看