
OpenAI・Gemini・Claude の構造化出力を徹底比較 — JSON スキーマ準拠を確実に得る方法
はじめに
LLM の出力を下流処理に流すとき、「返ってきた JSON がスキーマに準拠していない」という問題に悩まされたことはないでしょうか。
プロンプトで「JSON で返して」と指示するだけでは、余計なキーが混入したり、型が違ったり、そもそも JSON として壊れていたりすることがあります。各プラットフォームはこの課題に対して異なるアプローチで「スキーマ準拠の保証」を実現しています。
この記事では、OpenAI・Gemini・Claude の構造化出力の仕組みを API コード例付きで比較し、さらに「Required vs Optional フィールド」「Nullable 型の書き方の違い」といった実装時にハマりやすいポイントまで掘り下げます。
3社の構造化出力アプローチ
まず、各プラットフォームがどのような仕組みでスキーマ準拠を実現しているかを整理します。

OpenAI — response_format + strict: true
OpenAI は response_format パラメータに JSON Schema を直接渡す方式です。strict: true を指定すると Constrained Decoding(制約付きデコーディング)が有効になり、モデルの出力が 100% スキーマに準拠することが保証されます。
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Extract: John is 30, lives in Tokyo"}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "person",
"strict": True,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"city": {"type": "string"}
},
"required": ["name", "age", "city"],
"additionalProperties": False
}
}
}
)
# message.content に JSON 文字列が返る
# {"name": "John", "age": 30, "city": "Tokyo"}
ポイント:
strict: trueは Constrained Decoding を有効にし、スキーマ準拠を 100% 保証するadditionalProperties: falseがすべてのオブジェクト階層で必須- すべてのプロパティを
requiredに含める必要がある(後述) - JSON Schema のサブセットのみサポート(
oneOf不可、$refは限定的)
Gemini — responseMimeType + responseSchema
Gemini は responseMimeType で出力形式を指定し、responseSchema で構造を定義する 2 段構えの方式です。
from google import genai
from google.genai import types
client = genai.Client()
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Extract: John is 30, lives in Tokyo",
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema={
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"city": {"type": "string"}
},
"required": ["name", "age", "city"]
}
)
)
# response.text に JSON 文字列が返る
# {"name": "John", "age": 30, "city": "Tokyo"}
ポイント:
response_mime_type単体 = 有効な JSON は保証されるがスキーマ準拠は保証されないresponse_schemaを追加すると、制約付きデコーディングでスキーマに準拠した出力が得られるenum指定で分類タスクにも対応- Pydantic モデルを直接スキーマとして渡すことも可能
Claude — Structured Outputs / Tool Use によるスキーマ抽出
Claude は 2025 年後半に Structured Outputs(response_format に json_schema を指定する方式)をネイティブサポートしました。それ以前から利用されている Tool Use + tool_choice 強制 も引き続き有効な手段です。ここでは、より広く使われている Tool Use 方式を例示します。
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Extract: John is 30, lives in Tokyo"}],
tools=[{
"name": "extract_person",
"description": "Extract person info",
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"city": {"type": "string"}
},
"required": ["name", "age", "city"]
}
}],
tool_choice={"type": "tool", "name": "extract_person"}
)
# tool_use ブロックの input フィールドから取得
tool_block = next(b for b in response.content if b.type == "tool_use")
result = tool_block.input
# {"name": "John", "age": 30, "city": "Tokyo"}
ポイント:
- Structured Outputs(
response_format: json_schema)によるネイティブ JSON mode も利用可能(2025年後半〜) - Tool Use のスキーマ +
tool_choice強制でも高い信頼性のスキーマ準拠を実現 - Tool Use 方式の出力は
message.contentのテキストではなく、tool_useブロックのinputフィールドに格納される - OpenAI の strict mode より広い JSON Schema サポート
比較まとめ
| OpenAI | Gemini | Claude | |
|---|---|---|---|
| メカニズム | response_format.json_schema |
responseMimeType + responseSchema |
Tool Use + tool_choice |
| スキーマ保証 | strict: true → 100% |
responseSchema → 制約付きデコーディング |
Tool schema → 高い信頼性 |
| JSON のみモード | type: "json_object"(スキーマなし) |
responseMimeType 単体 |
なし |
| Constrained Decoding | あり(明示的) | あり | あり(Structured Outputs で保証) |
| 出力の取得場所 | message.content |
response.text |
tool_use.input |
| スキーマ方言 | JSON Schema サブセット | OpenAPI スタイル | JSON Schema(フルサポート) |
Required vs Optional — 「必須フィールド」の落とし穴
構造化出力を使い始めると、すぐにぶつかるのが「フィールドをオプショナルにしたい」というケースです。ここで 3 社の挙動が大きく異なります。

OpenAI: すべてのフィールドが required 必須
OpenAI の strict: true モードでは、すべてのプロパティを required 配列に含めなければならない という制約があります。
{
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": { "type": "string" },
"nickname": { "type": ["string", "null"] }
},
"required": ["name", "nickname"], // nickname も required
"additionalProperties": false
}
}
// 出力: {"name": "John", "nickname": null} ← フィールドは必ず存在、値が null
// フィールド自体が欠落することはない
「オプショナル」を表現するには、型を ["string", "null"] にして required だが nullable とします。フィールドのキー自体が出力から欠落することはありません。
Gemini: 真のオプショナルフィールドが可能
Gemini では required に含めないフィールドは出力から完全に省略される可能性があります。nullable の構文も OpenAPI スタイルの nullable: true を使います。
response_schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"nickname": {"type": "string", "nullable": True}
},
"required": ["name"] # nickname は required に含めない
}
# 出力: {"name": "John"} ← nickname キー自体がない
# 出力: {"name": "John", "nickname": null} ← これも有効
Claude: 標準 JSON Schema のルールに従う
Claude の Tool Use スキーマは標準的な JSON Schema に準拠しており、required に含めないフィールドは省略可能です。
input_schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"nickname": {"type": ["string", "null"]}
},
"required": ["name"] # nickname はオプショナル
}
# 出力: {"name": "John"} ← OK
# 出力: {"name": "John", "nickname": null} ← OK
# 出力: {"name": "John", "nickname": "JD"} ← OK
Required の挙動比較
OpenAI (strict) |
Gemini | Claude | |
|---|---|---|---|
| オプショナルフィールド | 不可 — 全プロパティ required 必須 |
可能 | 可能 |
| 「オプショナル」の代替手段 | "type": ["string", "null"] |
required から除外 |
required から除外 |
| フィールド欠落 | 絶対に起きない | 起こりうる | 起こりうる |
OpenAI の strict mode を使う場合、下流のパーサーは 「キーは必ず存在するが値が null かもしれない」 という前提で実装します。一方、Gemini や Claude を使う場合は 「キー自体が存在しない可能性がある」 ことを考慮する必要があります。
Nullable 型 — JSON Schema vs OpenAPI の方言差
「値が null になりうる」ことを表現する構文が、プラットフォームによって異なります。これは JSON Schema と OpenAPI 仕様の歴史的な違いに由来します。
JSON Schema 方式: type 配列
OpenAI と Claude は JSON Schema の標準構文である type 配列を使います。
{ "type": ["string", "null"] }
これは「string または null のいずれか」を意味します。JSON Schema の Union Type です。
OpenAPI 方式: nullable キーワード
Gemini は OpenAPI(旧 Swagger)スタイルの nullable キーワードを使います。
{ "type": "string", "nullable": true }
意味は同じですが、構文が異なります。
Nullable 構文の比較
| 構文 | 例 | 方言 | |
|---|---|---|---|
| OpenAI | "type": ["string", "null"] |
{"nickname": {"type": ["string", "null"]}} |
JSON Schema |
| Gemini | "nullable": true |
{"nickname": {"type": "string", "nullable": true}} |
OpenAPI |
| Claude | "type": ["string", "null"] |
{"nickname": {"type": ["string", "null"]}} |
JSON Schema |
実装上の注意点として、スキーマ定義を複数プラットフォームで共有する場合は、この方言差を吸収する変換レイヤーが必要になります。
まとめ
- OpenAI は
strict: true+additionalProperties: falseで最も明示的にスキーマ準拠を保証する。ただし全フィールドrequired制約があり、オプショナルは nullable で代替する - Gemini は
responseMimeType+responseSchemaの 2 段構えで、API としては最もクリーン。OpenAPI スタイルのnullableに注意 - Claude は Tool Use +
tool_choice強制というやや間接的な方法だが、JSON Schema のフルサポートにより最も柔軟
どのプラットフォームを使う場合も、以下を意識すると良いです。
- 「JSON を返して」とプロンプトで指示するだけでは不十分 — 必ず専用の構造化出力機能を使う
- オプショナルフィールドの扱いはプラットフォームで異なる — 下流パーサーの実装に直結する
- スキーマの方言差に注意 — マルチプラットフォーム対応時は変換レイヤーの検討が必要









