人気のあるLLMプロバイダー(OpenAI、Gemini、Anthropic、Mistral、AWS Bedrock)における構造化出力の比較

わずかに異なるAPIには特別なアプローチが必要です。

目次

以下は、提供されたHugoページコンテンツの日本語への翻訳です。すべてのHugoショートコードと技術要素は正確に保持されており、日本語の文法、表記、文化に合った表現が使用されています。


以下は、構造化された出力(信頼性の高いJSONを取得)をサポートする、人気のあるLLMプロバイダーの比較、および最小限のPythonの例です。

colorised bars standing

すでに、OllamaでホストされているLLMから構造化された出力をリクエストする方法について確認しました。ここでは、他のプロバイダーから同じようにリクエストする方法を確認します。

TL;DR マトリクス

プロバイダー ネイティブな「JSONモード」 JSON Schemaの強制 一般的な設定 メモ
OpenAI はい はい(第一級) response_format={"type":"json_schema", ...} Responses APIまたはChat Completions経由で動作します。関数呼び出しも可能です。
Google Gemini はい はい(第一級) response_schema= + response_mime_type="application/json" スキーマを設定すると、厳密に検証されたJSONを返します。
Anthropic (Claude) 間接的 はい(Tool Use経由でJSONスキーマ) tools=[{input_schema=...}] + tool_choice モデルに「あなたのスキーマ定義されたツールを呼び出す」ことを強制します。
Mistral はい 部分的(JSONのみ;サーバーサイドのスキーマなし) response_format={"type":"json_object"} JSONを保証しますが、スキーマに対する検証はクライアントサイドで行います。
AWS Bedrock (プラットフォーム) モデルごとに異なる はい(Tool/Converseスキーマ経由) toolConfig.tools[].toolSpec.inputSchema BedrockのConverse APIは、ツール入力をJSONスキーマに対して検証します。

LLM構造化出力 - 一般的な情報

LLM構造化出力とは、大規模言語モデル(LLMs)が、自由形式のテキストではなく、事前に定義された特定の形式や構造に厳密に従って出力を生成する能力を指します。この構造化された出力は、JSON、XML、テーブル、テンプレートなどの形式で、データが機械読み取り可能、一貫性があり、ソフトウェアによる解析が容易になります。

構造化された出力は、伝統的なLLM出力(通常はオープンエンドな自然言語テキスト)と異なります。構造化された出力では、JSONオブジェクトの定義されたキーと値のタイプ、または出力の特定のクラス(例:複数選択の回答、感情クラス、データベース行の形式)などのスキーマや形式を強制します。このアプローチにより、信頼性が向上し、エラーや幻覚が減少し、データベース、API、ワークフローなどのシステムへの統合が簡略化されます。

LLMでの構造化出力の生成には、以下のような技術が用いられます:

  • モデルに目的の形式で出力を生成するよう、詳細なプロンプト指示を指定する。
  • PythonのPydanticなどの検証および解析ツールを使用して、出力がスキーマに一致することを保証する。
  • ときには、文法や有限状態機械に基づいたデコード制約を強制し、形式に従ったトークンレベルのコンプライアンスを保証する。

構造化LLM出力の利点には以下があります:

  • 機械読み取り可能で統合が容易。
  • 変動性やエラーの減少。
  • 一貫したデータ形式が必要なタスクにおいて、予測可能性と検証性の向上。

課題には、効果的なスキーマの設計、複雑なネストデータの処理、および自由形式テキスト生成と比較して、論理的思考能力の潜在的な制限が含まれます。

全体的に、構造化出力により、LLMが単なる人間読み取り可能なテキストではなく、正確でフォーマットされたデータが必要なアプリケーションでより役立ちます。

構造化出力のPythonの例

すべてのスニペットは、イベント情報をJSONとして抽出します:{title, date, location}。キーまたはモデルを必要に応じて変更してください。

1) OpenAI — JSON Schema(厳密)

from openai import OpenAI
import json

client = OpenAI()

schema = {
    "name": "Event",
    "schema": {
        "type": "object",
        "properties": {
            "title": {"type": "string"},
            "date":  {"type": "string", "description": "YYYY-MM-DD"},
            "location": {"type": "string"}
        },
        "required": ["title", "date", "location"],
        "additionalProperties": False
    },
    "strict": True
}

resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "user", "content": "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}
    ],
    response_format={"type": "json_schema", "json_schema": schema},
)

data = json.loads(resp.choices[0].message.content)
print(data)

OpenAIの構造化出力機能は、サーバーサイドでこのスキーマを強制します。


2) Google Gemini — response schema + JSON MIME

import google.generativeai as genai
from google.genai import types

# APIキーで構成
# genai.configure(api_key="your-api-key")

schema = types.Schema(
    type=types.Type.OBJECT,
    properties={
        "title": types.Schema(type=types.Type.STRING),
        "date": types.Schema(type=types.Type.STRING),
        "location": types.Schema(type=types.Type.STRING),
    },
    required=["title", "date", "location"],
    additional_properties=False,
)

resp = genai.generate_content(
    model="gemini-2.0-flash",
    contents="Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'",
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json",
        response_schema=schema,
    ),
)

print(resp.text)  # スキーマに従ったJSONが既に有効

Geminiは、response_schemaに従った厳密なJSONを返します。


3) Anthropic (Claude) — Tool Use with JSON schema

from anthropic import Anthropic
import json

client = Anthropic()

tool = {
    "name": "extract_event",
    "description": "イベントの詳細を返します。",
    "input_schema": {
        "type": "object",
        "properties": {
            "title": {"type": "string"},
            "date": {"type": "string"},
            "location": {"type": "string"}
        },
        "required": ["title", "date", "location"],
        "additionalProperties": False
    }
}

msg = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=256,
    tools=[tool],
    tool_choice={"type": "tool", "name": "extract_event"},  # スキーマを強制
    messages=[{"role": "user", "content":
        "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}],
)

# Claudeはツールを「呼び出す」; スキーマに一致する引数を取得
tool_use = next(b for b in msg.content if b.type == "tool_use")
print(json.dumps(tool_use.input, indent=2))

Claudeには汎用的な「JSONモード」のスイッチはありません。代わりに、input_schemaを使用したTool Useにより、検証済みでスキーマに一致した引数が得られます(使用を強制することも可能です)。


4) Mistral — JSON mode(クライアントサイド検証)

from mistralai import Mistral
import json

client = Mistral()

resp = client.chat.complete(
    model="mistral-large-latest",
    messages=[{"role":"user","content":
        "Return JSON with keys title, date (YYYY-MM-DD), location for: "
        "'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}],
    response_format={"type": "json_object"}  # 有効なJSONを保証
)

data = json.loads(resp.choices[0].message.content)
print(data)
# ヒント: `data`をローカルでPydantic/JSONスキーマに検証してください。

Mistralのjson_objectJSONの形状(正確なスキーマではありません)を強制します — クライアントサイドで検証してください。


5) AWS Bedrock — Converse API Tool schema(モデルに依存しない)

import boto3, json

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0"

tools = [{
    "toolSpec": {
        "name": "extract_event",
        "inputSchema": {
            "json": {
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "date": {"type": "string"},
                    "location": {"type": "string"}
                },
                "required": ["title","date","location"],
                "additionalProperties": False
            }
        }
    }
}]

resp = bedrock.converse(
    modelId=model_id,
    toolConfig={"tools": tools},
    toolChoice={"tool": {"name": "extract_event"}},  # スキーマを強制
    messages=[{"role":"user","content":[{"text":
        "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}]}],
)

# toolUseの内容を取得
tool_use = next(
    c["toolUse"] for c in resp["output"]["message"]["content"] if "toolUse" in c
)
print(json.dumps(tool_use["input"], indent=2))

Bedrockはツール入力をあなたのJSONスキーマに対して検証し、多くのホストされているモデル(例:Claude)はConverse経由でサポートしています。


実用的なガイドラインと検証

  • サーバーサイドの保証が最も強いために:OpenAIの構造化出力またはGeminiのresponse schemaを使用してください。
  • Claude/Bedrockを使用している場合:JSONスキーマを持つToolを定義し、その使用を強制してください。ツールの引数をあなたの型付きオブジェクトとして読み取ってください。
  • Mistralを使用している場合json_objectを有効にして、ローカルで検証してください(例:Pydanticを使用)。

検証パターン(すべてのプロバイダーに適用)

from pydantic import BaseModel, ValidationError

class Event(BaseModel):
    title: str
    date: str
    location: str

try:
    event = Event.model_validate(data)  # `data`は任意のプロバイダーから取得
except ValidationError as e:
    # ハンドリング / リトライ / モデルに修正を依頼する
    print(e)

有用なリンク