PydanticAI ビジュアルガイド

Lesson CH08-L01

Delegation

親Agentが子Agentに部分タスクを委譲する。

読了目安
11 min
Colab目安
16 min
合計
27 min
前提
Deferred Tools

関連: 公式ドキュメント

一行サマリ

親 Agent の ツール内から子 Agent.run を呼ぶ だけで、親が制御を保ったまま部分タスクを子に委譲できる。usage=ctx.usage を渡せば トークン消費が親に集約 され、コスト計測も一元化される。マルチエージェントの第一歩。

ヒーロー: ツールが「別 Agent」になる

Ch3 のツールは Python 関数でした。Delegation ではそのツールの中身を 別の Agent.run にすり替えるだけで、親 Agent が 「専門サブ Agent を呼び出すオーケストレータ」 に変わります。LLM 側からは「ツールを呼んだ」ようにしか見えませんが、内部では別 Agent が独自モデル + 独自 instructions で考えています。

図を読み込み中…
図1. Delegation の構造

概念: Hand-off との違い

観点Delegation (本レッスン)Hand-off (次レッスン)
制御親が保持。子は終わったら親に戻る完全に別 Agent に移譲。元 Agent は関与しない
呼び出し場所親のツール内アプリケーションコードで順次切り替え
結果子の戻り値が親のツール戻り値になる各 Agent の result を独立して扱う
usage の集約usage=ctx.usage で親に集約各 run の usage を別々に計測

「親が編集権を持って細かく指揮するなら Delegation、専門 Agent に丸投げして次工程に渡すなら Hand-off」と覚えると判断しやすい。

コード: 3 つのパターン

パターン 1: 子 Agent をツール内から呼ぶ最小例

from pydantic_ai import Agent, RunContext
from pydantic_ai.models.google import GoogleModel
 
model = GoogleModel('gemini-3-flash-preview')
 
# 子 Agent: 1 つの仕事に特化
joke_agent = Agent(
    model,
    instructions='与えられたお題で、短い日本語のジョークを 1 つだけ作ってください。',
)
 
# 親 Agent: 子を「ツール」として持つ
parent = Agent(
    model,
    instructions='ユーザーのお題に対して generate_joke を呼んでジョークを返してください。',
)
 
@parent.tool
async def generate_joke(ctx: RunContext, topic: str) -> str:
    """指定トピックでジョークを生成する。"""
    result = await joke_agent.run(topic, usage=ctx.usage)
    return result.output
 
print(parent.run_sync('猫').output)
print(parent.run_sync('プログラマー').output)

ポイント:

  • @parent.tool 内で await child_agent.run(...)
  • usage=ctx.usage を渡すと子 Agent のトークン消費が親の usage に積算される
  • 子は stateless で別ファイルに切り出して再利用可能

パターン 2: 専門 Agent 複数を「束ねる」オーケストレータ

from pydantic_ai import Agent, RunContext
from pydantic_ai.models.google import GoogleModel
 
translator = Agent(
    GoogleModel('gemini-3-flash-preview'),
    instructions='与えられた文を自然な日本語に翻訳してください。',
)
 
summarizer = Agent(
    GoogleModel('gemini-3-flash-preview'),
    instructions='与えられた日本語を 2 文以内で要約してください。',
)
 
orchestrator = Agent(
    GoogleModel('gemini-3-flash-preview'),
    instructions=(
        '英語のニュース文が来たら、まず translate_to_ja で翻訳し、'
        '次に summarize で要約してから返してください。'
    ),
)
 
@orchestrator.tool
async def translate_to_ja(ctx: RunContext, en_text: str) -> str:
    """英語を日本語に翻訳する。"""
    return (await translator.run(en_text, usage=ctx.usage)).output
 
@orchestrator.tool
async def summarize(ctx: RunContext, ja_text: str) -> str:
    """日本語を 2 文以内に要約する。"""
    return (await summarizer.run(ja_text, usage=ctx.usage)).output
 
print(orchestrator.run_sync(
    'OpenAI announced new model series with improved coding ability.'
).output)

専門 Agent 同士はお互いを知らず、親が呼出順序を組む ことで「翻訳 → 要約」のパイプラインが組み上がります。

パターン 3: 構造化出力とのコンビ

子 Agent の output_type を BaseModel にすると、親のツールが構造化データを返す ようになります。

from pydantic import BaseModel, Field
from pydantic_ai import Agent, RunContext
from pydantic_ai.models.google import GoogleModel
 
class Sentiment(BaseModel):
    polarity: str = Field(description='positive / neutral / negative のいずれか')
    confidence: float = Field(description='0.0 〜 1.0 の確信度')
    reason_ja: str = Field(description='判断理由の日本語要約 (60 字以内)')
 
sentiment_agent = Agent(
    GoogleModel('gemini-3-flash-preview'),
    output_type=Sentiment,
    instructions='文の感情を分析し、極性 / 確信度 / 理由を日本語で返してください。',
)
 
parent = Agent(
    GoogleModel('gemini-3-flash-preview'),
    instructions='ユーザーが与える文の感情を analyze_sentiment で分析して報告してください。',
)
 
@parent.tool
async def analyze_sentiment(ctx: RunContext, text: str) -> Sentiment:
    """文の感情を構造化分析する。"""
    return (await sentiment_agent.run(text, usage=ctx.usage)).output
 
print(parent.run_sync('今日の発表は本当にひどかった。').output)

子 Agent の構造化出力 (Sentiment) がそのままツール戻り値になり、親 LLM がそれを読んで自然言語に整形します。

図を読み込み中…
図2. Delegation で usage を集約する流れ

観察: usage を見て本当に集約されているか確認

result = parent.run_sync('プログラマー')
print(result.usage())
# RunUsage(requests=2, input_tokens=350, output_tokens=120, ...)
#                ^^^ 親 1 回 + 子 1 回 = 2

requests親 + 子の合計 になっていれば集約成功。usage=ctx.usage を忘れると親 = 1、子の usage はロストします。

まとめ

  • 親 Agent のツール内から await child_agent.run(prompt, usage=ctx.usage) を呼ぶだけで Delegation
  • usage=ctx.usage を必ず渡してトークン消費を親に集約
  • 専門 Agent を複数組み合わせて オーケストレータ が呼出順序を決められる
  • 子の output_type=BaseModel で構造化データのまま親に戻せる
  • ネストは 2 段まで が推奨。深いワークフローは Hand-off / Graph へ

次レッスンでは Hand-off — 制御を完全に別 Agent に渡す Programmatic ハンドオフを扱います。

Colab で実際に動かす

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

Open in Colab

notebooks/ch08/01-delegation.ipynb