lumo

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

  1. 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.
  2. 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.
  3. Handle barge-in, back-channels, silence, spoken numbers and hand-offs while keeping a chat assistant’s safety limits on the phone.
  4. 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 min
  1. Speech to text and text to speech17 min
  2. Design: a safe customer-facing assistant30 min
  3. Where the time goes in an LLM request17 min

Video 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.

Check yourselfAnswer in your head first, then reveal
Amara stops talking. How does the system know she has finished her question, and not just paused to think?Show answerHide answer
It can’t know for sure. It waits for a short silence, half a second by default in one popular framework, and may also ask a small model whether her words sound finished. That wait comes before any model call, on every turn. Not “the speech to text tells it”: a transcript says what was said, not whether more is coming.

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.

Every stage streams: each passes pieces to the next as soon as it has them, instead of waiting for a whole.

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.

Check yourselfAnswer in your head first, then reveal
Tomás streams every stage but keeps Chat’s prompts and settings. Which stage now takes longest?Show answerHide answer
The model’s hidden thinking: about 2 s, more than everything else together. Chat assumed 200 thinking tokens, and at 100 tokens a second no audio can start until they are done. Not speech to text or text to speech: streamed, each takes a few hundred milliseconds.

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_tokens reports 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.

With preemptive generation, retrieval and the classifier run inside the turn wait. The caller hears the first sentence while the model writes the second.

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.

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.

Code checks each sentence before it is spoken. The slow classifier moves after speech, where it audits rather than blocks.

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.

python
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 sent

allowed_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.

A cascade keeps text in the middle, where code can check it. A speech-to-speech model has fewer stages, but its text arrives with the audio.

Should Deskora Voice run on a cascaded pipeline or a speech-to-speech model?

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.

cost per minute=telephony+speech to text+characters spoken×text-to-speech price+turns×cost per turn+recording
cost per turn = input tokens × input price + (thinking + reply tokens) × output price, summed over the turn’s calls

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.

Recording

$0.0025 per minute

Twilio’s recording price; storage is extra 6.

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

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

Keeping words the caller never heard

Trim the history to the audio that was played, or the model refers to things the caller never heard.

Treating every sound as an interruption

A cough then stops Deskora mid-sentence. Set a minimum speech length, and resume after a false start.

Timing only the average turn

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

Check yourselfAnswer in your head first, then reveal
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 answerHide answer
About 2 s of thinking lands before any audio, so the turn goes from about 1.3 s to about 3.3 s, past the 2-second limit. The cost rises by only about $0.0001 a turn. Not “the bill grows”: in voice, thinking is a delay problem first.
Check yourselfAnswer in your head first, then reveal
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 answerHide answer
Only the first sentence, because that is all she heard. If the second stays in, the next reply may refer to a delay she never heard about. Not the whole generated reply: the history must match the audio that was played.
Check yourselfAnswer in your head first, then reveal
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 answerHide answer
Whether code can check each reply before the caller hears it. With transcripts that arrive alongside the audio, a promise is caught only after it was spoken. Not the speed: Deskora already meets its 1.5-second goal.
Check yourselfAnswer in your head first, then reveal
A shop moves its line to a toll-free number. What does one call minute cost now?Show answerHide answer
Toll-free inbound is $0.0220 instead of $0.0085 a minute, so the minute rises by $0.0135, from about $0.040 to about $0.054. The model’s $0.0034 doesn’t change.

Summary

  1. 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.
  2. 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.
  3. Code checks each sentence before it is spoken, the input classifier gates the first sentence, and the output classifier audits after speech.
  4. 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.
  5. A call minute costs about $0.040: telephony and text to speech are over half, and the model is under a tenth.

Report a mistake

Tell us what’s wrong in this lesson and we’ll fix it.