テーマを切り替える

AIエージェントのコスト管理:モデルルーティング、ツール予算、キャッシュ、リトライ制御

Easton editorial illustration: agent rollout and rollback rail
7
予算レイヤー
user、tenant、workflow、task、tool、retry、cache。
4
制御アクション
route、degrade、pause、abort。
3
キャッシュ種別
prompt prefix cache、business result cache、tool response cache。
数据来源: このエンジニアリングチェックリストは、フェーズ 1 の公式ドキュメント調査をもとに整理しています。価格、割引、モデル提供状況は公開前に公式ページで再確認してください。

"OpenAI API Pricing"

午前 3 時、バックグラウンドの report Agent が空のレスポンスを返します。HTTP ステータスは 200 なのに、body は空。リトライロジックはステータスコードだけを見ているため、そのまま再試行を続けます。1 回のリクエストで 500 token を送信し、1,500 回リトライした結果、75 万 token を消費。翌朝に請求を見て、ようやく異常に気づくわけです。

根本原因は「高いモデルを選んだこと」ではありません。欠けていたのは 3 つ。サーキットブレーカー、予算チェック、エラー分類です。Agent のコスト暴走には、典型的に次の 3 パターンがあります。

リトライに上限がない。 失敗モードの分類がなく、空レスポンスをリトライ可能なエラーとして扱います。サーキットブレーカーもないため、1,500 回連続で失敗しても止まりません。各リトライで完全なコンテキストを送り直し、コストが 2〜5 倍に膨らみます。

コンテキストが膨張する。 長時間タスクを 6 時間走らせた結果、会話履歴が 80K tokens まで膨らみます。checkpoint がないため、失敗すると最初から再実行し、各ステップで再び料金が発生します。

モデルを使いすぎる。 すべてのタスクで frontier モデルを使い、ルーティング戦略がありません。単純な分類タスクまで最も高いモデルに投げ、token の 70% を無駄にします。

Agent のコスト管理は単発の最適化ではありません。予算オブジェクト、ルーティング戦略、キャッシュヒット、リトライ遮断、コストログ、アラート閾値を重ねて設計するものです。この 6 つのエンジニアリング対象を、判断表や実行可能なチェックに落とします。

予算オブジェクト設計:何を記録し、どこに持ち、どう遮断するか

コスト管理は、まず予算オブジェクトから始めます。総額だけを記録しても足りません。分解されていないと、請求異常が起きたときに、どのユーザー、どのタスク、どのツールがコストを燃やしたのか分かりません。

7 層の予算オブジェクト

予算オブジェクトは粗い粒度から細かい粒度へ、7 層に分けられます。

予算レイヤー予算オブジェクト予算上限の目安アラート条件
Layer 1userユーザーごとの日次/月次上限残り < 20% でアラート
Layer 2tenanttenant ごとの独立予算プール残り < 30% でアラート
Layer 3workflowworkflow 種別ごとの独立予算残り < 40% でアラート
Layer 4taskタスク種別ごとの独立予算残り < 50% でアラート
Layer 5toolツール呼び出しごとの独立予算上限超過でツールをスキップ
Layer 6retryリトライ回数上限 + サーキットブレーカー連続失敗 N 回でツールを停止
Layer 7cacheキャッシュヒット率の監視ヒット率 < 期待値でアラート

各層の予算上限は業務モデルに依存するため、変わりやすい設定です。一方で、階層構造そのものは安定しています。残予算フィールドはコストログに書き込み、アラートと遮断判断に使います。

各層で記録するフィールド

各予算層では、次のフィールドを記録します。

フィールド名用途記録する理由
modelモデルの特定stringモデルルーティングが妥当か判断するため
inputTokens入力 token 数integer入力コストを計算するため
outputTokens出力 token 数integer出力コストは入力と異なるため、独立して記録する
cachedTokensキャッシュヒットした token 数integerキャッシュによる節約を計算するため
costEstimate今回の推定コストfloatリアルタイムのコスト累積に使う
budgetRemaining残予算floatサーキットブレーカーの判断根拠になる

予算オブジェクト設計の中心は「軸ごとに記録すること」です。「total_cost だけを記録すること」ではありません。マルチテナントでは tenantId ごとにコストを按分し、複数ツールを使う場合は toolName でブラックボックスを特定します。

遮断ロジック

残予算が閾値を下回ったら、サーキットブレーカーを発動します。

def check_budget_before_retry(budget_remaining, retry_cost_estimate):
    if budget_remaining < retry_cost_estimate:
        return "skip_retry"  # 予算超過になるためリトライをスキップ
    if budget_remaining < threshold:  # threshold は例えば 20%
        return "wait_approval"  # 残予算が少ないため承認を待つ
    return "continue"

遮断ロジックは、リトライ後ではなくリトライ前に残予算を見ます。毎回のリトライ前にコストを見積もり、予算超過なら止める。これで「空レスポンスを 1,500 回リトライして 1 日分の予算を燃やす」事故を避けられます。

安定した prefix に入れるコンテキストと、実行時変数として後ろに置くコンテキストは、同シリーズのコンテキストエンジニアリング記事で扱います。

モデルルーティング戦略:すべてのタスクに最も高いモデルは要らない

ticket triage Agent の実際の分布を考えると、70% は単純分類、20% は返信下書き、顧客向けメールを送る前に frontier モデルへ上げるべきものは 10% 程度です。ルーティング戦略によって、コストを 40〜85% 抑えられる場合があります。

モデル層ルーティング

モデル層ルーティングは、タスクの複雑さで分けます。

タスク層典型タスク推奨モデル層割合コスト特性
70% - S 級分類、抽出、フィルタリング、簡単な Q&Anano/flash(最も安い)70%出力が短く、ターン数とツール呼び出しが少ない
20% - M 級下書き、要約、コード生成、中程度の推論mid-tier(中価格帯)20%出力は中程度で、ツールを呼ぶ場合がある
10% - L 級レビュー、アーキテクチャ設計、複雑な推論、複数ツールの調整frontier(最も高い)10%出力が長く、ターン数とツール呼び出しが多い

ルーティング戦略は 3 ステップです。

第 1 歩:タスクを分類する。 各 workflow に S/M/L の複雑さ基準を定義します。出力長、ツール呼び出し回数、推論の深さ、リスクレベルを含めます。

第 2 歩:デフォルトは S 級にする。 複雑な特徴に該当したときだけ、M または L に昇格します。

第 3 歩:段階的に昇格する。 S 級モデルが失敗したら M 級へ、M 級が失敗したら L 級へ上げます。L 級でも失敗したら人間の介入に回します。昇格前に残予算を確認し、予算超過なら昇格をスキップします。

サービス層ルーティング

同じモデルでも latency priority によって分流できます。割引率と完了時間は変わりやすいため、公開前に公式価格を再確認します。

サービス層コスト割引完了時間適した場面
リアルタイム API割引なし即時応答対話型 Agent、高優先度タスク
Batch API50% cost discount(公開前に再確認)24-hour turnaround(公開前に再確認)バッチ eval、分類、embedding、コンテンツリポジトリ処理
Flex Processing低コスト(公開前に再確認)応答は遅めで、まれに利用不可低優先度の非同期タスク、model evaluations、data enrichment

オフラインタスクのルーティング判断リストです。

  • 即時応答が必要ですか?はい -> リアルタイム API。モデルルーティングを使います。
  • 24 時間の遅延を許容できますか?はい -> Batch API。
  • 低優先度で、まれな失敗を許容できますか?はい -> Flex Processing。
  • eval、分類、embedding のようなバッチ処理ですか?はい -> Batch API。

リスク分級

タスクはリスクレベルでも分け、ルーティング経路を変えます。

リスクレベル典型操作ルーティング戦略予算分岐
低リスク分類、抽出、内部要約S 級モデル + 自動経路承認なし、予算上限は緩め
中リスク顧客返信の下書き、コード変更の提案M 級モデル + 任意承認予算超過時に承認へ回せる
高リスク顧客メール送信、課金、アーキテクチャ変更L 級モデル + 必須承認承認待ち、拒否、タイムアウトが予算分岐になる

承認待ち、拒否、タイムアウトが予算分岐にどう影響するかは、同シリーズの Human-in-the-Loop 記事で扱います。

重要な制約

ルーティング戦略には、次の制約が必要です。

  • 価格数字をハードコードしない:価格は変わりやすい事実です。コスト式を業務ロジックに固定せず、model + pricingVersion を記録します。
  • 残予算を確認する:昇格前に budgetRemaining を確認します。予算超過なら、昇格をスキップするか承認へ回します。
  • エラーを分類する:ルーティング失敗は、復旧可能なモデル能力不足と、復旧不能なパラメータエラーや権限拒否を分けます。

ツール呼び出し予算:per-tool budget、timeout、リトライ上限

ツール呼び出しには schema と API の二重コストがあります。各呼び出しでは schema、引数、応答解析のコンテキストを送ります。さらに外部 API のレート制限やタイムアウトもあります。これらのコストはモデル呼び出しとは別です。

ツール予算の制御

各ツールに独立した予算制御を設定します。

制御項目設定の目安監視フィールド発動する動作
Per-tool budget1 回の呼び出し上限tool_cost_estimate上限超過でツールをスキップ、または降格処理
Tool timeout外部 API のタイムアウトtool_durationタイムアウトをリトライ可能エラーとして記録
Retry limit per tool単一ツールのリトライ上限tool_retry_count上限超過でツールを諦め、リトライループに入らない

検索、データベース、外部サービスなどの外部 API ツールは個別に記帳します。そうしないと、ツール呼び出しがコストのブラックボックスになります。

ツール失敗の分類

ツール失敗は、復旧可能と復旧不能の 2 種類に分けます。

失敗タイプ典型エラー処理戦略コスト影響
復旧可能な失敗ネットワークタイムアウト、503 Service Unavailable、429 Rate Limit指数バックオフと retry-after で自動リトライ各リトライで完全なコンテキストを消費
復旧不能な失敗403 Permission Denied、400 Bad Request、ツール不存在リトライせず、エラーを注入してモデルに判断させるリトライせず、無効な消費を避ける

失敗分類の中心は、「外部要因による一時的な失敗だけをリトライし、内部設定ミスはリトライしない」ことです。

サーキットブレーカー

連続失敗 N 回でツールを停止し、「空レスポンスを 1,500 回リトライ」を防ぎます。

def circuit_breaker_tool(tool_name, consecutive_failures, threshold=5):
    if consecutive_failures >= threshold:
        return "disable_tool"  # ツールを停止
    return "continue"

サーキットブレーカーの状態はログに書きます。「なぜツールが停止されたのか」を後で追えるようにするためです。遮断後は人間の介入、または自動復旧チェックを待ち、不安定なツールを繰り返し呼びません。

ツール呼び出しの基本はツール呼び出しで扱っています。この記事では、per-tool budget、timeout、リトライ上限を拡張します。

Prompt Caching 設計:安定 prefix、変数の位置、1024 token 閾値

Prompt Caching は、安定した prompt prefix の入力 token を最適化する仕組みです。業務結果キャッシュではありません。同じ prompt prefix を最近処理したサーバーへルーティングし、レイテンシと入力 token コストを下げます。

Prompt Caching は結果キャッシュではない

Prompt Caching がキャッシュするのは安定した prompt prefix であり、業務結果ではありません。違いは重要です。

  • Prompt Caching:system prompt や tool schema など、安定した prompt prefix をキャッシュします。ヒットすると入力 token を節約できますが、推論自体は再実行されます。
  • 業務結果キャッシュ:ツール呼び出し結果やデータベースクエリなど、完全な出力をキャッシュします。ヒット時は直接返し、モデルを呼びません。

目的が違います。Prompt Caching は入力 token コストを最適化します。業務結果キャッシュは呼び出し全体のコストを最適化します。両方を使えます。安定した前置きは Prompt Caching に、高頻度のツール結果は業務キャッシュに載せます。

構造上の要件

Prompt Caching の鍵は、安定した前置きと実行時変数を分けることです。

内容タイプ位置キャッシュヒットの可能性典型例
安定 prefix(cache に入る)Prompt 前部高いSystem prompt、Tool schema、Policy 文書、Few-shot 例
実行時変数(cache に入れない)Prompt 後部低いUser input、File fragments、Runtime state(現在の会話ターン、一時変数)

設計ステップです。

  1. System prompt、Tool schema、Policy を前部に置く:これらは複数回の呼び出しで安定しているため、キャッシュに当たりやすくなります。
  2. User input、File fragments、Runtime state を後部に置く:これらは毎回変わるため、安定 prefix に含めるべきではありません。
  3. Cache hit rate を監視する:cachedTokens と総入力 token を記録し、ヒット率を計算します。Cache hit rate が 40% 超なら健全、20% 未満なら prefix 構造を見直します。

閾値と効果

Prompt Caching の自動有効化閾値と効果は変わりやすい事実です。公開前に公式ドキュメントで再確認します。

  • 閾値:1024 tokens 以上で自動有効化(公開前に再確認)。
  • 効果:ヒット時にコストとレイテンシを下げられる(具体的な比率は再確認)。
  • ヒット確認usage.prompt_tokens_details.cached_tokens フィールド。

Prompt Caching の対応モデル、閾値、割引率は変わります。公開前に公式 pricing ページで確認してください。安定している設計原則は「静的内容を前に、変動内容を後ろに置く」ことです。

失敗リトライ遮断:冪等性、状態保存、残予算チェック

リトライはコスト暴走の最大要因になりやすい部分です。バックグラウンドの report Agent が不安定なツールで止まり、各失敗で完全なコンテキストを送り直すと、1,500 回のリトライ後には通常経路から大きく外れたコストになります。

リトライ前に予算を確認する

リトライ後ではなく、リトライ前に残予算を確認します。

def should_retry(error_type, budget_remaining, retry_cost_estimate):
    # エラー分類
    if error_type in ["403", "400", "tool_not_exist"]:
        return False  # 復旧不能エラー。リトライしない

    # 予算チェック
    if budget_remaining < retry_cost_estimate:
        return False  # 予算超過。リトライしない

    return True  # リトライ可能

予算チェックはリトライロジックの前に置きます。これで「空レスポンスを 1,500 回リトライして 1 日分の予算を燃やす」ことを避けられます。

冪等性

リトライで、メール送信や課金のような副作用を重複実行してはいけません。

  • ツール呼び出しには requestId などの冪等 ID を使います。外部 API が同じ ID を受け取ったら、処理を繰り返さずキャッシュ済み結果を返すようにします。
  • 冪等 ID はコストログに書き、重複呼び出しを判断できるようにします。

冪等設計の中心は「同じ操作で二重に消費しない」ことです。これがないと、リトライがコストと副作用の両方を増幅します。

状態保存(Checkpoint)

長時間タスクは、失敗しても全体を再実行しない形で復旧します。

  • Agent が重要なノードに到達したら、checkpoint を保存します。完了済みステップ、現在状態、コンテキスト要約を含めます。
  • 失敗復旧時は最初からではなく checkpoint から続行します。
  • Checkpoint は永続化ストレージに書きます。メモリ内だけに置かないでください。

checkpoint と thread state の設計は、LangGraph Agent アーキテクチャで扱っています。

リトライ戦略

エラー種別ごとに、リトライ戦略を変えます。

エラー種別典型エラーリトライ戦略コスト影響
ネットワークタイムアウト10 秒応答なし指数バックオフ + retry-after、最大 3 回各リトライで完全なコンテキストを消費
503/429Service Unavailable、Rate Limitレート制限ウィンドウ + retry-after を待ち、最大 3 回待機時間は token を消費しないが、リトライは消費する
403/400Permission Denied、Bad Requestリトライせず、エラーを注入してモデルに判断させるリトライせず、無効な消費を避ける

リトライ戦略の中心は「復旧可能なエラーだけをリトライする」ことです。無効なリクエストを何度もモデルに投げ直してはいけません。

サーキットブレーカー

連続失敗 N 回でリトライを止め、人間の介入を待ちます。

def circuit_breaker(consecutive_failures, threshold=5):
    if consecutive_failures >= threshold:
        return "stop_retry"  # リトライを停止
    return "continue"

サーキットブレーカーの判断はログに書きます。「なぜリトライを止めたのか」を後で説明できるようにするためです。遮断後は、人間の介入または予算回復を待ち、不安定なツールやモデルを繰り返し呼びません。

コストログとアラート:記録するフィールドと閾値

コスト可観測性は、コスト管理の前提です。ログ項目が不足していると、問題の場所を特定できません。

OpenTelemetry trace span attributes

コストログは 3 層の span で設計します。

Agent run span(最上位)

フィールド名用途記録する理由
runId個別実行を特定string同じ workflow の複数実行を区別する
tenantIdtenant を特定stringマルチテナントでコストを按分する
userIdユーザーを特定stringユーザーごとのコスト傾向を見る
workflowNameworkflow を特定stringworkflow 種別ごとにコストを見る
totalCost総コスト見積もりfloatリアルタイムのコスト累積に使う
budgetRemaining残予算floatサーキットブレーカーの判断根拠
totalRetries総リトライ回数integerリトライ増幅を把握する

Model call span(子 span)

フィールド名用途記録する理由
modelモデルを特定stringモデルルーティングが妥当か判断する
pricingVersion価格バージョンstringコスト式をハードコードしないため
inputTokens入力 token 数integer入力コストを計算する
outputTokens出力 token 数integer出力コストを独立して記録する
cachedTokensキャッシュ hit token 数integerキャッシュ節約を計算する
costEstimate今回のコスト見積もりfloatリアルタイムのコスト累積に使う
latencyMs呼び出しレイテンシintegerBatch/Flex に回せるか判断する

Tool call span(子 span)

フィールド名用途記録する理由
toolNameツールを特定stringツール呼び出しの overhead を特定する
toolBudgetツール予算上限floatサーキットブレーカーの判断根拠
toolTimeoutツールタイムアウトintegerタイムアウト失敗を分類する
retryCountリトライ回数integerリトライ増幅を把握する
errorTypeエラー種別string復旧可能か復旧不能かを分ける

total_cost だけを記録してはいけません。軸ごとに分解します。これらのフィールドがないと、請求異常を見ても「超過した」ことしか分からず、どのユーザー、どのツール、どのリトライが原因か分かりません。

ログ、アラート、失敗復旧の全体設計はAgent 監視と復旧で扱っています。この記事では、コストフィールドと予算オブジェクトを補います。

アラート閾値

アラート閾値は軸ごとに設定します。

アラート軸アラート閾値アラート方法発動する動作
全体予算消費70%、90%、100%Slack/メール通知70% で通知、90% で降格、100% で遮断
単一 user/tenant の消費平均の 3 倍超Slack/メール通知異常呼び出しの有無を確認
単一モデルの失敗率> 5%Dashboard アラートモデルルーティングまたはサービス状態を確認
単一ツールのリトライ回数> 閾値Dashboard アラートツール安定性を確認
キャッシュヒット率< 期待値Dashboard アラートprompt 構造の誤りを確認

アラート閾値はコストログに書き、自動アラートとサーキットブレーカーの発動に使います。

降格戦略

アラート発動後の降格経路です。

降格経路降格方法適した場面コスト影響
モデル降格大きいモデル -> 小さいモデル単一モデルの失敗率が高いコストは下がるが、品質が下がる可能性
経路降格リアルタイム API -> Batch API -> Flex Processing全体予算の消費が速すぎるレイテンシは増え、コストは下がる
機能降格非コアのツール呼び出しを停止単一ツールのリトライ回数が多いツール呼び出し overhead を減らす
ユーザー降格レート制限、キュー投入、「後で再試行」案内単一ユーザーの消費が異常1 人のユーザーが予算を燃やすのを防ぐ

降格戦略は予算ロジックの中に書きます。アラート発動時に自動で降格し、人間が気づいてから対応する形にしないためです。

次に見るもの

Agent のコスト管理は単独では成り立ちません。監視、ツール呼び出し、コンテキストエンジニアリングと合わせて考えます。

  • 公開済み:Agent 監視と復旧:ログ項目、アラート設定、失敗復旧。この記事ではコストフィールドと予算オブジェクトを補います。
  • 公開済み:LangGraph Agent アーキテクチャ:checkpoint、thread state、失敗復旧。長時間タスクの状態保存に使います。
  • 公開済み:ツール呼び出し:ツール呼び出しの基本。この記事では per-tool budget、timeout、リトライ上限を拡張します。
  • 同シリーズ:コンテキストエンジニアリング:安定 prefix とキャッシュヒット。どのコンテキストを安定 prefix に入れ、どれを実行時変数にするか。
  • 同シリーズ:Human-in-the-Loop:承認待ち、拒否、タイムアウトの予算分岐。承認がコストとリトライ経路にどう影響するか。

まずは Session 単位の防線から始めます。per-session cost limit を設定し、単一セッションが予算を超えたら自動で終了します。暴走した 1 タスクが 1 日分の予算を燃やすのを止める、最も早く効く一手です。その後、予算オブジェクトの階層化、モデルルーティング、ツール呼び出し予算、Prompt Caching、リトライ遮断、コストログへ広げていきます。

Agent のコスト予算とサーキットブレーカーを設計する

予算オブジェクト、モデルルーティング、サービス層ルーティング、キャッシュ、ツール予算、リトライ制御を使い、Agent のコスト管理を各 run の実行前に前倒しします。

⏱️ 目安時間: 45 分

  1. 1

    ステップ 1: すべてのコスト経路を洗い出す

    Agent のモデル呼び出し、ツール呼び出し、ファイル読み取り、外部 API、バッチ処理、キャッシュ、リトライ経路を列挙します。
  2. 2

    ステップ 2: 予算オブジェクトを定義する

    tenant、user、run、workflow、model、tool、retry、cache、time window の軸で予算オブジェクトを記録します。
  3. 3

    ステップ 3: ルーティング戦略を設定する

    タスク種別ごとにモデル層ルーティングとサービス層ルーティングを設定し、online、batch、flex、queue を分けます。
  4. 4

    ステップ 4: キャッシュヒットを設計する

    安定したコンテキストを prompt prefix に置き、変動する内容を後ろに置きます。同時に prompt caching、業務結果キャッシュ、ツール応答キャッシュを区別します。
  5. 5

    ステップ 5: ツールとリトライを制限する

    各ツールに timeout、max retries、idempotency key、per-tool budget、fallback を設定します。
  6. 6

    ステップ 6: コスト span を記録する

    run/span に token、cached token、tool、retry、latency、estimated cost、budget remaining、traceId を記録します。
  7. 7

    ステップ 7: 降格と遮断を設定する

    降格、停止、サーキットブレーカー、アラート閾値を設定し、実際の失敗ケースで回帰テストします。

FAQ

Agent のコストはユーザー、セッション、タスク、ツールのどの単位で集計すべきですか?
user、tenant、workflow、task、tool、retry、cache の 7 層で集計します。各層に独立した予算とサーキットブレーカーを持たせます。total_cost だけでは、請求異常の原因になったユーザー、ツール、リトライ経路を特定できません。
モデルルーティングは、簡単なタスクを小さいモデルに替えるだけで十分ですか?
十分ではありません。モデル層ルーティングに加えて、Batch/Flex/リアルタイムのサービス層ルーティングとリスク分級が必要です。同じモデルでも latency priority で分岐できます。オフライン処理は Batch API、低優先度の処理は Flex Processing に回せます。
Prompt Caching と通常の業務キャッシュは何が違いますか?
Prompt Caching は system prompt、tool schema、policy などの安定した prompt prefix をキャッシュします。業務結果そのものではありません。業務結果キャッシュは完全な出力やツール応答を保存します。目的が違うため、併用できます。
ツール呼び出しが失敗したら何回リトライすべきですか?
固定回数だけで決めません。失敗分類、残予算、サーキットブレーカーを見ます。復旧可能なエラーだけ少数回リトライし、403、400、ツール不存在などの復旧不能エラーはリトライしません。
長時間タスクの途中で Agent の予算が尽きそうなときは、降格、停止、失敗のどれを選ぶべきですか?
まず pause して checkpoint を保存し、予算回復または人間の承認を待つのが堅実です。即失敗は進捗を失い、無理な降格は品質を落とします。多くの場合、停止がより安全な設計です。
コストログにはどの項目を記録すべきですか?
少なくとも runId、tenantId、workflow、model、input/output/cached tokens、toolName、retryCount、latency、costEstimate、budgetRemaining、decision、traceId を記録します。

10分で読めます · 公開日: 2026年9月17日

コメント

GitHubアカウントでログインしてコメントできます

Easton BlogEaston Blog