AI

LLM構造化出力とは?JSON Schemaで出力形式を保証する仕組みと実装・見送り条件を解説【2026年版】

LLMが返した文章から正規表現でJSONを切り出し、パースに失敗したらもう一度投げる。この後処理を丸ごと捨てる仕組みが、LLMの構造化出力(Structured Outputs)です。スキーマをAPIに渡すと、モデルはそれに違反するトークンを生成できなくなります。この記事ではJSONモードとの保証範囲の違い、OpenAI・Claude・Geminiで異なるスキーマの制限、vLLMでのバックエンド選択、そして「形は正しいのに中身が誤っている」失敗の扱い方を、2026年8月時点の一次情報で整理します。

まとめ:構造化出力の保証範囲と、スキーマ設計で先に決める3点

構造化出力が保証するのは「形」だけです。キー名・型・必須項目・列挙値の範囲はデコード時点で強制されますが、そこに入る値が事実として正しいかは保証されません。導入で消えるのはパースエラーであって、誤った値の混入ではない。この前提を設計の最初に共有してください。

スキーマを書く前に決めるのは3点あります。第1に任意項目の表現方法で、OpenAIのstrictモードは全フィールドを必須にするよう求めるため、任意項目はnullとの合併型で表します。第2に自由文をどのフィールドへ閉じ込めるか。判定や分類をenumで固定できれば、下流の分岐が壊れません。第3に、理由を書かせるフィールドを結論より前へ置くかどうか。この並びで精度が変わります。

採否はシンプルです。出力を型付きのプログラムが受け取り、件数が日次で数百を超え、人手レビューを挟まないなら採ってください。逆に自由文が成果物で、スキーマが案件ごとに変わり、処理が1日数件なら、後処理で拾うほうが安く済みます。

LLM構造化出力の定義と、JSONモードや関数呼び出しとの担当範囲の違い

言葉の指す範囲が製品ごとにずれているため、定義と隣接機能との線引きから固めます。

制約デコーディングでスキーマ違反トークンを落とす仕組みと保証の範囲

LLMの構造化出力とは、モデルの生成結果をあらかじめ定義した機械可読フォーマットへ準拠させる仕組みです。実現方法は2種類。プロンプトで「JSONだけを返して」と依頼する方法と、デコードの各ステップでスキーマに違反するトークンの選択確率をゼロへ落とす方法があります。前者に保証はなく、後者では文法上そもそも違反トークンを選べません。後者を制約デコーディング(constrained decoding)と呼びます。

OpenAIがこの方式をAPIとして提供したのは2024年8月でした。response_formatjson_schema を指定して strict を真にすると、スキーマ準拠が保証されます。2026年8月時点では主要3社ともスキーマを文法へコンパイルする方式で揃っており、プロンプトでの依頼は旧モデル向けの代替手段です。

JSONモードとの違いは、構文の妥当性とスキーマ準拠のどちらを保証するか

混同されやすいのがJSONモード(json_object)との違いです。JSONモードが保証するのは「返ってきた文字列がJSONとして壊れていないこと」だけで、キー名も型も必須項目も保証しません。スキーマに status と書いても state が返る余地が残ります。

比較軸 JSONモード 構造化出力
構文の妥当性 保証あり 保証あり
キー名と型の一致 保証なし 保証あり
必須項目の充足 保証なし 保証あり
列挙値の範囲 保証なし 保証あり
値の事実正しさ 保証なし 保証なし
2026年の位置づけ 後方互換の旧方式 本番の既定

下流のコードがキーを直接参照しているなら、JSONモードのままではKeyErrorの可能性が残ります。

関数呼び出しとの関係は、引数の検証と最終出力の固定で役割が分かれる

関数呼び出し(Function Calling)は、モデルに「どの関数を呼ぶか」を選ばせ、引数を生成させる仕組みです。構造化出力は「最終的な返答の形」を固定する仕組みで、目的が違います。仕組みそのものの解説はFunction Callingの仕組みとOpenAI APIでの実装に譲ります。

両者は排他ではありません。Claude APIではツール定義側に strict を真で置くと、引数を定める input_schema にも同じ強制が掛かります。ツールを呼ぶ経路と最終回答を返す経路の両方で型を固定したいなら、2つを併用してください。

OpenAI・Claude・GeminiのAPI差分と、スキーマに掛かる数値上限の比較

同じJSON Schemaを渡しても、通るスキーマの範囲は3社で異なります。移植時に最初に踏むのがここです。

OpenAIのstrictモードで通らないスキーマと、ネスト10段の上限

OpenAIのstrictモードには、公式ドキュメントに明記された構造上の制約があります。全フィールドを required に列挙すること、オブジェクトには必ず additionalProperties を偽で置くこと。規模の上限はネスト最大10段、オブジェクトプロパティ最大5,000個です。

文字列側の上限も見落としやすい箇所でしょう。プロパティ名・定義名・列挙値・定数値の合計文字長は12万文字まで、列挙値はスキーマ全体で最大1,000個です。商品カテゴリのような大きなマスタをそのままenumへ展開すると、ここで弾かれます。

非対応のキーワードは allOfnotdependentRequireddependentSchemasifthenelse です。既存の検証用スキーマを流用すると、条件分岐を if で書いた箇所が引っかかります。再帰は自己参照と定義参照の両方が使え、木構造なら表現できる。モデルが安全上の理由で拒否したときは専用のフィールドが返るため、パース前に分岐を置いてください。

Claude APIの現行指定と、SDKが非対応制約を説明文へ移す挙動

Claude APIでは、現行の指定方法が output_config の下の format へ移りました。以前の output_format と専用のbetaヘッダはレガシー扱いです。2026年8月時点では、Opus 5・Sonnet 4.6・Haiku 4.5をはじめとする主要モデルで一般提供されています。

スキーマのサポート範囲はOpenAIと一致しません。再帰スキーマ、minimummaximum といった数値制約、minLengthmaxLength の文字列長制約は非対応で、配列の minItems は0か1しか受け付けません。挙動が独特なのは、公式SDKが非対応の制約を削除したうえで、その内容をフィールドの説明文へ書き足して送る点でしょう。制約は文法ではなくプロンプトとして伝わります。

性能面では、スキーマから文法をコンパイルする初回リクエストだけが遅くなります。コンパイル結果は最終使用時点から24時間キャッシュされ、スキーマ構造かツール定義を変えると破棄される仕様です。

Geminiの応答スキーマ指定と、プロパティ順の一致という固有の前提

Geminiは生成設定の2つのフィールドで指定します。JSONを強制する応答MIMEタイプの指定と、形を決める responseSchema の組み合わせが基本形で、JSON Schemaをそのまま渡したいときは responseJsonSchema を使います。responseSchema が受け取るのはOpenAPI 3.0のスキーマオブジェクトのサブセットです。

Gemini固有の注意点として、公式ドキュメントはプロンプト側に書いた項目の説明や例示の順序を、スキーマのプロパティ順と揃えるよう求めています。順序がずれるとモデルが混乱し、不正な出力につながると明記されている。3社で唯一、プロンプトとスキーマの並びに依存関係がある実装です。

観点 OpenAI Claude Gemini
指定先 response_format output_config.format 生成設定
スキーマ方言 JSON Schema部分集合 JSON Schema部分集合 OpenAPI 3.0部分集合
全項目required 必須 推奨 requiredで指定
再帰スキーマ 対応 非対応 制限あり
数値の範囲制約 非対応 説明文へ退避 一部のみ
拒否時の応答 専用フィールド 通常応答で判別 安全性の理由で終了

スキーマ設計の実務:任意項目・列挙・説明文・ネスト分割の決め方

APIの制限を踏まえると、書けるスキーマの形はかなり絞られます。実装で毎回問題になる4点を順に決めます。

任意項目はnullとの合併型で表し、キーの有無で分岐させない

全フィールド必須という制約は、一見すると業務データと相性が悪く見えるでしょう。実際には、任意項目を「型かnullのどちらか」を取る合併型として宣言すれば表現できます。請求書から取引先の担当者名を抜く処理なら、担当者名は文字列またはnullとし、記載がなければnullを返させます。

副次的な利点もあります。キーが存在しないケースが消えるため、下流のコードから存在チェックの分岐が丸ごと落ちる。テストで作るべき入力パターンも減ります。

判定と分類はenumとconstで固定し、自由文フィールドは1つに閉じ込める

スキーマの効果が最も出るのは、下流が分岐に使うフィールドです。承認・保留・却下のような判定は必ず enum で列挙し、文字列型のまま返させないでください。列挙にしておけば、想定外の値が来たときの分岐がそもそも発生しません。

一方で理由や要約のような自由文は避けられないでしょう。これは1つのフィールドに閉じ込めます。複数持たせると、モデルが同じ内容を別のフィールドへ書き分け、後段で重複排除が必要になる。マスタ由来の分類コードは前述の列挙値上限に当たるため、上位カテゴリだけを列挙してください。

数値の下限や文字列長は説明文で伝え、検証はアプリ側で二重に行う

スコアを0から100の整数で返させたいとき、数値の範囲制約は3社とも十分にはサポートされていません。フィールドの説明に上限と下限を明記し、そのうえでアプリ側でも範囲を検証する二重構えにしてください。

Pythonなら、Pydanticでモデルを定義してスキーマを生成する構成が一般的でしょう。2026年8月9日時点のPydanticは v2.13.4(2026年5月6日公開・MIT)で、検証エラーの内容がそのまま再送プロンプトの材料になります。文法で縛れない制約も、ループの中で吸収できます。

10段のネストに当たる前に、抽出を2回へ分けて浅いスキーマに倒す

契約書や仕様書を1回のリクエストで完全に構造化しようとすると、ネストが深くなりプロパティ数も膨らみます。上限の10段に実際に当たる設計は稀ですが、その手前でモデルの精度が落ちる。実務では抽出を2段に分けるほうが安定します。1回目は章の見出しと種別だけを浅いスキーマで抜き、2回目で章ごとに詳細スキーマを当ててください。

セルフホストLLMでの構造化出力:vLLMの指定方法とバックエンドの選び分け

商用APIを使わない構成でも同じ保証は得られます。むしろ制約デコーディングの実装は、オープンソース側のほうが選択肢の広い領域です。

vLLMで指定できる5種類の制約と、旧フィールド廃止による移行の注意

vLLMは公式に構造化出力へ対応しており、2026年8月9日時点の最新リリースは v0.26.0(2026年7月27日公開・Apache-2.0・GitHubスター88,535)です。OpenAI互換サーバでは、JSON Schemaに加えて正規表現、選択肢からの単一選択、文脈自由文法、指定タグ内でのJSON強制という5種類の制約を掛けられます。分類タスクで選択肢を数個に絞るだけなら、選択肢指定のほうが素直に書けるでしょう。

移行時の注意として、guided_json のような旧フィールド名は v0.12.0 で削除済みです。古いサンプルコードをそのまま持ち込むと動きません。スループットやレイテンシを詰める段階の設計と推論基盤の選定は、LLM推論の仕組みと高速化・推論基盤の選び方で扱っています。

xgrammarとoutlinesとllguidanceの性格差と版数の実測

バックエンドは3つあり、性格が違います。xgrammarは文法を再利用する場面でキャッシュが効き、1トークンあたりの生成時間が短く済みます。outlinesは有限状態機械のコンパイルコストを前払いする方式で、複雑なスキーマを何千回も使い回す構成に向く。llguidanceはトークン単位で制約を計算し、初回応答までの時間が短い点が持ち味です。

  • mlc-ai/xgrammar:v0.2.5(2026年7月22日公開・Apache-2.0・スター1,813)
  • dottxt-ai/outlines:1.3.3(2026年8月6日公開・Apache-2.0・スター15,540)
  • guidance-ai/llguidance:リリースタグなし・MIT・スター830(最終更新2026年8月6日)

迷ったら既定の自動選択のまま始めてください。切り替えを検討するのは、同じスキーマを大量に回すバッチで生成時間が読めないときか、初回応答の待ち時間が体感へ響く対話用途の2択です。

構造化出力の導入後に起きる4つの失敗と、リトライ設計での吸収の仕方

導入後に持ち込まれる不具合は、種類がほぼ決まっています。原因の切り分け順に並べます。

スキーマには従うのに値が誤る、という最も多い失敗の切り分け方

最も件数が多いのは、形式は完璧なのに中身が違うケースです。請求書の金額フィールドに合計ではなく小計が入る。日付フィールドに発行日ではなく支払期日が入る。どちらもスキーマ上は正当な出力なので、制約デコーディングでは止められません。

対処はスキーマではなく評価の側にあります。抽出結果を正解データと突き合わせるテストセットを作り、フィールド単位で一致率を見てください。形式エラーがゼロになると動いているように見えるため、内容の精度を測らないと、誤った値が静かに業務データへ流れ込みます。

生成トークン上限での打ち切りと、安全上の拒否をパース前に分岐する

構造化出力を使っていても、壊れたJSONが返ることはあります。原因の大半は生成トークン数の上限に達した打ち切りでしょう。文法上は正しい途中まで生成し、閉じ括弧に到達する前に止まるためパースは失敗します。長い配列を返させる設計ほど起きやすい。

実装では、パースを試みる前に終了理由を確認してください。上限到達ならスキーマの分割かトークン上限の引き上げで対処します。安全上の拒否も別形式で返るため、この分岐を持たない実装は例外で落ちます。

形式の縛りが推論品質を下げる場合に、思考用フィールドを先頭へ置く

出力フォーマットの制約が推論タスクの性能を下げうることは、arXiv:2408.02442 をはじめとする研究で報告されています。厳しい形式を課すと、モデルが答えに至るまでの中間的な検討を書く場所を失うためです。

回避策はスキーマの並び順にあります。結論フィールドの前に、根拠を書くための文字列フィールドを1つ置いてください。JSONは定義順に生成されるので、先に理由を書かせてから結論を出させる流れが作れる。逆に結論を先頭へ置くと、モデルは検討前に答えを確定させ、判定が難しいタスクほど精度が落ちます。

検証エラーを添えて再送し、リトライ回数を2回で打ち切る設計にする

アプリ側の検証で弾かれた出力は、エラーメッセージを添えて再送すると多くが通ります。Pythonでこの再送を定型化するライブラリが instructor で、2026年8月9日時点の版は v1.15.4(2026年6月28日公開・MIT・スター13,702)です。Pydanticモデルを渡すと、検証失敗時に例外内容をモデルへ戻して再試行します。

回数は2回で打ち切ってください。3回目以降で通るケースは少なく、待ち時間と費用だけが積み上がります。打ち切ったレコードは失敗として記録し、人手のキューへ送る。再試行をエージェントの制御ループへ組み込む設計はエージェントループと終了条件の設計で整理しています。

構造化出力を採らない条件と、受託開発で要件定義に書かせる項目

ここからは判断です。全案件で構造化出力を使うべきかというと、そうではありません。

自由文が成果物・スキーマが毎回変わる・日次数件、この3条件なら見送る

次の3つのいずれかに当たるなら、構造化出力は入れないでください。第1に、記事下書きやメール文面のように自由文そのものが最終成果物である場合。JSONで包む意味がなく、文字列フィールド1つのスキーマを維持する手間だけが残ります。

第2に、スキーマが案件ごと・顧客ごとに変わり、動的に組み立てざるを得ない場合。文法コンパイルのキャッシュが効かず、初回コストを毎回払い続けます。素のJSONで受け、アプリ側の検証と再送で吸収するほうが安く済むでしょう。第3に、処理件数が1日数件で人手レビューが前提の場合。パースエラーを人が直すコストのほうが、スキーマの設計と保守より小さくなります。

型付きの下流・日次数百件・人手レビュー無し、この組み合わせなら採用する

逆に採用条件も言い切ります。出力を受け取るのが型付きの言語で書かれたプログラムで、処理件数が日次で数百を超え、人手レビューを挟まず業務データへ書き込む。この3つが揃うなら入れてください。パースエラーの発生率が低くても、無人で流れる経路では1件の失敗がジョブ全体の停止につながります。

よくある誤りは、モデルの精度を上げる手段として構造化出力を検討することでしょう。これは精度の施策ではなく、出力を受け取る側のインターフェースを固定する施策です。抽出精度が足りないなら、スキーマではなくプロンプトと評価セットを見直してください。

外注する場合に要件定義へ書かせる4項目と、見積もり時の確認事項

外部に委託するなら、要件定義書に次の4項目が書かれているかを確認してください。第1にスキーマの版管理方法。第2に検証エラー時のリトライ回数と、打ち切り後の退避先。第3に形式エラー率とは別に測る、フィールド単位の内容一致率とその目標値。第4に、対象モデルを変更したときスキーマが通るかを確かめる回帰テストの有無です。

特に3番目が抜けた見積もりは危険で、「JSONが必ず返る」ことをもって完成とされる余地が残ります。抽出対象の文書種別と許容誤り率を発注側から先に提示しておけば、この曖昧さは消えるでしょう。スキーマ設計から評価セットの整備までを含めて相談したい場合は、生成AI導入支援で要件の切り分けから対応しています。

よくある質問

構造化出力の導入検討で実際に多い質問を、5つに絞って答えます。

構造化出力を使えばハルシネーションは無くなりますか?

無くなりません。制約デコーディングが縛るのは出力の形式だけで、値の内容は対象外です。存在しない取引先名がスキーマ通りの文字列フィールドに入って返ることは普通に起こります。減らせるのはパース失敗と、想定外のキー名・型による下流のエラーだけです。内容の誤りには、フィールド単位の一致率測定と値の存在確認を別工程で用意してください。

JSONモードと構造化出力はどちらを使うべきですか?

対象モデルが構造化出力に対応しているなら、構造化出力を選んでください。JSONモードが保証するのはJSONとして壊れていないことだけで、キー名・型・必須項目は保証されません。2026年8月時点では、JSONモードは後方互換のための旧方式です。JSONモード側の指定方法や、あえて残してよい条件はJSONモードとは?構文だけを保証する仕組みと構造化出力への移行判断にまとめています。新規実装でJSONモードを選ぶ理由は、対応していない旧モデルを使い続ける制約がある場合に限られるでしょう。

スキーマに任意項目を入れたい場合はどうしますか?

OpenAIのstrictモードは全フィールドを必須にすることを求めるため、任意項目はnullとの合併型で宣言し、値がなければnullを返させます。キーの有無で表現しようとするとstrict指定が通りません。この書き方にすると下流から存在チェックの分岐が消えるので、結果的にコードも短くなります。

構造化出力を使うと応答が遅くなりますか?

初回リクエストだけ遅くなります。スキーマをトークン生成用の文法へコンパイルする処理が入るためです。Claude APIではコンパイル結果が最終使用時点から24時間キャッシュされ、2回目以降はこの分が乗りません。逆にリクエストごとにスキーマを組み立てる実装はキャッシュが効きません。スキーマは定数として持ち、可変部分は入力データ側へ寄せてください。

ローカルLLMやセルフホスト環境でも同じことができますか?

できます。vLLMは公式に構造化出力へ対応しており、JSON Schemaのほか正規表現・選択肢・文脈自由文法での制約も掛けられます。バックエンドは xgrammar・outlines・llguidance から選べ、既定は自動選択です。ただし旧来の guided_json 形式のフィールドは v0.12.0 で削除されている点に注意してください。

関連記事

資料請求

RELATED POSTS 関連記事