はじめに:AI出力のパースエラー問題を根本解決する
「JSONで出力して」とプロンプトに書いても、AIが気を利かせて「以下が結果のJSONです:
json ... 」と出力してしまい、json.loads() で構文エラーを起こすトラブルは日常茶飯事でした。
近年のAPI(OpenAI o3-mini / GPT-4o、Google Gemini 3.7 Flash)では、モデルのトークンサンプリング段階で特定のJSONスキーマ以外の出力を物理的に禁止する「Structured Outputs」が導入され、パースエラーの発生確率を0%にすることが可能です。
1. Pydantic + Instructor による統一的な構造化抽出
import os
from pydantic import BaseModel, Field
from typing import List, Optional
import instructor
from openai import OpenAI
# 1. 抽出したいデータ構造をPydanticモデルで定義
class ProductSpec(BaseModel):
brand: str = Field(description="メーカー名・ブランド名")
model_name: str = Field(description="型番または製品名")
jan_code: Optional[str] = Field(None, description="13桁または8桁のJANコード")
features: List[str] = Field(description="主な特徴やスペックのリスト")
estimated_price_yen: Optional[int] = Field(None, description="推定参考価格(円)")
# 2. InstructorでOpenAIクライアントをラップ
client = instructor.from_openai(OpenAI(api_key=os.environ["OPENAI_API_KEY"]))
# 3. o3-miniでの構造化推論
text = "シマノの24ヴァンフォード C3000HG。JANは4969363045300。定価34,000円。超軽量マグナムライトローター搭載。"
extracted: ProductSpec = client.chat.completions.create(
model="o3-mini",
response_model=ProductSpec,
messages=[{"role": "user", "content": text}]
)
print(f"Brand: {extracted.brand}")
print(f"JAN: {extracted.jan_code}")
print(f"Features: {extracted.features}")
バリデーションエラーが発生した場合も、Instructorが自動でモデルにエラー内容をフィードバックして自己修復するため、後続のデータベース格納処理が100%安全に実行できます。