For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
メインナビゲーション
Theme

モデルの構造化出力

モデルのテキスト応答を、定義した JSON スキーマに準拠させます。

JSON は、アプリケーション間でデータをやり取りする形式として、世界で最も広く使われているものの一つです。

構造化出力は、モデルが生成する応答を、指定した JSON スキーマに常に準拠させる機能です。モデルが必須キーを省略したり、列挙型に定義されていない値を生成したりする心配がありません。

構造化出力には、次のような利点があります。

  1. 型安全性の保証: 応答の形式を検証したり、形式が正しくないためにリクエストを再試行したりする必要がありません。
  2. 拒否の明示: モデルが安全上の理由でリクエストを拒否したことを、プログラムで検出できます。
  3. プロンプトの簡素化: 出力形式を統一するために、プロンプトで強く念を押す必要がありません。

OpenAI の REST API は JSON スキーマに対応しています。また、OpenAI の Python ライブラリと JavaScript ライブラリでは、それぞれ pydantic.BaseModel と z.object を使ってオブジェクトのスキーマを定義できます。次の例では、非構造化テキストから情報を抽出し、コードで定義したスキーマに沿った形で取得します。

Ruby SDK は Sorbet の T::Struct で定義したスキーマに対応しており、パースした結果を型付きで返します。

構造化されたレスポンスの取得
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()


class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]


response = client.responses.parse(
    model="gpt-6-astra",
    input=[
        {"role": "system", "content": "Extract the event information."},
        {
            "role": "user",
            "content": "Alice and Bob are going to a science fair on Friday.",
        },
    ],
    text_format=CalendarEvent,
)

event = response.output_parsed

対応モデル

構造化出力は、GPT-4o 以降の OpenAI の最新の大規模言語モデルで利用できます。新しいプロジェクトでは、まず gpt-6-astra を使用してください。gpt-4-turbo やそれ以前のモデルでは、代わりに JSON モードを利用できる場合があります。

構造化出力で使い分ける Function Calling と text.format

OpenAI API では、次の 2 つの方法で構造化出力を利用できます。

  1. Function Calling を使う方法
  2. json_schema 応答形式を使う方法

Function Calling は、モデルとアプリケーションの機能を連携させる場合に役立ちます。

たとえば、データベースに問い合わせる関数をモデルから呼び出せるようにすれば、ユーザーからの注文に関する問い合わせに対応する AI アシスタントを構築できます。UI を操作する関数をモデルから呼び出せるようにすることもできます。

一方、モデルがツールを呼び出す際ではなく、ユーザーに回答する際のスキーマを指定したい場合は、response_format を使った構造化出力が適しています。

たとえば、数学の学習を支援するアプリケーションでは、アシスタントに特定の JSON スキーマに沿って回答させることで、モデルの出力の各部分をそれぞれ異なる形式で表示する UI を生成できます。

用途に応じて、次のように使い分けます。

  • モデルをシステム内のツール、関数、データなどに接続する場合は、Function Calling を使います。ユーザーへの応答を構造化する場合は、text.format で構造化された応答形式を指定します。

以降では、Responses API で Function Calling を使わずに構造化出力を利用する場合について説明します。Function Calling と構造化出力を併用する方法については、

Function Calling

ガイドを参照してください。

構造化出力と JSON モードの違い

構造化出力は、JSON モードを発展させた機能です。どちらも有効な JSON が生成されることを保証しますが、スキーマへの準拠まで保証するのは構造化出力だけです。Responses API、Chat Completions API、Assistants API、Fine-tuning API、Batch API は、いずれも構造化出力と JSON モードの両方に対応しています。

可能な場合は、常に JSON モードよりも構造化出力を使うことをおすすめします。

ただし、response_format: {type: "json_schema", ...} を使った構造化出力に対応するのは、gpt-4o-mini、gpt-4o-mini-2024-07-18、gpt-4o-2024-08-06 およびそれ以降のモデルスナップショットに限られます。

構造化出力 JSON モード
有効な JSON の出力 はい はい
スキーマへの準拠 はい(対応スキーマを参照) いいえ
対応モデル gpt-4o-mini、gpt-4o-2024-08-06 およびそれ以降のモデル gpt-3.5-turbo、gpt-4-*、gpt-4o-* および JSON モードに対応する GPT-5 モデル
有効にする設定 text: { format: { type: "json_schema", "strict": true, "schema": ... } } text: { format: { type: "json_object" } }

使用例

思考の連鎖

モデルに解答をステップごとに構造化して出力するよう指示すると、ユーザーが解き方を順にたどれるようになります。

思考の連鎖を用いた数学指導のための構造化出力
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()


class Step(BaseModel):
    explanation: str
    output: str


class MathReasoning(BaseModel):
    steps: list[Step]
    final_answer: str


response = client.responses.parse(
    model="gpt-6-astra",
    input=[
        {
            "role": "system",
            "content": "You are a helpful math tutor. Guide the user through the solution step by step.",
        },
        {"role": "user", "content": "how can I solve 8x + 7 = -23"},
    ],
    text_format=MathReasoning,
)

math_reasoning = response.output_parsed

レスポンス例

{
  "steps": [
    {
      "explanation": "Start with the equation 8x + 7 = -23.",
      "output": "8x + 7 = -23"
    },
    {
      "explanation": "Subtract 7 from both sides to isolate the term with the variable.",
      "output": "8x = -23 - 7"
    },
    {
      "explanation": "Simplify the right side of the equation.",
      "output": "8x = -30"
    },
    {
      "explanation": "Divide both sides by 8 to solve for x.",
      "output": "x = -30 / 8"
    },
    {
      "explanation": "Simplify the fraction.",
      "output": "x = -15 / 4"
    }
  ],
  "final_answer": "x = -15 / 4"
}

構造化出力の設定方法:text.format

会話の途中での応答形式の変更

次の応答から新しいスキーマを使うには、configuration_update 項目を input に追加し、その後に次のユーザーメッセージを追加します。各リクエストのトップレベルの text.format には元の値を指定してください。これにより、それまでのプロンプトの先頭部分が引き続きプロンプトキャッシュの対象になります。

応答形式の更新は、GPT-6 以降の対応モデルを標準のシングルエージェントモードで使用する場合に利用できます。

前のターンで、出荷する荷物には赤い木箱が 17 個、青い木箱が 26 個あるとプレーンテキストで説明したとします。previous_response_id で会話を続け、次の配列を input として渡します。

プレーンテキストから木箱の合計数を返す構造化出力への切り替え
from openai import OpenAI
from pydantic import BaseModel, ConfigDict


class CrateCount(BaseModel):
    model_config = ConfigDict(extra="forbid")
    total_crates: int


client = OpenAI()
model = "gpt-6-astra"

first = client.responses.create(
    model=model,
    input="Summarize this shipment in one sentence: 17 red crates and 26 blue crates.",
    store=True,
)

second = client.responses.parse(
    model=model,
    previous_response_id=first.id,
    input=[
        {
            "type": "configuration_update",
            "text": {
                "format": {
                    "type": "json_schema",
                    "name": "crate_count",
                    "schema": CrateCount.model_json_schema(),
                    "strict": True,
                }
            },
        },
        {"role": "user", "content": "How many crates are in the shipment?"},
    ],
    parse_as=CrateCount,
)

if second.status == "completed" and second.output_parsed is not None:
    print(second.output_parsed.total_crates)

期待される回答は {"total_crates":43} です。応答オブジェクトの text.format には、リクエストのトップレベルで指定した元の形式が引き続き表示されます。リクエストが拒否されておらず、応答が完全であることを確認してから、直近の形式更新で指定したスキーマを使って回答をパースしてください。

形式を更新するたびに、応答形式全体が置き換わります。更新後の形式は後続のターンでも有効で、reasoning.effort だけを更新した場合も引き継がれます。プレーンテキストに戻すには、{"type":"configuration_update","text":{"format":{"type":"text"}}} を追加します。更新時に指定できる形式は json_schema と text だけです。

会話履歴を手動で再送する場合は、各更新項目を元の位置に残してください。コンパクションを明示的に実行した後は、変更した設定をすべて再指定してください。詳しくは、設定更新の互換性を参照してください。

構造化出力でのリクエスト拒否

ユーザー入力に対して構造化出力を使うと、OpenAI のモデルが安全上の理由でリクエストを拒否することがあります。拒否時の応答は response_format で指定したスキーマに従うとは限らないため、API の応答には、モデルがリクエストを拒否したことを示す新しいフィールド refusal が含まれます。

出力オブジェクトに refusal プロパティが含まれている場合は、拒否メッセージを UI に表示したり、応答を受け取るコードに条件分岐を追加して拒否時の処理を実行したりできます。

class Step(BaseModel):
    explanation: str
    output: str


class MathReasoning(BaseModel):
    steps: list[Step]
    final_answer: str


response = client.responses.parse(
    model="gpt-6-astra",
    input=[
        {
            "role": "system",
            "content": "You are a helpful math tutor. Guide the user through the solution step by step.",
        },
        {"role": "user", "content": "how can I solve 8x + 7 = -23"},
    ],
    text_format=MathReasoning,
)

for output in response.output:
    if output.type != "message":
        continue

    for item in output.content:
        if item.type == "refusal":
            # If the model refuses to respond, you will get a refusal message
            print(item.refusal)
            continue

        if not item.parsed:
            raise Exception("Could not parse response")

        print(item.parsed)

リクエストが拒否された場合、API は次のような応答を返します。

{
  "id": "resp_1234567890",
  "object": "response",
  "created_at": 1721596428,
  "status": "completed",
  "completed_at": 1721596429,
  "error": null,
  "incomplete_details": null,
  "input": [],
  "instructions": null,
  "max_output_tokens": null,
  "model": "gpt-4o-2024-08-06",
  "output": [{
    "id": "msg_1234567890",
    "type": "message",
    "role": "assistant",
    "content": [
      {
        "type": "refusal",
        "refusal": "I'm sorry, I cannot assist with that request."
      }
    ]
  }],
  "usage": {
    "input_tokens": 81,
    "output_tokens": 11,
    "total_tokens": 92,
    "output_tokens_details": {
      "reasoning_tokens": 0,
    }
  },
}

ヒントとベストプラクティス

ユーザー入力の扱い

アプリケーションがユーザー入力を受け取る場合は、その入力から有効な応答を生成できないときの対処方法をプロンプトで指示してください。

モデルは常に指定されたスキーマに従おうとするため、入力がスキーマとまったく無関係な場合は、入力に根拠のない情報を生成することがあります。

たとえば、入力がタスクに適していないと判断した場合は、パラメータを空にして返すか、特定の文を返すようにプロンプトで指示できます。

出力内容に誤りがある場合の対処

構造化出力でも、内容に誤りが含まれることがあります。誤りが見つかった場合は、指示を調整する、システム指示に例を加える、タスクをより単純なサブタスクに分けるといった方法を試してください。入力の調整方法について詳しくは、プロンプトエンジニアリングガイドを参照してください。

JSON スキーマと型の不一致の防止

JSON スキーマとプログラミング言語側の型の定義が食い違わないように、SDK に組み込みのスキーマヘルパーがある場合は、その利用を強くおすすめします。

JSON スキーマを直接指定する場合は、スキーマまたは対応するデータオブジェクトのどちらかが編集されたことを検出して知らせる CI ルールを追加できます。また、型定義から JSON スキーマを自動生成する CI ステップや、逆にスキーマから型定義を自動生成する CI ステップを追加する方法もあります。

ストリーミング

ストリーミングを使うと、モデルの応答や関数呼び出しの引数を生成中に逐次処理し、構造化データとして解析できます。

応答全体の生成が完了するのを待たずに処理を始められます。 JSON のフィールドを一つずつ表示したい場合や、関数呼び出しの引数を受け取り次第処理したい場合に特に便利です。

構造化出力のストリーミングには、SDK の利用をおすすめします。

from openai import OpenAI
from pydantic import BaseModel


class EntitiesModel(BaseModel):
    attributes: list[str]
    colors: list[str]
    animals: list[str]


client = OpenAI()

with client.responses.stream(
    model="gpt-6-astra",
    input=[
        {"role": "system", "content": "Extract entities from the input text"},
        {
            "role": "user",
            "content": "The quick brown fox jumps over the lazy dog with piercing blue eyes",
        },
    ],
    text_format=EntitiesModel,
) as stream:
    for event in stream:
        if event.type == "response.refusal.delta":
            print(event.delta, end="")
        elif event.type == "response.output_text.delta":
            print(event.delta, end="")
        elif event.type == "response.error":
            print(event.error, end="")
        elif event.type == "response.completed":
            print("Completed")  # print(event.response.output)

    final_response = stream.get_final_response()
    print(final_response)

対応スキーマ

構造化出力は、JSON Schema の仕様の一部に対応しています。

対応する型

構造化出力では、次の型を使用できます。

  • 文字列
  • 数値
  • 真偽値
  • 整数
  • オブジェクト
  • 配列
  • 列挙型
  • anyOf

対応するプロパティ

プロパティの型に加えて、次の制約を指定できます。

string で使用できるプロパティ

  • pattern — 文字列が一致する必要のある正規表現
  • format — 文字列に適用する定義済みの形式。現在は次の形式に対応しています。
    • date-time
    • time
    • date
    • duration
    • email
    • hostname
    • ipv4
    • ipv6
    • uuid

number で使用できるプロパティ

  • multipleOf — 数値は指定値の倍数である必要があります。
  • maximum — 数値は指定値以下である必要があります。
  • exclusiveMaximum — 数値は指定値未満である必要があります。
  • minimum — 数値は指定値以上である必要があります。
  • exclusiveMinimum — 数値は指定値を超える必要があります。

array で使用できるプロパティ

  • minItems — 配列の要素数は指定値以上である必要があります。
  • maxItems — 配列の要素数は指定値以下である必要があります。

型ごとの制約の使用例を以下に示します。

{
    "name": "user_data",
    "strict": true,
    "schema": {
        "type": "object",
        "properties": {
            "name": {
                "type": "string",
                "description": "The name of the user"
            },
            "username": {
                "type": "string",
                "description": "The username of the user. Must start with @",
                "pattern": "^@[a-zA-Z0-9_]+$"
            },
            "email": {
                "type": "string",
                "description": "The email of the user",
                "format": "email"
            }
        },
        "additionalProperties": false,
        "required": [
            "name", "username", "email"
        ]
    }
}

スキーマのルートはオブジェクト型に限定(anyOf は使用不可)

スキーマのルートはオブジェクトでなければならず、ルートで anyOf は使用できません。たとえば Zod で判別可能なユニオン型を使うと、最上位に anyOf が生成されます。そのため、次のようなコードは動作しません。

import { z } from "zod";
import { zodResponseFormat } from "openai/helpers/zod";

const BaseResponseSchema = z.object({
  /* ... */
});
const UnsuccessfulResponseSchema = z.object({
  /* ... */
});

const finalSchema = z.discriminatedUnion("status", [
  BaseResponseSchema,
  UnsuccessfulResponseSchema,
]);

// Invalid JSON Schema for Structured Outputs
const json = zodResponseFormat(finalSchema, "final_schema");

すべてのフィールドに required の指定が必須

構造化出力を使用するには、すべてのフィールドまたは関数パラメータを required として指定する必要があります。

{
    "name": "get_weather",
    "description": "Fetches the weather in the given location",
    "strict": true,
    "parameters": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "The location to get the weather for"
            },
            "unit": {
                "type": "string",
                "description": "The unit to return the temperature in",
                "enum": ["F", "C"]
            }
        },
        "additionalProperties": false,
        "required": ["location", "unit"]
    }
}

すべてのフィールドが必須で、モデルは各パラメータの値を返します。null を含むユニオン型を使えば、省略可能なパラメータと同様に扱えます。

{
    "name": "get_weather",
    "description": "Fetches the weather in the given location",
    "strict": true,
    "parameters": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "The location to get the weather for"
            },
            "unit": {
                "type": ["string", "null"],
                "description": "The unit to return the temperature in",
                "enum": ["F", "C"]
            }
        },
        "additionalProperties": false,
        "required": [
            "location", "unit"
        ]
    }
}

オブジェクトのネストの深さとサイズの上限

1 つのスキーマに含められるオブジェクトのプロパティは合計 5,000 個までで、ネストの深さは最大 10 階層です。

文字列の合計長の上限

1 つのスキーマ内のプロパティ名、定義名、enum の値、const の値をすべて合わせた文字列の長さは、120,000 文字までです。

enum のサイズの上限

1 つのスキーマ内のすべての enum プロパティに指定できる値は、合計 1,000 個までです。

文字列を値とする 1 つの enum プロパティに 250 個を超える値を指定する場合、それらの文字列の合計長は 15,000 文字までです。

すべてのオブジェクトで additionalProperties: false の指定が必須

additionalProperties は、JSON Schema に定義されていないキーと値の組をオブジェクトに追加できるかどうかを制御します。

構造化出力で生成できるのは、スキーマで指定したキーと値だけです。そのため、構造化出力を使用するには、additionalProperties: false を指定する必要があります。

{
    "name": "get_weather",
    "description": "Fetches the weather in the given location",
    "strict": true,
    "schema": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "The location to get the weather for"
            },
            "unit": {
                "type": "string",
                "description": "The unit to return the temperature in",
                "enum": ["F", "C"]
            }
        },
        "additionalProperties": false,
        "required": [
            "location", "unit"
        ]
    }
}

キーの順序

構造化出力では、スキーマに記述した順序でキーが出力されます。

現時点で未対応の型別キーワード

  • スキーマの合成: allOf、not、dependentRequired、dependentSchemas、if、then、else

ファインチューニングしたモデルでは、以下のキーワードも使用できません。

  • 文字列: minLength、maxLength、pattern、format
  • 数値: minimum、maximum、multipleOf
  • オブジェクト: patternProperties
  • 配列: minItems、maxItems

strict: true を指定して構造化出力を有効にし、対応範囲外の JSON Schema で API を呼び出すと、エラーが返されます。

anyOf 内の各スキーマに適用される JSON Schema の要件

anyOf 内の各スキーマは、構造化出力が対応する JSON Schema の範囲で有効である必要があります。以下は、使用できる anyOf スキーマの例です。

{
    "type": "object",
    "properties": {
        "item": {
            "anyOf": [
                {
                    "type": "object",
                    "description": "The user object to insert into the database",
                    "properties": {
                        "name": {
                            "type": "string",
                            "description": "The name of the user"
                        },
                        "age": {
                            "type": "number",
                            "description": "The age of the user"
                        }
                    },
                    "additionalProperties": false,
                    "required": [
                        "name",
                        "age"
                    ]
                },
                {
                    "type": "object",
                    "description": "The address object to insert into the database",
                    "properties": {
                        "number": {
                            "type": "string",
                            "description": "The number of the address. Eg. for 123 main st, this would be 123"
                        },
                        "street": {
                            "type": "string",
                            "description": "The street name. Eg. for 123 main st, this would be main st"
                        },
                        "city": {
                            "type": "string",
                            "description": "The city of the address"
                        }
                    },
                    "additionalProperties": false,
                    "required": [
                        "number",
                        "street",
                        "city"
                    ]
                }
            ]
        }
    },
    "additionalProperties": false,
    "required": [
        "item"
    ]
}

定義の使用

サブスキーマを定義すると、スキーマ内の各所から参照できます。以下に簡単な例を示します。

{
    "type": "object",
    "properties": {
        "steps": {
            "type": "array",
            "items": {
                "$ref": "#/$defs/step"
            }
        },
        "final_answer": {
            "type": "string"
        }
    },
    "$defs": {
        "step": {
            "type": "object",
            "properties": {
                "explanation": {
                    "type": "string"
                },
                "output": {
                    "type": "string"
                }
            },
            "required": [
                "explanation",
                "output"
            ],
            "additionalProperties": false
        }
    },
    "required": [
        "steps",
        "final_answer"
    ],
    "additionalProperties": false
}

再帰スキーマの使用

以下は、# でルートを参照する再帰スキーマの例です。

{
    "name": "ui",
    "description": "Dynamically generated UI",
    "strict": true,
    "schema": {
        "type": "object",
        "properties": {
            "type": {
                "type": "string",
                "description": "The type of the UI component",
                "enum": ["div", "button", "header", "section", "field", "form"]
            },
            "label": {
                "type": "string",
                "description": "The label of the UI component, used for buttons or form fields"
            },
            "children": {
                "type": "array",
                "description": "Nested UI components",
                "items": {
                    "$ref": "#"
                }
            },
            "attributes": {
                "type": "array",
                "description": "Arbitrary attributes for the UI component, suitable for any element",
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string",
                            "description": "The name of the attribute, for example onClick or className"
                        },
                        "value": {
                            "type": "string",
                            "description": "The value of the attribute"
                        }
                    },
                    "additionalProperties": false,
                    "required": ["name", "value"]
                }
            }
        },
        "required": ["type", "label", "children", "attributes"],
        "additionalProperties": false
    }
}

以下は、再帰参照を明示したスキーマの例です。

{
    "type": "object",
    "properties": {
        "linked_list": {
            "$ref": "#/$defs/linked_list_node"
        }
    },
    "$defs": {
        "linked_list_node": {
            "type": "object",
            "properties": {
                "value": {
                    "type": "number"
                },
                "next": {
                    "anyOf": [
                        {
                            "$ref": "#/$defs/linked_list_node"
                        },
                        {
                            "type": "null"
                        }
                    ]
                }
            },
            "additionalProperties": false,
            "required": [
                "next",
                "value"
            ]
        }
    },
    "additionalProperties": false,
    "required": [
        "linked_list"
    ]
}

JSON モード

JSON モードは、構造化出力の前身にあたる、より基本的な機能です。JSON モードはモデルが有効な JSON を出力することを保証します。構造化出力では、モデルの出力が指定したスキーマに準拠することも保証されます。用途に応じて構造化出力が利用できる場合は、その使用をおすすめします。

JSON モードを有効にすると、一部の例外を除き、モデルの出力が有効な JSON であることが保証されます。例外はアプリケーション側で検出し、適切に処理してください。

Responses API で JSON モードを有効にするには、text.format を { "type": "json_object" } に設定します。Function Calling を使う場合、JSON モードは常に有効です。

次の点に注意してください。

  • JSON モードを使う場合は、システムメッセージなど、会話内のいずれかのメッセージで、必ず JSON を生成するようモデルに指示してください。明示的な指示がないと、モデルが空白文字を出力し続け、トークンの上限に達するまでリクエストが終了しない場合があります。指示の入れ忘れを防ぐため、コンテキスト内に文字列「JSON」が含まれていない場合、API はエラーを返します。
  • JSON モードが保証するのは、出力が有効な JSON であり、エラーなくパースできることだけです。特定のスキーマへの準拠は保証されません。出力を目的のスキーマに準拠させるには、構造化出力を使ってください。利用できない場合は、検証ライブラリで出力がスキーマに準拠しているかを確認し、必要に応じてリクエストを再試行してください。
  • モデルが完全な JSON オブジェクトを出力できない場合があるため、アプリケーションでこうした例外を検出し、処理する必要があります(以下を参照)。

参考資料

構造化出力について詳しくは、次の資料を参照してください。