The Supervisor pattern is a voice agent design where one agent owns the call from hello to goodbye. When a focused job comes up, like taking an order or verifying who is calling, it lends the conversation to a small task. The task does that one job with its own short prompt and tools, hands back a typed result, and the supervisor carries on.
The caller hears one continuous agent. Behind the scenes, no single prompt has to hold every rule for every phase.
Use it when
- Several intents share one call. “I want to order delivery. Actually, can I also book a table for Friday?” The supervisor is still there when the caller changes their mind. It starts a different task, and the call never feels like a phone tree.
- You need clean, structured data out of the conversation. An order, an address, a verified identity, a consent answer. Each task returns a dataclass you can store, bill against or send to a CRM.
- The same step appears in several agents. An address task written once can be reused by the delivery agent, the catering agent and the complaints agent.
Avoid it when
There is only one intent and a few tools: stay Solo. Or a phase needs a different voice, a different model, or a hard permission boundary, like the only agent allowed to touch card details: that is a Relay.
Supervisor or Relay?
Ask one question. Should the first agent still be around after this phase ends? If yes, it is a Supervisor. If it should be gone, it is a Relay.
Latency
Close to Solo. Tasks run inside the same session, so there is no handoff to pay for. Each task does start with a smaller prompt, which often makes its turns slightly faster than a bloated Solo agent.
Build it on LiveKit
A task is an AgentTask with a typed result. It calls complete() when it has what it needs. The supervisor awaits it like any other coroutine.
from dataclasses import dataclass
from livekit.agents import Agent, AgentTask, RunContext, function_tool
@dataclass
class Address:
street: str
city: str
postcode: str
class GetAddress(AgentTask[Address]):
def __init__(self, chat_ctx=None) -> None:
super().__init__(
instructions="Collect the delivery address. Read it back and wait for a yes.",
chat_ctx=chat_ctx,
)
@function_tool()
async def address_confirmed(self, street: str, city: str, postcode: str) -> None:
"""Call once the caller has confirmed the address."""
self.complete(Address(street, city, postcode))
class FrontDesk(Agent):
def __init__(self) -> None:
super().__init__(instructions="You run the phone line. Start a task for each job.")
@function_tool()
async def collect_address(self, ctx: RunContext) -> str:
"""Collect the delivery address for the current order."""
address = await GetAddress(chat_ctx=self.chat_ctx.copy(exclude_instructions=True))
return f"Delivery address saved: {address.street}, {address.city}."
The supervisor keeps the full conversation. Each task gets a scoped copy of the history without the supervisor’s instructions, so it knows what was said but only follows its own rules. See LiveKit’s supervisor pattern guide for the full reference.
How it combines
A Supervisor’s task can be a Guided Path when it needs several ordered steps. A Supervisor can hand off to a payments agent in a Relay at the end of the call. And an Observer next to the supervisor catches what no single task can see, like frustration building across three tasks.
Common questions
What is the difference between a Supervisor and a Relay?
In a Supervisor the main agent stays on the call and gets control back after each task. In a Relay each agent hands off and is gone. Ask whether the first agent should still be around after the phase ends.
Does a Supervisor add latency?
Very little. Tasks run inside the same session, so there is no handoff cost, and each task has a smaller prompt than one large agent would.
What does a task return to the supervisor?
A typed result, such as a dataclass with the street, city and postcode. The supervisor can store it, bill against it or send it to a CRM.
Sources and further reading
Mahimai Raja
Mahimai builds production voice agents on LiveKit for small businesses and maintains the open-source livekit-starter. He is building ShipVoice, the voice AI platform for agencies. Find him on X or at [email protected].