Structured outputs(構造化出力)は、指定したJSON Schemaにモデルの応答を必ず従わせるLLM APIの機能です。制約付きデコーディングを使い、APIがスキーマを文法または状態機械にコンパイルしてトークン生成を制限することで、OpenAI、Anthropic、GoogleのAPIにおける構文エラーやフィールドの欠落を防ぎます。
LLM APIにおけるStructured Outputsとは?
LLM APIのstructured outputsは、モデルが生成するトークンを制約し、完成した応答が開発者指定のJSON Schemaに一致するようにします。AnthropicとGoogleはいずれも、その基盤となる仕組みを制約付きデコーディングと説明しています。サンプリング前に、APIがスキーマを文法または有限状態機械にコンパイルし、スキーマの構造、キー名、値の型に違反する次のトークンを除外します。
structured outputsを使わず、プロンプトで言語モデルにJSONでの応答を求めても、本番環境では安定して動作しません。Anthropicのドキュメントは、プロンプトの指示だけに頼る場合によくある4つの失敗として、無効なJSON構文による解析エラー、必須フィールドの欠落、データ型の不一致、エラー処理と再試行を要するスキーマ違反を挙げています。実際には、次のような一般的な例があります。
- 無効なJSON構文による解析エラー: 例えば、閉じられていない波括弧、末尾のカンマ、データを囲むMarkdownの文章によってJSONパーサーが動作しなくなることがあります。
- 必須フィールドの欠落: 例えば、後続のアプリケーションコードが期待するキーをモデルが省略することがあります。
- データ型の不一致: 例えば、数値フィールドが文字列として届いたり、配列の代わりに単一の値が届いたりします。
- エラー処理と再試行を要するスキーマ違反: 例えば、要求していないキーや範囲外の値によって、再試行ループや防御的な検証コードが必要になります。
制約付きデコーディングにより、プロンプトで強い表現のフォーマット指示をする必要がなくなります。アプリケーションの型をAPIに渡すスキーマと一致させるため、OpenAIとGoogleはPythonのPydantic BaseModelクラスとJavaScriptのZodオブジェクトを受け付けるネイティブSDKヘルパーを提供しています。一方、AnthropicのPython SDKはclient.messages.parse()を介してPydanticモデルを受け取り、応答をその型に解析します。
Structured OutputsとJSON mode、プロンプトだけによるJSONの違いは?
Structured outputsは指定したJSON Schemaへの厳密な準拠を保証します。一方、JSON modeやプロンプトだけの指示では、特定のプロパティ名、ネスト構造、必須フィールドを強制できません。
JSON modeは、完全なスキーマ制約が登場する前の中間段階でした。OpenAIでは、{ "type": "json_object" }でJSON modeを有効にすると、オブジェクトの途中で出力トークンが尽きるなどの特殊なケースに遭遇しない限り、モデルが構文上有効なJSONを生成します。ただし、OpenAIのドキュメントには明確な注意点があります。会話のコンテキストのどこかにJSONを出力する指示を含める必要があり、単語JSONがないとAPIはエラーを返します。また、そうしない場合、モデルはトークン上限まで空白を延々と生成することがあります。
OpenAIはstructured outputsをJSON modeの進化形と位置付け、対応している場合は常にstructured outputsを使い、JSON modeはgpt-4-turbo以前の古いモデルに使うことを推奨しています。
| 機能 | プロンプトのみのJSON | JSON mode | Structured Outputs |
|---|---|---|---|
| 有効なJSON構文を保証 | いいえ | はい、途中で切れなければ | はい、拒否や途中切れがなければ |
| 正確なスキーマと型を強制 | いいえ | いいえ | はい |
| プロンプトにJSONの指示が必要 | はい | はい、OpenAIでは必須 | いいえ |
| フォーマットの再試行を解消 | いいえ | 一部 | はい、スキーマの構造について |
| PydanticとZodによるネイティブ解析 | 手動 | 手動 | 各社のSDKが対応 |
Structured OutputsとFunction Calling:それぞれいつ使う?
Structured outputsはアプリケーションやエンドユーザーに対するモデルの最終応答を整形し、function callingは実行中にモデルを外部ツール、API、データソースに接続します。
どちらもJSON Schemaの定義に基づくため、開発者は両者を混同しがちです。OpenAIとGoogleはいずれも、同じアーキテクチャ上の区別を示しています。
- structured outputsを使う場合: データ抽出や構造化分類など、最終的なアシスタントメッセージをUI、データベース、後続のパイプライン向けに整形したいときです。
- function calling(tool use)を使う場合: 回答を終える前に、モデルがアクションを要求したり、リアルタイムの記録を検索したり、バックエンドの処理を呼び出したりする必要があるときです。ツール定義と現代のプロトコルの関係については、tool callingとMCPのガイドをご覧ください。
OpenAIは厳格なスキーマを2つの異なる形式で提供しています。モデルをツールやデータに接続する際の厳格なスキーマ付きfunction callingと、ユーザーへの応答を構造化するjson_schema応答形式です。Anthropicは、Claudeの最終応答用のoutput_config.formatと、有効なツール名と入力引数を保証する各ツールのstrict: trueに機能を分け、1つのリクエストで両方を使うことを明示的にサポートしています。途中のツール呼び出しと最終要約が厳格な取り決めに従う必要があるAIエージェントのワークフローでは、両者の組み合わせが特に実用的です。
OpenAI、Anthropic、GoogleによるStructured Outputsの実装
2026年11月現在、OpenAI、Anthropic、Googleはいずれもスキーマのコンパイルによってstructured outputsを実装していますが、リクエストパラメーター、対応するJSON Schemaキーワード、複雑さの上限は異なります。
| プロバイダー | 有効化方法 | SDKのスキーマヘルパー | 主なスキーマ規則 | 主な制限 |
|---|---|---|---|---|
| OpenAI | Responses APIのtext.formatまたはChat Completionsのresponse_formatにtype: "json_schema"とstrict: trueを指定 |
Pydantic BaseModel(Python)、Zod(JS) |
ルートはanyOfではなくオブジェクト、全フィールドをrequiredに指定、additionalProperties: falseが必須、キーはスキーマ順に出力 |
最大5,000プロパティ、ネスト10階層、名前とenumの合計120,000文字、enum値1,000個 |
| Anthropic Claude | output_config.formatにtype: "json_schema"、さらにツールにstrict: trueを指定 |
client.messages.parse()とPydantic(Python) |
$ref/$defs、anyOf、allOfに対応(制限あり)、additionalProperties: falseが必須、数値や文字列長の境界は指定不可 |
厳格なツール最大20個、任意パラメーター24個、unionパラメーター16個、コンパイルのタイムアウト180s、文法キャッシュ24時間 |
| Google Gemini API | テキスト型、mime_type: "application/json"、スキーマを応答設定に指定 |
Pydantic(Python)、Zod(JS) | minimum/maximum、minItems/maxItems、prefixItemsに対応、非対応の境界は通知なく削除 |
大規模または深くネストしたスキーマは400 INVALID_ARGUMENTで失敗 |
OpenAI
OpenAIはGPT-4o以降でstructured outputsをサポートし、新しいプロジェクトにはgpt-6-astraを推奨しています。strict: trueと、allOf、not、if/then/else、dependentRequired、dependentSchemasなどの非対応キーワードを指定してAPIを呼び出すと、通知なく失敗するのではなくエラーが返ります。OpenAIはdate-time、time、date、duration、email、hostname、ipv4、ipv6、uuidなどの文字列形式に加え、数値の境界(minimum、maximum、exclusiveMinimum、exclusiveMaximum、multipleOf)と配列の境界(minItems、maxItems)に対応しています。
Anthropic Claude
AnthropicはMessages APIのすべてのモデルでstructured outputsをサポートしていますが、Managed Agents内では対応していません。Python SDKでclient.messages.parse()を使うと、minimum、maximum、minLength、maxLengthなどの非対応の制約は、Claudeが意図を理解できるよう自動的にフィールドの説明へ移されます。また、SDKは返されたデータを元のPydanticモデルに照らしてローカルで検証します。
Claudeはコンパイル済みの文法を最後の使用から24時間キャッシュします。スキーマの構造やツールの集合を変更すると文法キャッシュは無効になりますが、フィールド名や説明の編集では無効になりません。また、ClaudeにはJSON形式を説明するシステムプロンプトが自動的に渡されるため、入力トークン数がわずかに増えます。output_config.formatを変更すると会話のプロンプトキャッシュは無効になります。必須プロパティが先に出力され、その後に任意プロパティが続きます。Structured outputsはストリーミング、トークン数の計測、Batch processing(Anthropicの50パーセント割引あり)に対応します。ただし、JSON出力とCitationsを組み合わせると400エラーが返り、structured outputsはメッセージの事前入力とは互換性がありません。
Google Gemini API
GoogleはGeminiのstructured outputsの主な用途として、データ抽出、構造化分類、エージェント型ワークフローを挙げています。OpenAIやAnthropicと異なり、GeminiはminLength、maxLength、minProperties、maxProperties、uniqueItems、contains、minContains、maxContainsなど、いくつかのキーワードをエラーを出さずに削除して無視します。これらの制約をアプリケーションコードで強制するため、Geminiの応答には常にPydanticのmodel_validate_json()またはZodの.parse()を実行してください。複雑すぎるスキーマに対してGeminiが400 INVALID_ARGUMENTを返す場合、Googleはネストを平坦化し、任意フィールドやenumを減らし、プロパティ名を短くし、構造以外の規則をフィールドの説明に移すことを勧めています。
LLM向けの信頼できるJSON Schemaを設計するには
LLMのstructured outputs向けの信頼できるJSON Schemaは、平坦な構造を保ち、フィールドを必須にし、任意キーの代わりにnullを許容する型を使います。複数のプロバイダーに共通する規則に基づいてスキーマを設計すると、コンパイルエラーや実行時の予期しない拒否を防げます。
OpenAI、Claude、Gemini向けにスキーマをリリースする前に、次のチェックリストを使ってください。
- ネストを浅くする: 可能な限り深いネストのオブジェクトを平坦化し、OpenAIの10階層の上限を十分に下回るようにして、GeminiやClaudeによるコンパイル時の拒否を防ぎます。
- すべてのフィールドを必須にする: 各オブジェクトのキーをすべて
requiredに記載します。OpenAIはこの規則を厳格に適用し、Claudeはリクエストごとの任意パラメーターの合計を24個に制限します。 - 欠損データにはnullを許容するunionを使う:
requiredからキーを省く代わりに、"type": ["string", "null"]で任意の値を表します。 - オブジェクトを制限する: OpenAIとAnthropicの両方が要求するように、すべてのオブジェクト定義に
"additionalProperties": falseを設定します。 - 固定の選択肢をenumで制約する: 有効なラベルが既知の集合に限られる場合、自由な文字列のカテゴリを明示的な
enum配列に置き換えます。 - 明確な説明を書く: 文字数の目安や代替処理の指示など、ドメイン規則や無視される制約を各プロパティの
descriptionに記載します。
以下は、カスタマーサポートのチケットを分類するスキーマの例です。ルートがオブジェクト、全フィールドが必須、任意のメタデータ用にnullを許容するフィールド、additionalProperties: false、振り分け用のenumという、上記の共通規則に従っています。
{
"type": "object",
"additionalProperties": false,
"required": ["category", "urgency", "summary", "order_id"],
"properties": {
"category": {
"type": "string",
"enum": ["billing", "bug_report", "account_access", "other"],
"description": "Primary topic of the support message."
},
"urgency": {
"type": "string",
"enum": ["low", "normal", "high"],
"description": "Escalation priority based on user impact."
},
"summary": {
"type": "string",
"description": "One-sentence summary of the issue in 25 words or fewer."
},
"order_id": {
"type": ["string", "null"],
"description": "Order identifier if present in the message, otherwise null."
}
}
}
Structured Outputsでも起こり得る問題は?
Structured outputsはスキーマに対して有効な構文を保証しますが、本番アプリケーションでは依然として、拒否、トークンの途中切れ、幻覚による値、enumの大文字・小文字のずれ、初回リクエストの遅延、信頼できないユーザー入力が発生します。スキーマへの準拠を真実性や安全性の検証ではなく構文の検証として扱うことで、本番環境での気付かれない失敗を防げます。
安全上の拒否とトークン上限による途中切れ
モデルは安全でないユーザーのリクエストを拒否したり、出力トークンの上限に達して生成を途中で停止したりすることがあります。OpenAIでは、安全上の拒否はJSON Schemaに従わない別のrefusalフィールドに入ります。Anthropic Claudeでは、拒否時にstop_reasonが"refusal"に設定されたHTTP 200が返り(料金は発生します)、スキーマより優先されます。同様に、出力上限に達するとstop_reasonが"max_tokens"となり、不完全なJSONが返ります。JSON.parse()やバリデーターを呼び出す前に、必ず拒否の指標と停止理由を確認してください。
スキーマに適合していても事実と異なる値
制約付きデコーディングでは、元のテキストに答えがない場合やスキーマの前提と矛盾する場合でも、モデルはスキーマを埋めるよう強制されます。応答はすべての型チェックとenumチェックを通過しても、架空の日付、誤ったカテゴリ、もっともらしい推測を含むことがあります。OpenAIは、入力がスキーマと両立しない場合にnullや空のフィールドを返すなど、何を返すべきかプロンプトでモデルに指示するよう勧めています。Googleは、コード内でビジネスルールに照らして値を検証するよう開発者に注意を促しています。自信ありげな出力が誤る理由については、AIのハルシネーション率のガイドをご覧ください。
Claudeのenumの大文字・小文字とコンパイル遅延
導入時にチームが意表を突かれる運用上の特徴が2つあります。
- Claudeのenumの大文字・小文字: Anthropicによると、制約付きデコーディングではenumの大文字・小文字が保証されないため、アプリケーションロジックでは返されたenum文字列を大文字・小文字を区別せずに比較してください。
- 初回リクエストのコンパイル遅延: Claudeは新しいスキーマを初めて文法にコンパイルする際に遅延が生じ、その後24時間キャッシュします。OpenAIもファインチューニング済みモデルで同様の初回スキーマ処理の遅延があると説明しています。
フィールド値内のプロンプトインジェクション
出力構造を制約しても、要約されたりJSON文字列フィールドに抽出されたりする信頼できないテキストに隠された悪意ある指示は無害化されません。モデルは有効なJSONを忠実に返しても、そのsummaryやnotesフィールドに、後続のツールや人間の確認者を狙った注入指示や偽の内容が含まれることがあります。解析された各フィールド値を信頼できない入力として扱い、抽出したフィールドを自動アクションに渡す前に、プロンプトインジェクションとは何かの入門記事を確認してください。
よくある質問
structured outputsはLLMの応答を遅くしますか?
Structured outputsでは新しいスキーマを使う初回リクエストに遅延が加わることがありますが、その後のキャッシュされたリクエストにはコンパイルのオーバーヘッドがありません。Anthropicはスキーマ構造やツール定義を変更しない限り、コンパイル済みのClaudeの文法を最後の使用から24時間キャッシュします。OpenAIが初回のスキーマ処理で追加の遅延に言及しているのは、ファインチューニング済みモデルを使う場合だけです。
structured outputsを使っていてもLLMは幻覚を起こしますか?
はい。LLMはスキーマに100パーセント適合するJSONを返しながら、値を幻覚で作り出すことがあります。制約付きデコーディングが制限するのはトークンの構文とフィールドの型であり、事実の正確さではありません。入力に必須情報がない場合、nullを許容するフィールドを用意し、いつnullを返すか指示しなければ、モデルは必須キーを満たすために値を作り出すことがあります。
structured output APIでPydanticやZodを使うべきですか?
ネイティブSDKのスキーマヘルパーを使うと、アプリケーションの型とAPIスキーマの整合性を保てます。OpenAIとGoogleはPythonのPydanticとJavaScriptのZodの両方に対応し、AnthropicのPython SDKはclient.messages.parse()を通じてPydanticに対応しています。これらのヘルパーはモデルをJSON Schemaに変換し、応答を型付きオブジェクトに解析し、Geminiが削除したりAnthropicがフィールドの説明に移したりする文字列長などの制約を検証します。
structured outputsの有効時にモデルがリクエストを拒否するとどうなりますか?
モデルが安全上の理由でリクエストを拒否すると、拒否がJSON Schemaより優先されます。OpenAIはスキーマに従う代わりに専用のrefusalフィールドに説明を入れ、Anthropicはstop_reasonが"refusal"に設定されたHTTP 200を返します。アプリケーションはJSONの解析を試みる前に、これらのフィールドを確認する必要があります。
ClaudeやGeminiがJSON Schemaを400エラーで拒否するのはなぜですか?
ClaudeとGeminiはいずれも、文法や状態機械の内部的な複雑さの上限を超えるスキーマを400エラーで拒否します。Claudeは、再帰スキーマや数値の境界などの非対応のスキーマ機能を含めた場合や、JSON出力とCitationsを組み合わせた場合にも400エラーを返します。ネストしたオブジェクトの平坦化、名前の短縮、任意パラメーターやunionパラメーターの削減で、通常はコンパイル時の拒否を解消できます。



