Pydantic AIPydantic AI 2.43 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
23 small wins to finish your pathNext lesson

Agents that call agents

One agent can hand part of the job to another by calling it inside a tool. Passing ctx.usage counts both agents' requests against one budget.

Example
from pydantic_ai import Agent, ModelResponse, RunContext, TextPart, ToolCallPart
from pydantic_ai.models.function import FunctionModel


def write(messages, info):
    return ModelResponse(parts=[TextPart("Sorry about the double charge. The extra payment will be back within 5 days.")])


writer = Agent(FunctionModel(write), instructions="Write a short, kind reply to the customer.")


def lead_model(messages, info):
    last = messages[-1].parts[-1]
    if last.part_kind == "user-prompt":
        return ModelResponse(parts=[ToolCallPart("draft_reply", {"ticket": last.content})])
    return ModelResponse(parts=[TextPart(f"Reply ready: {last.content}")])


desk = Agent(FunctionModel(lead_model), instructions="Handle the ticket. Use draft_reply for the wording.")

writer is good at wording; desk handles the ticket. The desk's model calls a draft_reply tool, and that tool runs the writer:

Example
@desk.tool
async def draft_reply(ctx: RunContext, ticket: str) -> str:
    """Ask the writer for the reply to send."""
    result = await writer.run(ticket, usage=ctx.usage)
    return result.output


result = desk.run_sync("I was charged twice")
print(result.output)
print(result.usage)

The tool is async and awaits writer.run. usage=ctx.usage makes the writer add its request to the desk's usage: three requests, two by the desk and one by the writer. Without it, the desk would report two, and a usage limit on the desk would not see the writer's calls.

Example
@desk.tool
async def draft_reply(ctx: RunContext, ticket: str) -> str:
    """Ask the writer for the reply to send."""
    result = await writer.run(ticket)
    return result.output


print(desk.run_sync("I was charged twice").usage)

Delegate or hand off

  • Delegation, above: the second agent works inside the first agent's run and returns to it.
  • Hand-off: your code runs one agent, looks at its output, and decides which agent runs next. The triage agent from lesson 9 choosing between a Ticket and NeedsHuman, followed by your if, is a hand-off.

The writer's messages are not part of the desk's history; the desk only sees the tool's return value. Each agent has its own instructions, tools and model, so the writer could use a cheaper model than the desk.

Try it yourself
  • Give writer a deps_type and pass deps=ctx.deps from the tool.
  • Run the desk with usage_limits=UsageLimits(request_limit=2), with and without usage=ctx.usage.
  • Write the hand-off version: run triage, then run the writer only for tickets.

This is what real progress feels like.