The transfer signal

Handoffs are not a hidden side effect. They are an explicit action. The triage agent exposes function tools like transfer_to_support and transfer_to_billing. When the LLM decides a caller needs a specialist, it calls the tool, and LiveKit swaps the active agent in the session.

triage_agent.py
python
async def _transfer_to_agent(self, name: str, context: RunContext_T) -> Agent:
    """Hand control to another agent registered in personas."""
    userdata = context.userdata
    current_agent = context.session.current_agent
    next_agent = userdata.personas[name]

    # Remember who was just active so the next agent can copy context
    userdata.prev_agent = current_agent

    # Returning an Agent from a function tool tells LiveKit to swap
    return next_agent

The method on BaseAgent records the previous agent for context copying, then returns the next agent. LiveKit sees the returned Agent and swaps the session.

triage_agent.py
python
from livekit.agents.llm import function_tool
from livekit.agents.voice import Agent
from base_agent import BaseAgent
from user_data import RunContext_T

class TriageAgent(BaseAgent):
    """First contact. Greets the caller and routes to a specialist."""

    @function_tool
    async def transfer_to_support(self, context: RunContext_T) -> Agent:
        """Transfer to Support for appointments, refills, medical records."""
        await self.session.say(
            "Let me connect you with our Patient Support team."
        )
        return await self._transfer_to_agent("support", context)

    @function_tool
    async def transfer_to_billing(self, context: RunContext_T) -> Agent:
        """Transfer to Billing for insurance and payment questions."""
        await self.session.say(
            "I will transfer you to our Billing department."
        )
        return await self._transfer_to_agent("billing", context)

Each tool has a descriptive docstring. The LLM reads those docstrings to decide when to call each tool. The session.say line gives the caller a verbal acknowledgement before the handoff lands.

A lot. The LLM sees the function name, arguments, and docstring when it decides which tool to call. A vague docstring leads to wrong routes. Write the docstring as if you are coaching a new employee on when to use the tool, with specific triggers like insurance, copay, prescription, or appointment.