The mental model: a relay race
In a relay race, runner one takes off, completes her leg, and hands the baton to runner two. Runner two takes over, completes his leg, and hands off to runner three, who hands off to the final runner. There's no central supervisor calling the shots from the sidelines, the runner holding the baton is simply the one racing. Once she hands it off, she's done, and the next runner becomes the active one.
That's the handoffs pattern. Every agent sits at the same hierarchy level, there are no sub-agents. One agent is designated the entry point, and whichever agent currently holds "the baton" is the active agent, the only one talking to the user at that moment.
Walking through it: a customer support call center
Say we build a support system with three peer agents: general support, billing support, and technical support. By default, every user starts talking to general support.
Notice the handoff isn't fixed-order: general support jumped straight to technical, and technical handed control back to general when the topic didn't match any specialist. Every agent knows about every other agent and can transfer to any of them.
At every point in time, exactly one agent is active, and it's the only one the user is speaking with. Nothing is being called in parallel; the system works by updating state to say "this agent is now in control," not by dispatching to multiple agents at once.
Characteristics of the handoffs pattern
- Distributed control - there's no single supervisor. An entry-point agent is chosen by default, but it sits at the same hierarchy level as every other agent; any of them can be active at any time.
- Direct user interaction with every agent - unlike the sub-agents pattern, where only the coordinator ever talks to the user, here the user converses with whichever agent is active, directly.
- Explicit transfer of control - agents use tool calls to hand off. There's no partial handoff; whichever agent is active has full control until it explicitly transfers it.
- Highly stateful transitions - the receiving agent gets the entire conversation history and state. If general support collected the user's name and account info, billing support already has it.
- Always sequential - you talk to exactly one agent at a time. There is no parallelism in this pattern.
Because transitions are stateful, you also get conversation continuity: the active agent can reference anything discussed with a previous agent without re-asking, right up until it needs to hand off a task outside its own domain.
Implementing handoffs in LangGraph
There are three ways to build this.
1. One agent, dynamically swapped persona (middleware)
Use a single create_agent, and swap its system prompt and tools at runtime based on state, using a wrap_model_call middleware. To the user, it feels like talking to a different specialist each time, because the prompt and available tools change underneath.
Sketch:
class SupportState(AgentState):
current_step: Literal["warranty", "classification", "resolution"]
warranty_status: str
issue_type: str
@wrap_model_call
def swap_persona(request, handler):
step = request.state.get("current_step", "warranty")
config = STEP_CONFIG[step] # {"prompt": ..., "tools": [...]}
request = request.override(system_prompt=config["prompt"], tools=config["tools"])
return handler(request)
agent = create_agent(
model=model,
tools=all_tools,
state_schema=SupportState,
middleware=[swap_persona],
checkpointer=checkpointer, # memory across turns
)2. Separate agents as graph nodes, conditionally routed
Build each specialist as its own agent (typically with create_agent if it's a plain ReAct-style agent), add them as nodes, and route between them based on a current_agent state field.
Sketch:
def route_to_agent(state: SupportState) -> str:
return state.get("current_agent", "general")
workflow = StateGraph(SupportState)
workflow.add_node("general", general_agent)
workflow.add_node("billing", billing_agent)
workflow.add_node("technical", technical_agent)
workflow.add_conditional_edges(START, route_to_agent)3. Explicit handoff inside a tool call
An agent can hand off by calling a dedicated tool that returns a Command, updating state and redirecting to the target node in one step.
@tool
def transfer_to_billing(reason: str, tool_call_id: str, runtime):
return Command(
update={
"messages": [ToolMessage(content=f"Transferring: {reason}", tool_call_id=tool_call_id)],
"current_step": "billing",
},
goto="billing",
)The next time the user's message is processed, current_step (or current_agent) already points at the new agent, so control flows there automatically.
Benefits vs. trade-offs
Benefits:
- Direct user interaction - every specialist talks to the user directly and can ask its own clarifying questions, no relay through a middleman.
- Sequential enforcement - you can require a condition to be met (e.g., authentication) before an agent will ever hand off to the next one.
- Stateful context - each agent inherits the full conversation history, so users never have to repeat information they've already given.
- Natural conversations - users interact with each specialist in natural language, which can feel more genuine than a single intermediary relaying everything.
- Flexible navigation - agents can hand off back and forth as many times as the conversation needs, not just forward in a fixed order.
- Clear responsibilities - each agent is focused on one purpose.
- Easier debugging of transitions - handoffs are explicit and traceable, so you can reconstruct exactly when and why control moved between agents.
Trade-offs:
- No parallelism - only one agent is active at a time. (You can nest a sub-agents or router pattern inside an individual agent to get parallelism within one leg of the relay, patterns can compose.)
- Sequential latency - since nothing runs concurrently, you pay for every step happening one after another.
- Context accumulation - the full conversation gets stuffed into every agent's context on every handoff, which bloats token usage over a long conversation. Summarization or compression strategies help manage this.
- Handoff overhead - a transfer takes a full turn to "land," the new agent has to be prompted before it starts acting like the new specialist.
- Complexity from nonlinear workflows - all that back-and-forth flexibility means more state machine complexity to reason about and debug.
- User experience friction - users notice the transfers ("transfer you to billing", "transfer you to technical") and it can feel bureaucratic, even though each specialist is more focused.
Stateful context is worth calling out twice: it's simultaneously a benefit (no repeated questions) and a trade-off (constant context growth), the same property, viewed from two costs.
When to use this pattern
Reach for handoffs when:
- Users need to talk directly to each specialist rather than through a coordinator.
- The task naturally moves through stages or departments, and any agent might need to hand off to any other.
- You want to enforce ordering (don't proceed to step 2 until step 1's conditions are met).
- Conversation continuity matters more than parallel speed.
Common use cases
- Customer support - transferring between general, billing, and technical departments.
- Multi-step forms - one agent per section (general info, contact info, experience), each able to validate before moving the user forward.
- Onboarding flows - guiding a new user through sequential setup steps, often without them noticing the agent has changed.
- Approval workflows - one step at a time, each gated on approval before handing off to the next.
- Interview bots - a general-info bot, a technical-questions bot, and a personality-assessment bot, each with its own focus.
- Sales funnels - qualification → demo → closing, with a different specialist agent driving each stage.
The handoffs pattern is the one to pick when the user's journey is genuinely sequential and each stage deserves its own direct, focused conversation, at the cost of giving up parallelism entirely.