LLM Frontline
開発・エージェント

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

ミナト開発・API担当
・ 約14分で読めます
構造化出力(Structured Outputs)とは何か|LLMの返答をプログラムで安全に扱うための仕組み

LLMに「この文章から会社名と金額と日付を抜き出してJSONで返して」と頼むと、たいていはそれらしいJSONが返ってきます。ところが本番のパイプラインに載せた途端、たまに前置きの一文が混ざったり、キー名がぶれたり、数値のはずが文字列で返ってきたりして、後段のプログラムが落ちます。構造化出力(Structured Outputs)は、この「たまに壊れる」を仕組みで抑え込むための機能です。この記事では、構造化出力が何を保証して何を保証しないのか、JSONモードとの違い、主要3社の考え方、実装の勘所と落とし穴を、LLMアプリを組む開発者の視点で整理します。

なぜ「valid JSON」だけでは足りないのか

LLMの返答をプログラムで扱う場面では、自由文ではなく機械可読な形が欲しくなります。そこで多くの人がまず試すのが、プロンプトで「JSONで返して」と指示する方法です。うまくいくことも多いのですが、この方法には2段階の落とし穴があります。

1つ目は、そもそもJSONとして壊れているケースです。前後に説明文が付く、コードブロックの記号で囲まれる、末尾のカンマが残る、といった崩れが混ざると、JSON.parsejson.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(制約付き生成)という考え方です。

メモ

constrained decodingは、モデルが次のトークンを選ぶたびに「今スキーマ的に許される候補」だけに選択肢を絞り込む方式です。OpenAIの解説では、JSONスキーマを文脈自由文法(CFG)に変換し、生成の各ステップで妥当なトークンだけを許可すると説明されています。仕組み上、スキーマを破るトークンはそもそも出力され得ない、という点が「プロンプトでお願いする」方式との決定的な違いです。

OpenAIは2024年に、この厳密なスキーマ準拠を保証するStructured Outputs(strictモード)をAPIに導入しました。従来のJSONモードは「整形式の保証」、Structured Outputsは「スキーマ準拠の保証」と、役割が重なりつつも保証範囲が異なるものとして併存しています。

あわせて読みたい

LLMのTool Use(関数呼び出し)とは|仕組みと精度を上げるツール設計

主要3社の対応を中立に整理する

保証したいこと(スキーマ準拠)は共通でも、そこへ至るAPIの入り口はプロバイダごとに少しずつ違います。2026年7月時点の各社公式ドキュメントをもとに、考え方を中立に整理します。

  1. 1

    OpenAI: response_formatにstrictなjson_schema

    レスポンス指定のresponse_formattype: "json_schema"とスキーマを渡し、strict: trueを設定します。strict時は、propertiesに挙げた全フィールドをrequiredに含める、各オブジェクトでadditionalProperties: falseにする、任意項目はnullとのunion型にする、といった追加ルールが課されます。
  2. 2

    Anthropic(Claude): 2つの入り口

    1つはtool use(ツール定義)の入力スキーマを使う昔ながらの手法で、tool_choiceで特定ツールの呼び出しを強制し、返ってきた引数をJSONとして受け取ります。加えて、レスポンス自体をスキーマに沿わせるネイティブな構造化出力も提供されており、公式ドキュメントではoutput_config.formatにjson_schemaを渡す形が案内されています。ツール定義側にstrict: trueを付ける厳密モードもあります。
  3. 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回だけ再生成させるリトライを挟むと、実運用での取りこぼしが目に見えて減ります。

最初はプロンプトで「JSONで返して」と頼むだけでしたが、数千件流すと必ず数件は形が崩れて落ちました。スキーマ準拠を効かせたうえで受信側でも型検証し、失敗時に1回リトライする三段構えにしてから、ようやく夜間バッチを安心して回せるようになりました。
抽出パイプラインを組む開発者

function callingとの関係

構造化出力とツール呼び出し(function calling)は目的が近く、混同しがちです。ざっくり分けると、ツール呼び出しは「モデルに外部の関数・APIを呼ばせる」ための仕組みで、その引数がスキーマに沿います。構造化出力は「モデルの最終的な返答そのもの」をスキーマに沿わせます。エージェント的にツールを実行させたいならツール呼び出し、単にきれいなJSONを1つ受け取りたいなら構造化出力、と役割で選ぶと整理できます。両方を組み合わせ、ツール呼び出しは厳密モードで、最終出力は構造化出力で、という使い方も可能です。

あわせて読みたい

RAGとは何か|仕組み・向き不向き・導入判断の考え方

落とし穴:構造が正しくても中身は別

構造化出力は強力ですが、万能ではありません。導入前に知っておきたい弱点を挙げます。

  • スキーマが複雑すぎると精度もコストも悪化しやすい。深いネストや大量のフィールドは、モデルの負担とトークン量を増やします。まずは必要最小限のスキーマから始めるのが無難です
  • 対応していない型・制約がある。JSON Schemaの全機能が使えるわけではなく、プロバイダごとに扱える範囲が異なります。使う前にサポート対象を確認してください
  • モデル・プロバイダ差がある。同じスキーマでも、モデルや世代によって得手不得手が出ます
  • レイテンシへの影響。文法をコンパイルする処理などが挟まるため、初回や複雑なスキーマで応答が遅くなる場合があります

そして最も重要なのが次の点です。

注意

構造化出力が保証するのは「形」であって「中身の正しさ」ではありません。スキーマに完璧に沿ったJSONが返ってきても、amountの数値が原文と食い違っていたり、存在しない会社名がもっともらしく入っていたりする幻覚(ハルシネーション)は依然として起こり得ます。構造の妥当性と内容の正しさは、必ず別のものとして扱ってください。

だからこそ、構造化出力は評価とセットで運用する価値があります。形が揃うことで、抽出結果を正解データと機械的に突き合わせる自動評価が組みやすくなります。「JSONが壊れないこと」ではなく「中身が正しいこと」を継続的に測る仕組みまで用意して、はじめて本番投入の土台が整います。

あわせて読みたい

LLMアプリの評価(eval)の作り方|「動いた気がする」で止めないための実務手順

小さく始める手順

  1. 1

    1つのユースケースに絞る

    まずは1種類の抽出・分類タスク(例: 問い合わせメールから件名・カテゴリ・緊急度を取り出す)に限定します。いきなり万能スキーマを目指さないことが成功の近道です。
  2. 2

    最小スキーマを書く

    必要なフィールドだけの小さなJSON Schemaを定義し、enumとrequired、nullableを最初から意識して設計します。
  3. 3

    構造化出力を有効にして試す

    使うプロバイダの構造化出力(またはtool useベースの手法)を有効にし、10-20件の実データで挙動を確認します。
  4. 4

    受信側の検証とリトライを足す

    PydanticやZodでの検証と、失敗時1回のリトライを実装します。ここまでで「壊れない」状態が作れます。
  5. 5

    評価データで中身を測る

    正解を用意したテストセットで、値の正しさ(内容の精度)を測る評価を回します。スキーマ準拠率と内容精度は別々に追いかけます。

よくある質問(FAQ)

JSONモードと構造化出力(Structured Outputs)はどちらを使えばよいですか。
スキーマ準拠まで保証したいなら構造化出力を選びます。JSONモードは整形式のJSONであることは保証しますが、キー名や型がスキーマ通りである保証はありません。後段のプログラムが特定の形を前提にしているなら、構造化出力のほうが安全です。
構造化出力を使えば、もうバリデーションは不要ですか。
不要にはなりません。プロバイダやモデルによる保証の差、非対応の型、そして金額や日付の妥当性といったビジネスルールはスキーマだけでは守れないため、受信側でのPydantic/Zod検証を残すことをおすすめします。
構造化出力を使えばハルシネーションは防げますか。
防げません。保証されるのは出力の形(スキーマ準拠)であって、値が事実として正しいことではありません。誤った値がスキーマに沿った形で返ることは起こり得るため、内容の正しさは評価や突き合わせで別途担保する必要があります。
OpenAI・Anthropic・Googleで書き方は共通ですか。
考え方(JSONスキーマで期待する形を宣言し従わせる)は共通ですが、パラメータ名や制約は異なります。OpenAIはresponse_formatのstrictなjson_schema、Anthropicはツールベースまたはネイティブなoutputフォーマットとstrictツール、GeminiはresponseMimeTypeとresponseSchemaです。実装時は各公式ドキュメントで最新仕様を確認してください。

まとめ

構造化出力は、LLMを「たまに壊れる相手」から「決めた形で応じるコンポーネント」へ近づけるための、地味だが効く仕組みです。ポイントは、整形式(valid JSON)とスキーマ準拠を区別し、後者を仕組みで担保しつつ、中身の正しさは別立てで評価することにあります。

構造化出力を導入する前のチェック

  • 整形式JSONの保証(JSONモード)だけで十分か、スキーマ準拠まで必要かを切り分けたか
  • 必須項目・nullable・enumを意識した最小スキーマを設計したか
  • 使うプロバイダの構造化出力の仕様(パラメータ名・対応する型・strictの制約)を公式ドキュメントで確認したか
  • 受信側でのPydantic/Zod検証と、失敗時のリトライを用意したか
  • 構造の妥当性とは別に、内容の正しさを測る評価データを用意したか

最初から完璧なスキーマや全社対応を目指す必要はありません。1つのユースケースで「壊れない」「測れる」状態を作り、そこから広げていくのが、構造化出力を実務で定着させる現実的な道筋です。まずはツール呼び出しとの違いを押さえたうえで、小さなスキーマ1枚から試してみてください。

あわせて読みたい

LLMのTool Use(関数呼び出し)とは|仕組みと精度を上げるツール設計

出典・参考

この記事をシェア

関連する記事

開発・エージェント

LLMアプリの評価(eval)の作り方|「動いた気がする」で止めないための実務手順

LLMアプリの品質を測る評価(eval)の作り方を、実務目線で整理します。実運用に似せた評価データの用意、タスク型に応じた指標設計、自動評価と人手レビューの併用、LLM-as-a-judgeの使いどころと落とし穴、失敗トレースをテストへ戻す継続的な評価まで、小さく始める手順にまとめました。