teachyou.ai academy
← All posts
AI Agents

Voice AI Agents: Architecture and Latency Challenges

Pramod Dutta · May 1, 2026 · 16 min read

Why voice agents feel broken even when every model works fine

You've probably talked to a voice AI agent that nailed the transcription, gave a coherent answer, and still felt unusable. The words were right. The experience was wrong. There was a beat of dead air before the reply started, or the agent barreled through your sentence because it didn't realize you were still talking, or it repeated itself after you interrupted it. None of that is a model quality problem. It's an architecture and latency problem, and it's the single biggest reason voice agents stall out in production while text-based chat agents ship without drama.

Text chat has a forgiving failure mode: a slow response just sits there as a loading spinner, and users wait. Voice has no such buffer. Human conversational turn-taking runs on a rhythm most people never consciously notice — the gap between one person finishing and the other starting is typically a few hundred milliseconds. Miss that window by even half a second and the interaction reads as sluggish, robotic, or rude. This is why building a voice agent is really an exercise in systems engineering wrapped around a few AI models, not a matter of "add a voice API to my chatbot." In this article we'll walk through the full pipeline, where the milliseconds actually go, and the architectural patterns that separate voice agents that feel alive from ones that feel like talking to a fax machine.

The core pipeline: STT, LLM, TTS, and everything between

Every voice agent, regardless of vendor, is built around the same three-stage backbone:

  • Speech-to-Text (STT): converts the caller's audio into text the LLM can reason over
  • Large Language Model (LLM): interprets intent, holds context, decides what to say or do (including tool calls)
  • Text-to-Speech (TTS): converts the LLM's text response back into audio

That sounds simple, but the naive version — record the whole utterance, transcribe it, send it to the LLM, wait for the full response, synthesize it, play it back — is exactly the architecture that produces the multi-second dead air users hate. Production systems instead treat each stage as a streaming pipeline, not a batch job. STT starts emitting partial transcripts while the user is still speaking. The LLM can begin generating tokens before it has "final" confirmation of what was said, in some architectures. And critically, TTS starts synthesizing audio for the first sentence of the response while the LLM is still generating the rest of it.

This streaming-everywhere approach is what compresses a pipeline that could easily take three or four seconds end-to-end down to something in the 700ms-to-1.2-second range, which is roughly the threshold where a response starts to feel conversational instead of transactional. Beyond the three core stages, real systems also need:

  • Voice Activity Detection (VAD): decides when the user has actually stopped talking versus just pausing to think
  • Turn-taking / endpointing logic: the decision layer that says "okay, now respond" — this is a distinct component from VAD, often with its own model
  • Interruption (barge-in) handling: lets the user cut the agent off mid-sentence, the way humans do constantly in real conversation
  • Orchestration layer: the glue that manages state, tool calls, retries, and hands audio between all the above components

Where the milliseconds actually go

If you want to optimize a voice agent, you have to know your enemy, and the enemy is distributed across the entire chain, not concentrated in one obvious bottleneck. A rough mental model of the latency budget looks like this:

  • Network transport (both directions): every hop between the caller's device, your telephony or WebRTC layer, and each model API adds round-trip time. This is why colocating your orchestration server near your model provider's region matters more than people expect.
  • VAD and endpointing: deciding "the user is done talking" isn't instant. Wait too short and you cut people off; wait too long and the agent seems unresponsive. This single decision is often the largest controllable lever in the entire pipeline.
  • STT processing: streaming ASR models emit partial hypotheses quickly, but the "final" transcript — the one you can safely hand to the LLM — usually lags a bit behind to allow for correction.
  • LLM time-to-first-token (TTFT): this is the time from "prompt sent" to "first token back," and it depends heavily on model size, prompt length, and provider load. It is usually the single most variable part of the pipeline.
  • TTS time-to-first-audio-byte: similar idea, but for speech synthesis — how fast can the TTS engine start producing playable audio from the first chunk of LLM text.
  • Function/tool call round trips: if the agent needs to look something up (an order status, a calendar slot, a database record), that's an entirely separate network call injected into the middle of the conversation turn, and it's often the least optimized part of a demo that suddenly falls over in production.

The uncomfortable truth is that no single fix solves voice latency. You cannot buy your way out of it by picking the fastest LLM alone, because a fast LLM paired with slow TTS, or slow endpointing, still produces a laggy agent. Latency budgets have to be managed holistically, stage by stage, with a target ceiling for the whole pipeline and a sub-budget for each component.

Streaming architecture in practice

Let's make the streaming concept concrete. Below is a simplified illustration of how an orchestration layer might coordinate STT partials, LLM token streaming, and TTS chunk synthesis without waiting for any stage to fully complete before starting the next one. This is pseudocode meant to show the shape of the control flow, not a drop-in production script.

async def handle_turn(audio_stream, llm_client, tts_client, stt_client):
    transcript_buffer = ""
    is_final = False

    # Stream STT partials as audio arrives
    async for stt_event in stt_client.stream(audio_stream):
        transcript_buffer = stt_event.text
        is_final = stt_event.is_final

        # Endpointing: only proceed once VAD + STT agree the user stopped
        if is_final and endpoint_detector.should_respond(stt_event):
            break

    # Kick off LLM generation as a token stream, not a blocking call
    llm_stream = llm_client.generate_stream(
        prompt=build_prompt(transcript_buffer),
        max_tokens=300,
    )

    sentence_buffer = ""
    async for token in llm_stream:
        sentence_buffer += token

        # Flush to TTS at sentence boundaries, not at end-of-response
        if is_sentence_boundary(sentence_buffer):
            audio_chunk_stream = tts_client.synthesize_stream(sentence_buffer)
            await play_audio(audio_chunk_stream)
            sentence_buffer = ""

    # Flush any trailing partial sentence
    if sentence_buffer:
        await play_audio(tts_client.synthesize_stream(sentence_buffer))

The key architectural decision baked into this snippet is the sentence-boundary flush: instead of waiting for the LLM to finish its entire response before sending anything to TTS, the orchestrator ships completed sentences (or clauses) to the speech synthesizer the moment they're available. This single pattern is responsible for a large share of the perceived latency improvement in modern voice agents, because it overlaps LLM generation time with TTS synthesis time and audio playback time instead of stacking them sequentially.

Interruption handling and the barge-in problem

Humans interrupt each other constantly, and a voice agent that can't handle being interrupted feels fundamentally broken, no matter how good its answers are. Barge-in — letting the user speak over the agent and having the agent stop, listen, and respond to the new input — sounds like a nice-to-have until you experience an agent that doesn't have it. It talks over you, ignores you, or worse, keeps finishing its sentence about your account balance while you're already asking a completely different question.

Implementing barge-in well requires several things working in concert:

  1. Continuous VAD monitoring even while the agent's TTS audio is playing, not just while waiting for user input
  2. A fast "cancel" signal that can halt TTS playback and LLM generation mid-stream when user speech is detected
  3. State management that discards or gracefully truncates the in-flight response instead of leaving the conversation history in a weird half-finished state
  4. Careful tuning to avoid false positives — background noise, coughs, or the agent's own audio bleeding into the microphone (echo) triggering false interruptions

That last point is genuinely tricky. Without proper echo cancellation, a voice agent can end up interrupting itself, hearing its own TTS output through the user's speaker-to-microphone loop and mistaking it for user speech. This is one of the areas where a lot of homegrown voice agent builds quietly fail, because it only shows up under real acoustic conditions, not in a quiet testing environment with a headset.

Turn-taking: the hardest unsolved problem in voice UX

Endpointing — deciding when the user is actually finished talking — deserves its own section because it's genuinely difficult and it's the part of the system most likely to make an otherwise well-built agent feel unnatural. Humans use a mix of prosody, syntax, semantic completion, and even eye contact or gesture (in person) to know when it's their turn to speak. A voice agent mostly has audio: silence duration, maybe some pitch and pace cues, and text-level cues from the transcript.

The naive approach — "if there's been N milliseconds of silence, the user is done" — creates two failure modes depending on how you tune N:

  • Too short: the agent cuts in while the user is mid-thought, especially during natural pauses like "I want to book a flight to... um... Chicago"
  • Too long: the agent feels laggy and unresponsive, especially for short, complete utterances like "yes" or "cancel my order"

More sophisticated systems use semantic endpointing, where a lightweight model looks at the partial transcript itself to judge whether it forms a complete thought, combined with adaptive silence thresholds that shorten for clearly complete sentences and lengthen for trailing conjunctions or filler words. Some architectures also maintain per-user or per-context tuning, since a customer support bot handling frustrated callers who talk fast benefits from different thresholds than a slower-paced conversational tutor.

This is worth dwelling on because it's an area where teams often underinvest. It's tempting to treat endpointing as a solved, boring configuration value, but in practice it's one of the highest-leverage places to spend engineering time if you want a voice agent that feels natural rather than mechanical.

Model selection tradeoffs across the pipeline

Every component in the pipeline has its own speed-versus-quality tradeoff curve, and the right choice depends on the use case rather than always picking the "best" model in isolation.

  • STT models: streaming ASR models optimized for low-latency partials will sometimes sacrifice a bit of transcription accuracy, particularly on accents, domain-specific jargon, or noisy audio. For a voice agent handling technical support calls full of product names, you may need custom vocabulary injection or fine-tuning even if it costs a few extra milliseconds.
  • LLMs: smaller, faster models reduce time-to-first-token dramatically but can produce shallower reasoning, which matters if your agent needs to handle multi-step logic, tool orchestration, or nuanced conversation. Many production systems use a smaller/faster model for simple intents and route to a larger model only when the conversation genuinely needs deeper reasoning.
  • TTS engines: some TTS systems optimize for expressiveness and naturalness at the cost of synthesis speed, while others are built for low time-to-first-audio-byte. Voice cloning or highly custom voice personas can also add processing overhead compared to stock voices.

The architectural implication is that a well-designed voice agent is rarely a single model triplet. It's often a small routing layer that picks the right STT/LLM/TTS combination based on conversation state, urgency, and complexity — treating latency budget as a resource to be spent deliberately rather than a fixed cost you simply absorb.

Telephony, transport, and the parts nobody demos

A huge amount of real-world voice agent complexity lives outside the AI models entirely, in the telephony and transport layer. If your agent needs to work over a phone line (PSTN), you're dealing with codec constraints, jitter buffers, and carrier-introduced latency that you have no control over. If it's a WebRTC-based browser or app experience, you're dealing with client-side network variability, NAT traversal, and device audio quality that varies wildly across hardware.

This matters architecturally because it means your orchestration layer needs to be resilient to variable input quality and variable network latency, not just fast in the best case. Things to design for:

  • Jitter buffering strategies that smooth out inconsistent audio delivery without adding unnecessary delay
  • Graceful degradation when STT confidence is low, rather than confidently acting on a garbled transcript
  • Reconnection and session resumption logic for dropped connections, especially on mobile networks
  • Region-aware routing so that a caller in one part of the world isn't round-tripping to a server on the other side for every single turn

None of this shows up in a polished demo recorded over a good office Wi-Fi connection with a quality microphone. It shows up the moment real users call from a moving car, a crowded room, or a budget Android phone, which is exactly why voice agent projects that look great in a demo often need substantial rework before they're production-ready.

Observability: you can't fix what you can't measure

Because latency in a voice agent is distributed across so many independent components, debugging "the agent feels slow" without instrumentation is close to impossible. You need per-stage timing captured on every single turn: STT partial latency, STT final latency, LLM time-to-first-token, LLM total generation time, TTS time-to-first-byte, and total round-trip turn time. Without that breakdown, "it feels slow" could mean a dozen different things, and engineers end up guessing instead of diagnosing.

Beyond raw timing, useful voice agent observability also tracks things like interruption frequency (a spike might mean your endpointing is too slow), transcript correction rate (how often the final STT output differs meaningfully from the partial), and fallback/escalation rate (how often the agent has to punt to a human or a clarifying question). These signals tell you where the conversation design itself is breaking down, not just where the infrastructure is slow.

A practical habit worth adopting early: log structured latency breakdowns per turn from day one, even in a prototype. Retrofitting observability into a voice pipeline after it's already tangled together with business logic is far more painful than building the instrumentation in from the start.

Tool calls and grounding without breaking the conversation

Most voice agents worth building need to do more than chat — they need to check an order status, book an appointment, pull account details, or update a record somewhere. That means tool calls, and tool calls are where a lot of otherwise well-tuned voice pipelines fall apart, because a database lookup or a third-party API call doesn't care that it's now sitting in the middle of a live phone conversation with a real human waiting on the other end.

A few patterns help keep tool calls from becoming dead air:

  • Filler / stalling responses: when the agent knows a tool call will take a noticeable amount of time, it can say something like "let me check that for you" before the call resolves, which buys a second or two of perceived responsiveness without lying about what's happening
  • Parallel tool calls where possible: if the agent needs two independent pieces of information, fire both requests concurrently instead of sequentially, since sequential round trips compound linearly and voice users notice every added second
  • Timeouts with graceful fallback: a tool call that hangs should never be allowed to hang the whole conversation; set an aggressive timeout and have the agent explain a delay or offer to follow up rather than going silent
  • Caching frequently requested, slow-changing data: if the same lookup happens across many calls (store hours, product catalog basics, FAQ-style facts), cache it aggressively so the tool call latency doesn't recur on every conversation

There's also a design tension worth naming directly: the more tool calls an agent needs to make to answer a question well, the harder it becomes to keep the conversation feeling natural, because each tool call is a real network hop with real variance. This is a legitimate reason to sometimes choose a simpler, less "smart" agent design that trades off some capability for a tighter, more predictable latency profile — especially for a voice-first product where responsiveness is part of the core user experience, not a secondary concern.

Testing voice agents is different from testing text agents

A pattern worth calling out separately: teams that have built solid evaluation pipelines for text-based LLM features often assume the same approach transfers cleanly to voice, and it mostly doesn't. Text agent evals can largely ignore timing and focus on correctness of output. Voice agent evals have to test correctness and timing simultaneously, plus a set of failure modes that simply don't exist in text.

Things worth building into a voice agent test suite specifically:

  • Latency regression tests: run the same set of scripted utterances against every build and flag any stage whose timing has drifted beyond an acceptable threshold, the same way you'd flag a performance regression in any other system
  • Interruption scenario tests: simulate a user barging in at different points in the agent's response — start, middle, end — and verify the agent handles all three cleanly instead of only the happy path
  • Noisy audio and accent variation tests: run the same test utterances through degraded audio (background noise, low bitrate, different accents) to catch STT accuracy cliffs before real users do
  • Adversarial silence tests: long pauses, mid-sentence hesitation, false starts — these are exactly the inputs that expose bad endpointing tuning, and they're trivial to script once you've identified them as a category worth testing

Building this kind of test harness early pays for itself quickly, because the alternative is discovering these failure modes from angry users or a spike in call abandonment, which is a far more expensive way to learn the same lesson.

Bringing it all together

Voice AI agents expose a truth that text-based agents can mostly hide: the model is only one part of the system, and often not even the hardest part to get right. The architecture around the model — streaming coordination, endpointing, interruption handling, telephony resilience, and observability — is where most of the engineering effort actually goes, and it's where most production voice agent projects either succeed or quietly stall out.

If you're building or planning to build voice agents, the mental shift that matters most is treating latency as a budget to be allocated across every stage of the pipeline, not a single number to optimize after the fact. Get the streaming architecture right, invest real effort in endpointing and barge-in handling, instrument every stage from day one, and treat the telephony/transport layer as a first-class engineering concern rather than an afterthought. Do that, and you end up with an agent that feels like a conversation. Skip it, and you end up with a transcription pipeline wearing a voice costume.

If you want to go deeper on building production-grade agent systems — including the orchestration patterns, tool-calling architectures, and evaluation practices that apply directly to voice agents — check out 30 Days of Hermes Agent here on teachyou.ai. It walks through building real agentic systems from the ground up, the same kind of systems-level thinking this article has been arguing voice AI genuinely requires.