Agent Handoffs
A handoff passes the conversation to another agent with a slimmer prompt and different tools. What to copy, what to drop, and how to avoid ping-pong.
- agents
- handoff
- orchestration
Support hands you to billing. Billing hands you back to support. Support apologizes. That is not orchestration. That is a missing stop rule. An agent handoff is a tool that swaps the active prompt and the tool list. The receiving agent becomes the speaker. If you do not record the path, the model will bounce until the step cap or the bill does.
A second persona is not a substitute for a better tool. Use a handoff when the user-facing specialist should own the rest of the turn. If you only need a lookup, call a specialist as a tool and keep the same speaker. That split is the whole design.
Key takeaways
- A handoff is a tool call: new instructions, new tools, new speaker.
- Copy a summary plus artifact ids, not the full dump, unless the specialist must quote.
- Record
from → toon the run. A second visit to the same agent is a cycle unless you allow it.- The specialist must finish, ask the user, or escalate to a human. Bouncing back is not the default.
- Need a subtask, not a new speaker? Do not hand off. Delegate.
An agent handoff is a tool
In the OpenAI Agents SDK, a handoff is exposed to the model as something like transfer_to_refund_agent. Your own loop can do the same: the model emits a structured transfer, your runtime loads another system prompt and allowlist, and the next completion uses that agent.
type AgentId = 'triage' | 'billing' | 'refunds';
type HandoffArgs = {
target: AgentId;
reason: string;
artifacts: { orderId?: string; ticketId?: string };
};
type Run = {
agent: AgentId;
path: AgentId[];
messages: Array<{ role: 'system' | 'user' | 'assistant' | 'tool'; content: string }>;
};
const MAX_VISITS = 1;
export function applyHandoff(run: Run, args: HandoffArgs): Run {
const visits = run.path.filter((id) => id === args.target).length;
if (visits >= MAX_VISITS) {
throw new Error(`handoff cycle: ${[...run.path, args.target].join(' -> ')}`);
}
if (args.target === run.agent) {
throw new Error('handoff to self');
}
const brief = [
`Handoff from ${run.agent}. Reason: ${args.reason}.`,
`Artifacts: ${JSON.stringify(args.artifacts)}`,
'Do not transfer back unless the user asks for a different department.',
'If you cannot finish, escalate to a human. Do not apologize-loop.',
].join('\n');
return {
agent: args.target,
path: [...run.path, args.target],
messages: [
{ role: 'system', content: specialistPrompt(args.target) },
{ role: 'user', content: brief },
],
};
}
Notice what did not copy: the triage novel, the old tool schemas, the ten “let me check” assistant turns. Those tokens teach the specialist to behave like triage.
Copy a brief, not a dump
The receiving window is a budget. Context engineering decides what survives.
| Take | Drop |
|---|---|
| User intent in one paragraph | Full chat dump by default |
| Ids: order, ticket, customer | Raw tool JSON from unrelated lookups |
| Policy quotes the specialist must follow | The previous agent’s chain-of-thought |
| Files the user already attached | The previous tool list |
If billing needs the exact refund clause, pass that quote as an artifact. Do not pass 40 turns of “hi / thanks / still there?” so the model can “have context.” That is how you pay for noise and still miss the SKU.
When you do need history (the user already answered “which card”), use an input filter: keep the last user messages plus the artifact bag. SDKs that hand off the entire transcript are a default, not a requirement. Override them.
Stop ping-pong
Cycles come from symmetric tools. If billing has transfer_to_support and support has transfer_to_billing, the model will use both when it is unsure. Fix the graph, not the adjectives.
- Acyclic by default. Refunds may receive from triage. Refunds may escalate to a human. Refunds may not transfer to triage.
- Visit cap. One visit per agent per run unless you store a reason to re-enter (“user changed the order id”).
- Terminal specialists. The last agent in the path has no peer handoff, or only
escalate_to_human. - Same question twice. If the user intent embedding (or a cheap classifier) matches the turn that caused the previous transfer, refuse the second hop and ask a clarifying question instead.
Ping-pong looks like progress in a trace viewer. Count hops. If hop 3 is a repeat of hop 1, fail the run.
When a handoff is the wrong primitive
A handoff takes over the microphone. Subagents report back. Multi-agent systems mix both; most products need neither.
Use a handoff when:
- The user should talk to a different policy and a different tool set (billing vs shipping).
- You want a slimmer prompt so the specialist stops seeing refund tools.
- The trace should show a named owner for the rest of the conversation.
Keep one agent when:
- You are about to invent five Slack personas.
- The “specialist” is one SQL query.
- You cannot write a stop rule.
The conversation UI still needs a single thread. Tell the user who is speaking if policy requires it (“Billing team now”). Do not role-play a roomful of names. That burns tokens and confuses conversation state.
Interview answer (out loud)
“An agent handoff is a tool that replaces the active prompt and tools so a specialist becomes the speaker. I transfer a summary plus artifact ids, not the full transcript. I record the path and forbid cycles: specialists finish, ask, or escalate to a human. If I only need a subtask, I call a specialist as a tool and keep one speaker. Five personas with no stop rule is ping-pong.”
Further reading (primary sources)
Related on this site
Related guides
- Graph Workflows for AgentsAn agent graph workflow makes loops, branches, and human pauses explicit. Use it when a hidden while-loop is how you lose the plot.
- Multi-Agent SystemsMultiple agents means multiple prompts, tools, and traces. Use them when roles truly diverge. A second agent is not a substitute for a better tool.
- Agent Failure ModesAgents loop, lie about tool results, skip confirmation, and get stuck apologizing. Catalog the failures and the circuit breakers.
- Agent MemoryAgents forget when the context window ends. Working memory, session summaries, and long-term stores are three different systems. Do not mix them.
- Agent System Design InterviewDesign a support or coding agent in a loop: tools, approvals, traces, evals, and cost. The frontend surfaces matter as much as the model.