<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Arya Sahay</title>
    <description>The latest articles on DEV Community by Arya Sahay (@aryasahay31).</description>
    <link>https://dev.to/aryasahay31</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4056678%2Fb7cee1d4-542b-475d-8cc9-6c30d80fd598.png</url>
      <title>DEV Community: Arya Sahay</title>
      <link>https://dev.to/aryasahay31</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aryasahay31"/>
    <language>en</language>
    <item>
      <title>Anatomy of a Voice Agent: VAD, STT, LLM, TTS and Why WebRTC Matters</title>
      <dc:creator>Arya Sahay</dc:creator>
      <pubDate>Wed, 23 Sep 2026 18:12:44 +0000</pubDate>
      <link>https://dev.to/aryasahay31/anatomy-of-a-voice-agent-vad-stt-llm-tts-and-why-webrtc-matters-236k</link>
      <guid>https://dev.to/aryasahay31/anatomy-of-a-voice-agent-vad-stt-llm-tts-and-why-webrtc-matters-236k</guid>
      <description>&lt;p&gt;Talking to a voice agent feels simple. You speak, it answers. But behind that half-second exchange sits a pipeline of models and network decisions, and every one of them can make the conversation feel natural or painfully robotic.&lt;/p&gt;

&lt;p&gt;In this post, I'll break down the four core components of a voice agent, where latency hides, and why the choice of transport protocol matters more than most people expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline: four core components
&lt;/h2&gt;

&lt;p&gt;Almost every voice agent follows the same high-level flow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🎙️ User audio → VAD → STT → LLM → TTS → 🔊 Agent audio&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Voice Activity Detection (VAD)
&lt;/h3&gt;

&lt;p&gt;VAD answers one question: &lt;em&gt;is the human speaking right now?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It sounds trivial, but it's foundational. Without VAD, you'd be streaming silence to your speech-to-text provider (and paying for it), and your agent would have no reliable signal for when to listen versus when to respond. A bad VAD is also the most common reason agents interrupt users mid-sentence.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Speech-to-Text (STT)
&lt;/h3&gt;

&lt;p&gt;STT turns raw audio into text the LLM can reason about. Accuracy here sets the ceiling for everything downstream: if the transcript is wrong, the smartest LLM in the world will still give a wrong answer.&lt;/p&gt;

&lt;p&gt;When choosing a provider, test with the languages, accents, and audio conditions your real users have, not just clean studio samples.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Large Language Model (LLM)
&lt;/h3&gt;

&lt;p&gt;This is the brain. The LLM decides what to say next, and it's where your prompts, conversation state, planning logic, and tool calls (booking an appointment, looking up an order) live.&lt;/p&gt;

&lt;p&gt;For voice, the key metric isn't total generation time. It's &lt;strong&gt;time to first token&lt;/strong&gt;, because you can start speaking before the full response is ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Text-to-Speech (TTS)
&lt;/h3&gt;

&lt;p&gt;TTS converts the LLM's reply into audio. This is the part users actually &lt;em&gt;hear&lt;/em&gt;, so voice quality, pacing, and personality matter a lot for how your product feels. Again, the metric to watch is &lt;strong&gt;time to first audio byte&lt;/strong&gt;, not total synthesis time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The supporting cast
&lt;/h3&gt;

&lt;p&gt;Beyond the core four, production agents rely on components that make them better conversational partners:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Noise suppression&lt;/strong&gt; filters out keyboard clicks, traffic, and fans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background voice cancellation (BVC)&lt;/strong&gt; removes other people talking nearby, so the agent doesn't respond to a TV or a coworker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-of-turn detection&lt;/strong&gt; goes beyond VAD. A pause doesn't always mean someone is done talking ("My account number is... uh..."). Smarter turn detection uses context to decide when it's actually the agent's turn.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Latency: the real enemy
&lt;/h2&gt;

&lt;p&gt;In natural conversation, people start replying to each other remarkably quickly. For a voice agent to feel natural, a good target is &lt;strong&gt;around 500ms&lt;/strong&gt; from the moment the user stops speaking to the moment the agent starts talking.&lt;/p&gt;

&lt;p&gt;Here's roughly how the budget breaks down:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Best case&lt;/th&gt;
&lt;th&gt;Typical&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VAD&lt;/td&gt;
&lt;td&gt;15–20 ms&lt;/td&gt;
&lt;td&gt;20–30 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;STT&lt;/td&gt;
&lt;td&gt;200–300 ms&lt;/td&gt;
&lt;td&gt;400–600 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM (time to first token)&lt;/td&gt;
&lt;td&gt;100–200 ms&lt;/td&gt;
&lt;td&gt;500–1000 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TTS (time to first audio)&lt;/td&gt;
&lt;td&gt;100–150 ms&lt;/td&gt;
&lt;td&gt;200–300 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~415 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~1.1–2 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice the gap. The best case just squeezes under the target, while a typical setup is 2–4x too slow. That difference is what separates an agent that feels like a conversation from one that feels like a phone menu.&lt;/p&gt;

&lt;h3&gt;
  
  
  How low-latency agents close the gap
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Stream everything.&lt;/strong&gt; Don't wait for one stage to finish before starting the next. Stream partial transcripts into the LLM, stream LLM tokens into TTS, and stream audio back to the user as soon as the first chunk is ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run work in parallel.&lt;/strong&gt; While the user is still speaking, you can already be warming up connections, fetching context, or running speculative processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoid blocking I/O.&lt;/strong&gt; A single synchronous database call or slow tool invocation in the hot path can blow your entire latency budget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose providers deliberately.&lt;/strong&gt; The most accurate STT or the most capable LLM isn't always the right choice. Pick the one that balances quality and speed for your use case, and measure it under realistic conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why WebRTC for voice?
&lt;/h2&gt;

&lt;p&gt;Here's the part that surprises people: you can optimize every model in the pipeline and still end up with a laggy agent because of the network layer.&lt;/p&gt;

&lt;p&gt;Real-time audio has very different needs than text. Let's compare the options.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP (over TCP)
&lt;/h3&gt;

&lt;p&gt;Great for request/response workloads like REST APIs, but a poor fit for live speech. TCP guarantees in-order delivery, so one lost packet holds up everything behind it (head-of-line blocking). HTTP also has no built-in understanding of audio: no timestamps, no jitter handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebSockets (over TCP)
&lt;/h3&gt;

&lt;p&gt;A step up: persistent and bidirectional, so you're not opening a new connection per message. But it still runs on TCP, so under packet loss you get the same retransmission stalls. On a shaky mobile connection, audio freezes and then arrives in a burst.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebRTC (over UDP)
&lt;/h3&gt;

&lt;p&gt;WebRTC was purpose-built for real-time media, and it shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Opus codec&lt;/strong&gt;: efficient compression that sounds good even at low bitrates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-packet timestamps&lt;/strong&gt;: the receiver knows exactly when each chunk of audio belongs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jitter buffering&lt;/strong&gt;: smooths out packets that arrive unevenly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive bitrate&lt;/strong&gt;: quality scales down gracefully when the network degrades instead of stalling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight: for live voice, &lt;strong&gt;a late packet is often worse than a lost one&lt;/strong&gt;. UDP lets WebRTC skip missing data and keep the conversation moving, where TCP would stop and wait.&lt;/p&gt;

&lt;p&gt;In practice, this means your agent delivers its first audio faster and stays responsive even on flaky networks, which is exactly where real users are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;A voice agent is more than an LLM with a microphone. It's a pipeline where:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;VAD, STT, LLM, and TTS&lt;/strong&gt; each contribute to the experience and to latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supporting components&lt;/strong&gt; like noise suppression and turn detection make it feel human.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming and parallelism&lt;/strong&gt; are what get you under the ~500ms bar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebRTC&lt;/strong&gt; keeps the transport layer from undoing all that work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the next post, I'll build a working voice agent using LiveKit's WebRTC infrastructure and measure latency at each stage.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What's been the hardest part of building voice agents for you: latency, turn-taking, or something else? Let me know in the comments.&lt;/em&gt; 👇&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webrtc</category>
      <category>llm</category>
      <category>voice</category>
    </item>
  </channel>
</rss>
