構造化出力(Structured Outputs)とは何か|LLMの返答をプログラムで安全に扱うための仕組み

LLMに「この文章から会社名と金額と日付を抜き出してJSONで返して」と頼むと、たいていはそれらしいJSONが返ってきます。ところが本番のパイプラインに載せた途端、たまに前置きの一文が混ざったり、キー名がぶれたり、数値のはずが文字列で返ってきたりして、後段のプログラムが落ちます。構造化出力(Structured Outputs)は、この「たまに壊れる」を仕組みで抑え込むための機能です。この記事では、構造化出力が何を保証して何を保証しないのか、JSONモードとの違い、主要3社の考え方、実装の勘所と落とし穴を、LLMアプリを組む開発者の視点で整理します。
なぜ「valid JSON」だけでは足りないのか
LLMの返答をプログラムで扱う場面では、自由文ではなく機械可読な形が欲しくなります。そこで多くの人がまず試すのが、プロンプトで「JSONで返して」と指示する方法です。うまくいくことも多いのですが、この方法には2段階の落とし穴があります。
1つ目は、そもそもJSONとして壊れているケースです。前後に説明文が付く、コードブロックの記号で囲まれる、末尾のカンマが残る、といった崩れが混ざると、JSON.parseやjson.loadsの時点で例外になります。
2つ目は、JSONとしては整形式でも、こちらが期待する形になっていないケースです。必須のはずのamountキーが抜けている、dateが"2026-07-13"ではなく"7月13日"で返る、想定していないstatusの値が入る。パースは通るのに、後段のバリデーションや型変換でこける状態です。
ここで重要なのが、次の2つを区別することです。
- 整形式(valid JSON)であること: 括弧や引用符の対応が取れ、パースできる状態
- スキーマ準拠(schema conformance)であること: 決めた型・必須項目・値の範囲まで満たしている状態
実務で欲しいのは後者です。構造化出力とJSONモードの違いも、まさにこの線引きにあります。
JSONモードと構造化出力の違い
多くのプロバイダには、古くから「JSONモード」に相当する機能があります。これは出力が整形式のJSONになることは保証しますが、その中身が特定のスキーマに沿うことまでは保証しません。つまり前述の1つ目の落とし穴は塞げても、2つ目は残ります。キー名や型がぶれる余地があるため、結局は自前のバリデーションとリトライで守る必要がありました。
構造化出力(Structured Outputs)は、ここを一歩進めて、開発者が渡したJSONスキーマへの準拠までを保証しようとする機能です。中心にあるのがconstrained decoding(制約付き生成)という考え方です。
メモ
OpenAIは2024年に、この厳密なスキーマ準拠を保証するStructured Outputs(strictモード)をAPIに導入しました。従来のJSONモードは「整形式の保証」、Structured Outputsは「スキーマ準拠の保証」と、役割が重なりつつも保証範囲が異なるものとして併存しています。
あわせて読みたい
LLMのTool Use(関数呼び出し)とは|仕組みと精度を上げるツール設計
主要3社の対応を中立に整理する
保証したいこと(スキーマ準拠)は共通でも、そこへ至るAPIの入り口はプロバイダごとに少しずつ違います。2026年7月時点の各社公式ドキュメントをもとに、考え方を中立に整理します。
- 1
OpenAI: response_formatにstrictなjson_schema
レスポンス指定のresponse_formatにtype: "json_schema"とスキーマを渡し、strict: trueを設定します。strict時は、propertiesに挙げた全フィールドをrequiredに含める、各オブジェクトでadditionalProperties: falseにする、任意項目はnullとのunion型にする、といった追加ルールが課されます。 - 2
Anthropic(Claude): 2つの入り口
1つはtool use(ツール定義)の入力スキーマを使う昔ながらの手法で、tool_choiceで特定ツールの呼び出しを強制し、返ってきた引数をJSONとして受け取ります。加えて、レスポンス自体をスキーマに沿わせるネイティブな構造化出力も提供されており、公式ドキュメントではoutput_config.formatにjson_schemaを渡す形が案内されています。ツール定義側にstrict: trueを付ける厳密モードもあります。 - 3
Google(Gemini): responseMimeTypeとresponseSchema
生成設定で出力をJSONにするmime type(application/json)と、従うべきスキーマ(responseSchema)を指定します。スキーマはJSON Schemaのサブセットに対応し、enumやobject/arrayを表現できます。PydanticやZodで定義した型からスキーマを渡す使い方も案内されています。
細かいパラメータ名やstrictモードの制約は各社で更新が入りやすいため、実装時は必ず最新の公式ドキュメントで確認してください。ここで押さえるべきは、「どの入り口でも、最終的にはJSONスキーマで期待する形を宣言し、それにモデルを従わせる」という発想が共通している点です。
各社の対応状況・パラメータ名は、OpenAI「Structured model outputs」、Anthropic「Structured outputs」「Tool use」、Google「Structured output」の各公式ドキュメント(2026年7月時点)を参照しています。
実装の勘所:スキーマ設計と検証
構造化出力を業務で安定させるコツは、モデル任せにする範囲を減らし、こちら側で決められるものは決めておくことです。
スキーマは小さく、値は制約する
- 必須項目と任意項目を明確にする。必須なら
requiredに入れ、値が無い可能性があるなら明示的にnullを許す設計にする(nullを返せる方が、無理に埋めて幻の値を作られるより安全なことが多い) - 取り得る値が決まっているフィールドはenumで列挙する。
statusを自由文にせず"open"|"closed"|"pending"のように縛るだけで、後段の分岐が安定します - 説明(description)をスキーマ内に書く。各フィールドが何を意味するかをモデルに伝えられ、意図とずれた抽出を減らせます
短いJSON Schemaの例を挙げます(疑似的な最小例で、実際のパラメータ名は各プロバイダの仕様に合わせてください)。
{
"type": "object",
"properties": {
"company": { "type": "string" },
"amount": { "type": "integer" },
"currency": { "type": "string", "enum": ["JPY", "USD"] },
"due_date": { "type": ["string", "null"], "format": "date" }
},
"required": ["company", "amount", "currency", "due_date"],
"additionalProperties": false
}
アプリ側でも必ず検証する
構造化出力がスキーマ準拠を保証してくれる場合でも、受け取った側でPydantic(Python)やZod(TypeScript)などを使ってもう一度検証する二重化をおすすめします。理由は、プロバイダやモデルによって保証の強さに差があること、非対応の型やエッジケースが残ること、そして自分たちのビジネスルール(金額は0以上、日付は未来日のみ、など)はスキーマだけでは表しきれないことです。検証に失敗したら、エラー内容を添えて1回だけ再生成させるリトライを挟むと、実運用での取りこぼしが目に見えて減ります。
function callingとの関係
構造化出力とツール呼び出し(function calling)は目的が近く、混同しがちです。ざっくり分けると、ツール呼び出しは「モデルに外部の関数・APIを呼ばせる」ための仕組みで、その引数がスキーマに沿います。構造化出力は「モデルの最終的な返答そのもの」をスキーマに沿わせます。エージェント的にツールを実行させたいならツール呼び出し、単にきれいなJSONを1つ受け取りたいなら構造化出力、と役割で選ぶと整理できます。両方を組み合わせ、ツール呼び出しは厳密モードで、最終出力は構造化出力で、という使い方も可能です。
あわせて読みたい
RAGとは何か|仕組み・向き不向き・導入判断の考え方
落とし穴:構造が正しくても中身は別
構造化出力は強力ですが、万能ではありません。導入前に知っておきたい弱点を挙げます。
- スキーマが複雑すぎると精度もコストも悪化しやすい。深いネストや大量のフィールドは、モデルの負担とトークン量を増やします。まずは必要最小限のスキーマから始めるのが無難です
- 対応していない型・制約がある。JSON Schemaの全機能が使えるわけではなく、プロバイダごとに扱える範囲が異なります。使う前にサポート対象を確認してください
- モデル・プロバイダ差がある。同じスキーマでも、モデルや世代によって得手不得手が出ます
- レイテンシへの影響。文法をコンパイルする処理などが挟まるため、初回や複雑なスキーマで応答が遅くなる場合があります
そして最も重要なのが次の点です。
注意
amountの数値が原文と食い違っていたり、存在しない会社名がもっともらしく入っていたりする幻覚(ハルシネーション)は依然として起こり得ます。構造の妥当性と内容の正しさは、必ず別のものとして扱ってください。だからこそ、構造化出力は評価とセットで運用する価値があります。形が揃うことで、抽出結果を正解データと機械的に突き合わせる自動評価が組みやすくなります。「JSONが壊れないこと」ではなく「中身が正しいこと」を継続的に測る仕組みまで用意して、はじめて本番投入の土台が整います。
あわせて読みたい
LLMアプリの評価(eval)の作り方|「動いた気がする」で止めないための実務手順
小さく始める手順
- 1
1つのユースケースに絞る
まずは1種類の抽出・分類タスク(例: 問い合わせメールから件名・カテゴリ・緊急度を取り出す)に限定します。いきなり万能スキーマを目指さないことが成功の近道です。 - 2
最小スキーマを書く
必要なフィールドだけの小さなJSON Schemaを定義し、enumとrequired、nullableを最初から意識して設計します。 - 3
構造化出力を有効にして試す
使うプロバイダの構造化出力(またはtool useベースの手法)を有効にし、10-20件の実データで挙動を確認します。 - 4
受信側の検証とリトライを足す
PydanticやZodでの検証と、失敗時1回のリトライを実装します。ここまでで「壊れない」状態が作れます。 - 5
評価データで中身を測る
正解を用意したテストセットで、値の正しさ(内容の精度)を測る評価を回します。スキーマ準拠率と内容精度は別々に追いかけます。
よくある質問(FAQ)
JSONモードと構造化出力(Structured Outputs)はどちらを使えばよいですか。
構造化出力を使えば、もうバリデーションは不要ですか。
構造化出力を使えばハルシネーションは防げますか。
OpenAI・Anthropic・Googleで書き方は共通ですか。
まとめ
構造化出力は、LLMを「たまに壊れる相手」から「決めた形で応じるコンポーネント」へ近づけるための、地味だが効く仕組みです。ポイントは、整形式(valid JSON)とスキーマ準拠を区別し、後者を仕組みで担保しつつ、中身の正しさは別立てで評価することにあります。
構造化出力を導入する前のチェック
- 整形式JSONの保証(JSONモード)だけで十分か、スキーマ準拠まで必要かを切り分けたか
- 必須項目・nullable・enumを意識した最小スキーマを設計したか
- 使うプロバイダの構造化出力の仕様(パラメータ名・対応する型・strictの制約)を公式ドキュメントで確認したか
- 受信側でのPydantic/Zod検証と、失敗時のリトライを用意したか
- 構造の妥当性とは別に、内容の正しさを測る評価データを用意したか
最初から完璧なスキーマや全社対応を目指す必要はありません。1つのユースケースで「壊れない」「測れる」状態を作り、そこから広げていくのが、構造化出力を実務で定着させる現実的な道筋です。まずはツール呼び出しとの違いを押さえたうえで、小さなスキーマ1枚から試してみてください。
あわせて読みたい
LLMのTool Use(関数呼び出し)とは|仕組みと精度を上げるツール設計
出典・参考
関連する記事
LLMのTool Use(関数呼び出し)とは|仕組みと精度を上げるツール設計
LLMが外部のツールやAPIを呼び出す「Tool Use(関数呼び出し/function calling)」の仕組みを、実務目線で解説します。tool_useとtool_resultの往復、クライアントツールとサーバーツールの違い、いつ呼ぶかの制御、そして呼び出し精度を上げるためのツール定義のコツまでを整理します。
LLMアプリの評価(eval)の作り方|「動いた気がする」で止めないための実務手順
LLMアプリの品質を測る評価(eval)の作り方を、実務目線で整理します。実運用に似せた評価データの用意、タスク型に応じた指標設計、自動評価と人手レビューの併用、LLM-as-a-judgeの使いどころと落とし穴、失敗トレースをテストへ戻す継続的な評価まで、小さく始める手順にまとめました。
RAGとは何か|仕組み・向き不向き・導入判断の考え方
社内文書をAIに答えさせる代表的な手法であるRAG(検索拡張生成)を解説します。検索と生成を組み合わせる仕組み、向いている用途と向かない用途、導入前に確認したい判断ポイントをまとめます。


