医療とAIの、これまでとこれから。

LLMアプリのコストを後から説明できるようにする — 計測を一点に集める設計

請求額は分かるのに「何にいくら使ったか」が説明できない。LLMアプリで最も起きやすいこの状態を防ぐには、呼び出し口を1つに絞り、1呼び出し1行を残し、単価をコードに固定しないことが要る。実際に運用しているシステムの実装と、そこで見つかった単価のずれを公開する。

LLMのコスト可観測性とは

LLMのコスト可観測性とは、請求総額を見られることではなく、「どの機能が・どのモデルで・いくら使ったか」を後から分解して説明できる状態のことである。

総額はプロバイダのダッシュボードを開けば分かる。だが総額しか無いシステムでは、コストが2倍になった翌日に打てる手が「全体を止める」しかない。分解できていれば、原因の機能だけを絞れる。

以下は、筆者が Cloudflare 上で運用している自律型メディアシステムの実装である。実際に動いているコードの話で、うまくいっていない部分も含めて書く。

なぜ「後から説明できない」が起きるのか

原因は3つに分かれる。どれも設計の初日に決まってしまう。

原因何が起きるか
呼び出し箇所が散らばる各機能が直接SDKやfetchを呼ぶと、計測は「全箇所に足す」作業になる。1箇所忘れた時点で総額と内訳が合わなくなる
単価が変わるモデルの改定・導入価格の終了・キャッシュやバッチの割引で、コードに書いた単価は静かに現実とずれる
失敗した呼び出しが消える例外を投げて終わる経路では、課金されたのに記録が残らない

3つ目は見落とされやすい。課金はレスポンスが返らなくても発生しうるので、記録の開始はレスポンス受信時ではなく呼び出し前でなければならない。

設計1: 呼び出し口を1つにする

このシステムでは、LLM呼び出しは例外なく src/lib/ai/router.ts の2つの関数(安価層 runCheap / Frontier層 runFrontier)を通る。各機能から直接 fetch や SDK を呼ぶことは規約で禁止し、開発ルールの文書にも明記している。

効果は計測だけではない。呼び出し口が1つだと、緊急停止・権限チェック・予算上限を「そこに1回書けば全経路に効く」。実装では、この2関数の先頭で共通の前処理を通している。停止フラグの確認、Agentごとの利用可能モデル層の確認、当日の予算超過の確認である。

分散したまま計測だけ後から足そうとすると、必ず抜けが出る。抜けた分は総額と内訳の差として現れるが、どこが抜けたかは差からは分からない

設計2: 1呼び出し = 1行。失敗も残す

呼び出しごとに agent_runs テーブルへ1行を書く。手順は2段階である。

  1. 呼び出しstatus='running' で INSERT(誰が・どのタスクで・どのモデルを使うか)
  2. 呼び出しに UPDATE(入力/出力トークン・コスト・レイテンシ・成功か失敗か・エラー文)

この形にすると、失敗した呼び出しもレイテンシとエラー付きで残る。さらに、running のまま終わっている行は「途中でワーカーごと落ちた呼び出し」として後から見つけられる。レスポンスが返ったときだけ記録する設計では、この2つがどちらも消える。

記録するのは actor(どのAgentか)・task(何をしていたか)・model の3つが要点である。この3列があると、月末に「どの機能が高いか」を素直に GROUP BY で出せる。逆に言えば、この3列を最初に決めておかないと後から分解できない

設計3: 単価はコードに書くと腐る

Frontier層のコスト計算のために、モデル別の単価表をコードに持っている。そしてこの記事を書いている時点で、その表は実際の請求とずれている

Anthropic が公開している料金表によれば、Claude Sonnet 5 は 2026年8月31日までの導入価格として入力 $2 / 出力 $10(100万トークンあたり)、9月1日以降が $3 / $15 である。一方、このシステムのコード内の表は Sonnet 5 を $3 / $15 で持っている。つまり導入価格の期間中、記録上のコストは実際より高く出ている。Claude Opus 5($5 / $25)は一致している。

安全側にずれている(過大計上)ため予算上限は早めに効くが、内訳の数字としては正しくない。ここから引ける教訓は3つある。

さらに、単価表だけでは足りない要素もある。同料金表は、プロンプトキャッシングが書き込みで基本入力単価の1.25倍(5分)または2倍(1時間)、キャッシュ読み取りで0.1倍になること、Batch API は入力・出力とも50%割引になること、ウェブ検索は1,000回あたり$10がトークン料金に加えて発生することを示している。キャッシュやツールを使い始めた瞬間に、「トークン数 × 単価」の素朴な式は現実から離れる。

トークン数そのものも安定しない。同料金表には、Claude 4.7以降のモデルが新しいトークナイザーを使っており、同じテキストに対して約30%多くトークンを生成するという注記がある。モデルを乗り換えた月の増分を「使いすぎ」と読むと原因を取り違える。

設計4: 上限は事後に見るのではなく事前に止める

コストの可観測性は、見るだけでは事故を防げない。このシステムでは、Agentごとに1日あたりの上限額を権限テーブルに持ち、LLM呼び出しの前処理でその日の合計と突き合わせている。超えていれば例外を投げてその日は打ち切る。

重要なのは、この判定が記録テーブルの合計値そのものを見ていることである。記録が壊れれば上限も効かない。計測と制御を別の情報源で持つと、片方だけ壊れたときに気づけない。

計測できていない穴は、穴として書いておく

正直に書いておくべき箇所がある。安価層(Workers AI)はニューロン課金で単価体系が異なるため、現在は記録上のコストを 0 として計上し、実測はプラットフォーム側のダッシュボードで見ている。コード内にもその旨をコメントとして残している。

これは望ましい状態ではない。だが「計測できていない」ことを明示的に書いた0と、理由の分からない0は、運用上まったく違う。後者は数か月後に「安価層はタダらしい」という誤った前提を生む。穴は塞ぐまでの間、穴として見える形にしておく。

ゲートウェイ側と二重に持つ

自前の記録だけに頼らないほうがよい。このシステムの Frontier 呼び出しは Cloudflare AI Gateway を経由しており、同ドキュメントによればゲートウェイ側でリクエスト数・トークン数・コストといった指標の閲覧と、リクエストやエラーのログ取得ができる。

二重に持つ意義は、食い違ったときに気づけることにある。自前の記録がゲートウェイの数字と合わなくなったら、計測漏れか単価のずれのどちらかが起きている。片方しか無ければ、ずれていること自体が観測できない。

FAQ

Q. 最初から作り込む必要がありますか。 呼び出し口を1つにすることと、1呼び出し1行を残すことだけは最初にやってください。後から足すと必ず抜けが出ます。

Q. コスト列は推定値でも意味がありますか。 あります。総額の正本はプロバイダ側で取り、自前の記録は「機能別の比率」を見るために使うと割り切ると運用が安定します。

Q. 単価表はどこに置くのが正解ですか。 デプロイなしで更新できる場所(設定や環境変数、DB)です。有効期限つきの価格があるため、コードに固定すると改定日に静かにずれます。

Q. 失敗した呼び出しも課金されますか。 経路により異なりますが、記録がなければ検証もできません。呼び出し前に行を作る設計にしておけば、少なくとも回数は分かります。

Q. 予算上限は何を見て判定すべきですか。 記録テーブルの当日合計です。制御と計測を同じ情報源にしておくと、記録が壊れたときに上限も同時に壊れて気づけます。


自律的に動くAIに予算を渡す設計は、賢いプロンプトより「計測と停止をどこに置くか」で決まります。社内の業務にAIを組み込む段階で、コストの説明責任まで含めた設計が必要になりましたら、ご相談を承っています。

出典

  1. Anthropic — 料金(モデル別単価・プロンプトキャッシング・バッチ処理)(参照 2026-08-09)
  2. Cloudflare Docs(公式ドキュメントのソース)— AI Gateway(参照 2026-08-09)