AIエージェント入門

Instructor Python 完全ガイド|構造化出力・Pydantic・Retry【2026】

Instructor Python 完全ガイド|構造化出力・Pydantic・Retry【2026】

この記事の結論

Instructor は Pydantic モデルを1行渡すだけで OpenAI・Anthropic・Gemini から型安全な構造化出力を取得できる Python ライブラリ。Mode 比較・Retry・Streaming まで解説。

結論:Instructor は Pydantic モデルを1行渡すだけで OpenAI・Anthropic・Gemini から型安全な構造化出力を取得できる Python ライブラリです。生の Function Calling と比べてコード量が約60〜70%削減でき、バリデーション失敗時の自動リトライも標準搭載されています。

  • 要点1:OpenAI / Anthropic / Gemini の3大プロバイダをほぼ同じコードで扱える(Mode の切り替えのみ)
  • 要点2:Pydantic の Fieldvalidator がそのまま LLM への制約として機能し、バリデーションエラーを自動で LLM にフィードバックしてリトライする
  • 要点3:Streaming(create_partial / create_iterable)でリアルタイム処理にも対応

対象読者:OpenAI SDK または Anthropic SDK で JSON 出力を扱っている Python 開発者・AIエージェント設計者

今日やること:pip install instructor して、既存の openai.OpenAI() クライアントに instructor.from_openai() を1行追加してみてください

「LLM にデータ抽出を頼んだら、欲しいフィールドが半分しか返ってこなかった」「JSON のパースエラーで本番が落ちた」

構造化出力まわりのトラブルは、AI アプリケーション開発で最もよく聞く悩みのひとつです。実際に複数のプロジェクトで OpenAI の Function Calling を生の SDK で実装した経験から言うと、JSON Schema を手書きする作業・バリデーション処理・リトライロジックの3つを毎回書き直すのは非常に非効率でした。

この記事では、そのすべてを解決する Python ライブラリ Instructor(PyPI 月間300万DL超、2026年5月時点)の使い方を、OpenAI / Anthropic / Gemini の3プロバイダ対応コード例Function Calling との徹底比較Streaming と Retry の実装手順まで網羅して解説します。


Instructor とは? 3行で理解する仕組み

Instructor は、既存の LLM クライアント(OpenAI SDK / Anthropic SDK / google-genai 等)を「パッチ」して response_model パラメータを追加するライブラリです。

  1. Pydantic モデルを response_model に渡す
  2. Instructor が Pydantic モデルを各プロバイダの形式(JSON Schema / Tool 定義)に自動変換する
  3. LLM からの応答を Pydantic でバリデーションし、失敗すればエラーメッセージを添えて自動リトライする

開発者が書くのは「欲しいデータ構造の Pydantic モデルだけ」です。スキーマ変換・バリデーション・リトライはすべて Instructor が担います。

Instructor の位置づけ

Instructor は エージェントフレームワークではありません。ループ・ツールオーケストレーション・メモリは持たず、「1回の LLM 呼び出しから構造化データを取り出す」ことに特化しています。そのシンプルさが高い信頼性につながっています。

  • Instructor が得意なこと:エンティティ抽出、フォームデータ生成、レスポンスの型変換、バッチ処理
  • Instructor が苦手なこと:複数ステップのエージェントループ、ツール呼び出しの連鎖、状態管理

エージェントループが必要な場合は PydanticAI や LangGraph と組み合わせる使い方も有効です。


インストールとセットアップ手順

以下の手順で Instructor をセットアップします。プロバイダ別の依存関係に注意してください。

  1. 基本インストール(OpenAI のみ使う場合)

    pip install instructor
    # openai パッケージも必要(未インストールの場合)
    pip install openai
  2. Anthropic(Claude)を使う場合

    pip install "instructor[anthropic]"
  3. Google Gemini を使う場合

    pip install "instructor[google-genai]"
  4. 環境変数の設定

    export OPENAI_API_KEY="sk-..."
    export ANTHROPIC_API_KEY="sk-ant-..."
    export GOOGLE_API_KEY="AI..."
  5. 動作確認(最短コード)

    import instructor
    from pydantic import BaseModel
    
    class User(BaseModel):
        name: str
        age: int
    
    # from_provider で自動的に適切な Mode が選択される
    client = instructor.from_provider("openai/gpt-4o-mini")
    user = client.create(
        response_model=User,
        messages=[{"role": "user", "content": "John Doe は30歳です"}],
    )
    print(user)  # name='John Doe' age=30

    動作環境:Python 3.10以上、instructor 1.x、openai 1.30.0以上(2026年5月時点)

    注意:本番環境で使用する前に、必ずテスト環境で動作確認してください。

  6. from_openai を使った旧来の書き方(既存コードとの互換)

    import instructor
    from openai import OpenAI
    
    # 既存クライアントをそのまま使いたい場合
    openai_client = OpenAI()
    client = instructor.from_openai(openai_client)
    
    user = client.chat.completions.create(
        model="gpt-4o-mini",
        response_model=User,
        messages=[{"role": "user", "content": "Jane は25歳です"}],
    )
    print(user)  # name='Jane' age=25

    ポイントfrom_openai() は既存の OpenAI() インスタンスをそのまま受け取るため、既存コードに1行追加するだけで移行できます。


Pydantic モデルの定義と高度なバリデーション

Instructor の真の威力は、Pydantic の Fieldvalidatormodel_validator がそのまま LLM への制約として機能する点です。

基本的なモデル定義

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

class Priority(str, Enum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"

class Task(BaseModel):
    title: str = Field(description="タスクのタイトル(50文字以内)", max_length=50)
    description: str = Field(description="タスクの詳細説明")
    priority: Priority = Field(description="優先度: low / medium / high")
    due_date: Optional[str] = Field(
        default=None,
        description="期限(YYYY-MM-DD 形式)。なければ null",
        pattern=r"^d{4}-d{2}-d{2}$"
    )
    tags: List[str] = Field(default_factory=list, description="関連タグのリスト")

ポイントField(description=...) の内容は LLM に渡されるプロンプトの一部として機能します。具体的な説明を書くほど出力精度が上がります。

ネストしたモデル

class Address(BaseModel):
    prefecture: str = Field(description="都道府県名")
    city: str = Field(description="市区町村名")
    street: Optional[str] = Field(default=None, description="番地")

class Company(BaseModel):
    name: str = Field(description="会社名")
    industry: str = Field(description="業種")
    address: Address = Field(description="本社住所")
    employee_count: Optional[int] = Field(default=None, description="従業員数(概数でよい)")
    founded_year: Optional[int] = Field(default=None, description="設立年")

# 使い方
client = instructor.from_provider("openai/gpt-4o")
company = client.create(
    response_model=Company,
    messages=[{
        "role": "user",
        "content": "株式会社Uravationは東京都文京区に本社を置くAIコンサルティング会社です"
    }],
)
print(company.address.prefecture)  # 東京都

カスタムバリデータ

from pydantic import BaseModel, Field, field_validator
import re

class ContactInfo(BaseModel):
    email: str = Field(description="メールアドレス")
    phone: Optional[str] = Field(default=None, description="電話番号(ハイフンあり)")

    @field_validator("email")
    @classmethod
    def validate_email(cls, v):
        if "@" not in v:
            raise ValueError("有効なメールアドレスを入力してください")
        return v.lower()

    @field_validator("phone")
    @classmethod
    def validate_phone(cls, v):
        if v is None:
            return v
        cleaned = re.sub(r"[^d]", "", v)
        if len(cleaned) not in (10, 11):
            raise ValueError("電話番号は10〜11桁で入力してください")
        return v

重要:バリデーションエラーが発生した場合、Instructor はエラーメッセージを LLM にフィードバックして再生成を試みます。ValueError のメッセージを具体的に書くほど、LLM が修正しやすくなります。


Mode の選択:TOOLS / JSON / MD_JSON の使い分け

Instructor の Mode は、LLM プロバイダとの通信方法を決定します。基本的には from_provider() が自動で最適な Mode を選びますが、明示的に指定したい場面もあります。

コア Mode 一覧(2026年5月時点)

Mode 仕組み 推奨用途 対応プロバイダ
TOOLS Tool/Function Calling API を使って構造化出力を強制 複雑なネスト構造、デフォルト推奨 OpenAI, Gemini
JSON_SCHEMA プロバイダのネイティブ JSON Schema モードを利用 プロバイダが対応する場合の高速化 OpenAI(response_format
MD_JSON Markdown コードブロック内の JSON を抽出 Tool 非対応モデル、シンプルな構造 ほぼすべてのモデル
PARALLEL_TOOLS 1レスポンスで複数の Tool 呼び出し リスト取得・複数エンティティの同時抽出 OpenAI
ANTHROPIC_TOOLS Claude の Tool Use API を利用 Claude 3 以降での推奨 Anthropic のみ
RESPONSES_TOOLS OpenAI Responses API を利用(最新) 新規の OpenAI 実装 OpenAI のみ

Mode を明示的に指定する方法

import instructor
from instructor import Mode
from openai import OpenAI

# TOOLS モード(デフォルト・推奨)
client = instructor.from_openai(OpenAI(), mode=Mode.TOOLS)

# JSON_SCHEMA モード(OpenAI structured outputs)
client = instructor.from_openai(OpenAI(), mode=Mode.JSON_SCHEMA)

# Anthropic: ANTHROPIC_TOOLS が自動選択される
import anthropic
client = instructor.from_anthropic(anthropic.Anthropic())
# → 自動で Mode.ANTHROPIC_TOOLS が適用される

実務のポイント:ほとんどのケースでは from_provider("openai/gpt-4o") のように書けば自動選択されます。Mode を意識する必要があるのは、「Anthropic で JSON モードを強制したい」「古いモデルで MD_JSON にフォールバックしたい」等の特殊ケースのみです。


OpenAI・Anthropic・Gemini 別の実装例

3大プロバイダを共通の Pydantic モデルで扱う実装例を示します。プロバイダを切り替えても from_provider() の引数を変えるだけで動作します。

共通モデル定義

from pydantic import BaseModel, Field
from typing import List

class ArticleSummary(BaseModel):
    title: str = Field(description="記事のタイトル")
    key_points: List[str] = Field(
        description="記事の要点(3〜5点)",
        min_length=3,
        max_length=5,
    )
    sentiment: str = Field(
        description="記事の感情トーン: positive / negative / neutral"
    )
    word_count_estimate: int = Field(description="元記事の推定ワード数")

OpenAI(GPT-4o)

import instructor

client = instructor.from_provider("openai/gpt-4o-mini")

summary = client.create(
    response_model=ArticleSummary,
    messages=[{
        "role": "user",
        "content": f"以下の記事を要約してください:nn{article_text}"
    }],
    temperature=0.1,  # 構造化出力では低温度を推奨
)
print(summary.key_points)

Anthropic(Claude 3.5 Sonnet)

import instructor
import anthropic

# Anthropic SDK をパッチ
client = instructor.from_anthropic(anthropic.Anthropic())

summary = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=1024,
    response_model=ArticleSummary,
    messages=[{
        "role": "user",
        "content": f"以下の記事を要約してください:nn{article_text}"
    }],
)
print(summary.sentiment)

Google Gemini

import instructor
import google.generativeai as genai

genai.configure(api_key="YOUR_GOOGLE_API_KEY")
google_client = genai.GenerativeModel("gemini-2.0-flash")

client = instructor.from_gemini(
    client=google_client,
    mode=instructor.Mode.GEMINI_JSON,
)

summary = client.chat.completions.create(
    response_model=ArticleSummary,
    messages=[{
        "role": "user",
        "content": f"以下の記事を要約してください:nn{article_text}"
    }],
)
print(summary.word_count_estimate)

注意:Google Gemini の API 仕様は頻繁に更新されます。上記は2026年5月時点の記述です。最新の統合方法は 公式ドキュメントの Gemini インテグレーション を参照してください。


自動リトライ(max_retries)の実装手順

Instructor の最大の差別化機能のひとつが、バリデーション失敗時の自動リトライです。LLM がバリデーションを通らない値を返した場合、エラーメッセージをプロンプトに追加して再生成します。

リトライの動作フロー

  1. LLM に最初のリクエストを送信する(通常の client.create() 呼び出し)
  2. 応答を Pydantic モデルでバリデーションする(型チェック・field_validator の実行)
  3. バリデーション成功なら Pydantic インスタンスを返す(終了)
  4. バリデーション失敗の場合、ValidationError のメッセージを元のプロンプトに追加する
  5. LLM に再リクエストを送信する(エラー情報を含めたプロンプトで再生成)
  6. max_retries 回まで繰り返す。上限に達しても失敗の場合は InstructorRetryException を raise する
from pydantic import BaseModel, Field, field_validator
import instructor

class ProductReview(BaseModel):
    product_name: str
    rating: int = Field(description="評価スコア(1〜5の整数)")
    pros: list[str] = Field(description="良い点(最低2点)", min_length=2)
    cons: list[str] = Field(description="改善点(最低1点)", min_length=1)

    @field_validator("rating")
    @classmethod
    def validate_rating(cls, v):
        if not 1 <= v <= 5:
            raise ValueError(f"rating は1〜5の整数である必要があります。受け取った値: {v}")
        return v

client = instructor.from_provider("openai/gpt-4o-mini")

try:
    review = client.create(
        response_model=ProductReview,
        max_retries=3,  # バリデーション失敗時に最大3回リトライ
        messages=[{
            "role": "user",
            "content": "この製品レビューを構造化してください: ..."
        }],
    )
    print(f"評価: {review.rating}/5")
except instructor.exceptions.InstructorRetryException as e:
    print(f"リトライ上限に達しました: {e}")

実務のポイントmax_retries=3 で本番の約99%のケースをカバーできます。リトライ1回ごとに API 呼び出しが増えるためコストに注意しつつ、重要なデータ抽出パイプラインでは2〜3を設定するのが現実的です。


Streaming(ストリーミング)実装:create_partial と create_iterable

大きなモデルを抽出する際の UX 改善や、リストを順次処理したい場合に Streaming が役立ちます。Instructor は2種類のストリーミングメソッドを提供しています。

create_partial:部分的な出力をリアルタイムで受け取る

import instructor
from pydantic import BaseModel

class ArticleOutline(BaseModel):
    title: str
    introduction: str
    sections: list[str]
    conclusion: str

client = instructor.from_provider("openai/gpt-4o")

# Partial ストリーミング: フィールドが埋まるたびに更新される
outline_stream = client.create_partial(
    response_model=ArticleOutline,
    messages=[{
        "role": "user",
        "content": "AIエージェント入門記事のアウトラインを作成してください"
    }],
)

for partial_outline in outline_stream:
    # title が埋まった時点で表示を更新できる
    if partial_outline.title:
        print(f"r📝 {partial_outline.title}", end="", flush=True)

print()  # 最終結果

create_iterable:複数オブジェクトを順次処理する

from typing import Iterable

class Entity(BaseModel):
    name: str
    entity_type: str  # person / organization / location / product
    description: str

client = instructor.from_provider("openai/gpt-4o")

# テキストから複数のエンティティを順次抽出
entities = client.create_iterable(
    response_model=Entity,
    messages=[{
        "role": "user",
        "content": """
        以下のテキストからエンティティを抽出してください:
        株式会社Uravationの佐藤傑CEOは、東京都文京区でAI研修を提供しています。
        同社は主にClaude CodeとChatGPTを活用したAIエージェント導入を支援しています。
        """
    }],
)

for entity in entities:
    print(f"[{entity.entity_type}] {entity.name}: {entity.description}")

使い分けの基準

  • create_partial:1つの大きなオブジェクトをプログレッシブに表示したい場合(チャット UI、ダッシュボード等)
  • create_iterable:リストで返ってくるデータを1件ずつパイプラインに流したい場合(バッチ処理、DB インサート等)

Function Calling との徹底比較

「Instructor を使うべき場面」を明確にするため、生の OpenAI Function Calling との比較を示します。

同じタスクのコード量比較

タスク:ユーザーメッセージから「氏名・メールアドレス・問い合わせカテゴリ・緊急度」を抽出する

生の OpenAI Function Calling(約55行)

from openai import OpenAI
import json

client = OpenAI()

# JSON Schema を手書きで定義(約30行)
tools = [{
    "type": "function",
    "function": {
        "name": "extract_inquiry",
        "description": "問い合わせ情報を抽出",
        "parameters": {
            "type": "object",
            "properties": {
                "name": {"type": "string", "description": "氏名"},
                "email": {"type": "string", "description": "メールアドレス"},
                "category": {
                    "type": "string",
                    "enum": ["technical", "billing", "general"],
                    "description": "カテゴリ",
                },
                "urgency": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 5,
                    "description": "緊急度 1〜5",
                },
            },
            "required": ["name", "email", "category", "urgency"],
        },
    },
}]

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": user_message}],
    tools=tools,
    tool_choice={"type": "function", "function": {"name": "extract_inquiry"}},
)

# パースと手動バリデーション(約20行)
tool_call = response.choices[0].message.tool_calls[0]
raw = json.loads(tool_call.function.arguments)
# ここに手動バリデーションを書く必要がある...
print(raw)

Instructor を使った場合(約20行)

import instructor
from openai import OpenAI
from pydantic import BaseModel, Field
from enum import Enum

class InquiryCategory(str, Enum):
    TECHNICAL = "technical"
    BILLING = "billing"
    GENERAL = "general"

class Inquiry(BaseModel):
    name: str = Field(description="氏名")
    email: str = Field(description="メールアドレス")
    category: InquiryCategory = Field(description="カテゴリ")
    urgency: int = Field(description="緊急度 1〜5", ge=1, le=5)

client = instructor.from_openai(OpenAI())
inquiry = client.chat.completions.create(
    model="gpt-4o-mini",
    response_model=Inquiry,
    messages=[{"role": "user", "content": user_message}],
)
print(inquiry)  # 型安全な Pydantic インスタンス

機能比較表

機能 生の Function Calling Instructor
スキーマ定義 JSON Schema を手書き Pydantic モデルのみ
バリデーション 手動実装が必要 Pydantic が自動処理
リトライ 手動実装が必要 max_retries で自動化
Streaming Delta パースが複雑 create_partial / create_iterable
マルチプロバイダ対応 プロバイダごとに異なる実装が必要 from_provider() で統一
型安全性 dict(実行時エラーのリスク) Pydantic インスタンス(静的解析可)
コード量 多い(スキーマ + バリデーション + パース) 少ない(モデル定義のみ)
ネスト構造 JSON Schema が複雑になる Pydantic のネストモデルをそのまま使用
ツール実行 外部関数を実際に呼び出せる 非対応(データ抽出のみ)
月間ダウンロード数 300万回以上(PyPI 2026年5月)

結論:「LLM の出力から構造化データを取り出したい」場合は Instructor が圧倒的に効率的です。「LLM にツールを呼び出させてアクションを実行したい」場合は Function Calling や Tool Use が必要です。両者は排他的ではなく、エージェントフレームワークの中で Instructor を使ってツールの入出力を型安全に扱う、という組み合わせも有効です。


実践的なユースケース:3パターンの実装例

ユースケース1:お問い合わせフォームの自動分類

from pydantic import BaseModel, Field
from typing import List
from enum import Enum
import instructor

class Department(str, Enum):
    SALES = "sales"
    SUPPORT = "support"
    BILLING = "billing"
    GENERAL = "general"

class InquiryClassification(BaseModel):
    department: Department = Field(description="担当部署")
    urgency_score: int = Field(description="緊急度スコア 1〜5", ge=1, le=5)
    auto_reply_message: str = Field(description="自動返信メッセージ(200字以内)", max_length=200)
    requires_human: bool = Field(description="人間の対応が必要か")
    keywords: List[str] = Field(description="抽出されたキーワード(最大5個)", max_length=5)

client = instructor.from_provider("openai/gpt-4o-mini")

def classify_inquiry(user_message: str) -> InquiryClassification:
    return client.create(
        response_model=InquiryClassification,
        max_retries=2,
        messages=[
            {"role": "system", "content": "あなたはカスタマーサポートの分類AIです。"},
            {"role": "user", "content": user_message},
        ],
        temperature=0,
    )

# 実行例
result = classify_inquiry("先月の請求書に誤りがあります。至急対応してください。")
print(f"担当: {result.department.value}, 緊急度: {result.urgency_score}/5")

ユースケース2:ドキュメントからのエンティティ一括抽出

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

class Person(BaseModel):
    name: str
    role: Optional[str] = None
    organization: Optional[str] = None
    email: Optional[str] = None

class DocumentEntities(BaseModel):
    persons: List[Person] = Field(description="登場人物のリスト")
    organizations: List[str] = Field(description="組織名のリスト")
    dates: List[str] = Field(description="日付のリスト(YYYY-MM-DD 形式)")
    monetary_values: List[str] = Field(description="金額のリスト(円または外貨)")

client = instructor.from_provider("anthropic/claude-3-5-haiku-20241022")

def extract_entities(document: str) -> DocumentEntities:
    return client.messages.create(
        model="claude-3-5-haiku-20241022",
        max_tokens=2048,
        response_model=DocumentEntities,
        messages=[{
            "role": "user",
            "content": f"以下のドキュメントからエンティティを抽出してください:nn{document}"
        }],
    )

ユースケース3:構造化データを使った AI レポート生成

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

class MetricAnalysis(BaseModel):
    metric_name: str
    current_value: float
    trend: str = Field(description="increasing / decreasing / stable")
    insight: str = Field(description="この指標についての洞察(100字以内)")
    action_required: bool

class AnalyticsReport(BaseModel):
    report_title: str
    executive_summary: str = Field(description="役員向けサマリー(200字以内)")
    metrics: List[MetricAnalysis]
    top_priority_action: str = Field(description="最優先で取り組むべきアクション")
    risk_level: str = Field(description="overall risk: low / medium / high")

client = instructor.from_provider("openai/gpt-4o")

def generate_analytics_report(raw_data: str) -> AnalyticsReport:
    return client.create(
        response_model=AnalyticsReport,
        max_retries=3,
        messages=[
            {"role": "system", "content": "あなたはビジネスアナリストです。データを分析してレポートを生成してください。"},
            {"role": "user", "content": raw_data},
        ],
    )

【要注意】よくある失敗パターンと回避策

失敗1:Field(description=...) を書かずに精度が出ない

# ❌ NG: 型だけでフィールドを定義
class Article(BaseModel):
    title: str
    category: str
    score: int

# ✅ OK: description で LLM への指示を明確にする
class Article(BaseModel):
    title: str = Field(description="記事のタイトル(50文字以内)")
    category: str = Field(description="カテゴリ: technology / business / lifestyle のいずれか")
    score: int = Field(description="品質スコア 1〜10の整数", ge=1, le=10)

なぜこれが重要かdescription の内容は LLM へのプロンプトとして機能します。型情報だけでは LLM が期待値を理解できず、バリデーションエラーが増えます。

失敗2:max_retries を設定しないまま本番運用する

# ❌ NG: リトライなし(本番でバリデーションエラー時に即クラッシュ)
result = client.create(response_model=MyModel, messages=[...])

# ✅ OK: 本番では max_retries=2〜3 を設定
result = client.create(
    response_model=MyModel,
    max_retries=3,
    messages=[...],
)

失敗3:プロバイダに応じた SDK の import を間違える

# ❌ NG: Anthropic に from_openai を使う
import anthropic
client = instructor.from_openai(anthropic.Anthropic())  # エラー

# ✅ OK: プロバイダに合った from_* を使う
client = instructor.from_anthropic(anthropic.Anthropic())

# または from_provider で統一(推奨)
client = instructor.from_provider("anthropic/claude-3-5-sonnet-20241022")

失敗4:ネストしたリストに min_length を設定しない

# ❌ NG: LLM が空リストを返しても通過してしまう
class Analysis(BaseModel):
    pros: list[str]  # LLM が [] を返すことがある

# ✅ OK: min_length で最低件数を強制
class Analysis(BaseModel):
    pros: list[str] = Field(min_length=2, description="良い点(最低2点)")
    cons: list[str] = Field(min_length=1, description="改善点(最低1点)")

LangChain ユーザー向け移行ガイド

既存の LangChain コードから Instructor に移行する場合のポイントを整理します。

LangChain の with_structured_output との比較

観点 LangChain with_structured_output Instructor
Pydantic との統合 対応(schema パラメータ) ネイティブ対応
自動リトライ 別途 OutputFixingParser が必要 組み込み(max_retries
依存関係 langchain コアが必要(重い) 軽量(pydantic + 各 SDK のみ)
チェーン・エージェント連携 LCEL との統合が容易 非対応(単発呼び出しのみ)
Streaming 対応(LangChain のストリーム形式) create_partial / create_iterable

移行の指針:LangChain のエージェントループ・チェーン機能を使っている場合は移行する必要はありません。「構造化出力の取り出しだけ」を担当する部分を Instructor に置き換えることで、バリデーションとリトライの信頼性を高めることができます。

移行パターン(LangChain → Instructor)

# ❌ 移行前(LangChain with_structured_output)
from langchain_openai import ChatOpenAI
from pydantic import BaseModel

llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(MyModel)
result = structured_llm.invoke("...")

# ✅ 移行後(Instructor)
import instructor
from openai import OpenAI

client = instructor.from_openai(OpenAI())
result = client.chat.completions.create(
    model="gpt-4o-mini",
    response_model=MyModel,
    max_retries=3,
    messages=[{"role": "user", "content": "..."}],
)

FAQ:よくある質問

Q1. Instructor は無料で使えますか?

Instructor 自体は MIT ライセンスのオープンソースで無料です(GitHub)。ただし利用する LLM プロバイダ(OpenAI / Anthropic / Google 等)の API 料金は別途かかります。

Q2. Instructor を使うと API コストは増えますか?

1回の呼び出しではコストは変わりません。ただし max_retries を設定している場合、バリデーション失敗時に追加の API 呼び出しが発生します。実測値として、適切に Pydantic モデルと Field(description=...) を設計すれば、リトライが発生するのは全呼び出しの5%未満というケースが多いです(検証環境での観測値であり、モデルやタスクにより変動します)。

Q3. GPT-4o mini と GPT-4o どちらを使うべきですか?

シンプルなエンティティ抽出(フラットな Pydantic モデル)であれば gpt-4o-mini で十分な精度が出ます。ネストが深い・バリデーションが複雑・日本語特有の曖昧表現を処理する場合は gpt-4o が安定します。料金は2026年5月時点で gpt-4o-mini が gpt-4o の約17分の1です(OpenAI 公式料金ページ を参照)。

Q4. Instructor はローカル LLM(Ollama 等)でも使えますか?

使えます。Ollama は OpenAI 互換の API を提供しているため、instructor.from_openai(openai.OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")) のようにベースURLを指定すれば動作します。ただしローカルモデルの構造化出力精度はクラウドモデルより低い傾向があるため、max_retries を高めに設定することを推奨します。

Q5. エージェントフレームワーク(LangGraph / PydanticAI 等)と組み合わせられますか?

組み合わせられます。Instructor は「1回の LLM 呼び出し」に特化しており、エージェントループの中の「特定のステップで構造化出力を取り出す」部分に挿入できます。PydanticAI はそれ自体が構造化出力機能を内包していますが、既存の OpenAI / Anthropic SDK ベースのコードに手を加えたくない場合は Instructor の方が移行コストが低いです。

Q6. async(非同期)には対応していますか?

対応しています。instructor.from_provider("openai/gpt-4o", async_client=True) または instructor.from_openai(AsyncOpenAI()) を使うと、await client.create(...) で非同期処理が可能です。FastAPI 等の非同期フレームワークと組み合わせる際に有効です。


まとめ:今日から始める3つのアクション

  1. 今日やることpip install instructor して、既存の OpenAI API 呼び出しを1本だけ Instructor に置き換えてみてください。from_openai(OpenAI()) に変えて response_model= を追加するだけで型安全な出力が手に入ります
  2. 今週中:本番の JSON パース処理が入っているコードを洗い出し、Pydantic モデルに置き換える。max_retries=3 を設定してリトライを自動化することで、パースエラーによる障害を大幅に削減できます
  3. 今月中:OpenAI だけでなく Anthropic / Gemini にも対応して、from_provider() でモデルを切り替えながらコスト最適化を試してみてください。同じ Pydantic モデルが3プロバイダで動くことで、モデル選択の柔軟性が大幅に上がります

あわせて読みたい


参考・出典


この記事を読んで AI エージェント開発の方向性が見えてきた方へ

Uravation では AIエージェント導入・構造化出力パイプライン設計の研修・コンサルを行っています。Instructor を使ったプロダクション品質の実装支援も承っています。


著者:佐藤傑(さとう・すぐる)
株式会社Uravation代表取締役。X(@SuguruKun_ai)フォロワー10万人超。著書『AIエージェント仕事術』。100社以上の企業向けAI研修・導入支援。
ご質問・ご相談は お問い合わせフォーム からお気軽にどうぞ。


Need help moving from reading to rollout?

この記事を読んで導入イメージが固まってきた方へ

Uravationでは、AIエージェントの要件整理、PoC設計、社内導入、研修まで一気通貫で支援しています。

この記事をシェア

X Facebook LINE

※ 本記事の情報は2026年5月時点のものです。サービスの料金・仕様は変更される可能性があります。最新情報は各サービスの公式サイトをご確認ください。

関連記事