· Applied AI · 7 min read
Real-time Audio VAD: The Hardest Problem in Voice Agents
Latency in Voice Agents isn't just network time; it's Turn-Taking latency. If your agent cannot reliably detect when the user has stopped speaking, the illusion of intelligence shatters instantly.

We spend an enormous amount of engineering time agonizing over Time-To-First-Token (TTFT). We benchmark different inference engines, we argue about quantization schemes, and we deploy massive GPU clusters on Kubernetes Engine just to shave 50 milliseconds off the generation time of our text-based LLMs.
But when you build a Voice Agent - a system where a user speaks into a microphone, the audio is streamed, transcribed, reasoned over, synthesized back to audio, and played through a speaker - the definition of “latency” fundamentally changes.
In a text chat, you explicitly hit an “Enter” key to tell the model, “It is your turn to speak.” In a natural voice conversation, there is no Enter key. There is only silence.
The hardest engineering problem in building a real-time Voice Agent is not generating the response. The hardest problem is answering a deceptively simple question: “Is the user done talking?”
The Turn-Taking Dilemma
When two humans converse, we rely on hundreds of unconscious micro-signals to negotiate turn-taking. A dropped vocal pitch, a deliberate pause, an intake of breath, a trailing sentence structure. If I pause for half a second mid-sentence to think of a word (“I was going to the… store”), you do not immediately start shouting your response. You know I am not finished.
A naive Voice Agent does not know this.
If you build a Voice Agent pieced together using standard transcription limits, it will ruthlessly interrupt the user. If the user breathes, the agent detects a 300ms gap in audio, assumes the turn is over, cuts the user off, and starts talking over them.
The user experiences this as incredibly rude and highly robotic. The illusion of a natural, conversational intellect shatters instantly.
Conversely, if you try to fix the interruption problem by setting a hardcoded silence threshold of 2,000 milliseconds (2 seconds) before the agent replies, the conversation feels agonizingly slow. The user asks a question, and then sits in absolute, dead silence for 2 seconds while the agent waits to confirm they are finished, only then incurring the processing and network latency of the LLM pipeline.
You need something smarter. You need Voice Activity Detection (VAD).
Not All Silence is Created Equal
VAD is a specialized, lightweight machine learning model that sits at the very edge of your audio ingestion pipeline. Its sole purpose is to look at a raw waveform and classify it as “Speech” or “Silence”.
But a production-grade VAD does not just output binary flags. It has to act as a sophisticated state machine managing the conversation dynamics.
When you implement a WebRTC stream from a browser or a mobile app to your python backend, you are generally sending audio chunks every 20 milliseconds. The VAD model evaluates each 20ms chunk in real-time.
import numpy as np
from silero_vad import load_silero_vad, get_speech_timestamps
# This runs on the edge or ingestion worker, far away from the heavy LLMs
model = load_silero_vad()
def process_audio_chunk(audio_chunk_16khz: np.ndarray, state_machine):
# This evaluates extremely fast on a single CPU core
speech_prob = model(audio_chunk_16khz, 16000).item()
if speech_prob > 0.5:
state_machine.trigger_speech_detected()
else:
state_machine.trigger_silence_detected()The real engineering happens inside that state_machine.
You cannot trigger a response just because the VAD detects silence. You have to implement a Dynamic Endpointing algorithm.
Implementing Dynamic Endpointing
Dynamic Endpointing means the length of silence required to trigger the agent’s turn changes based on the context of the utterance.
If the user says a short affirmative phrase like “Uh huh” or “Yeah,” they are likely utilizing a “backchannel” communication. They are indicating they are listening, but they do not expect the agent to take the conversational floor. If the user asks a direct question ending with a rising intonation (“What time is the meeting?”), they expect an immediate response.
To build this in Python, you need a multi-layered approach:
- The Fast VAD (Silero/WebRTC): Evaluates every 20ms frame. Identifies gaps.
- The Intermediate Trigger: If the VAD detects 500ms of contiguous silence, it triggers the initial “Speech Paused” event.
- The Semantic Evaluator: This is the secret. While the audio is streaming, you should be streaming the partial transcription alongside it using a fast, local Whisper model or a low-latency Cloud Speech-to-Text API.
When the 500ms pause triggers, the system urgently looks at the transcript of what was just said:
- “I was walking down the…” -> Syntactically incomplete. Increase the endpointing threshold to 1500ms. Keep listening.
- “Can you book the flight for Tuesday?” -> Syntactically complete. Question syntax. Endpoint immediately (500ms) and trigger the LLM reasoning loop.
This requires your Python asyncio loop to gracefully handle concurrent streams of audio bytes and semantic text strings, merging them into a unified conversational context.
The Interrupt Mechanics (Barge-In)
Now we must handle the most chaotic scenario: The user interrupts the agent.
The agent is halfway through a 30-second explanation of an architectural diagram when the user suddenly says, “Wait, go back to the database part.”
This is known as Barge-In.
To handle Barge-In, your VAD model must constantly evaluate the incoming audio stream from the user’s microphone even while the agent is actively transmitting synthesized audio back to the user’s speaker.
This introduces a nightmare problem known as “Echo”. If the user’s microphone picks up the agent’s voice playing from the speaker, the VAD will detect “Speech,” assume the user is interrupting, and halt the agent mid-sentence. The agent is essentially interrupting itself.
You MUST implement Echo Cancellation (AEC) on the client device ideally at the hardware level, or natively in the browser via WebRTC constraints. Only pristine, echo-canceled audio should reach your backend VAD model.
When true user Barge-In is detected by the VAD:
- Halt Audio: The backend immediately sends a WebRTC signal to halt the audio playback queue on the client.
- Halt Generation: The backend brutally terminates the asynchronous LLM generation loop (e.g., cancelling the Gemini 2.5 Pro streaming task).
- Compute Offset: This is the critical step. You must calculate exactly how many words the user actually heard before the interruption. If the LLM generated 100 words, but the user interrupted after word 10, the “Conversational State Database” (e.g., Cloud Spanner) must only record those 10 words. If you record the full 100 words in the agent’s memory, the agent will base its next response on information the user never actually heard.
The Architecture of Real-Time Interaction
Building a voice agent forces you to abandon the comfortable paradigm of stateless HTTP requests.
You are building a real-time, bi-directional, stateful streaming engine.
Your ingress node - running on a sturdy GKE pod - maintains an open WebSocket or WebRTC connection with the client. It receives a frantic stream of Opus encoded audio packets. It immediately routes those packets to a local VAD process.
Simultaneously, it buffers those packets and ships them to a transcription service. As the transcription arrives, it evaluates semantic completeness.
When the endpointing logic finally decides “The user is done,” it fires a massive payload to the LLM (like Gemini 2.5 Pro utilizing its multimodal audio ingestion capabilities for extreme low-latency).
As the LLM streams text tokens back, a Text-To-Speech (TTS) service instantly converts those partial sentences into PCM audio chunks, which your ingress node immediately flushes down the WebSocket to the user.
Every step in this pipeline must be measured in single-digit milliseconds. The network hops must be minimized. The garbage collector pauses must be eliminated.
But if you orchestrate it perfectly - if the VAD flawlessly detects the nuanced gap between a thoughtful pause and a completed thought - the technology vanishes. The user stops talking to an application and starts talking to an intelligence. And that illusion is the foundation of the Agentic future.



