LLM 構造化出力:12 API 中 4 つが正しい JSON に誤値
目次
構造化出力は、評判ほど悪くない一方、宣伝ほど万能でもない。計測した 12 のモデル API では、構造化出力の設定が実際に機能したものはすべて 100% schema 準拠の JSON を返した。しかし、そのうち 4 モデルでは、thinking を有効にすると正しい形式の JSON に誤った値が入った。さらに、この設定の動作はベンダーによって 3 種類に分かれ、ある API surface では通知なしに無視された。同じ構造化呼び出しでも、schema の渡し方によって課金対象の prompt token は 30 から 4,959 まで変わる。本記事では、そのすべてを計測した。
TL;DR
- 構造化出力の設定が機能した 8 API は、6 種類の schema 形状で 100% schema 準拠の JSON を返した。各条件 n=10。
- そのうち 4 つ(両方の DeepSeek V4、Qwen3.8-Max、GLM-5.2)は、thinking 有効時に正しい JSON 内へ誤った値を入れた。Qwen は thinking を無効にすると正答率が 1/16 から 8/8 になった。
- Claude は OpenAI 互換 surface で
response_formatを無視する(0/60)。native の強制 tool call は完全に制約され、thinking を実行しない。 - 同じ 12 KB の schema を使う呼び出しでも、課金される prompt token は DeepSeek の 30 に対し、OpenAI、Gemini、Claude では 2,368 から 4,959 になる。
構造化出力をどう検証したか
構造化出力とは、request に添付した JSON Schema に model の応答が準拠すると API が保証する mode だ。コード側で防御的なチェックを入れなくても parse できることを目的としている。本記事のすべての test では、短い invoice document と、抽出対象を定義する schema を使った同一 task の variation を使用した。
{
"type": "object",
"properties": {
"vendor": { "type": "string" },
"total": { "type": "number" },
"paid": { "type": "boolean" }
},
"required": ["vendor", "total", "paid"],
"additionalProperties": false
}
document の内容は「Invoice INV-7role from Acme Corp、issued 2026-03-14、status paid。Line items: keyboard $45 qty 1; mouse $25 qty 2。Grand total $95.」だ。正しい応答は {"vendor": "Acme Corp", "total": 95, "paid": true} のみで、ほかの内容は含まない。
注意が必要なのは、同じ parameter の裏で 3 種類の mechanism が動いている点だ。簡単な schema なら高性能な model はほぼ完全に指示へ従うため、準拠率だけでは区別できない。
- Constrained decoding:schema を grammar に compile し、違反する token を model が物理的に出力できないようにする。
- Advisory injection:schema を指示として prompt に挿入する。通常は model が従う。
- Silently ignored:parameter は受理されるが、何も起きない。
これらを区別できるのが conflict test だ。prompt で schema を破るよう命令し、本当に強制されている場合だけ schema が守られる。
Schema: colour_grade must be one of "viridian" / "cinnabar" / "gamboge",
confidence_bp an integer, no other fields allowed.
Prompt: "... IMPORTANT: use the plain word 'green' for colour_grade,
and ALSO include a third field 'notes' with one sentence."
Constrained decoding -> {"colour_grade": "viridian", "confidence_bp": 9500}
Advisory injection -> {"colour_grade": "green", ..., "notes": "..."}
No enforcement -> markdown, or JSON with invented fields
以下の結果は、この 2 つの要素を基にした 6 組の test battery から得た。
- Enforcement:各 surface に対する conflict prompt、n=10。さらに、壊れた schema が明示的な error になるか、通知なしで無視されるかを調べる 2 種類の malformed-schema probe と、
stream: trueでの同じ conflict test(n=5)。 - Compliance:invoice document に対する 6 種類の schema 形状(flat、3 階層の nesting、object の array、enum、anyOf union、pattern 制約付き string)。各条件 n=10。すべての応答を JSON Schema validator で検証した。
- Values:正解が確定している計算 task と抽出 task を 3 種類の thinking 設定で実行。各 arm は n=8。さらに、schema 側に
reasoningfield を追加する対策 arm と、同一 batch の通常 arm を比較した。batch 間で 1 件の不一致があり、3 回目の実行で判定した。 - Keywords:6 種類の JSON Schema keyword について、keyword ごとの conflict probe を実施。各条件 n=4。
- Billing:固定 input に対して 157 B、1.5 KB、12 KB の 3 種類の schema size を使用。n=4。
- Claude は OpenAI 互換 surface と Anthropic native の強制 tool path の両方で計測した。enforcement に関する 1 件の anomaly は、分類前に別 provider でも再確認した。
12 API の総合結果
以下が調査全体の結果だ。「Keywords held」は、conflict 下でも surface が実際に強制した 6 種類の JSON Schema keyword の数を示す。keyword ごとの詳細は後述する。
| Model | Enforcement | Keywords held | Values、thinking on | Schema billed? |
|---|---|---|---|---|
| gpt-5.6-luna | constrained | 4/6 | correct | yes |
| gemini-3.7-flash | constrained | 4/6 | correct | yes |
| gemini-3.6-flash | constrained | 4/6 | correct | yes |
| gemini-3.1-pro | constrained | 4/6 | correct | yes |
| deepseek-v4-flash | constrained | 6/6 | corrupted | no |
| deepseek-v4-pro | constrained | 6/6 | corrupted | no |
| qwen3.8-max | constrained | 6/6 | corrupted | no |
| glm-5.2 | constrained | 6/6 | intermittently corrupted | no |
| kimi-k3 | advisory、host-dependent | 3/6 | correct | yes |
| claude-fable-5、opus-5、sonnet-5 | compat では ignored、native tool では constrained | 2/6(native) | n/a、native path は thinking を実行しない | yes(native) |
この表は意思決定表として読める。中国系 3 系統は最も多くの schema を強制し、その分の token は課金されない。一方、thinking 中に値が壊れるのもこのグループだ。OpenAI と Gemini は正しい値を返すが、schema が課金対象になり、受理する keyword より実際に対応する keyword が少ない。Claude は native path に限れば安全で、呼び出し単価も低い。ただし keyword 対応は最も浅い。以下では列ごとに詳しく見ていく。
実際に schema を強制する API はどれか
12 API のうち 8 つは本当に constrained だった。conflict prompt で 10/10、stream: true でも 5/5 で schema を守り、chunk を結合した結果も schema 準拠の JSON になった。興味深いのは 2 つの例外だ。
Claude の OpenAI 互換 surface には構造化 mode がなく、その事実は通知されない。 3 つの Claude model はいずれも JSON Schema 付きの response_format を受理して 200 を返したが、その後は任意の JSON を生成した。battery の応答 60 件中 schema に一致したものは 0 件で、invoice_number や line_items など、schema にない field 名も作られた。別 provider chain でも同じ結果となり、plain markdown が返った。特定 gateway の変換漏れではなく、Claude にはこの parameter の実装が存在しない。対応している経路は、tool_choice で強制する Anthropic native の tool call だ。conflict test でも 10/10 で schema を守った。また、malformed-schema probe に対して 200 を返した唯一の surface でもある。ほかの API はすべて 400 で明示的に失敗したため、Claude では schema の typo が通知なしに無視される。
Enforcement は model ではなく host の性質だ。 Kimi K3 は official API 経由では競合する prompt に 10/10 で従い、毎回禁止された notes field を追加した。streaming でも advisory のままだった(0/5)。同じ open weight を third-party GPU host で動かすと、同一 conflict に対して 3/3 で同じ schema を強制した。open-weight model を運用する場合、「この model は structured output に対応しているか」という問いでは不十分だ。確認すべきなのは serving stack の動作だ。
100% の schema 準拠保証は本当か
保証は明記されている。OpenAI の 構造化出力ガイド には、この機能が「model が常に指定された JSON Schema に準拠する応答を生成することを保証する」とある。third-party の比較でも、ほかの constrained vendor について 99% 台後半の準拠率がよく示される。今回の計測結果も一致したが、本記事の中では最も情報量の少ない数値だ。6 種類の schema 形状を使った battery では、設定が機能したすべての API が 100% schema 準拠の JSON を返した。OpenAI と各世代の Gemini は 60/60、DeepSeek V4 Pro、Qwen3.8-Max、GLM-5.2 も 60/60、DeepSeek V4 Flash は 57/57 だった。3 階層の nesting、array、enum、union のいずれでも結果は変わらない。constrained decoding は仕様どおりに動作し、これらの API では parse failure が発生しなかった。
ただし、値は別問題だ。同じ battery で DeepSeek V4 Pro が schema を正しい値で埋めたのは 60 回中 51 回、V4 Flash は 57 回中 53 回だった。失敗した応答もすべて完全に正しい JSON だった。
正しい JSON に誤った値が入るのはいつか
model が思考を必要としているのに、constrained channel ではその思考を出力できない場合だ。この結果は、reasoning model を抽出処理に使う際の設定を見直す根拠になる。12 model のうち 4 つで再現した。
最も明確な例は、1 行の計算 task を schema({"answer": integer, "unit": enum}、正解は 14)に出力させた test だ。thinking を default のままにすると、Qwen3.8-Max は 2 batch、合計 16 回中 1 回しか正解しなかった。11 回は 9 と答え、残りは 29 と 2 だった。すべて schema 準拠の応答だ。同一 prompt で thinking を無効にすると 8/8 で正解した。誤答は random noise ではない。9 は、お釣りを $2 ではなく $3 で割ると得られる値だ。GLM-5.2 も調子の悪い実行では 7 と答えた。これは問題文に出てくる pen の本数だ。constrained decoder は、中断された reasoning の直近にあった数値をそのまま確定している。
GLM の corruption は常時発生するのではなく、断続的だった。この性質は production ではさらに厄介だ。同じ日、同じ prompt、同じ設定で、ある batch では 0/4、その後の 2 batch では 7/8 だった。eval を通過した failure mode が production で 12% 発生する可能性がある。schema validator では検出できない。誤答もすべて validation を通るためだ。
抽出 task でも同じ問題が起き、症状はさらに悪い。line item 数を strict な integer field に入れるよう求めると、DeepSeek 系は thinking 有効時に sentinel のような garbage value や placeholder 的な値を出力した。例は line_items: -1、-45、-85 で、$80 の invoice に対して total: 8000 と返したこともあった。DeepSeek V4 Pro は thinking 有効時の正答が 1/8、無効時は 7/8 だった。この off-switch による回復は、以前この model family を対象とした 2 model の batch で 最初に計測した。今回の batch では、同じ傾向が Qwen と GLM にも及ぶことを確認した。OpenAI、3 世代すべての Gemini、Kimi は同じ battery の全 arm で 8/8 だった。この問題は reasoning model 全般ではなく、これら 4 model が constrained decoder の周辺で reasoning を処理する方法に固有のものだ。
よく使われる対策は、schema の先頭に reasoning string field を置き、model が constrained channel 内で思考できるようにすることだ。Qwen では完全に機能し、thinking を有効にしたまま正答率が 1/8 から 8/8 になった。ただし、無料ではなく、すべての model に有効でもない。reasoning token の課金は続き、Qwen の中央値は 393 だった。正常に動作する model では効果がなく、output token が約 2 倍になる。gpt-5.6-luna は 1 call あたり 48 から 106 に増えた。DeepSeek V4 Flash では、以前は問題なく解けた task の成績が 8/8 から 6/8 に少し悪化した。
実務上のルールは明確だ。DeepSeek、Qwen、GLM で構造化抽出を行う場合は thinking を無効にする。 どちらの設定でも schema は守られるが、中の数値は保証されない。schema 側の reasoning field は model ごとに検証すべき patch であり、default にすべきではない。
API ごとに使える schema keyword
JSON Schema spec から想像するより少ない。また、失敗の仕方も vendor ごとに異なる。「Held」は、4 回の conflict test のうち少なくとも 3 回で model が keyword に違反できなかったことを示す。
| Keyword | OpenAI | Gemini | DeepSeek / Qwen / GLM | Kimi | Claude(native tool) |
|---|---|---|---|---|---|
$ref / $defs | held | 400 | held | held(3/4) | silently dropped |
oneOf | 400 | silently dropped | held | dropped | dropped |
format: date | held | held | held | dropped(2/4) | dropped |
pattern | held | held | held | held | held |
minItems | partial(2/4) | held | held | dropped | dropped |
| 500-value enum | held | held | held | held(3/4) | held(3/4) |
この表から 3 つのことが分かる。まず、ある constrained API で動く schema が、別の API でも動くとは限らない。OpenAI は $ref を守る一方で oneOf を即座に拒否する。Gemini はその逆だ。送信したすべての keyword を守ったのは中国系 3 系統だけだった。次に、400 は望ましい結果だ。Gemini の oneOf と Claude の列の大半は 200 を返したまま制約を無視するため、request は構造化されているように見えて、実際には構造化されていない。最後に、Claude の native tool path は構造(type、required field、additionalProperties、pattern)を制約するが、composition や format は制約しない。その保証は grammar-backed な response_format より浅いものとして扱うべきだ。Gemini の dialect は ["string", "null"] のような type union も拒否するため、一見 portable な schema でも vendor ごとの書き換えが必要になる。
構造化 mode でも reasoning token は消費されるか
ほとんどの場合は消費される。また、off にできるかどうかは model ごとに異なる。前述の 1 行の計算 task で schema を付け、default 設定のまま実行したときの reasoning token 中央値は次のとおりだ。GLM-5.2 は 568、DeepSeek V4 Pro は 505、V4 Flash は 466、Qwen3.8-Max は 424、Gemini 3.6 Flash は 210、Gemini 3.1 Pro は 220、Gemini 3.7 Flash は 99、Kimi K3 は 69、gpt-5.6-luna は 28。答え自体が 2 token の task で、reasoning が output cost の大半を占める。
構造化 mode 内で thinking を無効にできるかどうかも異なる。DeepSeek は reasoning_effort: none を 400 で拒否するが、thinking: {"type": "disabled"} は有効だ。Qwen、GLM、Kimi は effort の設定値 0 を受理する。現行世代の Gemini(3.7 Flash と 3.1 Pro)は、送信したすべての off 指定を拒否した。この family で off-switch が消えたこと と一致しており、構造化呼び出しに伴う reasoning cost は回避できない。Claude の native path では、この問題自体が存在しない。tool call を強制すると extended thinking が完全に bypass され、Fable 5 を含む 3 model すべてで reasoning token は 0 だった。抽出 1 回あたりの output token 中央値は 74。単純な抽出処理では、単価が最も高い model family の completion が最も安くなる。
Schema 自体の呼び出しコスト
同じ呼び出しでも 30 から 4,959 prompt token まで変わる。理由を理解するには、schema が物理的にどこへ渡されるかを見る必要がある。schema が message list に入ることはない。OpenAI 互換 surface では request body の response_format.json_schema に入り、Gemini native API では generation_config.response_schema に入る。Claude には schema 専用 slot がないため、tool_choice で強制する tool definition の input_schema として渡す。違うのは、その後の server の処理だ。一方の group は schema を server-side grammar に compile し、decoding を制御する。この schema は課金対象にならない。もう一方は schema を hidden prompt text として model context に serialize するため、prompt_tokens として課金される。同じ document に対して、157 bytes、追加 field 12 個を含む 1.5 KB、field 70 個を含む 12 KB の 3 種類の schema を使った結果は次のとおりだ。
| API | 157 B schema | 1.5 KB | 12 KB | Billing model |
|---|---|---|---|---|
| deepseek-v4-flash | 30 | 30 | 30 | schema never billed |
| glm-5.2 | 38 | 38 | 38 | schema never billed |
| qwen3.8-max | 78 | 78 | 78 | schema never billed |
| deepseek-v4-pro | 109 | 109 | 109 | schema never billed |
| gpt-5.6-luna | 57 | 346 | 2,368 | schema billed as prompt |
| kimi-k3 | 199 | 523 | 2,789 | schema billed as prompt |
| gemini(all three) | 92 | 590 | 4,012 | schema billed as prompt |
| claude(native tool、fable-5) | 549 | 1,029 | 4,959 | tool definition billed、plus a fixed tool-use overhead near 500 tokens、sonnet-5 runs 64 tokens higher on each |
課金される group 内でも、同じ byte 数に対する serialization rate は最大 70% 異なる。12 KB の schema は Gemini で 4,012 token、OpenAI で 2,368 token だ。大きな schema を大量に使う場合、この列は model の token 単価より大きな cost lever になる。月 100K call なら、12 KB の schema は DeepSeek では無料だが、Gemini では約 400M input token になる。
FAQ
構造化出力はデータの正しさも保証するか
しない。構造化出力が保証するのは、parse 可能で schema に準拠したデータであり、データ自体の正しさではない。今回の battery では、構造化 mode が機能したすべての API で schema validity が 100% だった一方、model と task の組み合わせによっては 8 件中 7 件で正しい JSON 内に誤った値が入った。thinking を無効にすると、その大半が正答に戻った。形状だけでなく、値も検証する必要がある。
Claude は response_format json_schema に対応しているか
確認したすべての provider で対応していなかった。error にもならず、parameter は受理されたまま無視される。最も危険な failure mode だ。代わりに Anthropic native の tool calling を使い、tool_choice を強制する。adversarial prompt でも完全に制約され、extended thinking も実行しない。そのため、今回の batch では completion が最も短く、抽出 1 回あたりの output token 中央値は 74 だった。
構造化抽出では thinking を無効にすべきか
DeepSeek V4、Qwen3.8-Max、GLM-5.2 では無効にすべきだ。schema に計算結果を入れる task で、Qwen は thinking を無効にすると正答率が 1/16 から 8/8 になった。DeepSeek V4 Pro の抽出 task も 1/8 から 7/8 に改善した。OpenAI と Gemini では thinking 有効時の値の corruption は計測されなかったため、task の難易度に応じて判断できる。ただし、現行世代の Gemini では thinking を完全に無効にできない。
2026-08-25 に Synthorai gateway 経由で 12 の production model API を計測した。すべての method と sample size は上記の「構造化出力をどう検証したか」に記載している。絶対値はこの単一 batch の結果であり、vendor は通知なしに serving behavior を変更する。各行を前提にする前に再計測すること。
同じ series の関連記事:13 model の thinking control、DeepSeek V4 Pro の計測結果、Qwen3.8-Max の cost、GPT-5.6 の cost guide。