Claude API Automatic Prompt Caching実践ガイド — cache_control一行でAPIコストを最大90%削減する
Claude APIのコストに頭を悩ませていませんか? 2026年2月、Anthropicは Automatic Prompt Caching を大幅アップデートしました。従来は複数のキャッシュブレークポイントを手動で管理する必要がありましたが、今は cache_control: {type: 'ephemeral'} を一行追加するだけ。システムが自動的に最適なブレークポイントを配置・移動し、長時間のエージェントセッションでは実測でコストが1/5〜1/10になります。
本記事では、仕組みの理解から実装パターン、コスト試算、ハマりポイントまでを一気通貫で解説します。
Automatic Prompt Cachingとは — 手動管理からの解放
従来の手動キャッシュブレークポイントの課題
従来のPrompt Cachingでは、キャッシュしたいブロックの末尾に cache_control を個別に配置する必要がありました。会話ターンが増えるたびに、キャッシュブレークポイントの位置を手動で移動しなければならず、実装の複雑さが導入の大きな障壁になっていました。
// Before: 手動管理 — ターンが増えるたびにcache_controlの位置を移動する必要がある
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
system: [
{
type: "text",
text: "あなたはTypeScriptの専門家です。...(長いシステムプロンプト)...",
cache_control: { type: "ephemeral" }, // ← システムプロンプト末尾に手動配置
},
],
messages: [
{ role: "user", content: "関数型プログラミングについて教えて" },
{ role: "assistant", content: "関数型プログラミングとは..." },
{
role: "user",
content: [
{
type: "text",
text: "具体的なコード例を見せて",
cache_control: { type: "ephemeral" }, // ← 最新メッセージの直前に手動配置
},
],
},
],
});
自動キャッシュの仕組み:一行追加で何が起きるか
Automatic Prompt Cachingでは、リクエストのトップレベルに cache_control を1つ置くだけで、システムが自動的に最適なブレークポイントを配置します。キャッシュ階層には優先順位があり、tools → system → messages の順でキャッシュが適用されます。会話が成長するにつれ、ブレークポイントは自動的に前方へ移動します。
// After: 自動管理 — トップレベルに一行追加するだけ
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
cache_control: { type: "ephemeral" }, // ← これだけ
system: "あなたはTypeScriptの専門家です。...(長いシステムプロンプト)...",
messages: [
{ role: "user", content: "関数型プログラミングについて教えて" },
{ role: "assistant", content: "関数型プログラミングとは..." },
{ role: "user", content: "具体的なコード例を見せて" },
],
});
正直、最初にこの差分を見たときは「本当にこれだけ?」と疑いました。でも本当にこれだけです。
料金体系とコスト削減シミュレーション
キャッシュ読取・書込の料金倍率
Prompt Cachingの料金構造はシンプルです。
| 操作 | ベース入力に対する倍率 |
|---|---|
| キャッシュ読取 | 0.1倍(90%OFF) |
| キャッシュ書込(5分TTL) | 1.25倍 |
| キャッシュ書込(1時間TTL) | 2.0倍 |
5分キャッシュ vs 1時間キャッシュの損益分岐点
- 5分TTL: 書込コスト1.25倍 → 1回の再利用で元が取れる(2回目の読取0.1倍で合計1.35倍 < キャッシュなし2.0倍)
- 1時間TTL: 書込コスト2.0倍 → 2回の再利用で元が取れる(書込2.0 + 読取0.1×2 = 2.2倍 < キャッシュなし3.0倍)
モデル別・会話ターン別コスト試算表
Claude Opus 4.6で、8,000トークンのsystemプロンプト + 10ターン会話(各ターン平均1,000トークン)を想定したコスト比較です。
| 方式 | 入力トークンコスト(相対値) | 削減率 |
|---|---|---|
| キャッシュなし | 100% | — |
| 5分TTLキャッシュ | 約19% | 81%削減 |
| 1時間TTLキャッシュ | 約15% | 85%削減 |
エージェントワークフローのように数秒〜数十秒間隔でリクエストを送るケースでは、5分TTLで十分です。一方、ユーザーが数分〜数十分おきに操作するチャットボットや、バッチ処理の間欠的な利用では1時間TTLを検討してください。
実装パターン:TypeScript / Python コード例
基本パターン:チャットボットの会話キャッシュ
最もシンプルなパターンとして、systemプロンプトをキャッシュする例を示します。
TypeScript:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
async function chat(conversationHistory: Anthropic.MessageParam[]) {
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 4096,
cache_control: { type: "ephemeral" }, // 自動キャッシュ有効化
system:
"あなたはフルスタック開発のエキスパートです。...(長いシステムプロンプト)...",
messages: conversationHistory,
});
// キャッシュヒット状況を確認
console.log(`Cache read: ${response.usage.cache_read_input_tokens ?? 0}`);
console.log(
`Cache creation: ${response.usage.cache_creation_input_tokens ?? 0}`
);
return response;
}
Python:
import anthropic
client = anthropic.Anthropic()
def chat(conversation_history: list[dict]):
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
cache_control={"type": "ephemeral"},
system="あなたはフルスタック開発のエキスパートです。...",
messages=conversation_history,
)
print(f"Cache read: {response.usage.cache_read_input_tokens or 0}")
print(f"Cache creation: {response.usage.cache_creation_input_tokens or 0}")
return response
エージェントパターン:toolsとsystemの長期キャッシュ
エージェントワークフローでは、tools定義とsystemプロンプトは変更頻度が低く、messagesは頻繁に変わります。これを3層のキャッシュ構成で最適化できます。
重要なルール: TTL混在時は、長いTTL → 短いTTLの順に配置する必要があります。 逆順にするとエラーになります。
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [
{
name: "search_codebase",
description: "コードベースをキーワード検索する",
input_schema: {
type: "object" as const,
properties: {
query: { type: "string", description: "検索クエリ" },
},
required: ["query"],
},
cache_control: { type: "ephemeral", ttl: 3600 }, // 1時間TTL
},
// ... 他のtools定義
];
async function agentStep(messages: Anthropic.MessageParam[]) {
const response = await client.messages.create({
model: "claude-opus-4-6",
max_tokens: 8192,
tools,
system: [
{
type: "text",
text: "あなたは自律的にコードを読み書きするAIエージェントです。...(長いシステムプロンプト)...",
cache_control: { type: "ephemeral", ttl: 3600 }, // 1時間TTL
},
],
messages: [
...messages.slice(0, -1),
// 最新メッセージのみ5分TTLでキャッシュ
{
...messages[messages.length - 1],
...(messages[messages.length - 1].role === "user" && {
content: [
{
type: "text" as const,
text:
typeof messages[messages.length - 1].content === "string"
? messages[messages.length - 1].content
: "",
cache_control: { type: "ephemeral" }, // デフォルト5分TTL
},
],
}),
},
],
});
return response;
}
この3層構成(tools: 1時間 → system: 1時間 → messages末尾: 5分)により、エージェントが何十回APIを呼び出しても、tools/systemの大部分はキャッシュ読取(0.1倍)で処理されます。
Compaction APIとの併用戦略
Compactionがキャッシュに与える影響
長時間のエージェントセッションでは、コンテキストウィンドウの上限に達する前にCompaction APIで会話を要約・圧縮するのが定番パターンです。ここで重要なのは、Compactionでmessagesが要約されるとmessagesのキャッシュは無効化されるが、system/toolsのキャッシュは維持されるという点です。
キャッシュを最大化するCompactionタイミング設計
system/toolsを1時間TTLにしておけば、Compaction後も最もトークン数の大きい部分(tools定義やシステムプロンプト)のキャッシュが効き続けます。
async function handleAgentLoop(messages: Anthropic.MessageParam[]) {
const response = await agentStep(messages);
// キャッシュ状況をログ出力して確認
const { usage } = response;
console.log(
[
`input: ${usage.input_tokens}`,
`cache_read: ${usage.cache_read_input_tokens ?? 0}`,
`cache_creation: ${usage.cache_creation_input_tokens ?? 0}`,
].join(" | ")
);
// Compaction後でも cache_read が大きければ system/tools キャッシュが効いている
return response;
}
Compaction発動後のレスポンスで cache_read_input_tokens が依然として大きな値を示していれば、system/toolsのキャッシュが正常に維持されていることを確認できます。
ハマりポイントと対処法
最小トークン要件を満たさない落とし穴
キャッシュが有効になるには、モデルごとに定められた最小トークン数を超える必要があります。
| モデル | 最小トークン数 |
|---|---|
| Claude Opus 4.6 / 4.5 | 4,096 |
| Claude Sonnet 4.6 | 2,048 |
| Claude Sonnet 4.5 / 4 / 3.7 | 1,024 |
| Claude Haiku 4.5 | 4,096 |
| Claude Haiku 3.5 / 3 | 2,048 |
要件を満たさない場合、エラーは出ません。キャッシュが静かに無視されるだけです。 これが地味に厄介で、コスト削減されていないことに気づかないまま運用してしまうケースがあります。
キャッシュが効かない3つの典型パターン
1. 最小トークン要件未満: 上記テーブルを確認し、systemプロンプトが短すぎないか確認してください。
2. メッセージ内容の変動: プロンプトが1バイトでも変わるとキャッシュミスします。タイムスタンプやランダム値を含む動的な部分は、キャッシュ対象のブロックから分離する設計が重要です。
3. ワークスペース分離の誤解: 2026年2月5日より、キャッシュの分離単位がorganizationからworkspaceに変更されました。同じorganization内でもworkspaceが異なればキャッシュは共有されません(Bedrock/Vertex AIはorganization単位を維持)。
キャッシュヒット率を継続的にモニタリングするユーティリティを用意しておくと安心です。
function logCacheMetrics(usage: Anthropic.Usage & {
cache_read_input_tokens?: number;
cache_creation_input_tokens?: number;
}) {
const total = usage.input_tokens;
const cacheRead = usage.cache_read_input_tokens ?? 0;
const cacheWrite = usage.cache_creation_input_tokens ?? 0;
const uncached = total - cacheRead - cacheWrite;
const hitRate = total > 0 ? ((cacheRead / total) * 100).toFixed(1) : "0.0";
console.log(
`[Cache] hit: ${hitRate}% | read: ${cacheRead} | write: ${cacheWrite} | uncached: ${uncached}`
);
// キャッシュが全く効いていない場合に警告
if (cacheRead === 0 && cacheWrite === 0) {
console.warn(
"[Cache] WARNING: キャッシュが効いていません。最小トークン要件またはプロンプト内容を確認してください"
);
}
}
まとめ
Automatic Prompt Cachingは、cache_control 一行の追加という最小限の実装コストで、APIコストを劇的に削減できる機能です。特にエージェントワークフローのように同じtools/systemプロンプトを繰り返し送信するユースケースでは、1時間TTLとの組み合わせで90%近いコスト削減が現実的です。
導入のステップとしては:
- まずトップレベルに
cache_control: {type: 'ephemeral'}を追加する(最短30秒) usageレスポンスのcache_read_input_tokensを確認する(効いているか検証)- エージェント用途なら3層TTL構成に拡張する(tools/system: 1時間、messages: 5分)
- モニタリングユーティリティを組み込む(継続的な効果測定)
まずは usage レスポンスのキャッシュヒット率をモニタリングするところから始めて、自分のワークロードでの削減効果を確認してみてください。
