0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LLM 構造化出力:12 API 中 4 つが正しい JSON に誤値

0
Posted at

この記事は Synthorai ブログの記事 LLM 構造化出力:12 API 中 4 つが正しい JSON に誤値 の転載です。原文(多言語対応)はリンク先をご覧ください。

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 V4Qwen3.8-MaxGLM-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 側に reasoning field を追加する対策 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-5opus-5sonnet-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_numberline_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、additionalPropertiespattern)を制約するが、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 controlDeepSeek V4 Pro の計測結果Qwen3.8-Max の costGPT-5.6 の cost guide

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?