Routing chooses a path based on a request. In a workflow router, an LLM selects among predefined handlers—for example, billing or technical support—but the developer controls every available path. An agent router goes further: it selects a specialist and transfers control to that agent. section 1.3.1 section 9.5.3
Use dynamic routing when a fixed specialist sequence would do unnecessary work. A question about AI trends may need research, a calculation may need code execution, and an analysis with a visualization may need both. One approach exposes specialists as tools so the LLM can call those it needs; another hands the conversation to a selected specialist. section 9.4 section 9.5.3
A handoff-based router follows this flow:
- Register specialists with names and descriptions that identify their capabilities. section 9.5.3
- Receive the request and call transfer_to_agent with the chosen specialist’s name. section 9.5.3
- Record the target in execution context; the run method detects it and performs the transfer. section 9.5.3
- Let the specialist inherit the conversation context and complete the task. If a follow-up falls outside its capabilities, transfer back to the router or to an allowed sibling. section 9.5.3
math_agent = Agent(
model=llm,
name="math",
description="Math calculation specialist",
instructions="Performs mathematical calculations.",
tools=[calculate],
)
search_agent = Agent(
model=llm,
name="search",
description="Information search specialist",
instructions="Searches for information on the web.",
tools=[web_search],
)
router = Agent(
model=llm,
name="router",
description="Routes user requests to appropriate specialists",
instructions="Analyze requests and route to the appropriate specialist.",
sub_agents=[math_agent, search_agent],
)
The router registers math and search specialists as sub-agents; their descriptions and tools distinguish the work each can handle. section 9.5.3
@dataclass
class ExecutionContext:
...
# Multi-agent fields
transfer_to: Optional[str] = None
transfer_tools: Dict[str, "FunctionTool"] = field(default_factory=dict)
ExecutionContext holds the selected target in transfer_to and caches each agent’s transfer tool in transfer_tools. section 9.5.3
The agent tree limits where control can go. An agent can transfer to its children and, if it has a parent, back to that parent. Sibling transfers are optional. Validate duplicate sub-agent names and multiple parents: transfers look agents up by name, while each agent must have an unambiguous place in the tree. section 9.5.3
Listing 9.12 Transfer targets retrieval
def _get_transfer_targets(self) -> List["Agent"]:
"""List of targets the current agent can transfer to."""
targets = []
# 1. Children
targets.extend(self.sub_agents)
# 2. Parent and siblings
if self.parent:
targets.append(self.parent)
# 3. Siblings (optional)
if not self.disallow_transfer_to_peers:
for sibling in self.parent.sub_agents:
if sibling.name != self.name:
targets.append(sibling)
return targets
The target-selection method makes the transfer policy explicit: children, parent, and optionally siblings. section 9.5.3





















