ブログ AI / JSON Schema

AI に JSON Schema が必要な理由:Structured Output、Function Calling、JSON Schema を整理する

ChatGPT、Gemini、DeepSeek を繋ぐと、ドキュメントにFunction CallingStructured OutputJSON Schema が同時に出てくる。三つの機能に見えるが、同じ契約言語の三つの使い方だ。

JSON Schema は「この JSON がどう見えるべきか」を記述する。現在の主流は2020-12。Function Calling はツール引数に、Structured Output は最終回答に使う。AI に必要なのは JSON が流行だからではなく、自然言語を下流コードが安定して消費できないからだ。

この記事では:

まずこれだけ:モデルが JSON を出すことと、schema に適合する JSON を出すことは別物だ。JSON Schema が定義するのは後者。Function Calling は「どのツールを、正しい引数で呼ぶか」;Structured Output は「最終回答の形」。以下、なぜ → 何か → ツール引数 → 最終回答 → 選び方 → 検証、の順で分解する。

三つの概念を一枚の表で

まず三語を一枚の表に置く。言語としての JSON Schema は共有するが、縛る対象が違う。

概念 縛る対象 典型フィールド / 規格 解く問題
JSON Schema JSON のフィールド、型、必須、enum JSON Schema 2020-12 契約そのもの:データがどう見えるべきか
Function Calling tool_calls.arguments tools[].parameters どのツールを呼ぶか、引数は合法か
Structured Output 最終回答の JSON response_format.json_schema / responseSchema 結果をそのまま保存・パイプライン投入できるか

三語が混ざるのは、ドキュメントが機能名と契約言語を一緒に書くからだ。層を覚える:JSON Schema は言語、Function Calling と Structured Output は二つの使い方。

AI が自然言語だけでは足りない理由

プロンプトに「JSON を返して」と書くだけだと、次の失敗が起きる。

失敗モード 典型的な症状 根本原因
構文エラー 引用符欠け、末尾カンマ、単引用符 モデルが散文のように生成し、token 列が制約されない
フィールドのドリフト 同じプロンプトでフィールド名が二回違う schema 契約がなく、モデルが自由に書く
型エラー 数値が文字列に、配列がオブジェクトに プロンプトが曖昧で、type の硬制約がない
回帰できない 同じケースを fixture 比較できない 自然言語には安定したフィールド集合がない

デモでは再試行や二次パースで隠せる。本番の Agent、パイプライン、自動化では下流が落ちる。JSON Schema は形式契約:生成前にフィールド、型、必須を宣言する。

JSON Schema は契約であり、コメントではない

JSON Schema は JSON 構造を記述する標準。AI 統合では人向けコメントではなく、機械が実行できる契約だ:

  1. 1
    フィールド名と型を宣言する

    properties の各 key が出力フィールド、type が string / integer / array などを指定する。

  2. 2
    必須と enum を限る

    required が必須フィールド、enum が取りうる値を制限し、モデルの「創造的」な値を防ぐ。

  3. 3
    余分なフィールドを止める

    additionalProperties: false は未宣言 key を禁止する。Strict Schema は通常これを強制する。

チケット schema の例
{
  "type": "object",
  "properties": {
    "ticket_id": {
      "type": "string",
      "description": "Support ticket ID, e.g. TCK-1042"
    },
    "priority": {
      "type": "string",
      "enum": ["low", "medium", "high"]
    },
    "summary": {
      "type": "string",
      "description": "One-sentence problem summary"
    }
  },
  "required": ["ticket_id", "priority", "summary"],
  "additionalProperties": false
}

この schema は tools にあるか response_format にあるかを気にしない。問うのは「この JSON は合法か」だけ。次の二節で Function Calling と Structured Output に載せる。

Function Calling:ツール引数を schema で縛る

Function Calling は、推測ではなく構造化されたツール呼び出しを返す。各ツールのtools[].parameters が JSON Schema だ:

OpenAI 風 Function Calling:parameters が schema
{
  "model": "gpt-4o",
  "messages": [
    { "role": "user", "content": "Create a high-priority ticket for checkout timeout." }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "create_ticket",
        "description": "Create a support ticket from the user request.",
        "parameters": {
          "type": "object",
          "properties": {
            "ticket_id": { "type": "string" },
            "priority": { "type": "string", "enum": ["low", "medium", "high"] },
            "summary": { "type": "string" }
          },
          "required": ["ticket_id", "priority", "summary"],
          "additionalProperties": false
        }
      }
    }
  ]
}

モデルが返すtool_calls[0].function.arguments は JSON 文字列だ。parameters に「できるだけ」合わせるが、毎回合法とは限らない。欠け、型違い、構文壊れは起きる。パースと検証は業務層の仕事。

Agent ループ、MCP inputSchema、より詳しいツールの書き方はAI Agent と JSON Schema の解説を参照。

Structured Output:最終回答を schema で縛る

Structured Output が縛るのはツール引数ではなく最終回答。OpenAI はresponse_format、Gemini はresponseSchema。中身はどちらも JSON Schema:

OpenAI Strict Structured Output:同じチケット schema
{
  "model": "gpt-4o-2024-08-06",
  "messages": [
    { "role": "user", "content": "Extract a support ticket from this email." }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "support_ticket",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "ticket_id": { "type": "string" },
          "priority": { "type": "string", "enum": ["low", "medium", "high"] },
          "summary": { "type": "string" }
        },
        "required": ["ticket_id", "priority", "summary"],
        "additionalProperties": false
      }
    }
  }
}

strict: true を付けると、推論時に schema が token を導く。生成してから祈るのではなく、適合するものだけを生成する。

ChatGPT と Gemini の設定差、JSON Mode と Strict Schema の保証範囲は2026 AI Structured Output ガイドを参照。

同じ schema、二つの載せ方

本稿の要点:ツール引数と最終回答で、ずれる二セットのフィールドを書かない。チケットは三フィールドのまま。変わるのは載せ先だけ。

Python:TICKET_SCHEMA を一つ、載せ先を二つ
TICKET_SCHEMA = {
    "type": "object",
    "properties": {
        "ticket_id": {"type": "string"},
        "priority": {"type": "string", "enum": ["low", "medium", "high"]},
        "summary": {"type": "string"},
    },
    "required": ["ticket_id", "priority", "summary"],
    "additionalProperties": False,
}

# 1) Function Calling: constrain tool arguments
tools = [{
    "type": "function",
    "function": {
        "name": "create_ticket",
        "description": "Create a support ticket.",
        "parameters": TICKET_SCHEMA,
    },
}]

# 2) Structured Output: constrain the final answer
response_format = {
    "type": "json_schema",
    "json_schema": {
        "name": "support_ticket",
        "strict": True,
        "schema": TICKET_SCHEMA,
    },
}

一つの契約、三つの着地:

  1. 1
    ツール呼び出し

    モデルが create_ticket を選び、arguments は schema に適合しなければならない。

  2. 2
    最終回答

    ツールが不要なときも、最終 JSON は同じフィールドで、そのまま保存できる。

  3. 3
    業務検証

    データが tool_calls でも message.content でも、同じ parse + validate を通す。

四つの経路の選び方

2026 年の LLM 構造化データは、だいたい四つの経路がある:

経路 API 設定 保証すること 向く場面
プロンプトのみ 特別な設定なし 形式を保証しない 試作、人が読む
JSON Mode response_format: json_object 合法な JSON オブジェクト 簡単な抽出、緩いフィールド
Function Calling tools[].parameters arguments は parameters にできるだけ合わせる 外部ツール / DB 書き込み / HTTP
Structured Output json_schema + strict: true schema への厳密な適合 最終結果を保存・パイプライン投入する

Agent の四層(LLM、Function Calling、MCP、JSON Schema)の組み立ては2026 AI Agent 技術スタックを参照。

本番の書き方と検証

Structured Output と Function Calling の成功率は schema の質に依存する。業務層はそれでも検証する。

  1. 1
    各 property に description を書く

    モデルは description を読んで値を決める。「Support ticket ID, e.g. TCK-1042」は裸のフィールド名より正確。

  2. 2
    散文で示唆せず enum を使う

    文中の「low/medium/high」よりenum の方が確実。

  3. 3
    additionalProperties: false を付ける

    OpenAI Strict は通常これを強制する。根オブジェクトにadditionalProperties: false を付け、未宣言キーの混入を防ぐ。

  4. 4
    ネストを深くしすぎない

    3 層超や大量の oneOf は失敗率を上げる。複雑な形は複数回の呼び出しに分ける方が安定する。

Python:parse + JSON Schema validate
import json
from jsonschema import validate, ValidationError

def parse_model_json(raw: str, schema: dict) -> dict:
    try:
        data = json.loads(raw)
    except json.JSONDecodeError as e:
        raise ValueError(f"Invalid JSON: {e}") from e
    try:
        validate(instance=data, schema=schema)
    except ValidationError as e:
        raise ValueError(f"Schema mismatch: {e.message}") from e
    return data

ChatGPT、Gemini、DeepSeek のどれでも、同じ parse → validate → 業務ロジック。開発中は schema と実出力を JSONNote に貼る方が print より速い。

JSONNote でモデル JSON をデバッグ

遅いのは API を書くことではなく、返ってきた JSON のどこが契約に反するか見ることだ。JSONNote はブラウザでローカルに動く:

  1. 1
    schema と出力を検証する

    API の JSON と schema をJSON Schema ページに貼ると、契約に合わないフィールドがすぐ分かる。

  2. 2
    API 応答を整形する

    JSON フォーマッタで構文とインデントを確認する。

  3. 3
    二回の呼び出しを比較する

    JSON Diff で二回の structured output や tool arguments を比較し、回帰チェックする。

よくある質問

Function Calling と Structured Output の違いは?

Function Calling は tool_calls.arguments(どのツールを、何の引数で)を縛る。Structured Output は最終回答の JSON を縛る。どちらも JSON Schema だが層が違う。Agent ではよく併用する:tools が外部動作、Structured Output が最終の構造化結果。

JSON Mode だけで十分ですか?

不十分。JSON Mode(response_format.type=json_object)は合法 JSON オブジェクトだけを保証し、フィールドと型は保証しない。本番の複雑な形には Structured Output(json_schema + strict:true または Gemini responseSchema)を使う。

Strict Schema を付けても手動検証は必要ですか?

必要。API 層の制約は業務層の validate の代わりにならない。空 content、途中切断、エッジケースは残る。try/except の parse + JSON Schema validate が本番の標準。

JSON Schema はどの版を使うべきですか?

2026 年の主要 LLM API と MCP ツール契約は JSON Schema 2020-12。type、properties、required、enum、items、additionalProperties などをサポートする。

1 つの schema をツールと最終出力の両方に使えますか?

使える。同じオブジェクト定義を tools[].parameters と response_format.json_schema.schema の両方に置ける。単一の真実を保ち、二重契約のずれを防ぐ。

まとめ

AI に JSON Schema が必要なのは、モデル出力を「データっぽいもの」から「機械が検証できる契約」に変えるからだ。

JSON Schema は言語。Function Calling はツール引数に、Structured Output は最終回答に使う。

schema を一つ書き、二箇所に載せ、業務層で validate する。JSONNote でローカルに整形・検証・Diff。デモから本番への最短経路だ。

← ブログに戻る