Module 13 · Lesson 4 of 7Free
Voice agents: real-time spoken conversation
- Level
- Advanced
- Topic
- Multimodal and voice applications
- Reading time
- 17 min
When you finish this page you can
- Trace one spoken turn through voice activity detection, turn detection, streaming speech to text, the streamed model call and streaming text to speech, and say what each stage waits for.
- Build a latency budget for one turn and cut it to about 1.5 seconds with streaming, speaking on the first sentence, lower effort and an earlier start.
- Handle barge-in, back-channels, silence, spoken numbers and hand-offs while keeping a chat assistant’s safety limits on the phone.
- Estimate what one call minute costs, starting from the formula, and say which part of the bill is the model.
Builds on 3 lessons
Start with lesson 317 minVideo lesson · coming soon
Eight seconds of silence on a phone line
Deskora is a help desk app. A shop’s support team uses it to answer customer emails and chats from one shared inbox, and each customer problem becomes a ticket. Priya is a support agent at a kitchenware shop on Deskora. Tomás is the Deskora engineer who builds its AI features.
Deskora Chat already answers shoppers on a shop’s website with no support agent in between: help-center answers and verified order status, everything else to Priya. Shops now want the same on their phone line, Deskora Voice.
Tomás’s first test reuses Chat’s safe path. A caller named Amara asks, “Has my order shipped?” Chat checks the question, drafts a reply and checks the whole reply before letting it out. On the phone that is about 8 seconds of silence. After two seconds Amara says “Hello?”, and the system treats that as a new question.
So the question for this lesson is: how do you make a model answer a phone call fast enough to feel like a conversation, without loosening any of Deskora Chat’s limits?
Amara, the call volumes and every stage time in this lesson are this lesson’s scenario and estimates. Chat’s stage times come from the Deskora Chat design.
Amara stops talking. How does the system know she has finished her question, and not just paused to think?Show answer
One spoken turn passes through six stages
A spoken assistant like this is often called a voice agent, even when it takes no actions. Here is one turn.
Diagram not drawn yet: voice-agent-pipeline.svg
Left to right. 1: the caller’s phone sends audio over the phone network. 2: telephony carries it to Deskora’s voice server as small audio packets. 3: voice activity detection and turn detection mark speech, silence and the end of the turn. 4: streaming speech to text turns audio into words while the caller talks. 5: the voice server runs the checks and a streamed Claude call. 6: streaming text to speech turns each finished sentence into audio. A dashed arrow carries that audio back through telephony to the caller.
Voice activity detection (VAD) is a small model that answers one question about each slice of audio: is someone speaking? Turn detection builds on it to decide the caller has finished. In LiveKit Agents, a turn detector model predicts the end of a turn from both the meaning and the sound of the speech, on top of VAD 3. Speech to text and text to speech covers the two speech services.
The point is overlap: earlier parts of a reply are spoken while the model writes later parts 2, and speech to text works while Amara talks. How much time does one turn have?
The budget: about 1.5 seconds from silence to voice
In a study of ten languages, people typically started replying within about 200 ms of a question ending 1. A machine must wait for silence first, so it can’t match that. Pipecat’s docs say typical voice interactions complete in 500 to 800 ms 2.
Deskora’s goal (an estimate for this scenario): first sound within 1.5 s of the caller’s last word on a typical turn, and never 2 s of silence. If a reply is late, code plays a short fixed phrase.
Tomás streams every stage but keeps Chat’s prompts and settings. Which stage now takes longest?Show answer
One help-center turn, timed from Amara’s last word, at the course’s assumed 100 output tokens a second:
| Stage | Chat path, held | Streamed | Streamed, with cuts |
|---|---|---|---|
| Phone network in | 0.1 s | 0.1 s | 0.1 s |
| Wait to confirm the turn ended | 0.5 s | 0.5 s | 0.3 s |
| Final transcript | 0.1 s | 0.1 s | 0.1 s |
| Input classifier and retrieval, side by side | 1.3 s | 1.3 s | 0.3 s (retrieval only) |
| Model: first token | 3.7 s for the whole reply | 0.4 s | 0.3 s |
| Model: thinking | (inside the 3.7 s) | 2.0 s | 0 s |
| Model: words before speaking | (inside the 3.7 s) | 0.15 s | 0.15 s |
| Output checks | 2.1 s | 0.01 s | 0.01 s |
| Text to speech, first audio | 0.2 s | 0.2 s | 0.2 s |
| Phone network out | 0.1 s | 0.1 s | 0.1 s |
| First sound | about 8.1 s | about 4.9 s | about 1.6 s |
The 1.3 s, 3.7 s and 2.1 s come from the Deskora Chat design; the other times are this lesson’s assumptions.
Each cut moves one row:
- Stream everything, and speak on the first sentence. Text to speech starts after about 15 tokens, not after a 3.7-second reply.
- Lower effort. At low effort, adaptive thinking may skip thinking on easy inputs 8;
usage.output_tokens_details.thinking_tokensreports what there was. 200 thinking tokens cost about $0.0001, but 2 seconds of silence. - A shorter turn wait. With a turn detector model, the silence wait can drop from 0.5 s to about 0.3 s.
- The classifier beside the draft. Code holds the first sentence until the input classifier passes.
- A short, cached prompt shortens prefill; see Caching for LLM apps. Claude Haiku 5.5 is already the small model.
One more cut reaches about 1.3 s. LiveKit calls it preemptive generation: the model starts on the interim transcript during the turn wait (on by default in livekit-agents 1.8.10). If the final words differ, the early call is redone. Order-status turns skip retrieval and reach about 1.0 s.
Diagram not drawn yet: voice-turn-overlap.svg
A time axis from 0 to 1.5 seconds after the caller’s last word. Row 1, caller: speech ends at 0. Row 2, phone network in: 0 to 0.1 s. Row 3, turn wait: silence from 0.1 to 0.4 s, final transcript at 0.5 s. Row 4, started early on the interim transcript: retrieval 0.2 to 0.5 s, model first token at 0.8 s, first sentence complete at 0.95 s. Row 5, input classifier: 0.2 to 0.8 s, a green pass mark before the first sentence. Row 6, text to speech: first audio at 1.15 s. Row 7, network out: the caller hears the first word at about 1.25 s, while the model is still writing sentence two. A dashed line marks the 1.5 s goal.
Where the time goes in an LLM request teaches this stage model. But streaming breaks Chat’s rule that every check finishes first.
Decision: keep Chat’s checks without the 8-second hold
Design: a safe customer-facing assistant held each reply and checked it whole, since a few seconds behind a typing indicator was acceptable. A phone has no typing indicator.
How should a reply reach the caller?
Hold the whole reply, check it, then speak it
About 8 s of silence every turn. Callers say “Hello?”, which starts a new turn and an argument with the machine.
Stream every token straight to text to speech
First sound at about 1.3 s, but nothing checks what is said. “I’ll send you a replacement” is spoken before any code sees it, and a spoken promise can’t be unsaid.
Speak sentence by sentence, each one checked in code first
First sound at about 1.3 s. Each sentence passes checks in code, about 10 ms, before text to speech. The input classifier must pass before the first sentence. The output classifier runs on the spoken reply afterwards, and a hit hands the call to a person.
Why this choice
The code checks are Chat’s output checks, run per sentence: no promise pattern, and no long number missing from the sources or the verified order. They are patterns, not model calls, so they are fast. Chat’s 2.1-second output classifier can’t run per sentence, so it moves after speech.
The smaller check is acceptable because the model has no tools, so the worst it can do is say something. The voice prompt also asks for short sentences that stand alone, so each check sees a whole claim.
When the choice changes. If Deskora Voice ever reads out account details or takes an action, hold that reply and check it whole, after a fixed “One moment while I check that”. If an output classifier ran in about 200 ms, it could move back in front of each sentence.
Diagram not drawn yet: voice-sentence-gate.svg
Left to right inside the voice server. Claude’s token stream enters a sentence splitter. Each complete sentence goes to code checks: promise pattern, long numbers found in the sources or the verified order. A green pass arrow goes to text to speech. A red fail arrow goes to a fixed hand-off phrase and a call transfer. Above, the input classifier runs beside the model and a lock on the first sentence opens only when it passes. Below, a dashed arrow sends the whole spoken reply to the output classifier after speech, which can trigger a hand-off.
Speak each sentence as soon as it passes
The say function hands one sentence to a streaming text-to-speech service and returns once it is queued.
import re
from collections.abc import Awaitable, Callable
import anthropic
from anthropic.types import MessageParam
client = anthropic.AsyncAnthropic() # reads ANTHROPIC_API_KEY from the environment
SENTENCE_END = re.compile(r"(?<=[.?!])\s+")
PROMISE = re.compile(r"\b(I|we)( will|['’]ll| can) (refund|replace|send|cancel|give)", re.I)
LONG_NUMBER = re.compile(r"\d{3,}")
HANDOFF = "I can't sort that out on this call, so I'm passing you to the team."
def is_safe(sentence: str, allowed_numbers: set[str]) -> bool:
if PROMISE.search(sentence):
return False
return all(n in allowed_numbers for n in LONG_NUMBER.findall(sentence))
async def speak_reply(system: str, messages: list[MessageParam], allowed_numbers: set[str],
say: Callable[[str], Awaitable[None]]) -> list[str]:
"""Stream one reply and speak each sentence that passes. Returns what was sent to speech."""
sent: list[str] = []
buffer = ""
async with client.messages.stream(
model="claude-haiku-5-5",
max_tokens=400, # thinking counts toward this too
system=system,
messages=messages,
output_config={"effort": "low"},
) as stream:
async for text in stream.text_stream:
buffer += text
*sentences, buffer = SENTENCE_END.split(buffer)
for sentence in sentences:
if not is_safe(sentence, allowed_numbers):
await say(HANDOFF)
return sent + [HANDOFF]
await say(sentence)
sent.append(sentence)
if buffer.strip():
last = buffer.strip() if is_safe(buffer, allowed_numbers) else HANDOFF
await say(last)
sent.append(last)
return sentallowed_numbers holds the verified order number and the numbers in the retrieved articles. The call loop runs speak_reply as an asyncio task and transfers the call if the last item is HANDOFF. When Amara talks over Deskora, the loop cancels the task and stops text to speech. Cancelling leaves the async with block, which closes the stream.
One run might send “Your order shipped on Monday.” and “It should arrive by Thursday.” as two sentences.
That reply is an example; model output differs between runs. On Claude Haiku 5.5, as of October 2026, one voice turn of about 3,600 input and 40 output tokens costs about $0.0004 7.
A phone call has rules a chat doesn’t
A call never pauses, so the voice server needs a rule for each of these moments.
| Moment | What happens | Deskora’s rule |
|---|---|---|
| Barge-in: the caller talks over Deskora | Speech while audio plays | Stop speaking, cancel the model stream, listen. LiveKit Agents 1.8.10 needs 0.5 s of speech by default |
| Back-channel: “mm-hm”, “right” | A short sound meaning “go on” | Keep talking. LiveKit’s adaptive mode can tell it from an interruption 3 |
| A false start | A sound, then quiet | Resume; LiveKit’s default is after 2 s of silence |
| Silence | The caller says nothing | After 6 s, “Are you still there?”; after two more, goodbye and a ticket for Priya (scenario numbers) |
| Spoken numbers | “Four eight two three six” comes back as “48 236” | Keypad entry, read back digit by digit |
| Hand-off | Out of scope, or “a person, please” | In opening hours, transfer to Priya; otherwise, a message becomes a ticket |
After a barge-in, the history must hold only what Amara heard. Text to speech reports how much audio it played, and the frameworks cut the history to match.
Keypad digits arrive as DTMF tones, the beeps a phone sends for each key, and they are exact. A transfer comes with a ticket holding the transcript, so Amara doesn’t repeat herself; Human in the loop covers what a good hand-back contains.
Same limits, new channel: safety on the phone
- The AI notice comes first, as recorded audio. Fixed words, approved by Hana, the shop’s manager: this is the shop’s AI assistant, what it can’t do, and that pressing 0 reaches a person. The model never writes it. AI regulation covers the law.
- Recording needs consent. Rules differ by country, and some places need everyone’s agreement. The notice says the call is recorded and why, and each shop can turn recording off. This is not legal advice.
- Verify before any order status. Chat’s rule: order number and the order’s email, compared in code, one reply for every failure, three tries, then a hand-off. The number comes by keypad; the spoken email is read back first. Caller ID is never proof, because it can be faked.
- No actions. The model has no tools. Code gives it the verified order as a document. Refunds, replacements and complaints go to Priya.
- Mask what callers say aloud. Callers read card numbers out, so the transcript is masked before the model, the logs or the ticket.
Every one of these rules works on text, which is the main argument against the alternative.
Speech-to-speech models skip the text
A speech-to-speech model takes audio in and gives audio out, with no separate speech steps. Google’s Gemini Live API is one: it streams audio both ways over a stateful , supports barge-in and function calling, and returns transcripts of both sides 4. The Claude API documents no audio input as of October 2026.
Diagram not drawn yet: cascaded-vs-speech-to-speech.svg
Two equal panels. Left, cascaded: audio in, speech to text, Claude, sentence checks in code, text to speech, audio out; a green label says text exists before every word is spoken. Right, speech to speech: audio in, one speech-to-speech model, audio out; a transcript comes out beside the audio; a red label says checks can only read what was already said.
Should Deskora Voice run on a cascaded pipeline or a speech-to-speech model?
Cascaded: speech to text, Claude, text to speech
More stages, each tuned on its own, and about 1.3 s to first sound. Every check, mask and eval from Deskora Chat works on text, unchanged.
A speech-to-speech model
Fewer hops, and the model hears tone of voice. But the transcript arrives with the audio, so a promise is checked after the caller heard it, and the reply comes from a model Deskora’s evals never tested.
Why this choice
The checks need text before speech, and only the cascade has it. A new model would also mean rebuilding Deskora’s eval sets and judges.
When the choice changes. For a line with nothing to protect, such as opening hours, fewer hops may win. If a speech-to-speech model lets code approve text before audio is made, the main objection goes away.
Frameworks and the phone network
Turn detection, barge-in and history trimming are hard to build by hand. Two open-source Python frameworks do them, described here from their docs and source:
| Pipecat | LiveKit Agents | |
|---|---|---|
| Model | A pipeline of frame processors; BSD-2 licence 2 | An agent session with configurable turn handling 3 |
| Turn end | VAD plus turn strategies; VAD waits 0.2 s of silence by default (1.14.0) | Turn detector model with Silero VAD; silence wait 0.5 to 3.0 s by default (1.8.10) |
| Interruptions | An interruption frame cancels the agent’s output | Minimum speech, false-start resume, back-channel handling |
| Phone calls | Serializers for Twilio, Telnyx, Plivo, Vonage and others | SIP, with a beta task that transfers a call to a person |
Either takes the same Claude call and sentence gate; choose on the phone connection and turn controls you need.
Telephony in one paragraph. A shop’s number lives with a telephony provider. When Amara calls, the provider streams the audio to Deskora over a WebSocket, or connects the call through a SIP trunk, a standard link between phone systems and software. Phone audio is narrowband: Pipecat’s Twilio serializer uses 8,000 samples a second, so pick a speech-to-text model that handles phone audio. The provider bills by the minute.
What one call minute costs
The unit is one completed call, priced by the minute. Per caller turn it counts the input classifier, retrieval on half the turns, the drafting call and the output classifier. Not counted: hosting the voice server and Priya’s time after a hand-off.
Prices as of October 2026: Twilio US, Deepgram pay-as-you-go, Claude Haiku 5.5 at $0.10 per million input tokens and $0.50 per million output tokens.
Telephony
$0.0085 local inbound + $0.0044 media stream = $0.0129 per minute
Twilio US: a local number, plus streaming its audio 6.
Speech to text
1 minute × $0.0077 = $0.0077 per minute
Deepgram Nova-3 streaming, one language, for the whole call 5.
Text to speech
75 words × 6 characters = 450 characters × $0.030 ÷ 1,000 = $0.0135 per minute
Deepgram Aura-2 5. Assumed: Deskora speaks half of each minute, at 150 words a minute.
The model, per turn
classifier $0.00011 + retrieval $0.0005 + draft (3,600 × $0.10 + 40 × $0.50) ÷ 1M = $0.00038 + output classifier $0.00014 = $0.00113
Classifier and rerank costs from the Deskora Chat design. Assumed: no thinking at low effort.
The model, per minute
3 turns × $0.00113 = $0.0034 per minute
Assumed: 3 caller turns a minute.
One minute, one call, one day
$0.0129 + $0.0077 + $0.0135 + $0.0034 + $0.0025 = $0.040 per minute; × 3 minutes = $0.12 a call; × 1,000 calls = $120 a day
A long, 8-minute call with 150 thinking tokens a turn costs about $0.32; the thinking adds only about $0.0002 a minute.
Call length, turns per minute, speaking time and 1,000 calls a day are this lesson’s estimates.
The model is under a tenth of the minute; text to speech and telephony are over half. So the levers are shorter spoken replies and the number type, not a cheaper model. A call costs about ten times a Deskora Chat conversation ($0.011). Deepgram also sells a bundled voice agent at $0.075 a minute, billed on connection time 5.
Where this shows up
- Lab: a voice agent with a latency budget builds this pipeline and times each stage.
- Design: a voice ordering assistant adds an action, an order, to the same turn budget.
- Design: an AI customer support platform puts voice next to chat and email.
Voice gets Amara’s words to the model. Next, a customer sends a photo of a broken lid instead, and asks which part to order.
Common mistakes
Trim the history to the audio that was played, or the model refers to things the caller never heard.
A cough then stops Deskora mid-sentence. Set a minimum speech length, and resume after a false start.
A turn with 300 thinking tokens is 3 seconds slower, and callers remember it. Time each stage and watch a such as p95.
Check your understanding
Tomás moves voice replies to medium effort for better answers. What happens to the first sound on a turn where the model thinks for 200 tokens, and to the cost?Show answer
Deskora says “Your order shipped on Monday.” The model has also written “It was delayed in Leeds.” Amara interrupts before that second sentence plays. What belongs in the history?Show answer
A shop wants Deskora Voice on a speech-to-speech model to save a few hundred milliseconds. What is the first thing to check?Show answer
A shop moves its line to a toll-free number. What does one call minute cost now?Show answer
Summary
- A natural phone turn needs first sound within about 1.5 s of the caller’s last word. Streaming, speaking on the first sentence, low effort and an early start give about 1.3 s, against 8 s on Chat’s held path.
- A turn passes through VAD and turn detection, streaming speech to text, a streamed Claude call and streaming text to speech. The stages overlap; the silence wait comes before any model runs.
- Code checks each sentence before it is spoken, the input classifier gates the first sentence, and the output classifier audits after speech.
- Calls need rules for barge-in, back-channels, silence, spoken numbers and hand-offs. The history keeps only what the caller heard, and order numbers come by keypad.
- A call minute costs about $0.040: telephony and text to speech are over half, and the model is under a tenth.