Claude Media
Compactionとプロンプトキャッシュを両立させるcache_controlの置き方

Compactionとプロンプトキャッシュを両立させるcache_controlの置き方

Compactionで会話を要約すると、そのままではsystem promptのキャッシュまで巻き込んで無効化されます。system prompt側に別のcache_controlを置いて防ぐ設計を実装例で示します。

system promptの末尾に独立したブレークポイントを置く

Compactionの影響がキャッシュに出るのは、要約が挟まった次のリクエストです。古いターンが要約ブロックに置き換わり、前回までに書き込んだキャッシュを読み出す足場が消えます。対策は、system の末尾に単独の cache_control を置き、system promptを会話とは別のキャッシュエントリにしておくことです。

ここで扱うのは、context_management に compact_20260112 を指定してAPIが自動で要約を挟む、しきい値型のCompactionです。ベータヘッダーは compact-2026-01-12 で、対応モデルには claude-opus-5・claude-sonnet-5-5・claude-fable-5-1 などが並びます。要約ブロックを会話に戻す実装はCompaction blocksを会話に戻す実装パターンにあります。

要約が入ると、なぜsystem promptまで書き直しになるのか

プロンプトキャッシュはリクエスト先頭からの一致(プレフィックスマッチ)で判定され、並びは tools → system → messages の順です(仕組みの全体像はClaudeのプロンプトキャッシュの仕組みを参照)。会話の末尾にだけブレークポイントを置いた構成では、Compactionの前後で次の3段階が進みます。

手順

末尾だけにブレークポイントを置いた構成で起きること

  1. 1

    通常のターン

    書き込みが起きるのは会話の末尾だけです。次のターンは、そこから最大20ブロック遡る探索で前回の書き込みを見つけて読み出します。

  2. 2

    Compactionの発動

    しきい値に達すると、compaction ブロックより前の内容は読み込まれなくなります。system promptの直後に要約が来る並びに変わります。

  3. 3

    次のリクエスト

    探索が見つけられるのは、以前のリクエストが書き込んだエントリだけです。system promptの末尾には書き込みがなく、読めるものが残りません。system promptも要約と一緒に新規書き込みの対象になります。

固定のガイドラインや大量のツール定義でsystem promptを埋めているエージェントほど、この再書き込みが重くなります。ツール定義はsystemより前に並ぶため、system末尾のブレークポイントはツール定義も含めて守ります。

system promptを分離キャッシュにする実装

system フィールドの末尾要素に cache_control: { type: "ephemeral" } を単独で付けます。Compactionで要約ブロックが増えても、system prompt側のキャッシュは読み出されたままで、新規に書き込むのは要約ブロックの分だけです。

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: compact-2026-01-12" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "max_tokens": 4096,
    "system": [
      {
        "type": "text",
        "text": "You are a helpful coding assistant...",
        "cache_control": { "type": "ephemeral" }
      }
    ],
    "messages": [{ "role": "user", "content": "Hello, Claude" }],
    "context_management": {
      "edits": [{ "type": "compact_20260112" }]
    }
  }'

例の text は短い文字列ですが、実際にキャッシュされるのはモデルごとの最小長を超えたときだけです。claude-opus-5 なら512トークン、Opus 4.6とHaiku 4.5は4,096トークンで、満たない場合はエラーなしでキャッシュなしとして処理されます。短いsystem promptにこの分離を足しても効果は出ません。

会話履歴側にも同じ発想を使えます。Compactionの要約ブロックに cache_control を付けると、要約の内容を独立したキャッシュエントリにできます(TTL内なら読み出されます)。トップレベルの compaction パラメータで要約を挟むon-demand型でも、ブロックに付けた cache_control は要約の直後にブレークポイントを置きます。

{
  "role": "assistant",
  "content": [
    {
      "type": "compaction",
      "content": "[summary text]",
      "cache_control": { "type": "ephemeral" }
    },
    { "type": "text", "text": "Based on our conversation..." }
  ]
}

1つのリクエストに置けるブレークポイントは最大4つです。長期会話のエージェントでは、次の配置が出発点になります。

置く位置守る対象TTLの目安
system の末尾守る対象ツール定義とsystem promptTTLの目安リクエスト間隔が5分を超えうるなら1時間
compaction ブロック守る対象直近の要約TTLの目安間隔が5分を超えうるなら1時間、続くなら既定の5分
会話の末尾守る対象要約より後の通常ターンTTLの目安次のリクエストが5分以内なら既定の5分

ブレークポイントを増やしても、それ自体に費用はかかりません。課金されるのは、キャッシュへの書き込み、キャッシュからの読み取り、キャッシュされない通常の入力トークンです。置く位置を増やす代わりに増えるのは、どこまでを独立して使い回せるかという選択肢です。

TTLは1時間のものを前に置く

キャッシュのTTL(生存時間)は既定で5分で、使われるたびに5分のカウントがリセットされます。1時間にするには cache_control: { type: "ephemeral", ttl: "1h" } と書きます。

くらべる

5分TTLと1時間TTL

ephemeral

5分(既定)

書き込みは基本入力単価の1.25倍です。リクエストが5分以内に続くチャットや、ツール呼び出しが連続するエージェントに向きます。

ttl: 1h

1時間

書き込みは基本入力単価の2倍です。1回の応答が長く、次のリクエストが5分を超えて始まりうる運用で、system promptの再書き込みを避けられます。

読み取りは基本入力単価の10%で、Claude Opus 5.5とSonnet 5.5は5%、Claude Fable 5.1は2.5%です。2つのTTLを同じリクエストに混ぜるときは、1時間のエントリを5分のエントリより前に置く必要があります。system末尾を1時間、要約ブロックと会話末尾を5分にする組み合わせは、この順序に収まります。

system promptを分離しても、会話履歴側のキャッシュはTTLが切れれば書き直しです。分離が防ぐのは「要約が挟まったことでsystem promptまで巻き添えになる」ことだけで、会話全体のキャッシュを保つ仕組みではありません。

分離しても残る、キャッシュを割る操作

system末尾のキャッシュが無傷なのは、tools と system を変えないあいだです。プロンプトキャッシュのドキュメントでは、ツール定義の変更は tools・system・messages のすべてを無効にし、Web検索や引用のオン・オフは system 以降を無効にします。Compactionを挟む長期運用の途中でこれらを切り替えると、分離の効果が消えます。

tool_choice の変更や画像の追加・削除は、無効になるのが messages だけです。system分離はそのまま残ります。thinkingの設定や output_config.effort を変えたときは、messages が必ず無効になります。tools と system まで巻き込むかどうかはモデルによって違うため、Compactionと併用する長期セッションでは、途中で値を変えない設計が安全です。

指示を途中で追加したいときの逃げ道もあります。messages の中に {"role": "system"} のメッセージを足せば、トップレベルの system を触らずに済み、systemとmessagesのキャッシュが保たれます。対象はClaude Fable 5.1・Mythos 5.1・Fable 5・Mythos 5・Opus 5.5・Opus 4.8・Opus 5・Sonnet 5.5で、Sonnet 5では使えません。

通常の長い会話では同じ対策は要らない

Compactionを使わず、会話に追記するだけの運用なら、末尾のブレークポイントだけで足ります。前のターンの内容が変わらないので、次のリクエストの探索が前回の書き込みを見つけるためです。注意が要るのは、1ターンで20ブロック以上を足すケースです。探索の窓から前回の書き込み位置が外れるため、近い位置に第2のブレークポイントを置きます。

Compactionは、この「前のターンの内容が変わらない」前提を崩します。対策の重みが上がるのはそのためです。

使用量を検証する手順

分離が効いているかは、レスポンスの usage で確かめます。Compactionを有効にすると、要約が発生しなかったリクエストでも usage.iterations が返ります。compaction 型のエントリが現れるのは、そのリクエストで新しく要約が発生したときだけです。以前の要約ブロックを再利用するだけなら、追加の要約コストは発生しません。

キャッシュを併用すると、input_tokens に入るのは最後のブレークポイント以降の分だけです。次のリクエストの規模を見積もるときは、cache_read_input_tokens と cache_creation_input_tokens も足します。

要約の生成は追加のサンプリングステップで、レート制限と課金の両方に計上されます。例のとおり、要約イテレーションの入力は18万トークンと、通常のメッセージより大きくなりえます。

{
  "usage": {
    "input_tokens": 23000,
    "output_tokens": 1000,
    "iterations": [
      { "type": "compaction", "input_tokens": 180000, "output_tokens": 3500 },
      { "type": "message", "input_tokens": 23000, "output_tokens": 1000 }
    ]
  }
}
手順

分離の効果を確かめる

  1. 1

    Compactionの前後で2つのリクエストを記録する

    compaction 型のイテレーションを含むリクエストと、その直前のリクエストが対象です。

  2. 2

    課金は iterations を全件合算する

    トップレベルの input_tokens と output_tokens は、compaction 以外のイテレーションの合計です。要約の分を足すには usage.iterations を合算します。

  3. 3

    キャッシュ書き込みの増え方を比べる

    cache_creation_input_tokens と cache_read_input_tokens を前後で並べます。書き込みの増分に要約の分しか乗っていなければ、分離は機能しています。system promptの長さ分が毎回乗るなら、system 末尾のブレークポイントが抜けています。

公式ドキュメントの iterations の例に載っているのは input_tokens と output_tokens だけです。キャッシュ関連のフィールドは、実際のレスポンスで読み取り量と書き込み量を確かめてください。既存の compaction ブロックを反映した実効トークン数は、/v1/messages/count_tokens で見られます。このエンドポイントは新しい要約を発動しません。

サーバーツールとツール定義を伴うエージェントの注意点

Web検索のようなサーバーツールを併用すると、Compactionのしきい値判定はサンプリングの各イテレーションの開始時に走ります。ツール呼び出しが連続すると、1つのリクエストの中で要約が複数回発生することがあります。system promptを分離していれば、その回数にかかわらずsystem prompt側は読み取りのままです。

ツール定義を持つリクエストでは、要約のステップでモデルがツールを呼んでしまい、content: null のcompactionブロックが返ることがあります。instructions に「要約を書く間はツールを呼ばず、テキストだけで答える」旨を書いておくと防げます。要約には、リクエストで指定したモデルがそのまま使われ、別の安いモデルには切り替えられません。

一時停止としきい値の調整

pause_after_compaction を使うと、要約の生成直後にレスポンスを止めて、呼び出し側でメッセージを足してから再開できます(合計トークン予算との組み合わせはCompactionの一時停止で合計トークン予算を強制する実装パターンで扱います)。再開のリクエストでも、system 末尾のブレークポイントを同じ位置に置き続けると、読み取りが保たれます。

要約の頻度は trigger.value で決まります(モデル別の考え方はCompactionの発動しきい値の決め方を参照)。しきい値を下げて要約が増えるほど、system promptの分離はコストに効きます。

この記事を共有:XはてブLinkedIn