OpenAI 構造化出力の使い方

OpenAIの構造化出力は、スキーマに一致するJSONを保証します。json_schema + strictとJSONモードの違い、具体例、制限事項、およびそのテスト方法をご確認ください。

INEZA Felin-Michel

INEZA Felin-Michel

26 6月 2026

OpenAI 構造化出力の使い方

Apidog エンタープライズ

オンプレミスデプロイ

SSO & RBAC

SOC 2 準拠

Apidog Enterpriseを見る

このガイドを読み終えるまでに、独自のコードからOpenAIの構造化出力を呼び出せるようになります。モデルにJSONスキーマを渡し、strict: trueを設定すると、要求した形式に確実に一致する応答が返されます。最初のリクエストを送信し、応答を読み取り、エッジケースを処理し、ペイロードが実際に準拠していることをアサートするAPIテストコレクションをApidogで生成します。

ボタン

始める前に必要なもの

構造化出力は、モデルの生成を制約し、指定したJSONスキーマに準拠する出力を生成します。strict: trueを設定したスキーマを渡すと、モデルはそれを侵害するフィールドを出力できなくなります。必要なキーはすべて存在し、すべての型が一致し、すべてのenum値が指定されたもののいずれかになります。これにより、防御的なパースコードを書く必要がなくなり、ペイロードを信頼できるようになります。

これは、代替手段に比べて大きな進歩です。「JSONのみで応答してください」といった自由形式のプロンプトは、機能しなくなるまでしか使えません。思考の逸脱が一度でもあれば、オブジェクトの周りに散文がラップされたり、整数を期待していた場所に日付が返されたりします。構造化出力は、期待を込めた指示を、デコード時に強制される制約へと移行させます。

このガイドを進めるには、次のものが必要です。

適切なモデルを選択する

構造化出力は、OpenAIの最近のモデル、GPT-4oファミリーからGPT-5シリーズに至るまで利用可能です。OpenAIのドキュメントでは、現在、最新の主力モデル(執筆時点ではgpt-5.5)で新しいプロジェクトを開始することを推奨しています。古いモデルやgpt-3.5時代のモデルはJSONモードをサポートしていますが、厳格なスキーマ強制はサポートしていません。strict: trueに依存する場合は、モデルのバージョンによってサポート状況が異なり、OpenAIがこれらを更新していくため、出荷前に特定のモデルIDがそれをサポートしていることを確認してください。

OpenAIは混同しやすい2つの関連機能をリリースしているため、実際にどの機能を使いたいのかを理解しておくことが役立ちます。

JSONモード (response_format: { "type": "json_object" }) は、出力が構文的に有効なJSONであることを保証します。それだけです。フィールド、型、必須キーについては認識しません。依然として、自分で形式を検証する必要があります。

構造化出力 (type: "json_schema"strict: trueを含むresponse_format) は、出力が有効なJSONであると同時に、スキーマに一致することを保証します。OpenAIは、構造化出力をJSONモードの進化版と説明しています。どちらも有効なJSONを生成しますが、スキーマへの準拠を強制するのは構造化出力だけです。新しい作業には、構造化出力が望ましいです。

JSONモード 構造化出力(厳格)
パラメータ response_format: {"type":"json_object"} type: "json_schema"strict: trueを含むresponse_format
有効なJSON はい はい
スキーマに一致 いいえ はい
必須フィールドの強制 いいえ はい
型とenumの強制 いいえ はい
下流での検証 常に必要 推奨(下記参照)

APIに関する注意点:Chat Completionsエンドポイントは上記のようにresponse_formatを使用します。新しいResponses APIでは、text.formatの下でtype: "json_schema"を使用して同じことを表現します。スキーマのルールは同じで、ラッパーのみが異なります。呼び出すエンドポイントの正確なフィールドパスについては、現在のドキュメントを確認してください。

最初のリクエストを行う

サポートチケットを型付きレコードに抽出するとします。厳格なスキーマを持つChat Completionsリクエストの例を以下に示します。

curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "messages": [
      { "role": "system", "content": "Extract the ticket into the schema." },
      { "role": "user", "content": "My checkout 500s every time I use a saved card. Started today. Account: acct_8842." }
    ],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "support_ticket",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {
            "summary":  { "type": "string" },
            "category": { "type": "string", "enum": ["billing", "bug", "account", "other"] },
            "severity": { "type": "integer" },
            "account_id": {
              "anyOf": [ { "type": "string" }, { "type": "null" } ]
            }
          },
          "required": ["summary", "category", "severity", "account_id"],
          "additionalProperties": false
        }
      }
    }
  }'

応答を読み取る

モデルは、そのスキーマに一致するJSON文字列をcontentとするメッセージを返します。例:

{
  "summary": "Checkout returns HTTP 500 when paying with a saved card",
  "category": "bug",
  "severity": 3,
  "account_id": "acct_8842"
}

account_idnullを伴うanyOfを使用していることに注目してください。これはオプションのフィールドをモデル化する方法であり、独自のスキーマを作成する際に従うべきルールへと直接つながります。

スキーマのサブセットに留まる

構造化出力は、JSONスキーマのサブセットを受け入れます。このサブセットが存在するのは、OpenAIが制約を確実に適用し、コンパイルされたスキーマをキャッシュできるようにするためです。記憶しておくべきルールは次のとおりです。

検証キーワードは強制されないため、「保証されたJSON」は「保証されたビジネスロジック的に有効」を意味しません。構造は固定されますが、内部の値は依然としてテストに値します。oneOf/anyOf/allOfでオプションまたはユニオンフィールドをモデル化した経験があるなら、そのメンタルモデルはなじみ深いでしょう。スキーマは形式を制約しますが、実際の値は別途アサートします。

拒否と切り捨てを処理する

出力が意図的にスキーマに一致しないケースが1つあります。モデルが安全でないリクエストを拒否した場合、スキーマに沿ったコンテンツの代わりに、メッセージにrefusalフィールドが返されます。パースする前に、コードでこれを分岐処理する必要があります。

msg = response.choices[0].message
if msg.refusal:
    handle_refusal(msg.refusal)
else:
    ticket = json.loads(msg.content)

拒否はプログラムで検出可能になり、謝罪のためにテキストをスキャンするよりもクリーンです。応答がスキーマに満たない可能性のある他の2つの方法としては、オブジェクトの途中でmax_tokensに達する(JSONが切り捨てられる)こと、または構造化出力がサポートしない並列ツール呼び出しを使用することです。これらを組み合わせる場合は、parallel_tool_callsfalseに設定してください。

Apidogでテストする方法

厳格モードは、生成時にスキーマを強制します。しかし、テストの必要がなくなるわけではありません。モデルが変更されたり、スキーマがずれたり、チームメイトがrequired配列を編集したり、拒否パスが変わったりする可能性があります。応答が契約に合致しなくなったときに、明確に失敗するテストが必要です。そこにApidogが役立ちます。

役割分担を正確に言うと、OpenAIの厳格モードはスキーマに準拠したJSONを生成するものです。Apidogはモデル側でスキーマを強制するわけではありません。Apidogが行うのは、受け取った応答を期待するスキーマに対して検証することであり、これにより本番環境ではなくCIでずれを検出できます。

ワークフローは次のとおりです。

  1. リクエストを送信します。 Apidogでresponse_formatブロックを使用してChat Completions呼び出しを構築します。繰り返し実行できるようにコレクションに保存します。
  2. 形式をアサートします。 Apidogで応答アサーションを追加します。categoryがenum値の1つであること、severityが整数であること、account_idが文字列またはnullであることを確認します。ApidogはJSONスキーマに対して応答を検証できるため、正確なスキーマをアタッチし、ペイロードが逸脱した場合に実行を失敗させることができます。
  3. CIで実行します。 コレクションをパイプラインに組み込み、モデルやプロンプトの変更があるたびに準拠性を再確認するようにします。静かなスキーマの破損は、ビルド失敗につながります。
  4. 契約をモックします。 実際のリクエストが存在する前、またはトークンを消費せずに下流のコンシューマをテストするために、スキーマに準拠したサンプル応答を返すモックAPIを立ち上げます。統合が固まる間、フロントエンドとテストは安定した形式に対して実行されます。

最後の点は過小評価されがちです。構造化出力を消費するすべてのものを、同じスキーマを尊重するモックに対して開発・テストし、準備ができたら実際のOpenAI呼び出しに置き換えることができます。Apidogをダウンロードすると、リクエスト、アサーション、モックをすべて一箇所で構築できます。

よくある質問

まとめ

これで、一連の流れがすべて揃いました。厳格モードをサポートするモデルを選択し、json_schemastrict: trueを指定してChat Completionsリクエストを送信し、サポートされるサブセット内にスキーマを保持し、refusalで分岐処理を行い、値レベルのルールは依然としてチェックする必要があることを忘れないでください。そして、テストでそれを証明します。Apidogでリクエストを構築し、スキーマに対して応答をアサートし、統合が安定するまでスタックの残りの部分が進行できるようにモックします。モデルは形式を約束しますが、テストがそれが維持されていることを証明します。

ボタン

ApidogでAPIデザイン中心のアプローチを取る

APIの開発と利用をよりシンプルなことにする方法を発見できる