Pydantic AI 実装ガイド
このガイドでは、MixSeek-Coreで使用されているPydantic AIの主要な概念と実装パターンを解説します。
目次
Pydantic AIとは
Pydantic AIは、型安全なエージェント開発を実現するPythonフレームワークです。
主な特徴:
Pydanticの型システムによる型安全性
Agent Delegationによる柔軟なエージェント連携
Usage Limitsによるリソース管理
Message Historyの永続化と復元
MixSeek-Coreでの役割:
Leader AgentとMember Agentの実装基盤
エージェント間通信の型安全な実装
リソース使用量の追跡と制限
Agent作成
基本的なAgent作成
from pydantic_ai import Agent
# 最もシンプルなAgent
agent = Agent(
model="gemini-2.5-flash-lite",
system_prompt="あなたは研究アシスタントです。"
)
# 実行
result = await agent.run("Pythonの型ヒントについて教えて")
print(result.output)
依存性注入を使ったAgent作成
from pydantic import BaseModel
from pydantic_ai import Agent, RunContext
class TeamDependencies(BaseModel):
team_id: str
submissions: list[dict] = []
# 依存性注入型を指定したAgent
agent = Agent(
model="gemini-2.5-flash-lite",
deps_type=TeamDependencies,
system_prompt="あなたはチームリーダーです。"
)
# 依存性を渡して実行
deps = TeamDependencies(team_id="team-001")
result = await agent.run("タスクを分析して", deps=deps)
参照実装: src/mixseek/agents/leader/agent.py:36
Agent Delegation
Agent Delegationは、Leader AgentがToolを通じて必要なMember Agentを動的に選択・実行するパターンです。
なぜAgent Delegationが必要か
従来の並列実行の問題点:
すべてのAgentを毎回実行するため、リソースが無駄
タスクに不要なAgentも実行される
コストと実行時間が増大
Agent Delegationの利点:
LLMがタスクを分析し、必要なAgentのみ選択
リソース効率の向上
柔軟なタスク実行
Agent Delegationの仕組み
User Prompt
↓
Leader Agent(LLMが判断)
↓
必要なToolのみ選択・実行
├─→ Tool: plain_agent_research
├─→ Tool: web_search_agent_latest_news
└─→ Tool: code_exec_agent_calculation
↓
Member Agentが実行され、結果を返す
参照実装: src/mixseek/agents/leader/tools.py:23
Tool / Toolset
ToolはPydantic AIにおけるエージェント間通信の仕組みです。各Member AgentはToolとして登録され、Leader Agentから呼び出されます。
Toolの基本構造
from pydantic_ai import Agent, RunContext
leader_agent = Agent("gemini-2.5-flash-lite")
@leader_agent.tool
async def research_tool(ctx: RunContext, task: str) -> str:
"""リサーチタスクを実行するツール
Args:
ctx: RunContext(依存性とUsage情報を含む)
task: 実行するタスクの説明
Returns:
リサーチ結果のテキスト
"""
# Member Agentを実行
result = await member_agent.run(task)
return result.output
Tool動的登録パターン(MixSeek-Core)
MixSeek-CoreではTOML設定から動的にToolを生成・登録します。
def register_member_tools(
leader_agent: Agent[TeamDependencies, str],
team_config: TeamConfig,
member_agents: dict[str, Agent]
) -> None:
"""TOML設定からMember Agent ToolをLeader Agentに登録"""
for member_config in team_config.members:
tool_name = member_config.get_tool_name()
member_agent = member_agents[member_config.agent_name]
# Toolクロージャー生成
def make_tool_func(mc: MemberAgentConfig, ma: Agent):
async def tool_func(ctx: RunContext[TeamDependencies], task: str) -> str:
# Member Agent実行(Usage統合)
result = await ma.run(
task,
deps=ctx.deps,
usage=ctx.usage # 重要: Leader AgentのUsageに統合
)
# 結果を記録
ctx.deps.submissions.append({
"agent_name": mc.agent_name,
"content": result.output,
"usage": result.usage()
})
return str(result.output)
# Tool関数のメタデータ設定
tool_func.__name__ = tool_name
tool_func.__doc__ = mc.tool_description
return tool_func
# Tool登録
tool = make_tool_func(member_config, member_agent)
leader_agent.tool(tool)
重要なポイント:
usage=ctx.usageで Leader Agent の Usage に統合Tool関数の
__name__と__doc__を設定(LLMがToolを選択する際の情報)クロージャーを使って設定情報をキャプチャ
参照実装: src/mixseek/agents/leader/tools.py:23
Run Usage
Run UsageはPydantic AIのリソース使用量測定データです。各Agent実行後に取得できます。
Run Usageの取得
# Agent実行
result = await agent.run("タスクを実行")
# Run Usage取得
usage = result.usage()
print(f"Input tokens: {usage.total_tokens()}")
print(f"Output tokens: {usage.details['output_tokens']}")
print(f"Requests: {usage.requests}")
Member Agent実行での統合
async def tool_func(ctx: RunContext[TeamDependencies], task: str) -> str:
"""Member AgentをToolとして実行"""
# Member Agent実行(Leader AgentのUsageに統合)
result = await member_agent.run(
task,
deps=ctx.deps,
usage=ctx.usage # ★ これでLeader AgentのUsageに統合される
)
# MemberSubmissionに記録
submission = MemberSubmission(
agent_name="research-agent",
agent_type="plain",
content=result.output,
usage=result.usage(), # Run Usageを記録
timestamp=datetime.now(UTC)
)
ctx.deps.submissions.append(submission)
return str(result.output)
記録される情報:
input_tokens: 入力トークン数output_tokens: 出力トークン数requests: リクエスト数total_tokens(): 総トークン数
参照実装: src/mixseek/agents/leader/tools.py:74
依存性注入(Dependencies)
依存性注入は、Agent実行時にチーム固有のデータを渡す仕組みです。
依存性型の定義
from pydantic import BaseModel
class TeamDependencies(BaseModel):
"""チーム固有の依存性データ"""
team_id: str
submissions: list[MemberSubmission] = []
context: dict[str, Any] = {}
Agent作成時に依存性型を指定
leader_agent = Agent(
model="gemini-2.5-flash-lite",
deps_type=TeamDependencies, # ★ 依存性型を指定
system_prompt="..."
)
Tool内で依存性にアクセス
@leader_agent.tool
async def research_tool(ctx: RunContext[TeamDependencies], task: str) -> str:
# 依存性からチームIDを取得
team_id = ctx.deps.team_id
# Member Agent実行
result = await member_agent.run(task, deps=ctx.deps)
# 依存性にSubmissionを追加
ctx.deps.submissions.append(
MemberSubmission(agent_name="research", content=result.output)
)
return result.output
Agent実行時に依存性を渡す
# 依存性インスタンス作成
deps = TeamDependencies(team_id="team-001")
# Agent実行
result = await leader_agent.run(
"市場調査を実施して",
deps=deps
)
# 依存性に記録されたSubmissionsを取得
for submission in deps.submissions:
print(f"{submission.agent_name}: {submission.content}")
依存性注入の利点:
Agent間でデータを共有
型安全なデータアクセス
テストでのモック化が容易
参照実装: src/mixseek/agents/leader/dependencies.py
Message History
Message HistoryはPydantic AI形式のメッセージ履歴です。Leader AgentとMember Agent間のやり取り、ツール実行結果などを記録します。
Message Historyの構造
from pydantic_ai import ModelMessagesTypeAdapter
# Message HistoryはPydantic AIの内部形式
# - ユーザーメッセージ
# - アシスタントメッセージ
# - ツール実行結果
# - システムメッセージ
Message HistoryのDuckDB永続化
import duckdb
from pydantic_ai import ModelMessagesTypeAdapter
# Message HistoryをJSON形式でDuckDBに保存
conn = duckdb.connect("mixseek.db")
# テーブル作成(JSON型を使用)
conn.execute("""
CREATE TABLE IF NOT EXISTS round_states (
team_id VARCHAR,
round_number INTEGER,
message_history JSON, -- ★ ネイティブJSON型
PRIMARY KEY (team_id, round_number)
)
""")
# Message Historyを保存
message_history_json = ModelMessagesTypeAdapter.dump_json(result.all_messages())
conn.execute(
"INSERT INTO round_states VALUES (?, ?, ?)",
[team_id, round_number, message_history_json]
)
Message Historyの復元
# DuckDBから読み込み
row = conn.execute(
"SELECT message_history FROM round_states WHERE team_id = ? AND round_number = ?",
[team_id, round_number]
).fetchone()
# JSONからPydantic AI Message構造に復元
message_history = ModelMessagesTypeAdapter.validate_json(row[0])
# 復元したMessage Historyを使ってAgent実行
result = await agent.run(
"次のタスク",
message_history=message_history
)
Message History永続化の目的:
長期実行時のメモリ負荷削減
ラウンド間での会話履歴の引き継ぎ
デバッグとトラブルシューティング
MVCCによる並列書き込み対応:
DuckDBはMVCC(Multi-Version Concurrency Control)を採用
複数チームが同時にMessage Historyを書き込んでもロック競合を回避
参照:
実装例
完全な実装例: Plain Member Agent
from pydantic_ai import Agent
from mixseek.agents.base import BaseMemberAgent
from mixseek.models.member_agent import (
MemberAgentConfig,
MemberAgentResult,
AgentType
)
class PlainAgent(BaseMemberAgent):
"""Plain Member Agent実装"""
def __init__(self, config: MemberAgentConfig):
super().__init__(config)
# Pydantic AI Agent作成
self.agent = Agent(
model=config.model,
system_prompt=config.instructions.text if config.instructions else None
)
async def execute(
self,
task: str,
context: dict[str, Any] | None = None,
**kwargs: Any
) -> MemberAgentResult:
"""タスクを実行"""
# Agent実行
return await self._execute_agent(task, context)
async def _execute_agent(
self,
task: str,
context: dict[str, Any] | None = None
) -> MemberAgentResult:
"""Agent実行(内部メソッド)"""
start_time = datetime.now(UTC)
# Pydantic AI Agent実行
result = await self.agent.run(task)
end_time = datetime.now(UTC)
execution_time_ms = (end_time - start_time).total_seconds() * 1000
# Usage記録
usage = result.usage()
self.record_usage(usage.total_tokens())
# 成功結果を返す
return MemberAgentResult.success(
content=result.output,
agent_name=self.agent_name,
agent_type=self.agent_type.value,
execution_time_ms=execution_time_ms,
usage_info={
"input_tokens": usage.details.get("input_tokens", 0),
"output_tokens": usage.details.get("output_tokens", 0),
"requests": usage.requests
}
)
参照実装: src/mixseek/agents/plain.py
完全な実装例: Leader Agent + Tool登録
from pydantic_ai import Agent, RunContext
from mixseek.agents.leader.config import TeamConfig
from mixseek.agents.leader.dependencies import TeamDependencies
def create_leader_agent(
team_config: TeamConfig,
member_agents: dict[str, Agent]
) -> Agent[TeamDependencies, str]:
"""Leader Agent作成"""
# system_prompt取得
system_prompt = team_config.leader.system_prompt or DEFAULT_LEADER_SYSTEM_PROMPT
# Leader Agent作成
leader_agent = Agent(
model=team_config.leader.model,
deps_type=TeamDependencies,
system_prompt=system_prompt
)
# Member Agent Toolを動的登録
register_member_tools(leader_agent, team_config, member_agents)
return leader_agent
def register_member_tools(
leader_agent: Agent[TeamDependencies, str],
team_config: TeamConfig,
member_agents: dict[str, Agent]
) -> None:
"""Member Agent Tool登録"""
for member_config in team_config.members:
tool_name = member_config.get_tool_name()
member_agent = member_agents[member_config.agent_name]
# Toolクロージャー生成
def make_tool_func(mc, ma):
async def tool_func(ctx: RunContext[TeamDependencies], task: str) -> str:
# Member Agent実行(Usage統合)
result = await ma.run(task, deps=ctx.deps, usage=ctx.usage)
# Submission記録
submission = MemberSubmission(
agent_name=mc.agent_name,
agent_type=mc.agent_type,
content=result.output,
usage=result.usage(),
timestamp=datetime.now(UTC)
)
ctx.deps.submissions.append(submission)
return str(result.output)
tool_func.__name__ = tool_name
tool_func.__doc__ = mc.tool_description
return tool_func
# Tool登録
tool = make_tool_func(member_config, member_agent)
leader_agent.tool(tool)
# 使用例
team_config = TeamConfig.load_from_toml("team.toml")
member_agents = {...} # Member Agentマップ
leader_agent = create_leader_agent(team_config, member_agents)
# Leader Agent実行
deps = TeamDependencies(team_id="team-001")
result = await leader_agent.run("市場調査を実施して", deps=deps)
# Submissionsを取得
for submission in deps.submissions:
print(f"{submission.agent_name}: {submission.content}")
参照実装: src/mixseek/agents/leader/agent.py
まとめ
Pydantic AIの主要な概念:
Agent - 型安全なエージェント実装
Agent Delegation - 動的なAgent選択・実行パターン
Tool - エージェント間通信の仕組み
Usage Limits - リソース使用量の制限
Run Usage - リソース使用量の測定
Dependencies - チーム固有データの依存性注入
Message History - 会話履歴の永続化と復元
MixSeek-Coreでは、これらの機能を組み合わせて、効率的で柔軟なマルチエージェントシステムを実現しています。
関連リソース
Leader Agent実装:
src/mixseek/agents/leader/agent.pyMember Agent基底クラス:
src/mixseek/agents/base.pyデータモデル:
src/mixseek/models/member_agent.py