ブログ • 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"} |
単独の1件。stop_reason は compaction。通常の返答は無い |
閾値圧縮の発火は既定で input_tokens 150000、下限は 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 ブロック1つだけ。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 を「以前の圧縮が残した不透明なメタデータであり、verbatim で round-trip せねばならない」と書く。クライアントで分解せず、要約本文としても見せない。失敗ブロックの content が null のとき、サーバは空操作として扱う。回送はできるが、有効な要約にはならない。
session 復元のために保存すべきフィールド
Agent が再起動すると、メモリ上の messages は消える。復元すべきは「圧縮前の会話全文」ではなく、圧縮後もリクエストを続けられる状態だ。
-
1
最新の compaction ブロック
JSON ブロック全体。
content、signature、encrypted_contentを含む。複数あるときは最新の1つだけでよい。再圧縮すると古い要約とその後ろをまとめて食う。 -
2
ブロック後ろの messages
今回の要約が覆わなかった user / assistant ターンを、元の順で。バックグラウンド圧縮のあいだに伸びたターンもここにある。
-
3
当時の system と tools
オンデマンド圧縮では、要約リクエストに会話の残りと同じ
systemとtoolsを付ける。thinking を残すモデルでは、ブロック後ろのターンの thinking が無効にならないかは、この二項目を変えたか、および tail が要約されたメッセージに密着しているかで決まる。 -
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 の許否を自動判定するのか。
Claude はどう AI 研究開発ワークフローに関わるのか。
バックグラウンド圧縮と末尾を残すときの入れ替え
オンデマンド圧縮のリクエストは、受け取ったメッセージだけを要約する。だから古いターンだけを圧縮に出し、直近数ターンの原文はローカルに残し、戻ってきたブロックをそれらのターンの前に差し込める。公式名は keep-tail。
バックグラウンド圧縮(async)はさらに進む。要約が走っているあいだ、会話は完全な履歴のまま先へ行く。戻ってきたら履歴全体を1ブロックに置き換えてはいけない。先頭から「当時圧縮に送った分」だけを外し、返ってきた 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 に記されることがある。保存側が見るのは相変わらずブロックと tail であり、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 は圧縮失敗を意味し、サーバは空操作として扱う。signature や encrypted_content を変えると、後続リクエストは失敗するか、要約を失う。
まとめ
Conversation Compaction は「ウィンドウが弾けそう」を、回送できる JSON ブロックに収める。閾値圧縮は通常のリクエスト内で自動で起きる。9 月のオンデマンド圧縮は、いつ圧縮するか、バックグラウンドでよいか、直近ターンの原文を残すかをあなたが決める。
session の復元は、最新の compaction ブロックとその後ろの tail であり、圧縮前の全文ではない。content、signature、encrypted_content は原状のまま保存し、原状のまま送る。ブロックの位置が違えば、API はあの履歴をまだ持っているふりをしない。
ブロックと tail を二つの契約として読む。人が見張る長いタスクでは、要約の instructions に残すべき実体と未決の問題を書く。割り込みを減らしたい Agent は、オンデマンド圧縮でバックグラウンドにブロックを入れ替える。メモリに自分で書いた説明だけを残さない。
compaction ブロックと tail を分けて見る