Azure OpenAI の推論モデル(例:o4-mini)を Chat Completions API で使うと、「max_completion_tokens を十分大きく設定したのに completion_tokens が上限を超えて見える」「しかも画面に出ていない推論トークンが課金される」という現象に遭遇することがあります。本記事では、この挙動の正体と、推論モデルを本番運用するためのコスト管理・請求透明性の作り方を、運用目線で具体的に整理します。
「max_completion_tokens を超えて見える」現象を最短で理解する
結論から言うと、推論モデルでは 課金・メトリクス上の completion_tokens が「画面に表示される出力トークン」だけではないためです。Azure の推論モデルでは、モデル内部で実行される “思考” に相当する 推論トークン(reasoning tokens) が存在し、これが レスポンス本文として返ってこない一方で、completion_tokens 側に合算されることがあります。
さらに重要なのが、Azure の挙動として max_completion_tokens は「見えている最終出力(回答文)」側を主に抑える一方で、内部推論(reasoning tokens)に対して厳密な上限(ハードキャップ)としては働かないケースがある、という点です。これが「completion_tokens が max_completion_tokens を超えて見える」直接原因になります。
用語の対応表(ここがズレると全部がズレる)
| 項目 | どこで見えるか | 意味 | この問題との関係 |
|---|---|---|---|
| prompt_tokens | APIレスポンス usage | 入力(プロンプト+会話履歴等)のトークン数 | 長い履歴ほどコスト増。推論以前にここが膨らみやすい |
| completion_tokens | APIレスポンス usage / ポータルのトークン系メトリクス | 出力側のトークン数(推論モデルでは内部推論分を含み得る) | 「表示されない推論分」が足されると上限超過に見える |
| completion_tokens_details.reasoning_tokens | APIレスポンス usage(取得できる場合) | 内部推論に使われた隠れトークン(本文には出ない) | 内訳が見えると、説明と再請求が一気に楽になる |
| max_completion_tokens | APIリクエスト | Chat Completions における出力側の上限指定 | 推論モデルでは「推論分を含めて厳密に止める」用途としては期待通りに働かないことがある |
推論トークン(reasoning_tokens)は「どこに消える」のか
推論モデルは、最終回答を生成する前に内部で推論ステップを積み上げます。この内部ステップに使われるのが推論トークンです。Azure の推論モデルでは、この推論トークンは 本文として返らないのに、usage では completion_tokens の一部として計上されます。
実際に Microsoft Learn の推論モデル解説ページでは、usage の中に completion_tokens_details.reasoning_tokens が含まれる例が提示されており、推論トークンが “隠れトークン” であることが明記されています。
レスポンス例(「見える出力」より completion_tokens が多い)
推論モデルの usage は、次のように completion_tokens_details が付くことがあります(項目名は API / SDK により表示形式が変わる場合があります)。
{
"usage": {
"prompt_tokens": 20,
"completion_tokens": 1843,
"total_tokens": 1863,
"completion_tokens_details": {
"reasoning_tokens": 448
}
}
}
このとき、運用上わかりやすい形に直すなら次の式です。
- completion_tokens(合計) = 見える出力トークン + reasoning_tokens(隠れ推論)
- 見える出力トークン(推定) = completion_tokens − reasoning_tokens(内訳が取れる場合)
なぜ推論トークンは max_completion_tokens で厳密に止まらないのか
推論モデルにとって推論トークンは「品質のための内部計算」です。プロンプトが曖昧だったり、複数条件が絡む問いだったり、ツール呼び出しや検証が必要だったりすると、内部推論が増えやすくなります。ここを “出力トークンと同じ感覚で” 途中停止させると、回答が破綻しやすくなるため、現時点の Azure では 推論トークンに対して厳密な上限を強制する手段は提供されていない、という整理になります。
Microsoft Q&A の回答でも、Azure では completion_tokens が可視出力+隠れ推論を表し、max_completion_tokens は可視出力のみを制限する趣旨が説明され、隠れ推論トークンに厳密なキャップを設ける方法はないと明言されています。
推論トークンを「直接キャップ」できない前提で、どう抑えるか
できないものはできない、で終わると本番では困るので、現実的には「発生しやすい条件を潰す」「増えるときに増えないように設計する」「増えたときに止血できる仕組みを入れる」の3層で考えるのが強いです。
reasoning_effort を下げる(最も効くレバー)
推論モデルは reasoning_effort で推論の深さを調整できます。一般に、高いほど reasoning_tokens が増え、低いほど減る傾向があります(ただし品質とのトレードオフ)。
| 設定 | 狙い | おすすめ用途 | 注意点 |
|---|---|---|---|
| low | コスト・レイテンシを抑える | 定型QA、要約、社内FAQ、まずは叩き台 | 難問では推論不足で精度が落ちやすい |
| medium | バランス | 一般的な業務支援、軽い設計レビュー | プロンプト次第で推論が増える |
| high | 品質優先 | 複雑な推論、バグ解析、仕様検討 | reasoning_tokens が膨らみやすい(コスト上振れ) |
運用のコツは「最初から high に固定」ではなく、例えば次のように段階化します。
- 通常は low / medium で走らせる
- 「回答の自信が低い」「条件が多い」「根拠提示が必要」などのケースだけ high に昇格
- 高コスト化が疑われる入力(長文・複雑)には、先に簡易モデルで要件を整理してから推論モデルへ渡す
プロンプトを「短く・狭く・判定しやすく」する
推論トークンは “思考の余地” が大きいほど増えやすいので、プロンプト設計での抑制が効きます。
- 目的を1つに絞る:1リクエストで「調査+比較+設計+実装」などをやらせない
- 前提条件を箇条書きで固定:曖昧な条件はモデルが内部で補完しようとして推論が増える
- 出力フォーマットを固定:JSON あるいは箇条書きで、答える範囲を限定する
- 長い会話履歴を毎回投げない:履歴は要約して圧縮し、必要な情報だけ残す
「stop で止める」運用は推論モデルでは頼りにくい
o4-mini など最新の推論モデルでは、API 仕様上 stop がサポートされない(または制約がある)ことがあります。つまり「stop シーケンスで強制的に短くする」戦略が取りづらいケースがあるため、max_completion_tokens と出力形式指定、そしてタスク分割で制御するのが安全です。
モデル選定で「推論モデルに投げる仕事」を減らす
推論モデルは万能ですが、すべてを推論モデルに投げるとコストの分散ができません。おすすめは “ゲート” を作ることです。
- 軽い質問・定型回答:通常のチャットモデル(推論特化でないモデル)
- 難問や検証が必要:推論モデル(o4-mini など)
- コード生成:まず小さめモデルで骨組み → 推論モデルでレビュー・修正
本番運用のコスト管理:Azure の監視とアプリ側ログを組み合わせる
Azure Monitor のトークン系メトリクスで「全体の増え方」を掴む
Azure OpenAI は Azure Monitor 側にトークン系メトリクスを持っており、代表的には以下が運用で効きます。
- Processed Prompt Tokens(入力側のトークン)
- Generated Completion Tokens(出力側のトークン)
- Processed Inference Tokens(入力+出力の合算)
Microsoft Learn のパフォーマンス/レイテンシ解説でも、ワークロード把握の基本として Processed Prompt Tokens と Generated Completion Tokens を使う考え方が説明されています。
| メトリクス名 | 意味(ざっくり) | 見たい理由 | 異常の例 |
|---|---|---|---|
| Processed Prompt Tokens | 入力に使ったトークン数 | 会話履歴肥大・プロンプト設計の問題を炙り出す | リリース後に急増(不要な履歴を投げている) |
| Generated Completion Tokens | 出力側のトークン数 | 推論モデルだと内部推論分も含まれ得る | 特定の機能だけ急増(難問誘発・推論過多) |
| Processed Inference Tokens | 入力+出力の合計 | 総量としての “燃えてる度” が一目で分かる | 利用者増より先に総量だけ増える(プロンプト肥大) |
Cost Management で「止血ライン」を作る
推論トークンはプロンプトの難易度で上振れしやすいので、予算アラートは “保険” ではなく “必須の安全装置” です。Microsoft Q&A の回答でも、Azure Cost Management でコストアラートを設定して予期せぬ請求を防ぐのがベストプラクティスとして触れられています。
アプリ側で「リクエスト単位」の usage を保存する
運用で最も効くのは、API レスポンスの usage を必ず自前で記録することです。特に請求の透明性を確保するなら、テナント・案件・ユーザーと紐づく形が必須になります。
| 保存項目 | 例 | 目的 | 補足 |
|---|---|---|---|
| timestamp | 2025-12-17T… | 期間集計・監査 | UTC推奨 |
| tenant_id / customer_id | cust_123 | クライアント別請求 | 必須 |
| deployment / model | o4-mini | 単価・挙動の切り分け | モデル更新時の差分検知にも使える |
| prompt_tokens | 2,100 | 入力課金・最適化 | 履歴肥大の検知に強い |
| completion_tokens | 12,500 | 出力課金・上振れ検知 | 推論モデルでは「内部推論込み」で増えやすい |
| reasoning_tokens(取れる場合) | 3,000 | 透明性・説明責任 | 取れないAPI/バージョンもあるのでNULL許容 |
| request_id | x-ms-request-id 等 | 問い合わせ対応 | サポートに渡せる証跡 |
API Management で「上限」と「可視化」を仕組み化する
推論トークン自体を 1 リクエストで直接キャップできないなら、システム全体としての上限(スパイク防止)を作るのが現実解です。Azure API Management(APIM)には、Azure OpenAI のトークン消費をキー単位で制限するためのポリシーが用意されています。
azure-openai-token-limit:トークンのレート制限/クォータ制限
azure-openai-token-limit は、キー(IP/サブスクリプションID/ユーザーIDなど)単位で、tokens-per-minute や 月次クォータを設定し、超過時に 429/403 を返すことでスパイクを抑えます。実際のトークン消費はプロンプトと生成結果に依存し、同時実行時には一時的に超過し得る点も仕様として説明されています。
<policies>
<inbound>
<base />
<azure-openai-token-limit
counter-key="@(context.Subscription.Id)"
token-quota="100000"
token-quota-period="Monthly"
remaining-quota-tokens-header-name="X-Remaining-Token-Quota"
tokens-consumed-header-name="X-Tokens-Consumed" />
</inbound>
<outbound>
<base />
</outbound>
</policies>
この構成にすると、アプリ側の再請求ロジックとは独立に「燃えたら止まる」ラインが作れます。推論モデルのコスト上振れに対する最後の砦として非常に有効です。
azure-openai-emit-token-metric:クライアント別の可視化(カスタムディメンション)
azure-openai-emit-token-metric は、Azure OpenAI のトークン消費(Total / Prompt / Completion)を Application Insights にメトリクスとして送れます。ディメンション(例:User ID、Subscription ID)を付けられるので、「クライアント別」「機能別」のダッシュボードが作りやすくなります。なお、ストリーミング時は推定になる場合があることも明記されています。
<policies>
<inbound>
<base />
<azure-openai-emit-token-metric namespace="AzureOpenAI">
<dimension name="User ID" value="@(context.Subscription.Id)" />
<dimension name="API ID" />
</azure-openai-emit-token-metric>
</inbound>
<outbound>
<base />
</outbound>
</policies>
請求の透明性:推論トークンをどう説明し、どう再請求するか
まず「請求単位」をズラさない
推論モデルのコストで揉める典型は、「画面に出た文字量=コスト」と誤認されることです。最初から次のように定義しておくのが安全です。
- 請求は トークン(入力+出力) を基準にする
- 推論モデルでは、出力側に 内部推論(reasoning) が含まれ得る(本文には表示されない)
- その合算が usage の completion_tokens になり、課金対象になり得る
内訳が取れるなら「reasoning_tokens を明細に出す」
API の usage に completion_tokens_details.reasoning_tokens が含まれる場合、請求明細で次のような内訳を提示できます。
| 明細項目 | 算出 | クライアントへの説明文(例) |
|---|---|---|
| 入力トークン | prompt_tokens | ご依頼内容(入力)を処理するためのトークン数 |
| 出力トークン(表示) | completion_tokens − reasoning_tokens | 画面に表示された回答文に相当するトークン数(推定) |
| 推論トークン(内部処理) | reasoning_tokens | 推論モデルが精度向上のため内部で消費したトークン数(本文には表示されません) |
| 出力合計 | completion_tokens | 表示+内部推論を含む出力側合計 |
| 総トークン | total_tokens | 入力+出力の総量(コスト算定の基礎) |
ポイントは、「推論トークンは隠れている=不正」ではなく、「推論モデルの仕様として内部処理にも課金対象がある」と最初から明文化することです。
内訳が取れない場合の現実的な説明
エンドポイントや API バージョンによっては、Q&A の回答のように「推論 vs 出力の別内訳が露出しない」ケースもあります。その場合は、completion_tokens を出力側の課金単位として一括扱いにし、次の2点を運用ドキュメントに書いておくのが現実的です。
- 推論モデルの completion_tokens は “表示テキスト量” と一致しないことがある
- 上振れはプロンプト難易度に依存するため、月次で実測平均+バッファを見込む
ストリーミング利用時の注意点(ログと可視化が抜けやすい)
ストリーミングを有効にすると、クライアント体験は良くなりますが、usage の扱いが実装で抜けやすくなります。
- v1 の API では、ストリーミング時に
stream_options.include_usageを有効にすると、終了前に usage を含む追加チャンクを流せます。 - APIM のトークン可視化/制限系ポリシーは、ストリーミング時に推定値になる場合があります(完全一致を期待しすぎない)。
「ストリーミング=請求が曖昧になる」を避けるため、ストリームでも usage を最終的に記録できる設計(include_usage か、サーバー側でレスポンスを集約して usage を保存)を先に決めておくのが安全です。
運用チェックリスト:推論モデルを“安全に”本番投入する
| 観点 | やること | 狙い |
|---|---|---|
| コスト上振れ | reasoning_effort を段階化(標準は low/medium) | 推論トークンの暴発を抑える |
| 設計 | プロンプト短縮、履歴要約、タスク分割 | 推論の余地を減らし、安定化 |
| 監視 | Azure Monitor で Processed Prompt Tokens / Generated Completion Tokens を継続監視 | 全体傾向・急増を検知 |
| 止血 | APIM の azure-openai-token-limit でクォータ/TPM 制限 | スパイクで請求が跳ねるのを防ぐ |
| 透明性 | usage をリクエスト単位で保存(可能なら reasoning_tokens も) | 監査・再請求・説明責任 |
| 請求 | 「推論モデルは内部推論も含めたトークン課金」を契約/利用規約に明記 | トラブル回避 |
まとめ:max_completion_tokens 超過に見えるのは“バグ”より“設計前提”として扱う
- 推論モデルでは、内部推論(reasoning tokens)が発生し、本文に出なくても課金・メトリクスに反映され得る
- max_completion_tokens は「表示される出力」を抑える意図で使い、推論コストは reasoning_effort・プロンプト設計・モデル選択で間接的に制御する
- 本番では、Azure Monitor のトークンメトリクス+アプリ側 usage ログ+APIM のトークン制限/可視化で、コストの上振れと透明性の両方を押さえる

コメント