AI

LLM API のコスト暴走を「呼ばない」で防ぐ三段防御 — 運用者目線で

個人開発で生成 AI 機能を載せると API 課金が青天井になりがちです。入力ゲーティング・キャッシュ・軽量モデルへのフォールバックという『そもそも呼ばない』方向の三段防御を、Zenn の実装事例を起点に運用者目線で整理しました。

SoSoraEndo2026年6月8日 09:348 min2,314

動画で読む

まず結論 — コストは「速くする」より「呼ばない」で効く

LLM API のコストを下げる一番確実な方法は、モデルを安いものに替えることでも、プロンプトを短くすることでもありません。「そもそも API を呼ばない」回数を増やすことです。1 リクエストの単価をいくら削っても、呼ぶ回数が読めなければ請求は青天井になります。

この「呼ばない」を実装に落とすと、(1) 呼ぶ前に入力を弾く、(2) 同じ入力は二度呼ばない、(3) 呼べない時の逃げ道を用意して上限を作る、という三段の防御になります。towanoji 氏が Zenn で公開した筋トレ管理アプリの AI 食事入力の実装(いかに投げずに済ませるか)が、この三段をきれいに踏んでいて参考になりました。順に運用者目線で読んでいきます。

第一段: 入力ゲーティングで「呼ぶ前」に弾く

最初の防御は、API に渡す前の入力チェックです。空文字・短すぎる文字列・あきらかに食品名でない入力など、呼んでも無駄になる確率が高いものをコードで先に落とします。ここはモデルを一切使わないので、コストはゼロです。

私も以前、サイドプロジェクトのフォームに Claude の要約機能を雑に繋いだことがあります。バリデーションを入れずに公開したら、テストで送られた空送信やゴミ入力でも律儀に API を叩いていて、翌朝のダッシュボードに何も生み出していない呼び出しがずらりと並んでいました。金額は数百円で済みましたが、無駄打ちが全リクエストの 2 割を超えていたのは地味にショックでした。if input.strip.length < 2 then return の一行があれば防げた支出です。

ゲーティングは「正規化」とセットにすると効きます。全角と半角を揃える、前後の空白を落とす、よくある表記ゆれを 1 つに寄せる。この正規化は次のキャッシュのヒット率を上げる下ごしらえにもなります。入力を綺麗にしてから判定する、という順序が大事です。

第二段: キャッシュとメモ化で「同じ入力」を二度呼ばない

二段目は、一度呼んだ結果を覚えておいて再利用するキャッシュです。同じ入力に対する LLM の出力は(temperature を下げていれば)ほぼ同じなので、2 回目以降は API を呼ばずに前回の答えを返せます。towanoji 氏は lru-cache(最近使われていない項目から捨てる方式のメモリキャッシュ)で入力文の完全一致をキャッシュしていました。

面白いのは、彼が単純な入力一致キャッシュの手前に「ローカル食品 DB での事前解決」を置いている点です。文部科学省の食品成分表と、過去に承認済みの食品を先に照合し、完全一致か前方一致で当たればそれを採用してしまう。曖昧な時だけ API へ回す。さらに AI が解析した食品は自動でローカル DB に書き戻されるので、使えば使うほど API を叩く割合が下がっていく構造になっています。キャッシュが時間とともに育つ設計で、これは embedding を扱った時に私がハッシュで再計算をスキップした話と発想が同じです。違うのは、あちらが「変わっていないなら計算しない」で、こちらが「すでに知っているなら聞かない」という点でした。

API 側のキャッシュも併用できます。Anthropic の prompt caching は、長い system プロンプトや共通の前置きをキャッシュして入力トークン単価を大きく下げる仕組みです。これは「呼ぶ回数」ではなく「1 回あたりの単価」を削る方向なので、アプリ側のキャッシュ(呼ぶ回数を削る)と二重にかけると効きます。レイヤーが違うので、どちらかを選ぶ話ではありません。

第三段: フォールバックと利用上限で「天井」を作る

三段目は、呼べない・呼びたくない時の逃げ道と、物理的な利用上限です。ここが効くと、最悪のケースでも請求額が予測可能になります。コストの怖さは平均値ではなく、想定外のスパイクで青天井になることなので、天井を先に決めておく意味は大きいです。

towanoji 氏は Upstash Redis(サーバーレスから使えるマネージド Redis)でユーザー単位と全体の 1 日あたり呼び出し回数を数え、上限を超えたら API を止める「キルスイッチ」を入れていました。IP はハッシュ化して保存し、プライバシーにも配慮しています。私はこの「全体の日次上限」を持っておく発想がいちばん実務的だと思いました。個人開発で本当に怖いのは、悪意ある誰かがフォームを連打して、寝ている間に数万円の請求が積み上がるシナリオです。ユーザー単位の制限だけでは複数アカウントで抜けられますが、全体上限があればそこで頭打ちになります。

もう一段の逃げ道が、軽量モデルや決定的なロジックへのフォールバックです。曖昧な入力だけ高いモデルに回し、定型的なものはローカルの照合で返す。あるいは上限に達したら「今日は AI 解析を止めて手入力をお願いする」とユーザーに正直に伝える。体験は少し落ちますが、サービスが落ちるよりはるかにましです。何を高いモデルに任せ、何を任せないかの線引きは、結局は計測から決めるしかありません。これは自動化の前にまず測れという話とそのまま地続きで、勘で「全部 AI」にした瞬間にコストは読めなくなります。

三段を重ねる順番と、運用者として測ること

最後に、自分のプロダクトへ持ち帰る順番を整理します。三段は独立ではなく、入力ゲーティング → キャッシュ → フォールバックの順に上流から効かせるのが筋です。上流で弾いた入力は下流のコストをまるごとゼロにするので、安いところから順に閉めていくのが定石になります。

運用で見るべき数字は 3 つだけです。総呼び出し回数、キャッシュのヒット率、そして「ゲーティングで弾いた割合」。この 3 つを毎日眺めていれば、どの段がサボっているかが分かります。私の経験では、最初に効くのはたいてい第一段の入力ゲーティングで、コードも一番安く、効果が一番早く出ます。逆にキャッシュは育つまで時間がかかるので、ヒット率が上がらないと焦りがちですが、これは仕込みの問題で、数日待てば DB が育って効いてきます。

なお、この記事自体も AI が下書きを書き、私が確認・編集して公開しています。AI 機能を載せる側として、コストを「呼ばない」で抑える設計は他人事ではありません。

よくある質問

LLM API のコストを下げるには、まず何から手をつけるべきですか?
入力ゲーティングです。空文字や短すぎる入力など、呼んでも無駄になる確率が高いものをコードで先に弾けば、モデルを一切使わずに呼び出し回数を減らせます。コードが一番安く、効果も早く出るので最初の一手に向いています。
アプリ側のキャッシュと Anthropic の prompt caching は、どちらを使えばよいですか?
どちらか一方ではなく併用します。アプリ側のキャッシュは「呼ぶ回数」を削り、prompt caching は「1 回あたりの入力トークン単価」を削る別レイヤーの手法です。両方かけると効果が二重になります。
個人開発で一番怖いコストのスパイクは何ですか?
悪意ある連打などで、想定外の呼び出しが寝ている間に積み上がるケースです。ユーザー単位の制限だけでは複数アカウントで抜けられるため、全体の日次上限(キルスイッチ)を先に決めておくと請求額が予測可能になります。

参考文献

  1. いかに投げずに済ませるか — LLM API コスト削減の実装(towanoji, Zenn)
  2. Prompt caching — Anthropic Docs
  3. Upstash Redis — Ratelimiting documentation

Reaction

Share

X (Twitter)