原典: Compaction on demand / 日本語版

一行要約

アプリが決めたタイミングで compaction パラメータ付きのリクエストを1本送ると、返答の代わりに署名つきの要約ブロックが1個返り、以降はそれを messages の先頭に置いて要約した範囲の代わりにする方式で、概要ページ(compaction)が「使えるならこちら」と推す側。

要点

プラットフォーム: Claude API / Claude Platform on AWS / Google Cloud / Microsoft Foundry(いずれも beta)。Amazon Bedrock では使えない(閾値方式 compaction-threshold は使える)。以前の取得では「Claude API のみ」だった。

A compaction request is separate from your conversation turns. You send the conversation as it stands with the compaction parameter, and the response contains a single compaction block. The block holds the summary as text you can read, and a signature. Send it in future requests exactly as it came.

compaction のリクエストは会話のターンとは別物である。いまの会話をそのまま compaction パラメータ付きで送ると、応答には compaction ブロックが1個だけ入っている。ブロックは読めるテキストとしての要約と、署名を持つ。以降のリクエストでは、返ってきたとおりに送れ。

要約を頼む

  • beta header compact-2026-09-04 は、要約を頼むリクエストと、ブロックを載せる以降のすべてのリクエストに付ける
  • "compaction": {"type": "summarize"} を送ると、送ったメッセージを全部1回要約し、返答は生成せず、ブロックだけを stop_reason: "compaction" で返す
  • 要約はリクエストのモデル・system・tools・thinking 設定・max_tokens で走る。 要約器はツール定義を読むがツールは実行せず、応答に thinking は付かない。max_tokens は要約前の thinking も含めた全体の上限なので、数千は確保する
  • system と tools は会話の他の部分と同じものを送る。 preserved thinking のあるモデルでブロックの後ろにターンを残すなら、残したターンの thinking が有効なのは system と tools が一致するときだけ
  • 送れないもの: 最後の assistant ターンが結果待ちのツール呼び出しで終わっている(先にツール結果を送る)/stop_sequences/structured outputs の output_config.format/tool_choice の any と tool/context_management(同じリクエストに併用できない)
  • 会話はまだモデルの context window に収まっている必要がある。溢れる前に compact する
  • ストリーミングではブロックが丸ごと届く(content_block_start が完全なブロックを持ち、content_block_delta は来ない)

どのモデルが対応しているかは、Models API を beta header 付きで呼んで各モデルの capabilities.compaction を読む。

要約から続ける(差し替えの規則)

  • ブロックは messages の先頭に置く(単独の assistant メッセージでも、先頭メッセージの最初のコンテンツブロックでもよい。先頭メッセージは user でも assistant でもよい)
  • 要約したメッセージは消す。 ブロックより前に残ると 400(compaction_block_misplaced)
  • 1リクエストに compaction ブロックはちょうど1個を、以降の毎リクエストに載せる
  • ブロックは signature ごと返ってきたまま送る
  • 要約を頼んだ後に進んだターンは、そのままブロックの後ろに続けてよい(これが「裏で要約する」子ページの土台)

Two mistakes in the swap raise no error. If summarized messages remain after the block, the API sends them to Claude again. If a later request leaves the block out, Claude gets no summary.

差し替えの誤りのうち2つはエラーにならない。要約したメッセージがブロックの後ろに残っていれば、API はそれを Claude にもう一度送る。以降のリクエストでブロックを落とせば、Claude は要約を受け取らない。

閾値方式とは向きが逆。閾値方式のブロックは要約した範囲の「後ろ」に来て、API が前を捨てる。

Python で client.messages を使って自分でシリアライズするなら、to_dict() か model_dump(exclude_none=True) を使う(素の model_dump() は citations: null と text: null を足し、API に拒否される)。

もう一度 compact する: すでにブロックで始まる会話に compaction をもう一度送ると、新しいブロックが古い要約とその後ろ全部を要約する。以降は新しいほうだけを送る。

いつ compact するか

完了したターンの後ならいつでも送れる。次のリクエストの大きさは、直前の応答の usage の input_tokens + output_tokens で見積もる(次のリクエストはその返答も送るから)。prompt caching を使っているなら input_tokens は最後のブレークポイントより後ろしか数えないので、cache_read_input_tokens と cache_creation_input_tokens も足す。その値を、モデルの context window より下に自分で決めた上限と比べる。

SDK の tool runner(Python / TypeScript / C# / Go / Java / PHP / Ruby)に要約を任せられる。compact すると決めたら runner の compact_before_next_turn()(言語により compactBeforeNextTurn() / CompactBeforeNextTurn())を呼ぶと、今のターンとそのツール呼び出しが終わった後に要約を頼んで履歴を差し替える。

  • runner は beta header を足さない。 compact-2026-09-04 を付けて runner を作る
  • 古い SDK(Python 1.8.0 / TypeScript 0.128.0 / C# 12.50.0 / Go 1.75.0 / Java 2.65.0 より前)は、要約のリクエストにも stop_sequences などを送ってしまい 400 になる
  • task budget の remaining を設定していると要約のリクエストが 400 になるので、設定しない
  • runner の context_management に compaction の edit があると、runner は compact を拒む。 1つの runner では一方の方式だけを使う

要約のプロンプトを自分で書く

instructions を指定しなければ API の要約プロンプトが使われる。空でない instructions(最大 16,384 文字)はそれを完全に置き換える。 要約器は instructions の有無に関わらず、それ以前の thinking も含めて会話全体を読む(閾値方式では、Claude 5.1 以降で instructions を指定すると見えている会話だけから要約する。compaction-threshold)。instructions には、要約に残すべきものと「ツールを呼ぶな」を書く。

要約が返らないとき・エラー

要約が出るのは、要約の呼び出しがテキストで正常終了しツールを呼ばなかったときだけ。 それ以外は 200 のまま content が空で返り、課金はされる。ブロックを探す前に stop_reason を見る。 どの場合も、要約なしで続けて後で compact し直せる。

stop_reason意味と対処
"max_tokens"要約が途中で切れた。max_tokens を増やして送り直す
"model_context_window_exceeded"要約プロンプトの入る余地が無かった。instructions を短くするかメッセージを減らす
"tool_use"ツールを呼んでしまった。instructions で禁じる
"refusal"拒否された。要約なしで続ける。stop_details にポリシー分類が入る
"end_turn"テキストが返らなかった。要約なしで続ける
エラー原因と対処
529 overloaded_error(error.details.error_code が compaction_unavailable)一時的なサーバ側の問題。再試行してよい
400 compaction_block_misplaced要約したメッセージがブロックの前に残っている
400 compaction_signature_invalid / compaction_content_mismatch返ってきた後にブロックの signature か content を変えた
400(コード無し)ブロックが2個以上ある/最後の assistant ターンが結果待ちのツール呼び出し
400 compaction_nothing_to_summarizemessages に user / assistant の中身が無い(空リストなど)
400(要約を頼むリクエスト)beta header が無い。メッセージが requires anthropic-beta: compact-2026-09-04 と言う
400(ブロックを載せた後のリクエスト)beta header が無い。ただしメッセージは compaction が想定されたブロック型ではないと言うだけで、header に触れない
400 Extra inputs are not permitted(…compaction.citations など)API が返していないフィールドを付けて送り返した

気づきにくいのは header の付け忘れで、要約を頼むリクエストでは header が要ると言われるが、ブロックを載せた後のリクエストでは言われない。

usage の数え方

The top-level input_tokens and output_tokens are zero because no reply was generated. To count what a conversation consumed, sum across usage.iterations, not the top-level fields. Sending a block back on later requests adds no compaction cost.

返答を生成しないので、トップレベルの input_tokens と output_tokens はゼロになる。会話が消費した量を数えるには、トップレベルではなく usage.iterations を合計せよ。 以降のリクエストでブロックを送り返しても compaction の費用は増えない。

併用の制限

  • compaction と context_management は同じリクエストに送れない。 閾値 compaction(compact_20260112)も、署名つきブロックを積んだリクエストでは走らない
  • prompt caching: ブロックに cache_control を置くと、要約の後ろにブレークポイントが置かれる
  • 要約した範囲の role: "system" メッセージは一緒に要約され、効力を失う。 まだ効かせたい指示は role: "system" で言い直す。置き場所は次の新しい user ターンの直後で、以降も履歴に残しておく
  • task budget の remaining(output_config.task_budget.remaining)を compaction と一緒に、またはブロックを積んだリクエストに送ると 400
  • token counting エンドポイントは compaction パラメータを無視する
  • 画像・ドキュメント・container_upload・取得した URL は要約に載らない。 ブロックが置き換えた時点で消えるので、後のターンで要る物は言い直すかアップロードし直す

そのまま使える具体例

要約だけを取りに行く(返るのはブロック1個):

response = client.beta.messages.create(
    model="claude-opus-5-5",
    # max_tokens caps the whole call, including any thinking, so allow several thousand tokens.
    max_tokens=4096,
    betas=["compact-2026-09-04"],
    messages=history,
    compaction={"type": "summarize"},
)
print(f"Stop reason: {response.stop_reason}")

ターンごとにトークンを数え、上限を超えたら compact するループ(原典の例。上限 2,500 はわざと低い。実際は入力予算の近くに置く):

COMPACT_AT_TOKENS = 2500
SYSTEM = "You help design a recipe app's data model. Keep answers short."
 
history: list[BetaMessageParam] = []
for turn, question in enumerate(QUESTIONS, start=1):
    history.append({"role": "user", "content": question})
    response = client.beta.messages.create(
        model="claude-opus-5-5",
        max_tokens=8192,
        system=SYSTEM,
        betas=["compact-2026-09-04"],
        messages=history,
    )
    history.append({"role": "assistant", "content": response.content})
 
    # The next request sends this reply too, so count it.
    conversation_tokens = response.usage.input_tokens + response.usage.output_tokens
    if conversation_tokens > COMPACT_AT_TOKENS and turn < len(QUESTIONS):
        summary = client.beta.messages.create(
            model="claude-opus-5-5",
            max_tokens=4096,
            system=SYSTEM,
            betas=["compact-2026-09-04"],
            messages=history,
            compaction={"type": "summarize"},
        )
        if summary.stop_reason == "compaction":
            history = [{"role": "assistant", "content": summary.content}]
            print(f"Compacted before turn {turn + 1}")

履歴は追記ではなく置き換える(返ったメッセージが、要約に送ったメッセージ全部の代わりになる)。stop_reason を先に見る —— 要約が返らなかったら履歴はそのままにして、次のターンの後にもう一度頼む。

原典で言及されている関連文書