Claude Prompt Cachingは、毎回同じ長いprompt prefixを再利用し、入力処理のコストとTTFTを減らす機能です。まずrequest最上位へ cache_control を置くautomatic cachingを検討し、system promptやtool定義の後ろだけが変わる処理では、最後の固定blockへ明示的なcache breakpointを置きます。
prefix(接頭部分)は、requestの先頭から特定位置までの内容です。Claudeでは tools → system → messages の順でprefixが作られます。breakpointより前を1文字でも変えると別のprefixになり、期待したcache hitが起きません。
この記事では、TypeScriptで初回のcache writeと2回目のcache readを実測し、cache_creation_input_tokens、cache_read_input_tokens、TTFT(Time To First Token:最初の文字が返るまでの時間)を比較します。2026年7月20日時点のAnthropic公式仕様を基準にしています。
情報確認日:2026年7月20日(日本時間)
結論:変わらない長いprefixの末尾をcacheする
tools(固定)
↓
system instructions(固定)
↓
長い仕様書・例文(固定)
↓ cache breakpoint
利用者の質問(毎回変わる)
↓
Claudeの回答
cache hitを作る条件
- 同じmodel・同じplatformで呼ぶ
- breakpointまでのtools・system・messagesを同じ順序・内容にする
- modelごとのminimum cacheable tokensを満たす
- 5分または1時間のTTL内に再利用する
- 最初のresponseが始まってから並列requestを送る
- usageのwrite/read tokenでhitを確認する
Prompt Cachingが効く入力
| 入力 | cache適性 | 理由 |
|---|---|---|
| 長いsystem prompt | 高い | 多くのrequestで同じ先頭部分を使う |
| 多数のtool定義 | 高い | schemaが同じなら再利用しやすい |
| 製品仕様・社内規程 | 高い | 同じ資料へ異なる質問を送る |
| multi-turn会話 | 高い | 過去の会話prefixが次requestにも含まれる |
| 短い単発質問 | 低い | minimum未満や再利用なしになりやすい |
| 毎回全体が変わる資料 | 低い | cache keyが一致しない |
schema(スキーマ)はtool引数などのデータ構造、multi-turnは複数往復の会話です。キャッシュは「似た意味」を判定する機能ではありません。空白、順序、tool description、timestampなどprefixの内容が変われば、別のcache entryになります。
automatic cachingから試す
automatic cachingはrequest最上位へ cache_control を1つ置き、最後のcache可能blockへ自動的にbreakpointを進めます。過去の会話がそのまま伸びるmulti-turnに向き、Anthropicは最も簡単な開始方法として案内しています。
const message = await anthropic.messages.create({
model,
max_tokens: 512,
cache_control: { type: "ephemeral" },
system:
"あなたはTypeScriptコードレビューの補助者です。",
messages: conversationHistory,
});
一方、最後のblockに現在時刻や毎回異なるuser messageを置く単発処理では、automatic breakpointも変化部分へ置かれます。以前のrequestが固定system末尾にentryを書いていなければ、lookbackしてもhitしません。その場合は明示breakpointを使います。
固定systemへ明示breakpointを置く
TypeScriptプロジェクトを準備する
mkdir claude-cache-sample
cd claude-cache-sample
npm init -y
npm pkg set type=module
npm install @anthropic-ai/sdk
npm install -D typescript tsx @types/node
export ANTHROPIC_API_KEY="your_api_key_here"
export CLAUDE_MODEL="your_available_model_id"
modelによってcache可能なminimum token数が異なります。短いsystem promptでは cache_control を付けてもerrorにならず、cacheされない場合があります。検証では、利用するmodelの公式minimumを超える十分な長さのダミー仕様書を使います。
同じsystem blockを再利用する
import fs from "node:fs";
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic();
const model = process.env.CLAUDE_MODEL;
if (!model) throw new Error("CLAUDE_MODEL is not set");
const specification = fs.readFileSync(
"product-specification.txt",
"utf8",
);
const cachedSystem = [
{
type: "text" as const,
text:
"次の製品仕様だけを根拠に日本語で回答してください。" +
"根拠がない場合は「仕様書に記載なし」と答えてください。",
},
{
type: "text" as const,
text: specification,
cache_control: {
type: "ephemeral" as const,
},
},
];
breakpointは2つ目のsystem block末尾にあります。異なる質問を送っても、このsystem配列を同じ内容・同じ順序で再利用します。ファイルをrequestごとに整形し直し、末尾の改行や空白が変わる実装は避けます。
usageとTTFTを比較する
streamで最初のtext時刻を測る
async function runQuestion(question: string) {
const startedAt = performance.now();
let firstTextAt: number | null = null;
const stream = anthropic.messages.stream({
model,
max_tokens: 512,
system: cachedSystem,
messages: [
{ role: "user", content: question },
],
});
stream.on("text", () => {
if (firstTextAt === null) {
firstTextAt = performance.now();
}
});
const message = await stream.finalMessage();
const completedAt = performance.now();
return {
responseId: message.id,
ttftMs: firstTextAt === null
? null
: Math.round(firstTextAt - startedAt),
totalMs: Math.round(completedAt - startedAt),
usage: message.usage,
};
}
const first = await runQuestion(
"無料プランの保存期間を教えてください。",
);
const second = await runQuestion(
"管理者がexportできる形式を教えてください。",
);
console.log({ first, second });
1回目は通常、固定prefixをcacheへ書くため cache_creation_input_tokens が増えます。2回目はTTL内かつprefix一致なら cache_read_input_tokens が増えます。TTFTは回線や負荷でも変わるため、1回だけで結論を出さず、cacheなし・ありを交互に複数回測ります。
usageの3項目を足す
total input tokens
= cache_read_input_tokens
+ cache_creation_input_tokens
+ input_tokens
| usage field | 日本語の意味 | 確認する場面 |
|---|---|---|
cache_creation_input_tokens |
今回cacheへ書いた入力token | 初回、prefix変更後、TTL切れ後 |
cache_read_input_tokens |
cacheから読めた入力token | 2回目以降のhit |
input_tokens |
最後のbreakpointより後の通常入力token | 毎回変わる質問など |
tokenはモデルが文章を処理する単位です。input_tokens だけを見て総入力が小さくなったと判断してはいけません。cache readとcache creationを足して全体を把握します。
比較ログを同じ形式で残す
| run | cache write | cache read | 通常input | TTFT | 判定 |
|---|---|---|---|---|---|
| 1 | 固定prefix分 | 0 | 質問分 | 実測ms | cache write |
| 2 | 0を期待 | 固定prefix分 | 質問分 | 実測ms | cache hit |
| 3(system変更) | 固定prefix分 | 0または部分hit | 質問分 | 実測ms | invalidation確認 |
validation(無効化)は、cache keyが一致せず新しく処理されることです。cache readが0でもAPI失敗とは限りません。minimum未満、TTL切れ、prefix変更、model・platform違いを順に調べます。
5分TTLと1時間TTLを選ぶ
| 処理 | 入力単価の倍率 | 使う目安 |
|---|---|---|
| 通常入力 | 1.0 × P |
cache対象外 |
| 5分cache write | 1.25 × P |
5分以内に繰り返し利用 |
| 1時間cache write | 2.0 × P |
5分を超え1時間以内に再利用 |
| cache read | 0.1 × P |
TTL内に同じprefixがhit |
P は利用modelの通常入力token単価です。5分writeは通常入力の1.25倍、1時間writeは2倍、readは0.1倍です。model別の金額は変わるため、公式Pricingから実行日時の単価を使います。
単純化した損益分岐
固定prefixを S token、同じTTL内の利用回数を N とし、変化部分・outputを除くと次のように比較できます。
cacheなし = N × S × P
5分cache = 1.25 × S × P + (N - 1) × 0.1 × S × P
1時間cache = 2.0 × S × P + (N - 1) × 0.1 × S × P
この単純モデルでは、5分cacheは同じprefixを2回以上、1時間cacheは3回以上読むと通常入力より安くなります。ただし、TTL切れ、cache miss、prefixの変更、最小token、platformの追加料金を含まないため、実際のusageから計算してください。
1時間TTLの書き方
const cacheControl = {
type: "ephemeral",
ttl: "1h",
} as const;
5分以内に継続的にhitする処理では、cache hitごとに既定TTLが追加費用なしでrefreshされるため、5分TTLが向きます。request間隔が5分を超えるが1時間以内に再利用されるprefixでは、1時間TTLを比較します。
tool定義をcacheする
最後の固定toolへbreakpointを置く
const tools = [
{
name: "search_products",
description: "商品名と条件から商品を検索する",
input_schema: {
type: "object",
properties: {
query: { type: "string" },
},
required: ["query"],
},
},
{
name: "get_inventory",
description: "商品IDから現在庫を取得する",
input_schema: {
type: "object",
properties: {
product_id: { type: "string" },
},
required: ["product_id"],
},
cache_control: { type: "ephemeral" },
},
];
Anthropicのtool cachingガイドは、tools配列の最後のtoolへbreakpointを置くと、最初からそのtoolまでの定義をcacheできると説明しています。tool名、説明、schema、順序を変えるとtools cacheだけでなく後続のsystem・messages cacheも無効になります。
無効化の階層を理解する
toolsを変更
└─ tools / system / messagesが無効
systemを変更
└─ system / messagesが無効
messageを変更
└─ その位置以降のmessagesが無効
current timestamp、request ID、利用者名など毎回変わる値を固定systemへ入れると、cache hit率が下がります。変動値はbreakpointより後のmessageへ置き、そもそもモデルへ不要な値は送らないようにします。
pre-warmで最初の利用者を待たせない
pre-warm(事前ウォームアップ)は、利用者requestの前にcache entryを書いておく処理です。現行仕様では max_tokens: 0 を使い、outputを生成せずcache writeだけを行えます。
const warmup = await anthropic.messages.create({
model,
max_tokens: 0,
system: cachedSystem,
messages: [
{ role: "user", content: "warmup" },
],
});
console.log({
stopReason: warmup.stop_reason,
cacheWrite: warmup.usage.cache_creation_input_tokens,
});
pre-warmではautomatic cachingではなく、共有するsystem・tool末尾へ明示breakpointを置きます。placeholder user messageへcacheを置くと、本番の異なる質問でhitしません。pre-warmもcache write料金が発生し、TTL内に利用がなければ節約になりません。
併用できない設定:max_tokens: 0 はstream、extended thinking、structured output、特定のtool choice、Message Batchesと併用できません。warmup専用requestとして分離します。
cache hitしない時の確認点
| 症状 | 原因候補 | 確認方法 |
|---|---|---|
| writeもreadも0 | modelのminimum token未満 | 公式minimumとtotal tokenを確認 |
| 毎回writeになる | prefixの空白・順序・timestampが変化 | breakpointまでのhashをアプリ側で比較 |
| 並列だけmiss | 最初のresponse開始前に次を送信 | 最初のmessage_start後に並列処理 |
| 長い会話でmiss | 20-block lookback外 | 固定位置に追加breakpoint |
| 400 error | TTL競合、breakpointが5個以上 | automaticを含むslot数を確認 |
Claudeは最大4つのcache breakpointを使え、automatic cachingも1slotを使います。lookback windowは各breakpointから最大20blockです。breakpointを増やすこと自体でtoken料金が増えるわけではありませんが、複雑な配置は無効化原因を追いにくくします。
本番前チェックリスト
- 再利用する固定prefixが十分長く、minimum tokenを満たす
- automaticと明示breakpointを用途で選んでいる
- 変動値をbreakpointより前へ置いていない
- tools→system→messagesの順序と無効化範囲を理解している
- 初回write、2回目read、TTL切れ後をテストした
- usageのcreation・read・inputを全て集計している
- TTFTと総時間をcacheなし・ありで複数回比較した
- 5分と1時間の料金倍率を実際のrequest間隔で比較した
- pre-warmの利用回数とwrite費用に上限がある
- model、SDK、platform、料金、データ保持を公式で再確認した
tokenの基礎はAI APIのトークンとは何か、Claude Messages APIの最小構成はClaude APIをTypeScriptから使う方法を参照してください。
まとめ:cache readをusageで確認して初めて成功
Claude Prompt Cachingは、cache_control を付ければ必ず節約できる機能ではありません。同じprefixをminimum以上の長さで作り、TTL内に再利用し、cache_read_input_tokens が増えたことを確認して初めてcache hitです。
growing conversationはautomatic caching、固定systemやtool定義の後ろに毎回違う質問が続く処理は明示breakpointから試してください。初回writeと2回目readのusage・TTFTを保存し、prefix変更、TTL切れ、並列実行、20-block lookbackをテストしてから本番へ広げます。