Claude Platform Docs
Messagesコンパクション

バックグラウンドでのコンパクション

会話が完全な履歴で継続している間にオンデマンドコンパクションの要約をリクエストし、到着した時点でブロックを差し替えます。

「background compaction」(バックグラウンドコンパクション)は、しばしば「async compaction」(非同期コンパクション)とも呼ばれ、コンパクションループにおける2つの点を変更します。コンパクションリクエストは会話が完全な履歴で継続している間に実行され、差し替えはブロックが到着するまで待機します。要約から続ける要約が返されない場合やエラーを処理するは、変更なしでそのまま適用されます。

作業を継続しながら差し替えを行う仕組み

コンパクションリクエストと、それが返すブロックは、ループの場合と同じです。リクエストを送信してからその結果を使用するまでの間に履歴は増えていくため、差し替えではその増加分をそのまま残す必要があります。

  1. 現時点の履歴でコンパクションリクエストを送信し、そこに含まれていたメッセージの数を記録します。
  2. そのリクエストの実行中も、完全な履歴で会話を継続します。新しいターンはそれぞれ追加し、履歴にすでにあるものは編集せず、このリクエストが差し替えられるか失敗するまでは別のコンパクションリクエストを開始しないでください。
  3. stop_reason"compaction" のレスポンスが到着したら、送信したメッセージとまったく同じ分だけを履歴の先頭から削除し、その位置に返されたメッセージを配置します。ステップ1以降に追加されたすべてのターンは、その後ろに残ります。
  4. ブロックが到着した後の最初のリクエストで差し替え済みの履歴を送信し、要約の作成中に生成された思考が有効なままになるようにします。

たとえば、コンパクションリクエストにメッセージ1〜5が含まれており、その実行中に会話にメッセージ6〜8が追加された場合、差し替え後の履歴はブロックの後にメッセージ6〜8が続く形になります。

Request sentmessages 1–512345While it runs6–8 arrive12345678compaction request: 1–5After the swapblock, then 6–8compaction block678

レスポンスの stop_reason がそれ以外の値である場合、要約は生成されておらず、これはステップ2における失敗として扱われます。完全な履歴を保持してください。要約が返されない場合やエラーを処理するに、その原因とそれぞれの対処方法が記載されています。

バックグラウンドで要約をリクエストする

コンパクションリクエストは、他のリクエストと同様に「rate limits」(レート制限)にカウントされ、その実行中はアプリケーションで2つのリクエストが同時に開いた状態になります。会話は差し替えまで完全な履歴で増え続けるため、その間に到着するターンを収める余裕が「context window」(コンテキストウィンドウ)にまだあるうちにコンパクションリクエストを開始してください。

次のプログラムは、ループでコンパクションするのループから、コンパクションリクエストを会話の処理経路から切り離したものです。この例は2つのリクエストを同時に実行することに依存しているため、PHP版はありません。ハイライトされた行はループとの違いを示しており、その後のリストではプログラムが実行する順にそれらを説明します。

from concurrent.futures import Future, ThreadPoolExecutor

import anthropic
from anthropic.types.beta import BetaMessage, BetaMessageParam

client = anthropic.Anthropic()
executor = ThreadPoolExecutor(max_workers=1)

# 実際の入力予算に近い値を設定してください。ここでは短い会話でもコンパクションされるよう低くしています。
COMPACT_AT_TOKENS = 2500
SYSTEM = "You help design a recipe app's data model. Keep answers short."

QUESTIONS = [
    "What are the main entities in the data model?",
    "Which fields should Recipe have?",
    "Which fields should Ingredient have?",
    "Which fields should RecipeIngredient have?",
    "Which fields should Step have?",
    "Which indexes should these tables have?",
    "Which fields should be required?",
    "Which fields should have default values?",
]


def swap_in(history: list[BetaMessageParam], summary: BetaMessage, sent: int) -> None:
    if summary.stop_reason == "compaction":
        # コンパクションリクエストが保持していたメッセージだけを正確に置き換えます。
        # 以降のターンはブロックの後ろに残ります。
        history[:sent] = [{"role": "assistant", "content": summary.content}]
        print(f"Swapped {sent} messages")


history: list[BetaMessageParam] = []
pending: Future[BetaMessage] | None = None
sent = 0
for turn, question in enumerate(QUESTIONS, start=1):
    if pending is not None and pending.done():
        swap_in(history, pending.result(), sent)
        pending = None

    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})

    # 次のリクエストではこの応答も送信されるため、カウントに含めます。
    conversation_tokens = response.usage.input_tokens + response.usage.output_tokens
    if (
        conversation_tokens > COMPACT_AT_TOKENS
        and turn < len(QUESTIONS)
        and pending is None
    ):
        sent = len(history)
        pending = executor.submit(
            client.beta.messages.create,
            model="claude-opus-5-5",
            max_tokens=4096,
            system=SYSTEM,
            betas=["compact-2026-09-04"],
            messages=history.copy(),
            compaction={"type": "summarize"},
        )

# 保存する前に、まだ生成中の要約に差し替えます
# または会話を続行します。
if pending is not None:
    swap_in(history, pending.result(), sent)
executor.shutdown()
  • コンパクションのタイミングの判断: サイズのチェックでは、保留中のコンパクションリクエストがないことも条件になります。
  • リクエストの開始: ループではコンパクションのレスポンスを待機していた箇所で、このバージョンでは履歴に含まれるメッセージの数を記録し、各言語固有の並行処理ツールを使って履歴のコピーでリクエストを開始し、待機せずに次のターンへ進みます。
  • 結果の確認: 各ターンの冒頭で、プログラムは保留中のリクエストが完了したかどうかを確認します。完了している場合、プログラムはそのターンのリクエストを送信する前に差し替えを行います。
  • 差し替えの実行: ループでは履歴全体を返されたメッセージで置き換えていた箇所で、このバージョンの差し替え関数は、リクエストに含まれていたメッセージだけを先頭から数えて置き換え、それ以降に追加されたものはすべて保持します。
  • ループの終了: ループの終了時にコンパクションリクエストがまだ保留中の場合、プログラムはその完了を待って差し替えを行います。これにより、会話を保存または継続する前に、まだ到着途中の要約が失われることはありません。

stop_reason のチェックはループから変更されていません。ブロックを含まないレスポンスの場合、履歴は元のまま残ります。保留中のものがなくなるため、プログラムはその後、新しいコンパクションリクエストを開始できます。

要約の作成中に思考を有効に保つ

要約の作成中に到着したターンは、残されるターンです。「preserved thinking」(保持された思考)に対応したモデルで思考ブロックを送り返す場合、それらのターンの思考は、残した思考が有効なままとなる条件が満たされている間のみ有効です。

Compatibility

Supported models
  • Fable 5 and 5.1
  • Mythos 5, 5.1, and Preview
  • Opus 4.6, 4.7, 4.8, 5, and 5.5
  • Sonnet 4.6 and 5
Supported platforms
  • Claude APIBeta
  • Claude Platform on AWSBeta
  • Google CloudBeta
  • Microsoft FoundryBeta

Was this page helpful?