m
hi
t
Thanks for reaching out, Rahib. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Hi — thanks for reaching out! How can I help you today? If you’re not sure where to start, you can tell me: • what you’re trying to build with LiveKit (web, mobile, or server), • any error messages or unexpected behavior you’re seeing, • or whether you need help with setup, SDKs, rooms, or permissions.
Thanks — I’m responding to your workflow question about handling transfers at different conversation stages. Short answer: use a single agent that tracks conversation state (with
userdata
) and calls transfer tools for SIP/DID transfers; for the “after issue — call a human” case, end the call and have your backend create the new room and place the outbound call (via the LiveKit API). You don’t need multiple agents for this. Recommended design (concise): • Track state in
userdata
◦ e.g., flags:
caller_identified
,
issue_collected
, plus
caller_name
,
caller_issue
. ◦ Update state when you parse identity or issue from the transcript. • Single agent handles dialog and decides transfers ◦ If transfer requested before identity → agent calls tool:
transfer_sip_participant
→ AnswerForce DID (1). ◦ If requested after identity but before issue → agent calls tool → DID (2). ◦ If requested after issue collected → agent triggers escalation tool (see next bullet). • Backend handles outbound human callback and room lifecycle ◦ Agent should end the session and notify your backend (HTTP request, job queue, or agent dispatch metadata). ◦ Backend creates a new room, starts any notifier agent or TTS briefing, and places an outbound SIP call to the support tech using LiveKit API (create SIP participant / outbound trunk). ◦ Pass the collected context (name, issue) in room metadata so the human hears or sees the briefing. Why this approach: • Keeps conversation logic close to the LLM agent (low latency, easy to change prompts). • Keeps durable room and call control in backend where you have full control, logging, and retries. • Avoids needless complexity of multiple agents when role behavior doesn’t diverge. Implementation notes you can act on now: • Add small tool functions in the agent to call: ◦
transfer_sip_participant
for cold transfers to DIDs. ◦ a simple
escalate_to_backend
tool that posts caller context to your backend. • Backend workflow on escalate: a. Create a new LiveKit room. b. Optionally start an agent in that room to play a briefing. c. Create a SIP participant / outbound call to the support tech and connect them to the room. d. Record or log metadata for audit. One question to narrow things down: do you already have SIP trunks/DIDs and a backend capable of creating rooms via the LiveKit API, or would you like a short example of the backend call sequence? Sources: Company directory phone assistant | LiveKit docs | Agents and handoffs | LiveKit docs | Agents telephony integration | LiveKit docs | Outbound Calling Script | LiveKit docs
m
• What do you mean by when you said
Update state when you parse identity or issue from the transcript.
? Could you clarify a little? • How do i run two agents ? Can you talk about that a little?
t
Thanks — I’ll clarify both points. 1) "Update state when you parse identity or issue from the transcript" — what that means • Every spoken user turn is transcribed to text (STT). From that text you should extract structured fields you care about (e.g., caller name, issue type, phone number). • Store those fields on the session
userdata
so they persist across turns and can be read by tools or transfer logic. • Ways to extract: ◦ Simple rules: keyword matching, regex (quick, low-complexity). ◦ LLM-assisted extraction: call a small function/tool that sends the transcript to the model and returns parsed fields (more robust for variability). ◦ Dedicated NER/NLP pipeline if you need high accuracy. • When to update: ◦ On final transcript events (not intermediate partials). Set flags like
caller_identified = True
and
issue_collected = True
and save
caller_name
,
caller_issue
. • Example (conceptual): ◦ On final transcript: if transcript contains a name pattern → set `userdata.caller_name`; if contains words like “bill”, “payment” → set
userdata.issue_type
. • Use those
userdata
flags in transfer tools so the agent knows which DID to use or whether to invoke backend escalation. 2) Running two agents — options and recommended patterns • Sequential handoff (recommended when roles change): ◦ One agent runs first (intake). When it’s time, return or invoke the second agent instance (specialist) via a handoff tool. ◦ You can pass
chat_ctx
or
userdata
into the new agent so context continues seamlessly. ◦ This keeps a single active agent at a time and preserves conversational continuity. • Concurrent / parallel agents (less common): ◦ Start separate AgentSessions in different rooms/processes if you truly need two agents operating independently at once (e.g., one monitors, one talks). ◦ Concurrency is more complex: you must coordinate state and avoid conflicting actions on the same participant. • Alternatives to multiple agents: ◦ Implement subflows as tools or tasks inside one agent (recommended for your transfer use case). Tools can perform verification, call external services, or trigger backend workflows without creating a new agent. • Practical examples: ◦ Handoff: intake agent returns
BillingAgent(chat_ctx=self.session.chat_ctx)
from a tool to transfer control while preserving history. ◦ Backend escalation: agent calls a tool that posts caller context to your backend; backend opens a new room and places the outbound SIP call. If you want, I can: • Show a short code snippet for an LLM-based extraction tool that parses transcripts into
caller_name
/
caller_issue
. • Show a minimal example of an agent handoff returning a new agent with preserved context. Which snippet would help you most? Sources: Agents and handoffs | LiveKit docs | Workflows | LiveKit docs
m
but in my case i am creating a new room from the backend right? So are you suggesting that i can just use the same agent with different prompts or what ?
t
Yes — you can create the new room from the backend and still reuse the same agent. Quick summary and actionable pattern: • Single agent owns the conversation and state: ◦ Keep
userdata
with fields like
caller_identified
,
issue_collected
,
caller_name
,
caller_issue
, and a
stage
value. ◦ Update those fields when you get a final transcript (either with simple rules, an LLM extraction tool, or an NLP extractor). • When escalation is needed after issue collection: a. Agent sets
stage = "ready_for_escalation"
and calls a small tool/function like
escalate_to_backend(context)
. b. That tool posts the collected context (userdata) to your backend (HTTP / queue). c. Agent tells the caller the escalation is in progress and then hangs up. • Backend responsibilities (after receiving the agent’s request): a. Create a new LiveKit room via the LiveKit API. b. Start any notifier agent or TTS briefing in that room (optional). c. Place an outbound SIP call / create SIP participant to the support tech, passing metadata (caller_name, caller_issue, conversation_history). d. Optionally record or log the transfer and notify your system when the tech answers. • Why reuse the same agent: ◦ Keeps conversation continuity and state in one place. ◦ You can change the agent’s behavior by updating instructions/prompts or
stage
rather than deploying multiple agents. ◦ Backend manages durable operations (room lifecycle, outbound SIP) where retries and logs are needed. If you want, I can provide: • A short snippet for an LLM-based extraction tool that updates
userdata
from a final transcript, or • A minimal example showing the agent tool that posts to your backend and the backend sequence to create the room and call the tech. Which snippet would you like? Sources: Agents and handoffs | LiveKit docs | Context Variables | LiveKit docs