PaPoo
cover
technews
Author
technews
世界の技術ニュースをリアルタイムでキャッチし、日本語でわかりやすく発信。AI・半導体・スタートアップから規制動向まで、グローバルテックシーンの「今」をお届けします。

AnthropicのPrompt CachingでAPI代を70%削減した話をやさしく解説する

キーポイント

そもそもPrompt Cachingって何?

この記事は、AnthropicのAPI、つまりClaudeを呼び出すときのPrompt Cachingで料金を大きく下げた、という体験談です。

ざっくり言うと、Prompt Cachingは​「前回と同じ部分は、また全部送らなくてもいいようにする仕組み」​です。
たとえば、毎回同じ長い説明文やルールを送り続けるのって、地味にムダですよね。そこで、変わらない部分をキャッシュしておくと、次回からはその部分を安く読み込めるわけです。

ここが面白いのは、著者が「モデルを変えたわけではない」のに、​プロンプトの切り方だけでAPI代を70%下げたところです。
これはかなり現実的な改善で、AI開発って「モデル選定」ばかりに目が行きがちだけど、実際にはどうリクエストを組み立てるかがめちゃくちゃ重要だとわかります。個人的には、こういう“地味だけど効く”改善がいちばん強いと思います。

この記事の5つのパターン

著者が使っているのは、次の5つのパターンです。

  1. system prompt を最初にキャッシュする
  2. tool definitions を別枠でキャッシュする
  3. conversation history が増えるごとにキャッシュする
  4. 5分ではなく1-hour TTLを使う
  5. キャッシュしやすい順番でプロンプト全体を組む

image_0003.svg

以下、順番に見ていきます。

1. system prompt を最初にキャッシュする

まず一番効くのがこれです。

system prompt とは、AIに対する「役割」や「ルール」を書く部分です。
たとえば、「あなたは丁寧なアシスタントです」「出力はこの形式で」「安全上の注意はこう」みたいな、毎回変わらない指示ですね。

著者のケースでは、この system prompt が 4,000 tokens もあったそうです。
tokens は文字数そのものではなく、AIが文章を細かく分けて数える単位だと思えばOKです。長文ほど増えます。

これを毎回フルで送ると、会話が30回続くだけで、同じ4,000 tokensを30回分払うことになります。
そりゃ高い。かなり高いです。

image_0004.svg

そこで、system prompt の最後の部分に cache_control を置いて、type: "ephemeral" にする。
すると、最初の1回はキャッシュを書き込むため少し高くなりますが、2回目以降はその部分をかなり安く読めます。

重要なのは、​キャッシュされる部分が 1,024 tokens 以上必要だという点です。
Claude Sonnet ではこれを満たさないと、キャッシュが効かないらしいです。
つまり、短い system prompt には向きません。200 tokens くらいの短文なら、正直あまり意味がないでしょう。

また、順番もかなり大事です。
キャッシュは先頭からの連続した一致で効くので、固定の指示は上に、毎回変わる情報は下に置く必要があります。
たとえば、日付やユーザー入力を先に混ぜると、毎回違うprefixになってしまい、キャッシュが壊れます。

著者は、ドキュメント分類の仕事で system prompt をキャッシュしたところ、​1日あたり680万 input tokens くらい削減できたと書いています。
これはさすがにインパクトが大きいです。
正直、AIコスト最適化の“最初の一本目”はこれでいい、という話だと思います。

2. tool definitions を別枠でキャッシュする

次に効くのが tool definitions です。

image_0005.svg

これは、Claudeに「外部ツールを使わせる」ための定義のことです。
たとえば、検索、DB参照、関数実行などをAIにやらせるとき、その使い方をJSON schemaで渡します。
このJSONが、意外とでかい。

著者の例では、14個のツール定義だけで 9,200 tokens もあるそうです。
しかもこれ、毎回ほぼ同じ。
なら、当然キャッシュしたくなります。

面白いのは、tool definitions と system prompt を別々にキャッシュできる点です。
Anthropicでは、1リクエストあたり最大4つまで breakpoint を置けるので、

これがなぜ便利かというと、​A/Bテストみたいに system prompt だけ変える場面でも、tool cache を生かせるからです。
全部ひとまとめにしてしまうと、system prompt を変えた瞬間にツール側まで再計算になりがちですが、分けておけば無駄が減ります。

ただし注意点もあります。
ツール定義を動的に生成していると、​JSONのキー順が毎回変わるだけでキャッシュが壊れます。
著者はそこにハマって、キーをソートして解決したそうです。
この話、すごくわかります。AIまわりって、アルゴリズムよりも​「完全一致しないせいでキャッシュが死ぬ」問題に時間を溶かされがちなんですよね。

image_0006.svg

3. conversation history を増えるごとにキャッシュする

3つ目は、長い会話で効くパターンです。

conversation history は、今までのやり取りの履歴です。
チャットが長くなるほど、この履歴はどんどん伸びます。

キャッシュなしだと、10ターン目にはかなり長くなった履歴を毎回まるごと送ることになり、料金が膨らみます。
そこで、著者はmoving breakpoint、つまり会話が進むたびにキャッシュの境界をずらす方法を使っています。

考え方としては、

ただし、ここはかなり繊細です。
キャッシュはやはりprefix match、つまり先頭からの一致なので、履歴を途中で削ったり、要約したり、順番を変えたりすると、そこから先が全部無効になります。

image_0007.svg

著者のルールはかなり現実的で、​履歴を編集しない。append-only、つまり追記だけにすること。
この方針はかなりいいと思います。
会話履歴を“整形したくなる”気持ちはわかるんですが、キャッシュ最適化の観点では、むしろ触らないほうが勝ちです。

また、​turn three、つまり3回目くらいから効果が出やすいとも書かれています。
最初のうちはキャッシュ書き込みのコストが少し重く、短いやり取りだと逆に得しないこともある。
ここは大事で、「キャッシュは常に得」と思い込むと失敗します。
短い会話には向かない場合がある、これは覚えておきたいポイントです。

著者は、コード支援アシスタントでこの方法を使い、1セッションあたりのinput billingを約60%削減したとのことです。
会話が長いアプリほど、これはかなり効きそうです。

4. 1-hour TTL を使う

次は TTL です。
TTL は “time to live” の略で、キャッシュが生きている時間のことです。
つまり「何分で期限切れになるか」です。

デフォルトは 5分
ただし、読み取りがあるたびにその期限は延びます。
会話が連続しているなら問題になりにくいです。

image_0008.svg

でも、バッチ処理や、ユーザーの考える時間が長いケースでは話が変わります。
たとえば、

こういう場面では、5分を超えてしまってキャッシュが切れ、次回また書き直しになります。
それなら、​1-hour TTL を使ったほうがいい、というのが著者の判断です。

ここはすごく実践的です。
「5分より長く空くなら、1時間のほうが得になりやすい」という考え方は、かなり納得感があります。
キャッシュの良し悪しって、性能の話というより業務のリズムに合っているかなんですよね。

著者は、1-hour TTL を

ただし、TTLはbreakpointごとに別々です。
system prompt のキャッシュは1時間、history のキャッシュは5分、みたいなことも起こるので、見た目がややこしくなることがあります。
このあたりは監視しないと、何が効いて何が切れたのか分かりづらいでしょう。

image_0010.png

5. キャッシュしやすい順番でプロンプト全体を組む

最後は、単発テクニックではなく設計思想です。

著者のおすすめは、リクエストを​「安定しているもの」から「変わりやすいもの」へ並べることです。

順番はこんな感じです。

  1. tools
  2. system prompt
  3. conversation history
  4. current user input

これはかなり重要です。
なぜなら、キャッシュは先頭から続く部分しか使えないからです。
途中で1文字でも変わると、その先は全部キャッシュ外になります。

image_0012.png

だから、毎回変わる情報はできるだけ下に置く。
変わらない情報は上に置く。
この“積み上げ方”が、キャッシュ効率を左右します。

また、著者はtimestamp(日付や時刻)​random ID がキャッシュを壊すと警告しています。
たしかに、毎回変わる要素を安定層に混ぜたら、そりゃ台無しです。
「今日の日付くらい入れてもいいか」と思いがちですが、キャッシュ視点では地雷です。

さらに、​non-deterministic serialization、つまり毎回同じ並びにならないデータ出力も問題です。
JSONのキー順、浮動小数点の表示、余計な空白など、見た目が少し違うだけで別物扱いになります。

著者は、こうした整形ルールを徹底した結果、キャッシュヒット率が 71% から 96% まで上がったとしています。
この数字はかなり説得力があります。
個人的には、この「キャッシュは魔法ではなく、データ整形の勝負」という点がいちばん現場っぽくて好きです。

どうしてここまで効くのか

この話の本質は、AIのAPI料金が「モデルの賢さ」だけで決まるわけではない、ということだと思います。

image_0013.png

同じ長い前置きを何度も送っていたら、当然コストは増えます。
でも、​変わらない部分を賢く再利用するだけで、請求額はかなり下がる。
これはAIに限らず、システム設計全般に通じる話です。

しかもPrompt Cachingは、派手な最適化ではありません。
モデルを小さくするわけでも、出力を削るわけでもない。
ただ、​無駄な再送を減らすだけです。
なのに効果が大きい。こういう改善は、現実ではかなり強いです。

個人的におもしろいと思った点

個人的におもしろいのは、著者が「まずモデルを変えた」のではなく、​cache breakpointの置き方を変えただけで大きく下げたことです。

AI開発って、つい「どのモデルが一番賢いか」に意識が向きます。
でも実際には、こういう地味なプロンプト設計とデータ整形のほうが、コストと運用の満足度に直結するんですよね。
しかも、効果が出るのが早い。
「4分で入れて、次の請求で効いた」という話は、かなり夢があります。

一方で、キャッシュは雑に使うと逆効果にもなりえます。
短い会話に無理に入れると損することもあるし、JSONの順番がズレるだけで失敗することもある。
なので、​万能の節約術ではなく、ちゃんと運用する技術だと思ったほうがよさそうです。

image_0014.png

まとめ

この元記事が伝えているのは、かなりシンプルです。

派手さはないけれど、実務ではかなり効く話です。
特にClaudeを使ったエージェントや長文処理をしている人には、かなり参考になるはずです。
私なら、まず system prompt のキャッシュ から入って、次に toolsの分離 をやります。ここが一番コスパが良さそうです。


参考: 5 Anthropic Prompt Caching Patterns That Cut My API Bill 70%

同じ著者の記事