LLM機能を本番投入したあと、不正な形式のJSONや欠落フィールドで処理が落ちた経験があるなら、PydanticAIは検討する価値があります。Pydanticを開発したチームによるPythonエージェントフレームワークで、型安全で検証済みの出力をエージェント実装の中心に置きます。この記事では、PydanticAIの基本、型安全性が必要な理由、実装で使う主要概念、そしてLangGraphなどのPythonエージェントフレームワークとの違いを、実装目線で整理します。
PydanticAIとは
PydanticAIは、Python向けのオープンソースかつプロバイダー非依存のエージェントフレームワークです。Pydantic ValidationとPydantic Logfireを開発しているチームによってメンテナンスされており、Pydanticの検証基盤と、FastAPIに近い開発体験をエージェント構築に持ち込みます。
PydanticAIでは、次の3つを明示的に定義します。
- エージェントが何を行うか
- 呼び出せるツールは何か
- 出力はどのPydanticモデルに一致すべきか
フレームワークはモデル呼び出しを処理し、返された出力をPydanticモデルで検証します。モデルがスキーマに合わない値を返した場合は、検証エラーをもとに再試行します。
インストールは次のどちらかです。
pip install pydantic-ai
uv add pydantic-ai
PydanticAIはベータ版を経て、2026年6月23日に安定版v2.0.0をリリースしました。v2では、ツール、フック、指示、モデル設定を再利用可能なユニットとして構成する、ハーネスファーストの設計が採用されています。
なぜ型安全性がエージェントに必要なのか
LLMの出力は非決定論的です。同じ入力でも、次のような揺れが起きます。
- JSONとして返すはずが自然文で包まれる
- 必須フィールドが欠落する
-
intを期待しているのに文字列が返る - 配列を期待しているのに単一オブジェクトが返る
チャットUIだけなら許容できる場合もあります。しかし、LLMの出力を次のような処理に接続すると問題になります。
- DBへの書き込み
- REST API呼び出し
- 請求金額の計算
- チケット分類
- ワークフローの分岐
典型的には、次のような防御コードが増えます。
try:
data = json.loads(raw_text)
except json.JSONDecodeError:
cleaned = raw_text.strip().removeprefix("```
json").removesuffix("
```")
data = json.loads(cleaned)
if "priority" not in data:
data["priority"] = 3
PydanticAIでは、この責務をフレームワーク側に寄せられます。Pydanticモデルを出力契約として定義し、エージェントに渡すだけです。
基本実装
1. エージェントを作成する
Agentが主要なエントリポイントです。モデル識別子と指示を渡して作成します。
from pydantic_ai import Agent
agent = Agent(
'anthropic:claude-sonnet-4-6',
instructions='Be concise, reply with one sentence.',
)
result = agent.run_sync('Where does "hello world" come from?')
print(result.output)
プロバイダーを切り替える場合は、基本的にモデル文字列を変更します。
agent = Agent('openai:gpt-4o')
このため、特定プロバイダーに強く依存しない実装にしやすくなります。
型付き出力を使う
PydanticAIの中心的な使い方は、output_typeにPydanticモデルを渡すことです。
from pydantic import BaseModel
from pydantic_ai import Agent
class SupportTicket(BaseModel):
category: str
priority: int
summary: str
agent = Agent(
'openai:gpt-4o',
output_type=SupportTicket,
)
result = agent.run_sync('My payment failed three times today.')
ticket = result.output
print(ticket.category)
print(ticket.priority)
print(ticket.summary)
この時点で、result.outputは単なる文字列ではなく、検証済みのSupportTicketです。
たとえばモデルが次のような不正な出力を返した場合、
{
"category": "payment",
"priority": "high"
}
priorityがintではなく、summaryも欠落しています。PydanticAIは検証エラーを使ってモデルに再試行を促します。下流のコードでは、曖昧なJSON文字列をパースするのではなく、検証済みオブジェクトを扱えます。
ツールを定義する
ツールは、エージェントが外部システムにアクセスするための関数です。
例:
- DBを検索する
- REST APIを呼び出す
- 料金を計算する
- 社内ナレッジを取得する
@agent.toolデコレーターで登録します。
from pydantic_ai import Agent, RunContext
agent = Agent('openai:gpt-4o', deps_type=str)
@agent.tool
async def get_user_balance(ctx: RunContext[str], account_id: str) -> float:
"""Return the current balance for an account."""
return await lookup_balance(ctx.deps, account_id)
PydanticAIは、関数の型ヒントとdocstringからツールスキーマを作成します。モデルがツールを呼び出すとき、関数実行前に引数が検証されます。
つまり、次のような不正な呼び出しをビジネスロジックに到達させにくくなります。
{
"account_id": 12345
}
account_idはstrとして定義されているため、PydanticAI側で検証されます。
依存関係を注入する
実際のエージェントでは、ツール内で次のような依存関係を使います。
- DBクライアント
- HTTPクライアント
- 現在のユーザー
- APIキー
- 設定オブジェクト
PydanticAIでは、deps_typeとRunContextを使って依存関係を渡します。
from dataclasses import dataclass
from pydantic_ai import Agent, RunContext
@dataclass
class AppDeps:
api_base_url: str
api_key: str
agent = Agent(
'openai:gpt-4o',
deps_type=AppDeps,
)
@agent.tool
async def fetch_order(ctx: RunContext[AppDeps], order_id: str) -> dict:
"""Fetch an order by ID."""
return await call_order_api(
base_url=ctx.deps.api_base_url,
api_key=ctx.deps.api_key,
order_id=order_id,
)
deps = AppDeps(
api_base_url='https://api.example.com',
api_key='secret',
)
result = agent.run_sync('Check order A-123', deps=deps)
print(result.output)
この形にしておくと、テスト時に依存関係をフェイクに差し替えやすくなります。
test_deps = AppDeps(
api_base_url='http://localhost:3000',
api_key='test-key',
)
モデル非依存のプロバイダーとストリーミング
PydanticAIは、複数のモデルプロバイダーをサポートしています。
- OpenAI
- Anthropic
- Gemini
- DeepSeek
- Grok
- Cohere
- Mistral
- Perplexity
- Azure AI Foundry
- Amazon Bedrock
- 自己ホスト型モデル
多くの場合、切り替えはモデル文字列の変更で済みます。
agent = Agent('openai:gpt-4o')
agent = Agent('anthropic:claude-sonnet-4-6')
また、構造化出力のストリーミングにも対応しており、データ到着時に検証を適用しながら部分結果を扱えます。型保証を完全に捨てずに、UIへ段階的に結果を表示したい場合に便利です。
Pydantic Logfireも同じチームが開発しているため、トレーシング、デバッグ、実行ごとのコスト追跡といったオブザーバビリティも意識されています。
PydanticAIと他のPythonエージェントフレームワークの比較
単一の「最良の」フレームワークはありません。どの設計を重視するかで選択が変わります。
| フレームワーク | 主な強み | 向いているケース |
|---|---|---|
| PydanticAI | 型安全で検証済みの出力とツール引数 | 本番環境で信頼できる型付きデータフローが必要な場合 |
| LangGraph | 明示的なステートフルグラフと制御フロー | 長時間実行される分岐付きの多段階ワークフロー |
| Google ADK | Googleエコシステムでのマルチエージェントオーケストレーション | GeminiやVertex AIとの深い統合 |
| OpenAI Agents SDK | OpenAIとの緊密な統合とハンドオフ | OpenAI中心のスタックで素早く構築したい場合 |
PydanticAIの強みは、検証レイヤーです。エージェントの出力を他システムに渡す場合、Pydanticモデルに一致するという保証がランタイムエラーの削減につながります。
一方、LangGraphはステートマシンや複雑な分岐フローを明示的に制御したい場合に向いています。OpenAI Agents SDKは、OpenAIに寄せた構成でエージェントハンドオフやMCPサーバーサポートを使いたい場合に自然な選択肢です。
組み合わせることもできます。たとえば、大きなワークフローは別のオーケストレーション層で管理し、各ステップの構造化出力をPydanticAIで検証する構成です。
PydanticAIを使うべきケース
PydanticAIは、次のようなケースに向いています。
- エージェント出力をチャット表示だけでなく、コードに渡す
- 出力形式の正しさが重要
- Pydanticモデルでデータ契約を明示したい
- 型チェッカーやIDEにエージェントの入出力を理解させたい
- すでにPydanticやFastAPIを使っている
- プロバイダーを差し替えられる設計にしたい
- Logfireによるトレーシングや可観測性に関心がある
逆に、複雑な分岐、長期間実行される状態管理、細かいステートマシン制御が中心であれば、LangGraphのようなグラフベースのフレームワークも検討してください。
エージェントの背後にあるAPIをテスト・モックする
PydanticAIは、モデル出力やツール引数を検証します。しかし、ツールが呼び出す外部APIそのものが正しい応答を返すかどうかまでは保証しません。
エージェントの実行では、次のようなAPIが関わります。
- LLMプロバイダーAPI
- 社内REST API
- 外部SaaS API
- 検索API
- 決済API
- 認証API
これらのAPIが不安定だったり、レスポンス形式が変わったりすると、PydanticAIの手前またはツール内部で問題が起きます。
Apidogは、PydanticAIの代替ではありません。エージェントが依存するAPIをテスト・モックするためのAPIプラットフォームです。
実装フローとしては、次のように使えます。
1. ツールAPIをモックする
開発中は、実APIではなく決定論的なモックAPIに向けます。
deps = AppDeps(
api_base_url='https://mock.example.com',
api_key='test-key',
)
これにより、次のメリットがあります。
- テストごとにLLMや外部APIのコストを消費しない
- レート制限を避けられる
- 同じレスポンスで再現性のあるテストができる
- 異常系レスポンスも固定して検証できる
2. レスポンス形式をAPIレイヤーでアサートする
ツール関数に組み込む前に、APIアサーションでレスポンス形式を確認します。
例として、ツールが次の構造を期待しているとします。
class BalanceResponse(BaseModel):
account_id: str
balance: float
currency: str
API側でも、次の項目を検証しておくと安全です。
-
account_idが存在する -
balanceが数値である -
currencyが存在する - HTTPステータスが期待通りである
エージェント実行の奥深くで失敗するより、APIレイヤーで早く検出できます。
3. 環境ごとにキーとURLを分ける
ローカル、ステージング、CI、本番で、接続先やAPIキーは変わります。Apidogの環境管理を使うと、コードを変更せずにターゲットを切り替えやすくなります。
deps = AppDeps(
api_base_url=os.environ["API_BASE_URL"],
api_key=os.environ["API_KEY"],
)
環境ごとに値を管理しておけば、CIではモック、ステージングでは検証用API、本番では本番APIという構成にできます。
4. LLMエンドポイントを直接検証する
HTTP経由でLLMプロバイダーを呼び出す場合、エージェントに組み込む前にApidogでChatGPT APIをテストできます。
確認すべき項目は次の通りです。
- 認証ヘッダーが正しい
- ストリーミング形式が期待通り
- ツール呼び出しのJSON構造が正しい
- エラー時のレスポンス形式を処理できる
Apidogはエージェントを構築したりオーケストレーションしたりするものではありません。PydanticAIでエージェントを構築し、Apidogでその背後のAPIサーフェスをテスト・モックする、という役割分担です。
試す場合は、Apidogをダウンロードして、まずエージェントのツールが呼び出すエンドポイントを1つモックしてみてください。
よくある質問
PydanticAIは無料かつオープンソースですか?
はい。PydanticAIはオープンソースで、PyPIからインストールできます。
pip install pydantic-ai
uv add pydantic-ai
ただし、PydanticAIはLLMプロバイダーのAPIを呼び出すため、使用するプロバイダーの料金は別途発生します。開発中のコストを抑えるには、毎回ライブモデルを呼び出す代わりに、API応答をモックする構成が有効です。
PydanticAIはどのモデルと連携しますか?
プロバイダーに依存しません。ドキュメントでは、OpenAI、Anthropic、Gemini、DeepSeek、Grok、Cohere、Mistral、Perplexity、Azure AI Foundry、Amazon Bedrock、自己ホスト型モデルなどが挙げられています。
モデルはAgentコンストラクタに文字列で渡します。
agent = Agent('anthropic:claude-sonnet-4-6')
agent = Agent('openai:gpt-4o')
多くの場合、切り替えはこの1行の変更で済みます。
PydanticAIはLangChainやLangGraphとどう異なりますか?
PydanticAIは、Pydanticモデルに基づく検証済みの構造化出力と、検証済みのツール引数を重視します。
LangGraphは、多段階の分岐ワークフローを明示的なステートフルグラフとして扱うことを重視します。
出力形式の保証と型付きデータフローを優先するならPydanticAIが適しています。複雑な状態遷移や分岐制御を細かく扱うなら、グラフベースのフレームワークが適しています。
PydanticAIを使うにはPydanticを知っている必要がありますか?
知っていると有利ですが、基本はシンプルです。BaseModelを継承したクラスでデータ形式を定義し、それを出力型として渡します。
from pydantic import BaseModel
class Result(BaseModel):
title: str
score: float
PythonでAPIテストをした経験がある場合や、FastAPIを使ったことがある場合は、PydanticAIの考え方も理解しやすいはずです。
結論
PydanticAIは、エージェント開発に型安全な出力契約を持ち込みます。モデル出力とツール呼び出しが宣言した型に一致することを検証できるため、本番環境で起きやすいJSONパースエラー、欠落フィールド、型不一致を減らせます。
大規模なグラフオーケストレーションよりも、信頼性の高い構造化出力と型付きデータフローを重視する場合、PydanticAIは有力な選択肢です。
どのフレームワークを選んでも、エージェントの背後にあるAPIのテストは必要です。ApidogでLLMやツールエンドポイントをモックし、レスポンス形式をアサートし、環境ごとにキーを管理すれば、検証済みのAPI基盤の上でエージェントを実行できます。

Top comments (0)