Handoff Tools
Utilities for creating agent-to-agent transfer tools used in multi-agent swarm patterns.
Import path: agentflow.prebuilt.tools.handoff
Handoff tools are special tools that tell an LLM it can transfer control to another agent. When the LLM calls a handoff tool, the graph routing layer intercepts the call and navigates to the target node directly — the tool function body never actually executes. This keeps the conversation history clean (no spurious tool-result messages).
create_handoff_tool
Factory function that creates a transfer_to_<agent_name> tool.
Concept
In a swarm or peer-to-peer multi-agent graph, each agent needs to know which other agents it can route to. create_handoff_tool creates one tool per target. The naming convention transfer_to_<name> is what the graph routing layer detects to perform the handoff.
Agent calls transfer_to_researcher
↓
Graph routing layer detects "transfer_to_" prefix
↓
Graph routes to the RESEARCHER node
(tool body never runs)
Signature
def create_handoff_tool(
agent_name: str,
description: str | None = None,
) -> Callable
Parameters
| Parameter | Type | Description |
|---|---|---|
agent_name | str | Name of the target agent/node. Must match a node name in the graph. |
description | str | None | LLM-visible description of when to use this handoff. Auto-generated if None. |
Returns
A callable with __name__ = "transfer_to_<agent_name>" and two metadata attributes:
__handoff_tool__ = True__target_agent__ = agent_name
Usage
from agentflow.prebuilt.tools.handoff import create_handoff_tool
from agentflow.core.graph import Agent, ToolNode
transfer_to_researcher = create_handoff_tool(
agent_name="researcher",
description="Transfer to the research specialist for web searches and fact finding.",
)
transfer_to_writer = create_handoff_tool(
agent_name="writer",
description="Transfer to the writer once all research is complete.",
)
triage_agent = Agent(
model="gpt-4o-mini",
tool_node=ToolNode([transfer_to_researcher, transfer_to_writer]),
system_prompt=[{
"role": "system",
"content": "Route the user's request to the right specialist.",
}],
)
In practice, you rarely call create_handoff_tool directly. Use SwarmAgent instead — it generates and injects handoff tools automatically based on the can_handoff_to configuration.
is_handoff_tool
Helper that checks whether a tool name follows the handoff naming convention.
Signature
def is_handoff_tool(tool_name: str) -> tuple[bool, str | None]
Returns
(is_handoff, target_agent_name):
- If
is_handoffisTrue,target_agent_namecontains the extracted target name. - If
is_handoffisFalse,target_agent_nameisNone.
Examples
from agentflow.prebuilt.tools.handoff import is_handoff_tool
is_handoff_tool("transfer_to_researcher") # (True, "researcher")
is_handoff_tool("calculate") # (False, None)
is_handoff_tool("transfer_to_") # (False, None) — empty target
This function is used internally by SwarmAgent's routing functions to detect handoff calls inside state.context[-1].tools_calls. You can use it when building custom routing logic for manual multi-agent graphs.
Manual multi-agent example
from agentflow.core.graph import Agent, ToolNode
from agentflow.core.graph.state_graph import StateGraph
from agentflow.core.state.agent_state import AgentState
from agentflow.prebuilt.tools.handoff import create_handoff_tool, is_handoff_tool
from agentflow.utils.constants import END
def make_route(allowed: list[str]):
def _route(state: AgentState) -> str:
last = state.context[-1] if state.context else None
if last and last.role == "assistant" and last.tools_calls:
for tc in last.tools_calls:
ok, target = is_handoff_tool(tc.get("name", ""))
if ok and target and target.upper() in allowed:
return target.upper()
return END
return _route
transfer_to_writer = create_handoff_tool("writer", "Send to the writer when research is done.")
researcher = Agent(
model="gpt-4o",
tool_node=ToolNode([transfer_to_writer]),
)
writer = Agent(model="gpt-4o-mini")
graph = StateGraph()
graph.add_node("RESEARCHER", researcher)
graph.add_node("WRITER", writer)
graph.add_conditional_edges("RESEARCHER", make_route(["WRITER"]), {"WRITER": "WRITER", END: END})
graph.add_edge("WRITER", END)
graph.set_entry_point("RESEARCHER")
app = graph.compile()
For most use cases, use SwarmAgent rather than wiring handoffs manually.