Handbooks  /  Voice AI Real-Time Agents
Handbook~15 min readAI · Engineeringworked math + runnable code
The Voice AI Real-Time Agents Handbook

Talk is
a stopwatch.

A voice agent has one job that a chat box doesn't: it has to answer fast enough to feel like talking. Under the hood it's three stages in a row — hear it (speech-to-text), think (the LLM), say it (text-to-speech) — and the user waits for all three. Human conversation has a rhythm: reply in a beat and it feels alive; pause a second too long and it feels like a dropped call. So a voice agent isn't really about intelligence — it's about a latency budget, and this handbook is the arithmetic of spending it.

01

Three stages, one wait

A real-time voice agent is a pipeline. The user speaks; speech-to-text (STT) turns the audio into words; the LLM reads those words and generates a reply; text-to-speech (TTS) turns the reply back into audio the user hears. Three stages, run in order, because each needs the previous one's output: the LLM can't think until it has a transcript, TTS can't speak until it has text.

The consequence is simple and unforgiving: the user hears nothing until all three have finished, so the response time they feel is the sum of the stage times. It doesn't matter that your LLM is brilliant if the round trip takes two seconds — by then the human has already felt the silence. That's what makes voice different from a chat interface, where a spinner is socially acceptable. In conversation, the clock is running the whole time, and the budget for that clock is tiny.

The one-sentence version

A voice agent is STT + LLM + TTS in series, and the user waits for the whole sum — so it feels conversational only when that sum stays under the human turn-taking threshold, roughly 800 milliseconds.

02

The latency budget

Human turn-taking is astonishingly fast. In natural dialogue people begin replying within a couple hundred milliseconds of the other person stopping — often before, by predicting the end of the sentence. A gap much beyond that starts to feel like something's wrong: the other person is confused, distracted, or the line is bad. Cross about a second and the illusion of conversation collapses entirely.

So a voice agent has a latency budget — a hard ceiling on the end-to-end response time, commonly targeted around 800 ms to feel snappy (and every millisecond under that feels better). Every stage spends against it: STT might take 150 ms, the LLM 400 ms, TTS 200 ms. Add them up and you're at 750 ms — under budget, barely. Swap in a slower LLM at 700 ms and you're at 1050 ms, over the line, and the agent now feels like a laggy phone tree no matter how good its answers are. The whole engineering problem is keeping that sum under the ceiling.

03

Attack the bottleneck

When the sum is over budget, not all stages are equal. The one to fix first is the bottleneck — the slowest stage — because a fixed reduction there moves the total the same as anywhere else, but the bottleneck is where the biggest reductions are available. If the LLM is 700 ms and STT/TTS are 150 ms each, shaving the LLM is the only move that can bring a 1000 ms pipeline under 800 ms; trimming an already-fast stage barely helps.

Where the milliseconds go
STTTranscribe speech → text. ~100–300 ms; stream partial transcripts to start the LLM early.
LLMGenerate the reply. Often the bottleneck; smaller/faster model + token streaming.
TTSSynthesize audio → speech. ~100–300 ms; stream audio as tokens arrive.
TotalUser feels the sum — must fit the ~800 ms budget to feel like conversation.

So the optimization loop is: measure each stage, find the max, cut it, remeasure. Sometimes the bottleneck is the LLM (use a faster model, or fewer tokens); sometimes it's a network hop (colocate the services); sometimes it's TTS warmup. The discipline is to always spend your effort on the slowest stage, because that's the only place the total budget has real slack to give.

04

The budget math

The stages run in series, so end-to-end latency is their sum, and the pipeline "fits" only when that sum is within the conversational threshold:

total  =  tSTT + tLLM + tTTS     fits  ⟺  total ≤ threshold

150 + 400 + 200 = 750 ms ≤ 800 → fits. Swap the LLM to 700 → 1050 > 800 → over budget, feels laggy.

The stage to optimize is the one that dominates the sum — the max — and shaving the bottleneck is what pulls an over-budget pipeline back under the line:

bottleneck  =  argmax(tSTT, tLLM, tTTS)   ⟹   cut it → total falls fastest toward the budget

Bottleneck of (150, 700, 200) is the LLM; halving it to 350 turns a failing 1050 ms into a passing 700 ms. The runnable version below computes the total, the fit, the bottleneck, and the headroom.

RUN IT YOURSELF

The latency budget, in numbers

A voice agent's response time is the sum of its three stages, because the user waits for STT, then the LLM, then TTS. It feels conversational only when that sum stays under the threshold — around 800 ms. The stage to optimize is the bottleneck, the slowest one, since that's where the biggest cuts live: halving a 700 ms LLM turns a failing 1050 ms pipeline into a passing 700 ms. Change the stage times or the threshold and watch the total, the fit, and the bottleneck move.

CPython · WebAssembly
05

Streaming buys time

The math above treats the stages as strictly sequential — a worst case. Production voice agents beat it with streaming: each stage emits partial output as it works instead of waiting to finish, so the stages overlap. Streaming STT sends transcript fragments while the user is still talking, so the LLM can start on the first words before the sentence ends. A streaming LLM emits tokens one at a time, and streaming TTS speaks those tokens as they arrive — so the user starts hearing a reply while the LLM is still generating the rest of it.

The effect is dramatic: the perceived time-to-first-audio becomes far smaller than the naive sum, because the user only waits for the pipeline to begin producing sound, not to fully complete. This is the main reason a voice agent can feel real-time even when each stage individually takes a few hundred milliseconds. The sequential sum is still the right mental model for the budget — it's the ceiling you'd hit without overlap — but streaming is the lever that lets a real system live comfortably under it.

06

Pitfalls

The first trap is optimizing the wrong stage. It's tempting to tune the part you understand best, but if you shave 50 ms off a 150 ms STT while a 700 ms LLM sits untouched, you've barely moved the total. Always measure all three and attack the max. The second is ignoring the network: STT, LLM, and TTS are often three separate services, and every hop between them adds real time. Colocating them, or cutting round trips, can matter as much as the model choice.

Two more that make voice feel broken even under budget. Turn-taking: the agent must know when the user has actually stopped speaking (voice-activity detection and endpointing) and must gracefully handle being interrupted — a human who keeps talking over you feels awful, and so does an agent that won't yield. And no thinking-sound: because the budget is so tight, good voice agents fill unavoidable gaps with a natural filler ("let me check…") so the silence never reads as a freeze. Get the budget right and the interruption handling humane, and a voice agent stops feeling like a phone menu and starts feeling like a conversation. The rule is the whole handbook: keep the sum under the clock, and always cut the slowest stage first.

Worth knowing

End-to-end latency is the sum of STT + LLM + TTS, so budget against that sum, optimize the bottleneck, and use streaming to overlap the stages. But a natural-feeling voice agent also needs good endpointing and interruption handling — low latency is necessary, not sufficient.

Frequently asked

Quick answers

What is a voice AI agent?

A pipeline that hears you (STT), thinks (LLM), and speaks back (TTS), fast enough to feel like a conversation.

Why does latency matter?

The user waits for all three stages, and human dialogue breaks down past ~800 ms — so the sum of stage times decides whether it feels alive.

How do you cut latency?

Find the bottleneck (usually the LLM), shave that first, cut network hops, and use streaming so the stages overlap.

What is streaming?

Each stage emits partial output as it works, so they overlap — the user hears a reply while the LLM is still generating it.

Finished this one? 0 / 99 Handbooks done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.