Compactionとプロンプトキャッシュを両立させるcache_controlの置き方
Compactionで会話を要約すると、そのままではsystem promptのキャッシュまで巻き込んで無効化されます。system prompt側に別のcache_controlを置いて防ぐ設計を実装例で示します。
CompactionとプロンプトキャッシュのCache Controlを両立させるには
Compaction(compact_20260112)は長時間の会話を自動要約してコンテキストウィンドウの限界を先送りするベータ機能です(積み戻し実装はCompaction blocksを会話に戻す実装パターンを参照)。要約が挟まると会話の先頭部分がまるごと差し替わるため、何も対策しないとプロンプトキャッシュのヒット率が急落します。対処はシンプルで、system promptの末尾に単独の cache_control を置き、要約とは別キャッシュに切り分けることです。
キャッシュが割れる理由から見ていきます。プロンプトキャッシュはリクエストの先頭からの一致(プレフィックスマッチ)でヒットを判定します(仕組みの全体像はClaudeのプロンプトキャッシュの仕組みを参照)。system promptと会話履歴を1本の連続したプレフィックスとしてキャッシュしていた場合、Compactionが会話部分に新しい要約ブロックを差し込むと、そこから後ろだけでなくsystem promptを含む全体のキャッシュキーが変わってしまいます。要約自体は数百〜数千トークン程度でも、長いsystem promptを毎回丸ごと再書き込みするコストが乗ります。
system promptを分離キャッシュにする実装
対処は system フィールドの末尾要素に cache_control: { type: "ephemeral" } を単独で付けることです。これでsystem promptは会話とは独立したキャッシュエントリになります。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" }]
}
}'会話履歴側にも同様の発想を使えます。Compaction要約ブロック自体に cache_control を付ければ、要約は要約で1つのキャッシュエントリとして固定され、その後の通常ターンだけが新規書き込み対象になります。
{
"role": "assistant",
"content": [
{
"type": "compaction",
"content": "[summary text]",
"cache_control": { "type": "ephemeral" }
},
{ "type": "text", "text": "Based on our conversation..." }
]
}system prompt用のブレークポイントと、compactionブロック用のブレークポイントは役割が異なります。前者は「長いsystem promptを何度要約が挟まっても書き直させない」ための固定点、後者は「要約そのものを次の要約が発生するまで使い回す」ための固定点です。両方を置いて初めて、Compactionが繰り返し発生する長期会話でもキャッシュ書き込みコストが積み上がらなくなります。
キャッシュブレークポイントを分けないとどうなるか
この設計をしなかった場合の挙動を具体的に書くと、Compactionが1回発生するたびに次の3つが連鎖します。①会話履歴の先頭に要約ブロックが挿入される ②system prompt + 要約 + 直近ターンを1本のプレフィックスとしてキャッシュキーを再計算する ③system promptがどれだけ長くても、要約が挟まった時点で丸ごとキャッシュ書き込み(cache_creation_input_tokens)扱いになる。system promptを固定のガイドライン集や大量のツール定義で埋めているエージェントほど、この再書き込みコストが重くなります。
system promptを分離キャッシュにしておけば、Compactionが何度発生してもsystem prompt部分は読み取りキャッシュ(cache_read_input_tokens)のまま維持されます。使用状況はレスポンスの usage フィールドで確認でき、cache_read_input_tokens が想定どおり大きく、cache_creation_input_tokens がCompaction直後だけ小さく立つ、という分布になっていれば設計は機能しています。逆に毎ターン cache_creation_input_tokens が大きいままなら、system prompt側のブレークポイントが抜けているサインです。
エージェント設計への影響
Compactionはエージェントの長時間タスクやマルチターンのチャットで使われることが多く、system promptにはツール定義や振る舞いの指示が数千トークン単位で入るケースが珍しくありません。system prompt側のキャッシュ分離を怠ると、Compactionのたびにこの数千トークンを再課金することになり、Compactionが「コンテキストを延ばすための機能」であるにもかかわらず、コスト面ではむしろ悪化しかねません。
このパターンはCompactionに限らず、会話の一部だけを差し替える構成すべてに共通します。会話履歴を要約・圧縮・書き換えるたびに、差し替わらない固定部分(system prompt、ツール定義)は独立したキャッシュブレークポイントで守る、という原則で設計すると見通しが良くなります。
Compactionの一時停止機能とキャッシュ設計の関係
Compactionには pause_after_compaction というオプションもあり、要約生成直後にレスポンスを一度止めて呼び出し側で内容を確認・調整できます(一時停止と合計トークン予算の組み合わせはCompactionの一時停止で合計トークン予算を強制する実装パターンで扱います)。一時停止を挟む運用でも、system prompt分離の原則は変わりません。一時停止から再開する際に送るリクエストでも、system prompt側のキャッシュブレークポイントを毎回同じ位置に置き続けることが、キャッシュヒット率を落とさない条件になります。
要約が発生する頻度そのものを調整したい場合は、trigger.value のしきい値設計が別の論点になります(モデル別の考え方はCompactionの発動しきい値の決め方を参照)。しきい値を下げるほどCompactionの発生回数が増え、その分だけsystem prompt分離の有無がコストに効いてくる場面も増えます。
キャッシュのTTL選択がCompactionの頻度と絡む理由
system prompt側のキャッシュは既定で5分のTTL(生存時間)です。キャッシュされた内容が使われるたびに、追加コストなしで5分間のカウントダウンがリセットされます。5分では短すぎる場合は1時間TTL(cache_control: { type: "ephemeral", ttl: "1h" })を追加コストで選べます。
Compactionの発動しきい値を低く設定するほど要約が頻繁に発生し、その間隔がキャッシュのTTLより長くなると、system promptを分離していても会話履歴側のキャッシュはTTL切れで結局書き直しになります。system prompt分離は「要約が挟まったときにsystem promptまで巻き添えにしない」ための対策であり、「会話全体のキャッシュを常温に保つ」対策ではありません。Compactionの発生間隔が数分単位で頻発するワークロードでは、system prompt分離に加えて1時間TTLの採用も検討する価値があります。
usage.iterationsでキャッシュ設計の効果を検証する
system prompt分離が実際に効いているかは、レスポンスの usage フィールドで確認します。Compactionを有効にすると、レスポンスには常に usage.iterations 配列が含まれます。
{
"usage": {
"input_tokens": 23000,
"output_tokens": 1000,
"iterations": [
{ "type": "compaction", "input_tokens": 180000, "output_tokens": 3500 },
{ "type": "message", "input_tokens": 23000, "output_tokens": 1000 }
]
}
}Compactionが発生したリクエストでは compaction イテレーションが先頭に入り、その後に本来の message イテレーションが続きます。トップレベルの input_tokens / output_tokens はCompactionイテレーション分を含みません。課金・コスト集計を usage.input_tokens だけで行っていた実装は、Compaction有効化後は usage.iterations を全件合算するロジックへ書き換える必要があります。ベータを有効にしていれば要約が発生しなかったリクエストでも iterations 配列自体は返りますが、compaction 型のエントリは新規にCompactionが発生した回だけに現れ、以前生成したCompactionブロックを再利用するだけの場合は追加コストが発生しません。
キャッシュ設計の検証はこの iterations 配列と、各イテレーションの cache_read_input_tokens / cache_creation_input_tokens を突き合わせて行います。system promptを分離できていれば、compaction イテレーションの cache_creation_input_tokens は要約分の増分にとどまり、system promptの長さに引きずられて跳ね上がることはありません。逆にsystem prompt分だけ cache_creation_input_tokens が毎回大きく計上されているなら、ブレークポイントの位置を見直します。
Web検索などのサーバーツールと組み合わせる場合の注意
Web検索のようなサーバーツールを併用すると、Compactionのトリガー判定はサンプリングの各イテレーション開始時にチェックされます。1回のリクエスト内でツール呼び出しが連続すると、しきい値と出力量の組み合わせ次第で1リクエスト中にCompactionが複数回発生することがあります。system prompt分離のキャッシュ設計をしていれば、Compactionが1リクエスト内で何度発生してもsystem prompt側は影響を受けません。分離していない場合は、複数回のCompactionのたびにsystem prompt相当のキャッシュ書き込みが重複して発生し、コストへの影響がさらに大きくなります。
Compactionとプロンプトキャッシュに関するよくある質問
system promptを分割しても効果は同じか
system フィールドを複数ブロックに分けて末尾だけに cache_control を付ける場合でも、キャッシュはプレフィックス一致で判定されるため効果は変わりません。重要なのは「会話履歴側の変化がsystem prompt側のキャッシュキーに波及しない位置」にブレークポイントを置くことです。
Compactionを使わない通常の長い会話でも同じ対策は必要か
必要です。会話が伸びるだけでキャッシュの末尾は動きますが、system promptが固定なら分離しておくメリットは同じです。Compactionはこの問題を「要約挿入」という形でより頻繁に、かつ予測しにくいタイミングで発生させるため、対策の重要度が上がります。