LLMからのデータ抽出:JSONの手動パースはやめて、Instructorを使おう

Artificial Intelligence tutorial - IT technology blog
Artificial Intelligence tutorial - IT technology blog

「Invalid JSON」という名の悩み

OpenAIのAPIを呼び出してデータを取得した際、こんな経験はありませんか?AIにJSONを返すよう指示したのに、「こちらが結果です:」といった余計な導入文が付いてきたり、あるいは末尾の括弧が欠けていたり。その結果、json.loads()が即座にJSONDecodeErrorを吐き出してしまいます。

以前は、Regex(正規表現)で結果を「整形」したり、AIに余計なことを言わないよう懇願する長いSystem Promptを書いたりしていました。しかし、Instructorプロダクション環境に導入して6ヶ月、そんな苦労とは完全におさらばしました。10〜15%あったパースエラー率は、ほぼ0%まで低下しました。

Instructorは単なるライブラリではありません。それは新しい考え方です。Pydanticを使用してSchema(スキーマ)を定義し、LLMにそれを厳守させます。もしAIが間違った回答をしたら?Instructorは自動的にエラーログをAIに突き返し、「間違っているから修正して!」と指示してくれるのです。

30秒で完了するインストール

Instructorは、OpenAIやAnthropicの純正クライアントを包み込む(ラッパー)インテリジェントな層として機能します。Pydantic v2と一緒にインストールするだけです:

pip install -U instructor openai pydantic

このライブラリはtool_use(function calling)メカニズムを強力にサポートしており、データ抽出をかつてないほど安定させます。

実践的な実装:Schema定義からクリーンなデータ取得まで

Instructorでのワークフローは、Schema의定義、クライアントのラップ、API呼び出しの3ステップに集約されます。以下の雑多なチャットテキストを処理する例を見てみましょう。

1. データの「型」を定義する

言葉で説明する代わりに、純粋なPythonコードを使用します。これにより、システム全体に完全なType Hint(型ヒント)が提供されます。

from pydantic import BaseModel, Field
from typing import List, Optional

class UserInfo(BaseModel):
    name: str = Field(..., description="フルネーム")
    age: int = Field(..., description="年齢")
    email: Optional[str] = Field(None, description="メールアドレス(任意)")
    skills: List[str] = Field(default_factory=list, description="スキル一覧")

2. クライアントの拡張と抽出

以前のpatch()関数の代わりに、最新バージョンのInstructorではfrom_openai()の使用が推奨されています。このアプローチは透明性が高く、デバッグも容易です。

import instructor
from openai import OpenAI

# インテリジェントなクライアントの初期化
client = instructor.from_openai(OpenAI(api_key="YOUR_KEY"))

text_input = "こんにちは、私はグエン・ヴァン・A、28歳です。PythonとJSが使えます。メール:[email protected]"

# オブジェクトに直接データを抽出
user = client.chat.completions.create(
    model="gpt-4o-mini",
    response_model=UserInfo,
    messages=[{"role": "user", "content": text_input}],
    max_retries=3 # 最大3回までAIに自動修正を試行させる
)

print(f"{user.name}({user.age}歳) 習得スキル:{', '.join(user.skills)}")

この時点で、userはPydanticのインスタンスになっています。Dictionary(辞書型)を使う時のようにキー名を打ち間違える心配もなく、IntelliSenseを活用して属性を安全に呼び出すことができます。

なぜ自動リトライ(Auto-retry)機能が重要なのか?

実際の運用では、LLMがデータ型を間違えることがたまにあります(例:年齢を「二十八」という文字列で返すなど)。Instructorがあれば心配無用です。Pydanticがバリデーションエラーを検知すると、ライブラリは詳細なエラー内容をAIに自動送信します。AIはそのエラーを見て、次のリトライで出力を自己修正します。

プロダクション導入時の実践的な知見

長期間の運用を経て、パフォーマンスとコストを最適化するための3つの鉄則を見出しました:

  • 小型モデルを優先する: 単純な抽出タスクにGPT-4o は不要です。gpt-4o-miniclaude-3-haikuをInstructorと組み合わせれば、同等の精度を維持しつつ、コストを最大20倍削減できます。
  • Validatorは最強の武器: field_validatorを使用してビジネスロジックを制御しましょう。例えば、抽出された年齢が負の数だった場合、即座にAIにやり直しをさせることができます。
  • リトライ回数を監視する: 特定のタスクで頻繁に3回目のリトライまで達する場合、それはPromptやSchemaが複雑すぎるというサインです。

Validatorを使って不正データを阻止する方法を見てみましょう:

from pydantic import field_validator

class UserInfo(BaseModel):
    name: str
    age: int

    @field_validator('age')
    @classmethod
    def validate_age(cls, v):
        if v < 0 or v > 120:
            raise ValueError("年齢は0から120の間でなければなりません")
        return v

LLMがage: -1を返すと、Instructorは「年齢に負の数は使えません。確認してください」とAIに伝えます。これは従来のJSONパース手法では不可能な「自己修復(self-healing)」能力です。

Instructorへの移行は、手作業から自動ラインへのアップグレードのようなものです。AIの後始末に追われるのではなく、アプリケーション本来のロジックに集中できるようになります。もしプロジェクトでLLMの出力形式が不安定な問題に直面しているなら、今すぐ導入を検討してみてください。

Share: