◉CLAUDE LABEN
●2.1.302 — fork サブエージェントが claude -p・Agent SDK・デスクトップで既定オンになりました。無効化は CLAUDE_CODE_FORK_SUBAGENT=0(10/16)●2.1.299 — 200K トークン超で約54分放置した会話はアイドル圧縮。MCP の許可ルールは名指ししたサーバーだけに一致します●SONNET 4.5 — Claude API での提供終了は 11/30、残り43日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました●2.1.302 — fork サブエージェントが claude -p・Agent SDK・デスクトップで既定オンになりました。無効化は CLAUDE_CODE_FORK_SUBAGENT=0(10/16)●2.1.299 — 200K トークン超で約54分放置した会話はアイドル圧縮。MCP の許可ルールは名指ししたサーバーだけに一致します●SONNET 4.5 — Claude API での提供終了は 11/30、残り43日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました
記事一覧/API & SDK
⬡ API & SDK/2026-07-19上級

プロンプトキャッシュを入れたのに請求が下がらなかったとき — cache_read を計測してヒット率の穴を塞ぐ運用メモ

cache_control を付けたのに Claude API の請求が下がらない。原因を推測で潰す前に cache_read と cache_creation を毎リクエスト記録し、ヒット率の穴を非決定プレフィックス・TTL 失効・しきい値未満の3系統に切り分ける運用メモです。

prompt-caching15cache-hit-ratecost-optimization28observability15api39

✦ プレミアム記事

先月の請求書を開いて、少し手が止まりました。システムプロンプトに cache_control を付けたのは3週間前。ヒット率のことは気にせず「これで9割引きになるはず」と思い込んでいたのですが、入力トークンの請求はほとんど変わっていません。

キャッシュは「有効にした/していない」の二値ではないのだと、このとき初めて腹に落ちました。付けてある。けれど読まれていない。その差は請求書の数字にしか現れず、コードを眺めても見えてこない。

この記事は、個人開発で回している要約バッチのキャッシュヒット率を 3割台から9割近くまで引き上げるまでの、計測と手直しの記録です。派手な最適化テクニックの話ではありません。まず計測し、穴を名指しし、ブレークポイントを1つずつ動かす。その地味な往復のメモです。

「有効にした」と「効いている」は別の状態です

プロンプトキャッシュは、リクエストの system・tools・messages の先頭(プレフィックス)をサーバー側に一時保存する仕組みです。同じプレフィックスが後続のリクエストで再利用されると、その部分は通常の入力料金の約1割で処理されます。

問題は、cache_control を書いた時点では何も保証されないという点です。実際にキャッシュが読まれたかどうかは、レスポンスの usage を見るまで分かりません。ここを見ていなかったことが、私の3週間の見落としの正体でした。

usage フィールド意味課金の目安(通常入力比)
cache_creation_input_tokensキャッシュへ書き込んだトークン(=この回はミス)約 1.25 倍(5分キャッシュ)
cache_read_input_tokensキャッシュから読んだトークン(=この回はヒット)約 0.1 倍
input_tokensキャッシュ対象外の通常入力1 倍

読み方はシンプルです。cache_read_input_tokens が毎回それなりの値で返っていれば効いています。逆に、cache_creation_input_tokens ばかりが立ち続けているなら、キャッシュは「作られては捨てられ」を繰り返しています。付けたのに効かない、はほぼこの状態です。

まず計測ハーネスを1枚だけ挟みます

原因を推測で潰し始める前に、全リクエストの usage を記録する薄い層を入れました。最適化より前に、まず現在地を数字で知りたかったからです。

import time
import logging
from dataclasses import dataclass, field
 
logger = logging.getLogger("cache")
 
@dataclass
class CacheProbe:
    """cache_read / cache_creation を毎リクエスト記録する最小ハーネス"""
    requests: int = 0
    hits: int = 0            # cache_read > 0 の回数
    rewrites: int = 0        # cache_creation > 0 の回数
    read_tokens: int = 0
    write_tokens: int = 0
    # 直近のミスを原因分類するための痕跡
    last_prefix_fingerprint: str | None = field(default=None)
 
    def record(self, usage) -> None:
        self.requests += 1
        read = getattr(usage, "cache_read_input_tokens", 0) or 0
        write = getattr(usage, "cache_creation_input_tokens", 0) or 0
        self.read_tokens += read
        self.write_tokens += write
        if read > 0:
            self.hits += 1
        if write > 0:
            self.rewrites += 1
 
    @property
    def hit_rate(self) -> float:
        # 「書き込みが発生しなかった」= ヒットとみなす
        return (self.hits / self.requests * 100) if self.requests else 0.0
 
    @property
    def rewrite_rate(self) -> float:
        return (self.rewrites / self.requests * 100) if self.requests else 0.0
 
    def snapshot(self) -> str:
        return (
            f"req={self.requests} hit={self.hit_rate:.1f}% "
            f"rewrite={self.rewrite_rate:.1f}% "
            f"read_tok={self.read_tokens:,} write_tok={self.write_tokens:,}"
        )

record() を呼ぶだけの層です。ここで見たかったのは1点だけ。書き込み率(rewrite_rate)が高止まりしていないかです。要約バッチで実測したところ、ヒット率は 34.8%、書き込み率は 61.2% でした。半分以上のリクエストがキャッシュを作り直している。これで請求が下がらない理由が、ようやく数字になりました。

なぜ「書き込み率」を主役にするかというと、ヒット率だけを見ていると原因が見えないからです。ヒットしていない = ミス、までは分かっても、ミスが「プレフィックスがずれた」のか「時間切れで消えた」のかを区別できません。書き込みが何回、どの間隔で起きているかを見ると、この切り分けが一気に進みます。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦usage から cache_read と cache_creation を取り出し、ヒット率とキャッシュ書き直し回数を毎リクエスト記録する最小ハーネス
✦ヒット率が伸びない原因を「非決定プレフィックス・TTL 失効・しきい値未満」の3系統に計測で切り分ける手順
✦cache_creation が繰り返し出続けるときにブレークポイントを1つずつ後ろへ動かして安定プレフィックスを探す実測の進め方
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ API & SDK2026-09-07
キャッシュ読み取りが 75% 安くなっても、請求の下がり方は構成しだいで 10 倍違います
Fable 5.1 と Mythos 5.1 でキャッシュ読み取りの倍率が 0.1 から 0.025 へ変わりました。自分のトークン内訳から削減幅を先に見積もる手順と、倍率を定数で持つコードの直し方をまとめます。
⬡ API & SDK2026-04-29
Claude API のセマンティックキャッシュを本番投入する — 類似プロンプト判定としきい値設計、汚染防止
類似プロンプトへの応答を再利用するセマンティックキャッシュをClaude APIの本番に組み込む設計を一気通貫で解説します。embeddingと類似度しきい値の決め方、マルチテナント分離、キャッシュ汚染を防ぐガード層、フォールバックと劣化モード、プロンプトキャッシュとの重ね方、効果を測る観測性の指標まで踏んだ地雷とともに紹介します。
⬡ API & SDK2026-04-26
Claude API のプロンプトキャッシュで月額コストを半分にした実装メモ
長文のシステムプロンプトを毎リクエスト送る本番ワークロードでは、プロンプトキャッシュの設計次第で月額コストが大きく変わります。ヒット率の計測から始める手順、TTLの選択、トークン下限と4ブロック上限、toolsを1つ足してヒット率が0になった失敗談に加え、ヒット率から入力コストを逆算する計算と損益分岐(5分TTLで約22%、1時間TTLで約53%)まで、半年の運用実例でまとめました。