PydanticAI ビジュアルガイド

Lesson CH04-L01

output_type

Pydanticモデルを output_type に渡し、型付き結果を得る。

読了目安
10 min
Colab目安
16 min
合計
26 min
前提
Advanced Tools

関連: 公式ドキュメント

一行サマリ

Agent(model, output_type=YourModel) で、Agent の 最終応答そのものを Pydantic Model に固定 できる。LLM 応答が型に合わなければ自動で retry が走り、result.output には 型付き・検証済み のオブジェクトだけが入る。

ヒーロー: テキスト応答 vs 構造化応答

通常 Agent は文字列を返しますが、output_type を指定すると Pydantic Model のインスタンス が返ります。後続の Python ロジックがそれを直接使えるようになり、「LLM 応答を文字列パースする」苦労が消えます。

図を読み込み中…
図1. output_type ありなしの違い

概念: output_type の仕組み

output_type=YourModel を Agent に渡すと、PydanticAI は内部で次のことを行います。

  1. YourModel の JSON Schema を生成 し、LLM への prompt に同梱
  2. LLM 応答を JSON としてパース して YourModel に変換
  3. 検証エラーがあれば 自動で retry (デフォルト 1 回)
  4. 成功したら result.output型付きインスタンス を入れる

開発者が書くコードは「Model クラスを定義 + Agent に渡す」の 2 行だけ。あとは PydanticAI が schema 生成・パース・検証・retry を肩代わりします。

コード: 4 つのパターン

パターン 1: シンプルな BaseModel

from pydantic import BaseModel
from pydantic_ai import Agent
 
class WeatherReport(BaseModel):
    city: str
    condition: str
    temp_c: float
 
agent = Agent(
    'google-gla:gemini-3-flash-preview',
    output_type=WeatherReport,
    instructions='ユーザーの問いから天気情報を構造化して返してください。',
)
 
result = agent.run_sync('東京は今日晴れで 22 度でした。レポートにして。')
print(type(result.output).__name__)  # WeatherReport
print(result.output.city)             # 'Tokyo' or '東京'
print(result.output.temp_c)           # 22.0

ポイント:

  • output_type=WeatherReport を渡すだけで result.outputWeatherReport 型に固定
  • フィールドを 数値で取り出して 後続処理に渡せる (もう正規表現パースは不要)
  • LLM が型に合わない応答を返したら自動で retry が走る

パターン 2: ネストした BaseModel

from pydantic import BaseModel
from datetime import date
 
class Customer(BaseModel):
    name: str
    age: int
 
class Booking(BaseModel):
    customer: Customer
    booking_date: date
    party_size: int
 
agent_b = Agent(
    'google-gla:gemini-3-flash-preview',
    output_type=Booking,
    instructions='予約情報を構造化してください。今日は 2026-05-11 です。',
)
 
result = agent_b.run_sync('明日の夜、田中 (35歳) で 4 名予約お願いします')
print(result.output.customer.name)   # 田中
print(result.output.customer.age)    # 35
print(result.output.booking_date)    # 2026-05-12
print(result.output.party_size)      # 4

ネストは 2〜3 段 くらいまでなら LLM が安定して組み立てられます。それ以上深いと組立失敗が増えるので、フラット化を検討。

パターン 3: List[BaseModel] で複数件返す

class TodoItem(BaseModel):
    title: str
    priority: int  # 1〜5
 
agent_t = Agent(
    'google-gla:gemini-3-flash-preview',
    output_type=list[TodoItem],
    instructions='ユーザーの文章から TODO リストを抽出してください。priority は 1 (低) 〜 5 (高)。',
)
 
result = agent_t.run_sync('明日までに資料レビューと、来週末までに発表資料準備、緊急で来客対応')
for item in result.output:
    print(f'[{item.priority}] {item.title}')

output_type=list[TodoItem] のように list[...] を直接渡せます。dict[str, T] も可。

パターン 4: 検証エラーは自動 retry

Annotated[..., Field(...)] を使えば、LLM の組立ミスを自動で直させられます (Ch3-L02 と同じ仕組みが output 側でも効きます)。

from typing import Annotated
from pydantic import BaseModel, Field
 
class Score(BaseModel):
    student: str
    points: Annotated[int, Field(ge=0, le=100, description='0〜100 の点数')]
 
agent_s = Agent(
    'google-gla:gemini-3-flash-preview',
    output_type=Score,
    instructions='学生名と点数を抽出してください。',
)
 
# わざと範囲外を提案する文を投げる
result = agent_s.run_sync('佐藤さんは 110 点でした (120 点満点中)')
print(result.output.points)  # LLM は 100 に丸めるか、別の解釈で範囲内に収めて返す
図を読み込み中…
図2. output_type の処理フロー

いつ output_type を使うべきか

状況output_type
ユーザーへの自然文応答だけ欲しい (チャット)不要 (str で OK)
後続の Python 関数に渡す情報を抽出したい必要
データを DB に保存したい必要
LLM 応答を JSON として API レスポンスに乗せたい必要
複数の選択肢から 1 つ選ばせたい必要 (Literal または Union = Ch4-L03)

output_type は「Agent と Python コードの境界面」を型で固める仕掛け と覚えてください。

まとめ

  • output_type=YourModel で Agent の最終応答を Pydantic Model インスタンス に固定
  • ネストや list/dict もそのまま OK。Annotated[..., Field(...)] で範囲制約も付けられる
  • 検証エラーは 自動 retry。Tool の Pydantic バリデーション (Ch3-L02) と同じ仕組み
  • Field(description=...) を書くと LLM の組立精度が上がる

次レッスンでは、構造化応答を Streaming で逐次受け取るパターンを扱います。

Colab で実際に動かす

本レッスンの内容を Google Colab 上で実行できるノートブックを用意しています。下のボタンから自分のColab環境に開けます (要 Google アカウント / GOOGLE_API_KEY)。

Open in Colab

notebooks/ch04/01-output-type.ipynb