Lesson CH03-L04
Advanced Tools
戻り値型・並列実行・条件付きツールなど発展トピック。
- 読了目安
- 11 min
- Colab目安
- 16 min
- 合計
- 27 min
- 前提
- ModelRetry
関連: 公式ドキュメント
一行サマリ
Tool の基本を超えて、並列実行・条件付き提供・構造化戻り値・動的 schema といった発展トピックを扱う。複数 tool を持つ Agent の挙動を読み解き、実用シナリオに必要な制御を効かせるためのテクニック集。
ヒーロー: 1 つの Agent に複数 tool を持たせると何が起きるか
LLM は すべての tool の schema を見て、必要に応じて 0 個以上を順に (場合によっては並列で) 呼ぶ 判断をします。Agent 側ができることは、tool の 見せ方を制御する こと。
パターン 1: 構造化戻り値で続く処理に橋渡し
Tool の戻り値は str や数値だけでなく Pydantic BaseModel でも OK。LLM への文脈に 構造化された情報 を渡せるので、後続の判断 / 別 tool への入力に直接使えます。
from pydantic import BaseModel
from pydantic_ai import Agent
class Weather(BaseModel):
city: str
condition: str
temp_c: float
WEATHER_DB = {
'Tokyo': Weather(city='Tokyo', condition='sunny', temp_c=22.0),
'Sapporo': Weather(city='Sapporo', condition='snowy', temp_c=-2.0),
}
agent = Agent('google-gla:gemini-3-flash-preview', instructions='短く日本語で。')
@agent.tool_plain
def get_weather(city: str) -> Weather:
"""指定都市の天気を構造化して返します。"""
if city not in WEATHER_DB:
return Weather(city=city, condition='unknown', temp_c=0.0)
return WEATHER_DB[city]
print(agent.run_sync('東京と札幌の天気を比較して').output)LLM への戻り値は JSON シリアライズされて文脈に積まれる ため、複数 tool 結果の比較・差分計算が LLM 側でしやすくなります。
パターン 2: 並列 tool 呼び出し
LLM (特に Gemini 3 系) は 同じ run の中で複数 tool を並列に呼ぶ ことができます。asyncio で書いておけば PydanticAI が裏で asyncio.gather 相当を回してくれます。
import asyncio
from dataclasses import dataclass
import httpx
from pydantic_ai import Agent, RunContext
@dataclass
class HttpDeps:
http: httpx.AsyncClient
agent_p = Agent('google-gla:gemini-3-flash-preview', deps_type=HttpDeps)
@agent_p.tool
async def fetch_summary(ctx: RunContext[HttpDeps], url: str) -> str:
"""URL の冒頭 200 字を返します。"""
r = await ctx.deps.http.get(url, timeout=5.0)
return r.text[:200]
# LLM は 3 件のサマリを並列に取りに行く
async def main():
async with httpx.AsyncClient() as http:
result = await agent_p.run(
'次の 3 サイトを比較して: https://a.example.com, https://b.example.com, https://c.example.com',
deps=HttpDeps(http=http),
)
print(result.output)ポイント:
async defで書いた tool は I/O 待ちで他のタスクに譲る ため、並列化が効く- 同期 tool (
def) でも複数呼ばれるが、その場合は逐次
パターン 3: 条件付き tool 提供 — prepare
「ユーザーの権限によって使える tool が違う」場合は prepare 関数 で run ごとに schema を組み立て直せます。
from dataclasses import dataclass
from pydantic_ai import Agent, RunContext
from pydantic_ai.tools import ToolDefinition
@dataclass
class Auth:
is_admin: bool
agent_a = Agent('google-gla:gemini-3-flash-preview', deps_type=Auth)
# 通常 tool
@agent_a.tool_plain
def list_users() -> list[str]:
"""ユーザー一覧を返します。"""
return ['tanaka', 'sato']
# 管理者だけが使える tool
async def only_for_admin(ctx: RunContext[Auth], tool_def: ToolDefinition) -> ToolDefinition | None:
return tool_def if ctx.deps.is_admin else None
@agent_a.tool_plain(prepare=only_for_admin)
def delete_user(name: str) -> str:
"""指定ユーザーを削除します (管理者専用)。"""
return f'削除: {name}'
# 一般ユーザーで実行 — delete_user は schema に出ない
print(agent_a.run_sync('tanaka を削除して', deps=Auth(is_admin=False)).output)
# 管理者で実行 — delete_user が呼べる
print(agent_a.run_sync('tanaka を削除して', deps=Auth(is_admin=True)).output)ポイント:
prepare関数は run ごとに呼ばれる。Noneを返すとその run では tool が 存在しない扱い になる (LLM は schema にも見えない)ToolDefinitionを返せばそのまま使われる。動的に description を変えるのも可能
パターン 4: tool の docstring を動的に変える
prepare 関数で tool_def.description を上書きすれば、状況に応じた hint を LLM に渡せます。
async def with_user_hint(ctx: RunContext[Auth], tool_def: ToolDefinition) -> ToolDefinition:
role = '管理者' if ctx.deps.is_admin else '一般ユーザー'
tool_def.description = f'{tool_def.description} (現在のロール: {role})'
return tool_def
@agent_a.tool_plain(prepare=with_user_hint)
def show_status() -> str:
"""現在のシステム状態を返します。"""
return 'OK'デバッグ — どの tool がどう呼ばれたか見る
複数 tool を持つ Agent では「LLM が想定と違う tool を呼んだ」場面がよくあります。result.all_messages() でメッセージ履歴を見るのが基本デバッグ。
result = agent.run_sync('東京の天気は?')
for msg in result.all_messages():
for part in msg.parts:
if hasattr(part, 'tool_name'):
print(f'{msg.kind}: {part.tool_name}({getattr(part, "args", "")})')これで「呼ばれた tool / 引数 / 戻り値」が時系列に並んで見えます。
まとめ
- 戻り値型に Pydantic BaseModel を返せば LLM の context に構造化情報を渡せる
async deftool は 並列実行 に乗る (Gemini 3 系で特に効く)@tool(prepare=fn)で run ごとに tool の表示・description を動的制御- 大量 tool は schema を肥大化させる → Toolset (Ch7) で整理 を検討
- デバッグは
result.all_messages()で tool 呼び出し履歴を観察
これで Ch3「Function Tools」全 4 レッスンが完了です。次の Ch4「Structured Output」 からは、Agent の最終応答そのものを Pydantic Model 等に固定する設計を扱います。
Colab で実際に動かす
本レッスンの内容を Google Colab 上で実行できるノートブックを用意しています。下のボタンから自分のColab環境に開けます (要 Google アカウント / GOOGLE_API_KEY)。
notebooks/ch03/04-advanced-tools.ipynb