スキーマ駆動抽出(スキーマクドウチュウシュツ)
PydanticやZod等の型定義ライブラリでデータスキーマを事前定義し、LLMにそのスキーマに準拠した構造化データを返させる開発手法。instructorやoutlinesなどのライブラリがスキーマ定義からプロンプト生成・バリデーション・リトライを自動化し、型安全なLLMデータ抽出を実現する。
スキーマ駆動抽出とは
スキーマ駆動抽出とは、Pydantic(Python)やZod(TypeScript)などの型定義ライブラリでデータの構造・型・制約を事前に定義し、LLMにそのスキーマに厳密に準拠した構造化データを出力させる開発手法である。従来のLLMデータ抽出では出力JSONの形式が保証されず、パース失敗やフィールド欠損が頻発していたが、2024-2025年にinstructor・outlines・guidance等のライブラリが成熟したことで、コンパイル時型チェックからランタイムバリデーションまで一貫した型安全性が実現可能になった。
主要ライブラリ詳細比較
| ライブラリ | 開発元 | 言語 | アプローチ | 対応LLM | GitHubスター |
|---|---|---|---|---|---|
| instructor | Jason Liu | Python | パッチ方式 | OpenAI/Anthropic/Google/Mistral/Ollama/LiteLLM | 9,200+ |
| outlines | .txt (dottxt) | Python | 文法制約デコーディング | HF Transformers/vLLM/llama.cpp/ExLlamaV2 | 8,500+ |
| guidance | Microsoft Research | Python | テンプレート + 制約 | OpenAI/HF Transformers/llama.cpp | 19,000+ |
| Marvin | Prefect | Python | デコレータ方式 | OpenAI | 5,000+ |
| zod-to-json-schema | 個人開発 | TypeScript | スキーマ変換 | 全JSON Schema対応LLM | 1,200+ |
| TypeChat | Microsoft | TypeScript | 型推論 + バリデーション | OpenAI | 8,300+ |
instructor の実装パターン
instructorはPythonのデファクトスタンダードで、Pydanticモデルを定義するだけでLLMから型安全なデータを取得できる:
- パッチ方式: OpenAI/Anthropic等のクライアントを
instructor.from_openai()でラップ - 自動リトライ: バリデーション失敗時にエラーメッセージをLLMにフィードバックし自動修正(最大3回)
- ストリーミング:
Partial[Model]型でパーシャルオブジェクトの逐次受信 - バリデーション: Pydanticの
field_validatorでビジネスロジックレベルの制約を定義 - マルチモーダル: 画像入力に対応(OpenAI Vision/Anthropic Vision/Google Vision)
- キャッシュ:
instructor.cacheで同一入力の結果をキャッシュし、APIコスト削減 - バッチ処理: Iterableレスポンスで複数レコードの一括抽出
- コスト: ライブラリ自体はOSS無料、LLM APIコストのみ
outlines の文法制約デコーディング
outlinesはローカルLLMでスキーマ準拠出力を実現する:
- CFG(文脈自由文法): JSON Schemaを文脈自由文法に変換し、トークン生成時に文法違反を防止
- 正規表現制約: 出力を正規表現パターンに完全に制約(例: メールアドレス形式のみ許可)
- FSM(有限状態機械): 状態遷移に基づく効率的な制約付きデコーディング
- 対応ランタイム: HF Transformers/vLLM/llama.cpp/ExLlamaV2/MLX
- 速度: 制約なしの生成と比較してオーバーヘッドは5-15%程度
- 型サポート: Pydantic BaseModel/dataclass/TypedDictに対応
Pydantic × instructor 実践パターン
スキーマ駆動抽出の典型的なワークフロー:
- Step 1: Pydanticモデルで抽出スキーマを定義(フィールド名・型・説明・制約)
- Step 2:
instructor.from_openai()でクライアントをパッチ - Step 3:
client.chat.completions.create(response_model=MyModel)で型安全な抽出を実行 - Step 4: バリデーション失敗時は自動リトライ(エラーメッセージがプロンプトに追加される)
- Step 5: 成功した結果はPydanticオブジェクトとして利用(
.model_dump()でJSON化)
ベストプラクティスとして、各フィールドのField(description=...)に具体的な抽出指示を書くことが精度向上の鍵。例えばprice: float = Field(description="税込価格を数値で抽出。通貨記号は除去。見つからない場合は0.0")のように指定する。
TypeScript(Zod)での実装
TypeScript環境ではZodスキーマを中心としたエコシステムが確立:
- zod-to-json-schema: ZodスキーマをJSON Schemaに変換し、OpenAI Structured Outputsに渡す
- AI SDK (Vercel):
generateObject()でZodスキーマ指定の構造化出力を取得 - TypeChat: Microsoft製。TypeScriptの型定義から直接抽出スキーマを生成
- LangChain.js:
StructuredOutputParser.fromZodSchema()でZod統合
設計上の注意点
- スキーマの粒度: フィールド数が多すぎると抽出精度が低下。20フィールド以下が推奨
- オプショナルフィールド:
Optional[str] = Noneで未発見時のnull許容を明示 - 列挙型の活用: カテゴリ分類には
Literal["A", "B", "C"]で候補を制約 - ネストの深さ: 3階層以内が推奨。深いネストはフラット化を検討
- 配列サイズ:
Field(min_length=1, max_length=10)で配列要素数を制約 - バリデーション:
@field_validatorでビジネスルール(日付範囲・金額上限等)を定義
よくある質問(FAQ)
Q1: instructorとoutlinesの使い分けは? A: クラウドLLM(OpenAI/Anthropic/Google)を使う場合はinstructorが最適。ローカルLLM(Llama 3.1/Mistral/Qwen)でスキーマ準拠出力が必要な場合はoutlinesを選択する。併用も可能で、instructorからoutlines対応バックエンドを呼び出せる。
Q2: バリデーション失敗時のリトライ回数は何回が適切ですか? A: instructorのデフォルトは3回で、実運用でも3回が最適。GPT-4oクラスのモデルでは1回目で95%以上成功し、3回以内に99%以上が解決する。3回失敗した場合はスキーマ設計の見直しが必要。
Q3: スキーマが頻繁に変わる場合はどう対応しますか?
A: Pydanticのモデル継承を活用し、基底スキーマに共通フィールド、派生スキーマに文書種別固有のフィールドを定義する。スキーマのバージョニングにはPydantic v2のmodel_configでスキーマバージョンをメタデータとして管理する。
まとめ
- スキーマ駆動抽出はPydantic/Zodで型定義→LLMが型安全データを返す手法
- instructorがPythonデファクト、outlines がローカルLLM向けの選択肢
- 自動リトライ・バリデーション・ストリーミングが実用性の鍵
- フィールド説明の具体性が抽出精度を大きく左右する
- TypeScript環境ではZod + Vercel AI SDK / TypeChat が主流