AI活用

Claude Prompt Cachingの使い方|長いsystem・tool定義のコストと遅延を減らす

Claude Prompt Cachingのautomatic cachingと明示breakpoint、5分・1時間TTL、usageの読み方、TTFT比較、pre-warm、tool定義の無効化条件を解説します。

この記事の目次
  1. 結論:変わらない長いprefixの末尾をcacheする
  2. Prompt Cachingが効く入力
  3. automatic cachingから試す
  4. 固定systemへ明示breakpointを置く
  5. TypeScriptプロジェクトを準備する
  6. 同じsystem blockを再利用する
  7. usageとTTFTを比較する
  8. streamで最初のtext時刻を測る
  9. usageの3項目を足す
  10. 比較ログを同じ形式で残す
  11. 5分TTLと1時間TTLを選ぶ
  12. 単純化した損益分岐
  13. 1時間TTLの書き方
  14. tool定義をcacheする
  15. 最後の固定toolへbreakpointを置く
  16. 無効化の階層を理解する
  17. pre-warmで最初の利用者を待たせない
  18. cache hitしない時の確認点
  19. 本番前チェックリスト
  20. まとめ:cache readをusageで確認して初めて成功

Claude Prompt Cachingは、毎回同じ長いprompt prefixを再利用し、入力処理のコストとTTFTを減らす機能です。まずrequest最上位へ cache_control を置くautomatic cachingを検討し、system promptやtool定義の後ろだけが変わる処理では、最後の固定blockへ明示的なcache breakpointを置きます。

prefix(接頭部分)は、requestの先頭から特定位置までの内容です。Claudeでは toolssystemmessages の順でprefixが作られます。breakpointより前を1文字でも変えると別のprefixになり、期待したcache hitが起きません。

この記事では、TypeScriptで初回のcache writeと2回目のcache readを実測し、cache_creation_input_tokenscache_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料金が増えるわけではありませんが、複雑な配置は無効化原因を追いにくくします。

本番前チェックリスト

  1. 再利用する固定prefixが十分長く、minimum tokenを満たす
  2. automaticと明示breakpointを用途で選んでいる
  3. 変動値をbreakpointより前へ置いていない
  4. tools→system→messagesの順序と無効化範囲を理解している
  5. 初回write、2回目read、TTL切れ後をテストした
  6. usageのcreation・read・inputを全て集計している
  7. TTFTと総時間をcacheなし・ありで複数回比較した
  8. 5分と1時間の料金倍率を実際のrequest間隔で比較した
  9. pre-warmの利用回数とwrite費用に上限がある
  10. 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をテストしてから本番へ広げます。

スポンサーリンク