OpenAI Structured Outputs:JSON出力を確実に得る方法

AIの出力が「ほぼJSON」では困る場面があります。スキーマ通りの構造化データを確実に得るStructured Outputsの仕組みと実装方法を解説します。

OpenAI Structured Outputs:JSON出力を確実に得る方法

AI初心者

AIを知りたい

GPT-4oからJSON形式で確実にデータを取得する方法はありますか?

AIエンジニア

AIエンジニア

Structured Outputsという機能を使えば、GPT-4oが指定したJSON Schemaに100%準拠した出力を保証できます。従来のjson_modeでは形式が不安定でしたが、Structured OutputsではスキーマのバリデーションがAPI側で強制されるため、パースエラーが発生しません。

AI初心者

AIを知りたい

どうやって使うのですか?

AIエンジニア

AIエンジニア

APIリクエストのresponse_formatにtype: “json_schema”とschemaオブジェクトを指定します。例えば{name: string, age: number, skills: string[]}というスキーマを定義すれば、GPT-4oは必ずその構造のJSONを返します。Python SDKならpydanticモデルで直接定義もできます。

Structured Outputsとは、OpenAI APIでAIモデルの出力をJSON Schemaに準拠させる機能で、プログラムからの後処理を確実にします。

response_formatにJSON Schemaを指定するだけで100%スキーマ準拠の出力が得られ、データ抽出、分類、構造化などのタスクでパースエラーが完全に排除されます。

活用例と3社比較

AI初心者

AIを知りたい

どんな場面で使うと便利ですか?

AIエンジニア

AIエンジニア

データ抽出(テキストから人名・日付・金額を構造化)、分類(問い合わせのカテゴリ分けを確実に)、API連携(後続処理に必要な形式で確実に出力)が代表的です。

AI初心者

AIを知りたい

ClaudeやGeminiにも同様の機能はありますか?

AIエンジニア

AIエンジニア

ClaudeはTool Useのinput_schemaで構造化出力が可能です。Geminiはresponse_schemaで同様の機能を提供しています。3社とも構造化出力に対応していますが、OpenAIのStructured Outputsが最も直感的な実装です。

機能 OpenAI Claude Gemini
JSON出力保証 Structured Outputs Tool Use経由 response_schema
スキーマ準拠率 100% 高い 高い
設定方法 response_format tools配列 generation_config

まとめ

Structured OutputsはAIの出力をプログラムから確実に処理するための必須機能です。JSON Schemaを指定するだけで100%スキーマ準拠の出力が得られ、パースエラーを完全に排除できます。

関連記事

OpenAI Structured Outputsで型安全なJSON応答を得る:json_schemaモードの実装

Structured Outputsのresponse_format: json_schemaを使い、モデルの出力を事前定義したJSONスキーマに厳密に準拠させる方法を実装します。パースエラーのない型安全なAPI連携を実現できます。


from openai import OpenAI
import json

client = OpenAI()

# ---- 基本的なStructured Outputs ----
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "ユーザーが指定した技術トピックについて構造化された解説を返してください。"},
        {"role": "user", "content": "Transformerについて解説してください"}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "tech_explanation",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "topic": {"type": "string", "description": "トピック名"},
                    "summary": {"type": "string", "description": "1〜2文の要約"},
                    "key_concepts": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "name": {"type": "string"},
                                "description": {"type": "string"}
                            },
                            "required": ["name", "description"],
                            "additionalProperties": False
                        },
                        "description": "主要概念のリスト(3〜5件)"
                    },
                    "difficulty_level": {
                        "type": "string",
                        "enum": ["beginner", "intermediate", "advanced"],
                        "description": "難易度レベル"
                    },
                    "use_cases": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "代表的なユースケース"
                    },
                    "related_topics": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "関連トピック"
                    }
                },
                "required": ["topic", "summary", "key_concepts", "difficulty_level", "use_cases", "related_topics"],
                "additionalProperties": False
            }
        }
    }
)

# 返り値は必ず指定スキーマ準拠のJSON
result = json.loads(response.choices[0].message.content)

print(f"トピック: {result['topic']}")
print(f"要約: {result['summary']}")
print(f"難易度: {result['difficulty_level']}")
print(f"\n主要概念:")
for concept in result["key_concepts"]:
    print(f"  - {concept['name']}: {concept['description']}")
print(f"\nユースケース: {', '.join(result['use_cases'])}")
print(f"関連トピック: {', '.join(result['related_topics'])}")

# ---- refusal対応(安全性フィルタ) ----
print(f"\nRefusal: {response.choices[0].message.refusal}")
# Noneなら正常、文字列ならモデルが回答を拒否した理由

Structured Outputsの最大の利点は、strict: trueを指定することでモデルの出力がJSONスキーマに100%準拠する保証が得られる点です。従来のjson_objectモードではスキーマ違反が起こり得ましたが、Structured Outputsではプロパティの欠落や型の不一致が発生しません。これにより、後続処理でのtry-catchやバリデーションコードを大幅に削減できます。注意点として、スキーマの全プロパティにrequiredを指定し、additionalProperties: falseを設定する必要があります。API連携・データ抽出・分類タスクなど、出力形式が厳密に決まっている場面では第一選択の手法です。