OpenAI Python SDK による Pydantic 連携と構造化レスポンスの活用術

型安全な AI 出力の実現

大規模言語モデル(LLM)からのレスポンスを扱う際、従来の JSON 文字列を手動で解析する手法は、型エラーやフィールド欠落のリスクを伴います。OpenAI Python SDK が提供する構造化出力機能は、Pydantic モデルを直接利用することで、この課題を解決します。これにより、自然言語による出力を、検証済みの Python オブジェクトとして即座に利用可能になります。

構造化出力の仕組み

この機能の核心は、定義した Pydantic モデルを基に、SDK 内部で厳格な JSON Schema が自動生成される点にあります。生成されたスキーマには、余分なプロパティの排除(additionalProperties: false)や必須フィールドの強制などが含まれており、モデルが预期する構造を AI に厳密に指示します。

これにより、開発者はパーシングロジックを記述する代わりに、データ構造の定義のみで済むようになります。

実装手順:3 ステップで導入

1. データモデルの定義

まず、AI から取得したいデータ構造を Pydantic の BaseModel で定義します。ここでは、為替換算の処理ログを記録する例を示します。

from pydantic import BaseModel
from typing import List

class ProcessLog(BaseModel):
    description: str  # 処理段階の説明
    value: str        # 当該段階での数値

class AnalysisResult(BaseModel):
    history: List[ProcessLog]  # 処理履歴
    conclusion: str            # 最終結論

2. 構造化リクエストの送信

OpenAI クライアントの parse メソッドを使用し、response_format に定義したモデルを指定します。これにより、返答が自動的に検証・変換されます。

from openai import OpenAI

client = OpenAI()

response_obj = client.chat.completions.parse(
    model="gpt-4o-2024-08-06",
    messages=[
        {"role": "system", "content": "You are a financial assistant."},
        {"role": "user", "content": "Convert 100 USD to JPY assuming 1 USD = 150 JPY."},
    ],
    response_format=AnalysisResult,
)

3. 型安全なオブジェクトの利用

レスポンスオブジェクトの parsed 属性を通じて、定義したクラスのインスタンスに直接アクセスできます。辞書のネストを辿る必要はありません。

msg = response_obj.choices[0].message
if msg.parsed:
    for record in msg.parsed.history:
        print(f"工程:{record.description}")
        print(f"値:{record.value}")
    
    print(f"結論:{msg.parsed.conclusion}")
else:
    print(f"拒否理由:{msg.refusal}")

ツール呼び出しとの統合

構造化出力は、関数呼び出し(Function Calling)と組み合わせることでさらに効果を発揮します。AI が外部ツールを呼び出す際にも、パラメータの構造を厳密に定義することが可能です。

FunctionDefinition を使用してツールの仕様を明確にします。

class FunctionDefinition(BaseModel):
    name: str
    description: Optional[str] = None
    parameters: Optional[dict] = None
    strict: Optional[bool] = None

ツール連携の具体例

計算ツールを定義し、AI に適切な引数で呼び出させる例です。

def execute_formula(formula: str) -> float:
    """数式を評価して結果を返す"""
    return eval(formula)

tools = [
    {
        "type": "function",
        "function": FunctionDefinition(
            name="execute_formula",
            description="提供された数式を計算する",
            parameters={
                "type": "object",
                "properties": {
                    "formula": {
                        "type": "string",
                        "description": "計算対象の数式(例:'10*5+2')"
                    }
                },
                "required": ["formula"],
                "strict": True
            }
        )
    }
]

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "37 * 24 + 18 の結果は?"}],
    tools=tools,
    tool_choice="auto"
)

if response.choices[0].message.tool_calls:
    call = response.choices[0].message.tool_calls[0]
    if call.function.name == "execute_formula":
        import json
        args = json.loads(call.function.arguments)
        result = execute_formula(args["formula"])

運用上のベストプラクティス

厳密モードの活用

スキーマ定義において strict=True を設定することを強く推奨します。これにより、AI は定義された構造から逸脱した出力を生成できなくなり、データの不整合を防げます。SDK 内部では、これに応じて additionalProperties が自動的に制御されます。

エラー処理の徹底

厳密モードを使用していても、ネットワークエラーやモデルの制約により解析が失敗する可能性があります。message.parsedNone になるケースを想定し、message.refusal を確認するフォールバックロジックを実装してください。

モデルの選定

構造化出力機能は、すべてのモデルでサポートされているわけではありません。現時点では gpt-4ogpt-3.5-turbo-1106 以降のバージョンで利用可能です。コストと精度のバランスを考慮し、開発段階では安価なモデルを、本番環境では高精度なモデルを選定するのが効果的です。

タグ: openai-python pydantic structured-outputs function-calling

8月1日 20:06 投稿