プロンプトキャッシュを有効にしても、何の節約にもなりませんでした。それどころか、OpenAI APIの請求額は約4分の1も跳ね上がりました。原因は、リクエストごとに変化するたった一行のコード、システムプロンプトに埋め込まれたタイムスタンプでした。
LLMプロバイダーは、トークン処理コストを削減するために、開発者がプロンプトの断片をキャッシュできるようにしています。キャッシュの読み取り(「ヒット」)は通常の料金の10分の1程度で済みますが、キャッシュの書き込み(「ミス」)は通常の価格の約1.25倍かかります。書き込みが発生したものの、そのキャッシュされた断片が一度も読み取られなかった場合、追加の25%の料金が無駄になります。タイムスタンプのせいでプロンプトが既存のキャッシュエントリと一致しなくなったとき、まさにこれが起こりました。
キャッシュが裏目に出る理由
プロンプトキャッシュは、キャッシュされた部分の正確なバイト列を一致させることで機能します。プロバイダーは入力をハッシュ化し、そのハッシュが保存されているエントリと一致すれば、システムは前回の計算結果を再利用し、安価な読み取り料金を適用します。わずか一文字の変化であっても、一致が崩れると新しい計算が強制され、より高い書き込み料金が請求されます。
私のケースでは、システムプロンプトは次のように始まっていました:
Current session started: 2026-07-14T09:41:07Z
APIコールごとにタイムスタンプが更新されていたため、リクエストの最初の数バイトが一致することはありませんでした。プロバイダーは各コールを新しいキャッシュエントリとして扱い、書き込みプレミアムを課しましたが、読み取りは一度も記録されませんでした。その結果、cache_read_input_tokensがゼロのまま、cache_creation_input_tokensが着実に上昇し続けました。これはキャッシュが全くヒットしていない明確な兆候です。
キャッシュの不具合を見つける方法
APIから提供される使用ログには、2つの重要なカウンターがあります:
- cache_creation_input_tokens – 書き込みをトリガーしたトークン。
- cache_read_input_tokens – 読み取りによって恩恵を受けたトークン。
前者が上昇し、後者が横ばいのままであれば、キャッシュは再利用されていません。簡単な確認方法として、全く同じリクエストを2回繰り返してみてください。キャッシュが機能していれば、2回目のコールで読み取りトークンが急増するはずです。
問題の解決策
解決策は簡単です。キャッシュされる領域が、コール間で静的であることを確認してください。次の2つのルールに従ってください:
- 不変のコンテンツを最初に配置する。 システムプロンプト、ツールの定義、または決して変わることのない指示は、リクエストの先頭のバイトを占めるようにします。
- 可変のコンテンツを最後に追記する。 タイムスタンプ、ユーザー生成テキスト、リクエストID、またはコールごとに変化するデータは、キャッシュセグメントの後に配置する必要があります。
たった一文字でもずれると、ハッシュが変わり、キャッシュミスが続きます。タイムスタンプが末尾に来るようにプロンプトを再構成すれば、キャッシュヒット率が回復し、請求額は期待通りの低コストなレベルに戻ります。
キャッシュが実際に役立つ場面
プロンプトキャッシュは、同じ指示セットが何度も再利用されるシナリオで真価を発揮します:
- エージェント・ループ: AIが固定されたツールのセットを繰り返し呼び出す場合。
- チャットセッション: 長い静的なドキュメントを参照しつつ、ユーザーの最新のクエリのみが変化する場合。
- 一括データ抽出: 同じパース用プロンプトを多数のレコードに適用する場合。
毎回新しいコンテキストを含むシングルショットのコール(独自の導入文を伴う単発の質問など)では、キャッシュのメリットはなく、リクエストが意図せず書き込みをトリガーした場合には、かえってコストが増える可能性さえあります。
隠れた落とし穴
プロンプト自体が静的であっても、ダウンストリームでリクエストが変更される可能性があります:
- プロキシやアグリゲーター: 並べ替えや空白の挿入を行うと、バイト単位の一致が壊れることがあります。
- ゲートウェイサービス: 認証ヘッダーを先頭に追加したり、JSONのフォーマットを変更したりすると、意図せずキャッシュされた断片が変わってしまうことがあります。
ゲートウェイ経由で全く同じリクエストを2回送信し、読み取りカウンターを確認することで、キャッシュの経路が維持されているかどうかを検証できます。
コストの全体像
書き込みに対する25%の追加料金は、キャッシュの使用に対するペナルティではありません。将来の再利用のために断片を保存するために必要な追加の計算リソースを反映したものです。キャッシュがヒットすれば、コストは劇的に下がり、多くの場合、通常の料金の数分の一になります。重要なのは、システムが実際にキャッシュをヒットさせるようにすることです。そうでなければ、節約することなくプレミアム料金を支払うことになります。
反論:キャッシュは死んでいない
静的なプロンプト部分と動的なプロンプト部分を管理する複雑さは、節約できるコストを上回ると主張する開発者もいます。しかし、その見解は、多くのプロダクション・パイプラインがすでに設定(静的)とユーザーデータ(動的)を分離しているという事実を見落としています。プロンプトを適切に構造化することで、APIの初期開発者が恩恵を受けたものと同じキャッシュメカニズムを、追加の労力なしに活用できます。トレードオフとなるのは、プロンプト設計におけるわずかな規律の必要性であり、技術的な根本的な欠陥ではありません。
次にすべきこと
- 使用状況ダッシュボード内の2つのキャッシュカウンターを毎週モニタリングしてください。
- プロンプトの構成を監査し、変数要素がキャッシュブロックの後ろに配置されていることを確認してください。
- 代表的なワークロードを用いて、キャッシュの有無によるA/Bテストを実施し、実際の節約額を定量化してください。
- プロキシの前後で生の要求ペイロードを比較し、ゲートウェイを検証してください。
まとめ
プロンプトキャッシュはLLM APIのコストを大幅に削減できますが、それはキャッシュされるセグメントが呼び出し間で完全に同一である場合に限られます。プロンプトの冒頭に、紛れ込んだタイムスタンプやその他の動的なトークンがあると、毎回高コストな書き込みが発生し、請求額が膨れ上がります。静的な指示を前方に配置し、変化するデータを末尾に配置することで、キャッシュを適切に機能させ、コストを抑制できます。
