Skip to main content
A handoff gives the call to another main agent. You choose a line said first, and what the next agent hears of the call so far. Each agent is its own AgentSpec, with its own prompt, tools, tasks, model and voice, and all of them share one CallState. In the salon tutorial, Hand off complaints builds this step by step.

Hand the call to another agent

Give the model a handoff tool. Its handler calls hand_off.
  1. The announce line plays first, and the handoff waits for it. The caller cannot cut it off.
  2. On LiveKit, build the next agent on self.state and without telemetry. On Pipecat, pass its spec, and UnmuteFlows swaps the prompt, tools, tasks and model.
  3. The next agent replies first. Name each agent’s handoff tools in its spec’s handoffs, so that first reply cannot hand the call straight back.

What the next agent hears

Pass a HandoffContext as context. With none, the next agent hears what was said, without tool calls.
  1. Calls to tools the next agent does not have are dropped, with their results.
  2. The announce line is the last thing the next agent hears, except with summary and reset.
  3. On a realtime model, only full works. See Realtime models.

Summarize the call for the next agent

  1. The summary is asked for while the announce line plays, so it does not hold up the handoff.
  2. summarizer is any ModelCall. Without one, the spec’s summarizer answers, else the model of the agent that hands off.
  3. A summary that fails, comes back empty, or takes longer than 8 seconds is dropped. The next agent then hears messages.

Give the next agent its own model and voice

Give the spec a model, and pass the agent its own voice.
FRONT_VOICE and BILLING_VOICE are two Fish Audio voice ids on SLNG. SLNG_KEY, SLNG_HOST and WORLD_PART come from your environment, as in the quickstart.
  1. The announce line plays in the old voice. Then the new model and voice take over.
  2. An agent whose spec has no model runs on the call’s first model. The same holds for the voice.
  3. On Pipecat, a spec with a model needs a ModelCallLLMService, and the voices go through a VoiceSwitcher.

Open the next agent on a tool

The next agent’s first reply comes from the model. When it must start with something code already knows, run a tool in code instead. Pass opens_with:
  1. The tool runs through the next agent’s ToolRegistry, so its assign rows save values.
  2. Its line is the first of the opening’s say lines that fills, else the registry’s ToolReply for the result.
  3. When a line fills, it is the agent’s first reply. The tool call and its result join the history, and no model request is made.
  4. When no line fills, or the tool is refused or fails, the model replies first as before.
  5. arguments maps tool parameters to State values, as on a task step’s opening. The tool does not run while one of them is unset.
  6. A realtime model refuses opens_with: it keeps the history on its server.

A handoff from a task step

A task can offer the agent’s handoff tools to its steps:
  1. The handoff tool must be one of the agent’s own tools: a native_tools entry, or a LiveKit @function_tool method.
  2. When the step’s model calls it, the step ends with task.handed_off, and nothing is saved.
  3. The next agent hears the step’s own chat, shaped by the handoff’s context.
A task group can hand the call off too, once it finishes. See Group transfer rules.

Hand off from a plain agent

You can hand off without UnmuteAgent or tasks.
  1. On LiveKit, shape_chat_ctx gives the next agent the history the library keeps. It does not say a line or write a summary.
  2. On Pipecat, a spec with only a prompt is enough for a full handoff.

Where to go next

Handoffs reference

Every argument, history rule, helper and error.

Tasks

Steps and groups that offer or run a handoff.

LiveKit

What UnmuteAgent does on a LiveKit session.

Pipecat

What UnmuteFlows does in a Pipecat pipeline.

Unmute handoffs

How agent.yaml declares a handoff.