Claude API のコンパクション 長い会話を続ける

Claude.ai エンジニアリング 6分で読めます

会話が長く続いてコンテキストウィンドウの上限に近づくと、それ以上リクエストを送れなくなります。コンパクション(compaction)は、サーバー側で過去のやり取りを自動的に要約して会話を続けられるようにするベータ機能です。本記事では有効にする方法と、実装で最も間違えやすい「レスポンスの返し方」を日本語で整理します。非公式の解説です。

コンパクションは要約 context editing は削除

Claude API には、長い会話を扱う仕組みが2つあります。名前も設定場所も似ていますが、やっていることは正反対です。

設定はどちらも context_management.edits に書きますが、戦略の型(type)が違います

この2つを取り違えると、意図と逆のことが起きます。「古いツール結果はもう要らない」つもりで compact_20260112 を指定すると、削除ではなく要約が走ります。逆に「会話の筋は保ちたい」つもりで clear_tool_uses_20250919 を指定すると、内容が要約されずに消えます。ベータヘッダーも別物なので、型だけ書き換えても動きません。

なお Claude Code の /compact コマンドは名前が同じですが、CLI 側の機能で、ここで説明している API パラメータとは別のものです。

有効にする最小のコード

ベータ版のメッセージエンドポイントを使い、ベータフラグ compact-2026-01-12 を付けて、context_management.edits に戦略を1つ指定します。

response = client.beta.messages.create(
    betas=["compact-2026-01-12"],
    model="claude-opus-5",
    max_tokens=16000,
    messages=messages,
    context_management={
        "edits": [{"type": "compact_20260112"}],
    },
)

TypeScript でも構造は同じです。

const response = await client.beta.messages.create({
  betas: ["compact-2026-01-12"],
  model: "claude-opus-5",
  max_tokens: 16000,
  messages,
  context_management: {
    edits: [{ type: "compact_20260112" }],
  },
});

設定はこれだけです。閾値に達したかどうかの判定も、要約の生成も、サーバー側が行います。クライアント側で「そろそろ要約すべきか」を計算する必要はありません。

content をそのまま返す理由

ここが実装で最も間違えやすい箇所です。 次のターンのために会話履歴へ追記するとき、response.contentそのまま入れてください。

# 正しい
messages.append({"role": "assistant", "content": response.content})

# 誤り — テキストだけを取り出している
text = next(b.text for b in response.content if b.type == "text")
messages.append({"role": "assistant", "content": text})

理由は、コンパクションが発生したとき、レスポンスの contentコンパクションブロックが含まれるからです。API は次のリクエストでこのブロックを見て、圧縮済みの履歴を差し替えます。テキストだけを取り出して文字列として追記すると、このブロックが失われます。

やっかいなのは、この失敗がエラーにならないことです。リクエストは通り、応答も返ってきます。ただコンパクションの状態が静かに失われ、会話が伸びるにつれてトークン数が減らなくなります。「有効にしたはずなのに効いていない」という症状のほとんどはこれです。

チャットの画面に文字列を出したい場合は、表示用にテキストを抜き出すのは構いません。履歴に積むオブジェクトと、画面に出す文字列を分けるのがポイントです。

messages.append({"role": "assistant", "content": response.content})  # 履歴には content ごと

text_block = next((b for b in response.content if b.type == "text"), None)
print(text_block.text if text_block else "")                          # 表示はテキストだけ

いつ発火するか・対応モデル

発火の閾値は既定で 15万トークンです。会話がこの水準に近づくと、サーバー側が自動的に過去の文脈を要約します。1Mトークンのコンテキストウィンドウを持つモデルでも、上限そのものではなく、この閾値が基準になります。

対応モデルは、Claude Fable 5、Claude Opus 5、Claude Opus 4.8、Claude Opus 4.7、Claude Opus 4.6、Claude Sonnet 5、Claude Sonnet 4.6 です。いずれもベータ扱いで、ベータヘッダーが要ります。

提供面では、Claude API に加えて Claude Platform on AWS、Amazon Bedrock、Google Vertex AI、Microsoft Foundry でもベータとして利用できます。この点はコンテキスト編集と同じ扱いです。

コンパクションが実際に走ったかどうかは、レスポンスの content にコンパクションブロックが含まれるかで判断できます。トークン数の推移を監視しているなら、usage の値が会話の伸びに対して頭打ちになる形で観測できます。

どちらを選ぶかの判断

2つの機能は排他ではありませんが、狙いが違うので選び分けが要ります。

長時間動くエージェントでは、両方を併用する構成もよく使われます。ツール結果はコンテキスト編集で削り、それでも伸びる会話本体はコンパクションで畳む、という分担です。さらに、セッションをまたいで状態を残したい場合はメモリツールが別途あります。セッション内の圧縮(コンパクション・コンテキスト編集)と、セッション間の永続化(メモリ)は別の層だと考えてください。

最後に注意点を2つ。第一に、要約である以上、細部は失われます。あとで正確に参照する必要がある事実は、会話履歴に頼らずファイルや外部ストレージに書き出しておくほうが安全です。第二に、コンパクションが起きるとプロンプトの前半が書き換わるため、そこに掛けていたプロンプトキャッシュは無効になります。キャッシュのヒット率を追っているなら、この地点で落ちるのは想定どおりの挙動です。

本記事は Anthropic の公式仕様に基づく非公式の日本語解説です。ベータ機能のため、フラグ名や仕様は変更される可能性があります。実装前に公式ドキュメントで最新の内容をご確認ください。