リアルタイムLLMコストアトリビューション:トークン数からチーム予算まで

Built for Speed: ~10ms Latency, Even Under Load
Blazingly fast way to build, track and deploy your models!
- Handles 350+ RPS on just 1 vCPU — no tuning needed
- Production-ready with full enterprise support
複数のプロバイダーからのLLM請求書は、支払いは簡単ですが、割り当てが困難です。AnthropicのコンソールはAPIキーごとに1つの数値を、OpenAIはプロジェクトごとに1つの数値を表示します。これらの数値は正確です。しかし、誰が、どのアプリで、どの機能で、誰の予算に対して使ったのかは分かりません。この記事では、ゲートウェイレベルの帰属レイヤーがそのギャップを埋める方法を説明します。具体的には、メタデータタグ付けスキーマ、プロバイダー横断的なトレースごとのコスト計算式、数十億のスパンを日次チーム集計に変換する集計パイプライン、実際の並行処理モデルを用いたソフトおよびハードな予算強制、そして最終的に生成されるチャージバックレポートについてです。
水曜日の午後、ノースウィンド社にて。 プラットフォーム担当副社長のサラは、Slackを受け取ります。「金曜日のQ2予算会議のために、チームごとのAIコストの内訳が必要です。」彼女はAnthropicのコンソールを開きます。表示されたのは1行だけです。月初来で47,234.12ドル、sk-ant-prod-northwind-sharedに請求されています。このキーは、4つのチームと12以上のアプリケーションにわたる50人以上のエンジニアによって使用されています。この数値は正確です。しかし、金曜日に彼女がする必要がある会話、つまりどのチーム、どのアプリケーション、どの機能、どの顧客に対する請求なのかを説明するには役に立ちません。
サラの選択肢はどれも芳しくありません。キーを4つに分割し、その代償としてゲートウェイのプロバイダー横断ルーティングを失うか。各チームリーダーに見積もりを依頼し、手動で調整するか — その数値は30%も間違っているでしょう。あるいは、作業がすでに記録されている場所、つまりゲートウェイのトレースストアから答えを引っ張ってくるか。この記事では、3番目の選択肢がどのように機能するかを説明します。
1. ネイティブ課金の盲点
プロバイダーネイティブ課金の構造的な問題は、チームの境界ではなく、認証情報の境界で集計されることです。AnthropicはAPIキーと組織で、OpenAIはプロジェクトで、Azureはデプロイメントとリソースグループで、AWS BedrockはIAMロールとアカウントで支出をグループ化します。これらのどれも、実際に課金したい運用単位(チーム、アプリケーション、機能、顧客)とは一致しません。
従来の回避策である「チームごとに1つの認証情報」は機能しますが、そもそもゲートウェイを持つ価値を損ないます。複雑なリクエストにはAnthropicを、シンプルなリクエストにはHaikuを選択し、Anthropicが過負荷になった際にはAzureにフォールバックし、地域デプロイメント間で負荷分散を行うルーティングロジックが失われるためです。これらすべてにおいて、ゲートウェイがすべてのプロバイダーの認証情報を保持する必要があります。チームごとのキーは、チームごとのプロバイダー関係を強制します。アプリケーション層での計測は、もう一つの行き止まりです。それはグリーンフィールドのコードベースでは機能しますが、2つのSDKバージョンとフォークされたLangChainを介して3つのプロバイダーを呼び出すエージェントには機能しません。帰属はゲートウェイで行われるべきです。なぜなら、すべてのプロバイダー呼び出しは結局そこを経由するからです。
2. メタデータタグ付け戦略:X-TFY-METADATAヘッダー
パターンは単純です。ゲートウェイへのすべてのリクエストには、呼び出し元が何であるか、誰のために呼び出しているか、何をしているかを記述するJSONオブジェクトが含まれます。ゲートウェイはこのオブジェクトをgen_ai属性とともにトレースに保存し、厳選されたフィールドのサブセットを集計に使用されるメトリックラベルに投影します。
HTTP — アプリケーションはすべてのゲートウェイ呼び出しでメタデータヘッダーを設定します
POST /v1/chat/completions HTTP/1.1
Host: gateway.northwind.internal
Authorization: Bearer ...
Content-Type: application/json
X-TFY-METADATA: {
"team": "platform-eng",
"app": "code-review-agent",
"feature": "pr-summary",
"env": "production",
"user_id": "u_12345",
"repo": "northwind/cargo-copilot",
"pipeline_id": "ci-run-98765"
}
{"model": "claude-sonnet-4-6", "messages": [...]}フィールドは意図的に異種混合です。カーディナリティの低いフィールド(チーム、アプリ、機能、環境)は、メトリックラベルにうまく投影されます。カーディナリティの高いフィールド(user_id、pipeline_id)は、投影されるとメトリックストレージを爆発させ、トレース上のみに留まります。ゲートウェイはその違いを知る必要があります。集計用タグフィールドは明示的な許可リスト(一般的な設定:team, app, feature, env, model_class)であり、日次ロールアップのキースペースの一部となります。それ以外のすべては監査用タグであり、フォレンジックやアドホッククエリのためにトレースに保存されます。
アプリケーションは、外側の呼び出しで一度だけメタデータを設定します。ゲートウェイはそれをスパンツリー全体に伝播させます。これには、フォールバック(異なるプロバイダーへのフォールバックでも同じメタデータを保持)や、複数ターンのエージェントループ(各ツール呼び出しは親呼び出しと同じメタデータを保持)が含まれます。継承は自動的であり、チームはすべての呼び出しサイトでメタデータをスレッド化する必要はありません。
3. トレースごとのコスト計算
コストは請求時ではなく、スパン終了時に計算されます。ゲートウェイは(プロバイダー、モデル)ペアごとにバージョン管理された料金表を保持し、最終応答チャンクで報告された使用トークンに適用します。出力はプロバイダースパン上のgen_ai.usage.cost_usdとして保存され、ルートにロールアップされます。
Python — プロバイダースパン終了時のコスト計算、完全な複数行項目式
# Pricing tables are versioned with the date in force when they were set.
PRICING = {
"openai:gpt-4o-2024-08-06": {
"input": 2.50, "cached_input": 1.25, "output": 10.00,
"version_date": "2024-08-06",
},
"anthropic:claude-sonnet-4-6": {
"input": 3.00, "cached_input": 0.30, "cache_write_5m": 3.75,
"cache_write_1h": 6.00, "output": 15.00,
"version_date": "2026-02-17",
},
"anthropic:claude-haiku-4-5": {
"input": 1.00, "cached_input": 0.10, "cache_write_5m": 1.25,
"output": 5.00, "version_date": "2025-10-22",
},
# ... one entry per (provider, model) pair the gateway routes to
}
def compute_span_cost_usd(span_attrs: dict) -> float:
key = f"{span_attrs['gen_ai.provider.name']}:{span_attrs['gen_ai.response.model']}"
p = PRICING[key]
in_tok = span_attrs.get("gen_ai.usage.input_tokens", 0)
out_tok = span_attrs.get("gen_ai.usage.output_tokens", 0)
c_read = span_attrs.get("gen_ai.usage.cache_read.input_tokens", 0)
c_write = span_attrs.get("gen_ai.usage.cache_creation.input_tokens", 0)
# Per OTel spec, gen_ai.usage.input_tokens INCLUDES both cache lines.
# Subtract both before applying the fresh-input rate.
fresh = max(in_tok - c_read - c_write, 0)
write_rate = p.get("cache_write_5m", p.get("cache_write", 0))
cost = (
fresh * p["input"] / 1_000_000 +
c_read * p["cached_input"] / 1_000_000 +
c_write * write_rate / 1_000_000 +
out_tok * p["output"] / 1_000_000
)
# Audio (per-minute) and image (per-image) add separately, not per token.
audio_sec = span_attrs.get("gen_ai.usage.audio_seconds", 0)
img_count = span_attrs.get("gen_ai.usage.image_count", 0)
cost += (audio_sec / 60) * p.get("audio_per_min", 0)
cost += img_count * p.get("image_each", 0)
return round(cost, 6)見落としがちな2つの重要な詳細があります。まず、料金表は日付スタンプでバージョン管理されています。これは、プロバイダーが料金を変更するため、請求書がリクエスト時に有効だった料金と一致する必要があるからです。過去のトレースを今日の料金で再計算してはいけません。次に、音声および画像入力はトークンごとではなく、分ごとおよび画像ごとに課金されます。これらは別々の項目として追加しないと、計算式がそれらを黙って見落としてしまいます。
キャッシュトークンの減算(fresh = input_tokens − cache_read − cache_write)は、OpenTelemetry GenAI仕様が指摘するのと同じ微妙な点です。gen_ai.usage.input_tokensは、両方のキャッシュ行を含む合計として定義されているため、cache_readに対しては一度減算するがcache_writeに対しては減算しないという一般的なバグは、キャッシュ書き込み部分を新規入力レートとキャッシュ書き込みレートの両方で二重にカウントしてしまいます。
4. 集計パイプライン:生トレースから日次チームロールアップへ
50人規模の単一組織がすべてのPRでエージェントを実行すると、1日あたり100万件のプロバイダースパンを容易に生成します。その生データ量に対して「プラットフォームチームが昨日何に費やしたか」という問いに答えるためにクエリを実行することは、技術的には可能ですが、運用上は非常に困難です。トレースストアに対するフルスキャンは、基盤となる推論と同じくらいのコストがかかります。解決策は、3つのレイヤーで行われる事前集計です。
集計パイプライン — 1分ごとのカウンター → 1時間ごとのロールアップ → 日次MV
# Layer 1 — Streaming counter (per-minute, in memory at the gateway worker)
key = (team, app, feature, env, model, provider)
delta = (tokens_in, tokens_out, cache_read, cache_write, cost_usd, 1)
counters[key] += delta
# Flush every 60s to Layer 2.
# Layer 2 — Hourly rollup table (ClickHouse / TimescaleDB)
CREATE TABLE llm_spend_hourly (
hour_ts DateTime,
team LowCardinality(String),
app LowCardinality(String),
feature LowCardinality(String),
env LowCardinality(String),
model LowCardinality(String),
provider LowCardinality(String),
input_tokens UInt64,
output_tokens UInt64,
cache_read_tok UInt64,
cache_write_tok UInt64,
cost_usd Float64,
request_count UInt32,
error_count UInt32
) ENGINE = SummingMergeTree
PARTITION BY toYYYYMM(hour_ts)
ORDER BY (team, app, hour_ts);
# Layer 3 — Daily materialized view (chargeback source of truth)
# Same schema, day-grained. Refreshed at 00:15 UTC.
# Indexed on (team, app, day_ts) for sub-second UI queries.これを費用対効果の高いものにするためのコスト規律は、トレースストアをクエリして集計しないことです。トレースストアはフォレンジック用です。集計はロールアップテーブルから行われます。Northwind規模(1日あたり100万スパン)では、ロールアップはギガバイトの範囲に収まり、クエリレイテンシーは1秒未満です。エンジンの選択は主に好みの問題ですが、ClickHouseは大規模な集計で高速であり、カーディナリティ制御が優れています。TimescaleDBは、すでにPostgresを実行しているチームにとってより使いやすいでしょう。どちらも、スタートアップから中規模の段階では単一のVMでこのワークロードを実行できます。1日あたり約10億スパンを超えると、ClickHouseが優位に立ちます。
5. 予算の強制適用:ソフトリミット、ハードリミット、および競合状態
チームの支出をほぼリアルタイムで計算できるようになれば、予算管理もそれに続きます。パターンは2段階です。
ソフトリミット(予算の80%) PagerDutyまたはSlack経由でチームオーナーに通知をトリガーします。リクエストはブロックされません。その目的は、問題になる前に傾向を表面化させることです。一部のチームでは、60% / 80% / 95%の3段階ソフトリミットを設定しています。
ハードリミット(予算の100%) ゲートウェイは、原因を特定するエラーボディとともにHTTP 429を返し、プロバイダーへの呼び出しを拒否します。
HTTP 429 — budget_exhausted レスポンス形式
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 86400
{
"error": {
"type": "budget_exhausted",
"code": "team_monthly_limit",
"message": "Team 'platform-eng' has exhausted its $25,000.00 May budget. Contact your budget owner or request an increase.",
"team": "platform-eng",
"limit_usd": 25000.00,
"spent_usd": 25032.18,
"period_end": "2026-05-31T23:59:59Z"
}
}並行処理の問題は現実的です。チームXが25,000ドルの予算のうち24,997ドルに達しており、10件の同時リクエストが到着した場合、素朴な読み書きロジックでは、すべてのワーカーが「予算内」と判断し、10件すべてがディスパッチされてしまいます。解決策はアトミックカウンターです。RedisのINCRBYFLOATがその主力となります。各ワーカーは、ディスパッチ前に予測されるリクエストコスト(トークン数の余裕と、チームに許可されている最も安価なモデルから推定)でインクリメントし、インクリメント後の値を制限値と比較し、超えていれば中止します。実際にかかったコストは、スパン終了時に予測値と照合されます。小さな調整は日次ロールアップで処理されます。
このパターンでは、中止されたリクエストでわずかに過剰にカウントされますが、過少にカウントされることはありません。予算のサーキットブレーカーとしては、それが正しい非対称性です。数セントの過剰カウントは、数千ドルの予算超過よりも優れています。
6. ルーティング統合:予算が上限に近づくにつれてモデルを切り替える
予算システムの最も脆弱な部分は、100%に達したときの「崖」です。最高の品質から一気に429エラーになるのは、ユーザー体験として好ましくありません。より良いパターンは、段階的な劣化(グレースフルデグラデーション)です。利用率が上昇するにつれて、より安価なモデルにルーティングします。
ゲートウェイのルーティングレイヤーとの統合は、デフォルトのモデル選択を上書きする予算認識ポリシーです。一般的な形式は次のとおりです。
YAML — 予算認識オーバーライドを含むルーティングポリシーの断片
- team: platform-eng
default_model: claude-sonnet-4-6
budget_routing:
- when: utilization < 0.80
use: claude-sonnet-4-6
- when: utilization >= 0.80 and utilization < 0.95
use: claude-haiku-4-5
notify: slack://platform-eng-budget
- when: utilization >= 0.95
use: claude-haiku-4-5
max_output_tokens: 500
notify: pagerduty://platform-eng-oncall80%に達すると、チームはSonnetからHaikuに切り替えられます。これは、ほとんどの重要でないタスクが許容できる品質低下で、コストを3分の1に削減します。95%では、出力上限も厳しくなり、リクエストあたりの最悪ケースのコストを制限します。各移行時にチームオーナーに通知されます。パイプラインは停止しません。
このトレードオフは、ワークロードごとに意識的に行う必要があります。コードレビューやチケットのトリアージは段階的に劣化しても問題ありませんが、医療記録の要約はそうすべきではありません。ルーティングポリシーは、アプリケーションの他の本番設定とともに管理されます。
7. チャージバックレポートのスキーマ
このループを閉じる成果物は、コスト配分作業を行う担当者(通常はFinOpsチームまたは財務パートナー)にエクスポートされる月次チャージバックレポートです。スキーマは日次ロールアップのコピーであり、月ごとにフィルタリングされ、(チーム、アプリ、モデル、プロバイダー)ごとに1行にグループ化されます。
`cache_savings_usd` 列は、プラットフォームへの投資の大部分を正当化するものです。これは、(キャッシュ読み取りトークン数 × 新規入力レート − キャッシュ読み取りトークン数 × キャッシュ済みレート) / 1e6 として計算されます。これは、これらのトークンがキャッシュされたレートでかかる費用と、新規でかかったであろう費用との差を示します。数千のリクエストにわたって安定したシステムプロンプトを持つエージェントの場合、この数値は総支出の3分の1から3分の2を占めることがよくあります。
このレポートは、組織の既存のFinOpsツールに取り込むためのCSV形式と、チームオーナー自身のためのレンダリングされたダッシュボード形式で生成されます。以下のモックアップは、月末のビューがどのように見えるかを示しています。

8. 実例:エンジニア50人、コードレビューエージェント、月額790ドル
2026年5月時点のClaude Sonnet 4.6の料金を使用した具体的な数値:入力3ドル、出力15ドル、キャッシュ読み取り0.30ドル、キャッシュ書き込み3.75ドル(100万トークンあたり)。
Northwindのコードレビューエージェントは、すべてのプルリクエストで実行されます。PRあたりの構成は、12,000トークンのシステムプロンプト(スタイルガイド、リポジトリ規約、チェックリスト、少数ショットの例)、1,500トークンの差分、約800トークンのインラインレビュー出力です。エンジニア50人 × 1日あたり10PR = 1日あたり500PR。
キャッシュなしの場合。 PRあたり: (12,000 + 1,500) × $3/1M + 800 × $15/1M = $0.0405 + $0.012 = $0.0525。日額: $26.25。月額: $787。
5分間のプロンプトキャッシュを使用した場合 12Kシステムプロンプトに対して — コールドパスでのキャッシュミス、ウォームパスでのキャッシュ読み取り、混雑したリポジトリでの約85%のウォームヒット率:
- ウォーム: 12,000 × $0.30/1M + 1,500 × $3/1M + 800 × $15/1M = $0.0201
- コールド(キャッシュ書き込み): 12,000 × $3.75/1M + 1,500 × $3/1M + 800 × $15/1M = $0.0615
- 混合 (0.85 × ウォーム + 0.15 × コールド) = $0.0263 → 日額 $13.15 → 月額 $394、50%の削減となります。
500人規模のエンジニア組織では、同じ計算が線形にスケールし、 月額7,870ドル(キャッシュなし) および 月額3,940ドル(キャッシュあり)。トレースごとのアトリビューションがなければ、この差は、区別されていない共有キーの合計の一部として見過ごされてしまいます。アトリビューションがあれば、チャージバックの行には次のように表示されます:platform-eng / code-review-agent / claude-sonnet-4-6 / anthropic — 394ドル使用、393ドルのキャッシュ節約、15,500リクエスト、エラー率0.1%。金曜日の会議は、もはや共有キーでの47,234ドルについてのものではなくなります。
9. よくある質問
チームごとに1つのAPIキーを使用し、プロバイダーのダッシュボードを見るだけではだめなのですか?
可能です。しかし、プロバイダー間のルーティング、自動フォールバック、ロードバランシング、統合されたレート制限、集中型キーローテーションといったゲートウェイの価値を享受できなくなります。そこから始めるほとんどのチームは、キーを分割するとトラフィックポリシーも分割せざるを得なくなることに気づくと、四半期以内にゲートウェイレベルのアトリビューションに移行します。メタデータタグのアプローチは、そのトレードオフなしにアトリビューションを提供します。
コストデータを常に最新に保つにはどうすればよいですか?日次ロールアップでは、リアルタイムダッシュボードには遅すぎます。
3層アーキテクチャは、両方を提供するために特別に存在します。時間ごとのロールアップは数分以内に最新の状態になります(1分ごとのカウンターが継続的にそれにフラッシュされます)。そして、日次ロールアップはチャージバックの信頼できる情報源です。リアルタイムダッシュボードには時間ごとのデータを、チャージバックには日次データをクエリしてください。
LLM以外のコスト(埋め込み、ベクトルストア、ファインチューニングなど)についてはどうですか?
埋め込みは推論と同じ使用形態を持ち、ゲートウェイは同じメタデータでそれらを属性付けします。ベクトルストアとファインチューニングは通常、ゲートウェイ外で請求されます。FinOpsパイプラインがそれらを取り込む場合、チャージバックレポートはそれらの外部項目を結合できます。
ストリームが閉じるまで最終的なコストがわからないストリーミング応答の場合、これはどのように機能しますか?
プロバイダーのスパンはストリーム全体で開いたままになり、最後の使用ブロックが末尾のチャンクに到着したときに、スパンが閉じると同時にコスト計算が実行されます。リクエスト開始時の予算事前割り当ては予測される上限を使用し、調整はスパンが閉じるときに行われます。
Redisの停止やネットワークパーティション発生時の予算の強制適用についてはどうですか?
アトミックカウンターは、ハードリミット強制適用の信頼できる情報源です。Redisに到達できない場合、ゲートウェイには2つの設定可能なモードがあります。フェイルオープン(リクエストを許可し、超過支出のリスクを受け入れる)またはフェイルクローズ(リクエストを拒否し、可用性の低下を受け入れる)です。適切な選択はワークロードによって異なり、ゲートウェイはそれをチームごとの設定として公開します。ソフトリミット通知はベストエフォートです。
TrueFoundryの役割は何ですか?
X-TFY-METADATAヘッダー、料金表のメンテナンス、ストリーミング集計パイプライン、および予算サーキットブレーカーはすべて、 TrueFoundry AI Gateway デフォルトで。チャージバックのエクスポートは、スケジュールされたCSV/JSON/webhookとして、または予算責任者向けのインタラクティブなダッシュボードとして実行されます。料金表はプロバイダーの料金カードを追跡し、履歴トレースはリクエスト時の有効な料金を使用し続けるため、先月のチャージバックを再実行しても、先月と同じ数値が生成されます。
現在、LLMワークロードが共有APIキーに対して課金されている場合、最も効果的な最初の一歩は、完全なアトリビューションパイプラインを展開する前であっても、すべての呼び出しサイトにメタデータヘッダーを追加することです。このヘッダーは前方互換性があり、設定された瞬間からトレースにデータが蓄積され始め、ロールアップテーブルが起動した日にはチャージバックレポートが役立つようになります。
参考文献
- TrueFoundry AI Gateway — 概要
- OpenAI API料金
- Anthropic API料金
- OpenTelemetry GenAIセマンティック規約
- Redis INCRBYFLOAT — アトミックカウンタープリミティブ
- ClickHouse SummingMergeTree
- TimescaleDBハイパーテーブル
ここでのNorthwind、Sarah、および具体的な金額は例示的なものです。データモデル、X-TFY-METADATAヘッダーの設計、およびパイプラインアーキテクチャは、TrueFoundryのような本番AIゲートウェイが実際に費用をアトリビュートする方法です。提示されているプロバイダー料金は、各プロバイダーの公開料金カードに基づき、2026年5月現在のものです。
TrueFoundry AI Gateway delivers ~3–4 ms latency, handles 350+ RPS on 1 vCPU, scales horizontally with ease, and is production-ready, while LiteLLM suffers from high latency, struggles beyond moderate RPS, lacks built-in scaling, and is best for light or prototype workloads.














.webp)
.webp)


.png)

.png)














