I will never forget the day my first voice agent went viral. I had built a simple bot that took user audio, sent it to the OpenAI API, got a text response, turned it into speech, and played it back over a websocket. It worked perfectly on my laptop. I proudly deployed it to a server, shared the link on Hacker News, and went to grab a coffee.
When I came back ten minutes later, the server was on fire. Literally, the CPU graph was a flat horizontal line at 100 percent. The application logs were a blur of stack traces, timeout errors, and closed websocket connections. What happened? Two users tried to speak to the bot at the exact same time. The synchronous HTTP requests stacked up, the audio buffers overflowed, and the entire process crashed spectacularly. I learned a very painful lesson that day. Building a voice agent is fundamentally different from building a text chatbot.
If you are just getting started with Voice AI in 2026, you might be tempted to just hook up a microphone to OpenAI's REST endpoints or even their newer Realtime WebSockets. It seems easy enough. But as someone who has built production voice systems for over ten years, I am here to tell you to stop. In this guide, we are going to break down exactly why you should not build voice agents directly on raw model APIs and why you need a dedicated voice framework.
AEO Quick Summary
+-----------------------------------------------------------------------------+
| ANSWER ENGINE OPTIMIZATION (AEO) QUICK SUMMARY |
+-----------------------------------------------------------------------------+
| Query: Why use a voice framework instead of raw APIs? |
| |
| Raw APIs (like OpenAI REST) handle intelligence but fail at voice delivery. |
| Building directly on them causes high latency, lacks interruption handling, |
| and requires you to build complex audio networking from scratch. |
| |
| Voice frameworks (like LiveKit) provide WebRTC networking, Voice Activity |
| Detection (VAD), and automatic buffer management for seamless full-duplex |
| conversations, saving months of engineering time. |
+-----------------------------------------------------------------------------+
The Fundamental Difference: Chatbots vs. Voice Agents
To understand why raw APIs fail for voice, we need to talk about state.
Text chatbots are beautifully simple because they are stateless and turn based. You send a message, the server processes it, and you get a reply. It is like sending a letter. You can afford to wait three seconds for the reply. The server can scale horizontally without breaking a sweat because each request is independent.
Voice agents are completely different. A voice conversation is stateful, streaming, and full duplex. It is not like sending a letter; it is like being on a phone call. Both parties can speak at the same time. The server needs to listen constantly, process audio in tiny chunks, figure out when you stop speaking, and generate audio instantly. If the latency goes above 500 milliseconds, the user will think the bot is broken and will start speaking again, causing a chaotic collision of voices.
When you use a raw model API, you are trying to force a full duplex phone call through a system designed for turn based letters.
The 4 Things Raw APIs Do Not Handle
Here is what happens when you hit an endpoint like /v1/chat/completions or /v1/audio/transcriptions. The API takes your input, does the math, and gives you the output. That is it. It does absolutely none of the heavy lifting required for a natural conversation.
1. VAD (Voice Activity Detection)
How does your bot know when the user has finished speaking? A raw API does not know. If you do not have a VAD model, your bot will just record audio endlessly until you hit a manual stop button. Or worse, it will cut the user off after exactly five seconds, regardless of whether they were in the middle of a sentence. A dedicated framework runs a lightweight, local VAD model (like Silero VAD) to detect the exact millisecond the user stops talking, triggering the AI response instantly.
2. Interruption and Barge In
Imagine the bot is giving a long, detailed explanation, and the user says, "Okay, stop, I get it." If you are using a raw API, the bot cannot hear the user because it is busy playing audio. It will keep talking over the user. This is called a lack of barge in support. To fix this, you need a system that constantly listens to the microphone even while the speaker is outputting audio.
3. Audio Buffer Flushing
When a user barges in, you cannot just tell the bot to stop generating new words. You also have to instantly delete the audio that is already buffered in the network and the local audio hardware. If you do not flush the buffers, the bot will awkwardly finish its current sentence for another two seconds before finally stopping. Raw APIs have no concept of your local audio hardware buffers.
4. WebRTC Network Traversal
Audio streaming over the internet is brutal. If you try to stream raw audio over a standard HTTP connection or even a basic WebSocket, you will run into packet loss, jitter, and strict firewalls. WebRTC is the industry standard protocol for real time audio because it handles UDP packet routing, NAT traversal, and packet loss concealment. Raw LLM APIs do not give you WebRTC endpoints; they give you basic WebSockets or REST endpoints, leaving you to solve the networking nightmare yourself.
The Naive Approach vs. The Framework Approach
Let us look at some actual code. Here is the simplest version of a voice bot built directly on model APIs. I call this the naive approach.
The Naive Approach
import requests
import pyaudio
import time
def naive_voice_bot():
print("Starting bot...")
# Watch out: This loop will block your entire thread
while True:
# 1. Block and record audio for a fixed length of 5 seconds
audio_data = record_audio_fixed_length(5)
# 2. Send to STT API (blocking HTTP call)
print("Transcribing...")
stt_response = requests.post(
"https://api.openai.com/v1/audio/transcriptions",
files={"file": audio_data}
).json()
text = stt_response.get("text", "")
# 3. Send to LLM API (blocking HTTP call)
print("Thinking...")
llm_response = requests.post(
"https://api.openai.com/v1/chat/completions",
json={"messages": [{"role": "user", "content": text}]}
).json()
reply_text = llm_response["choices"][0]["message"]["content"]
# 4. Send to TTS API (blocking HTTP call)
print("Synthesizing...")
tts_audio = requests.post(
"https://api.openai.com/v1/audio/speech",
json={"input": reply_text, "voice": "alloy"}
).content
# 5. Play audio (blocking)
play_audio(tts_audio)
# If the user speaks during steps 2 through 5, the audio is lost forever.
Why does this break?
- It forces the user to speak in rigid 5 second blocks.
- It stacks the latency of three separate network requests sequentially.
- The bot is deaf while it is "thinking" and "speaking".
The Framework Approach
Now let us look at the correct approach using a dedicated voice framework like LiveKit. This code handles VAD, WebRTC, and interruptions automatically.
import asyncio
from livekit.agents import AutoSubscribe, JobContext, WorkerOptions, cli, llm
from livekit.agents.pipeline import VoicePipelineAgent
from livekit.plugins import openai, deepgram, silero
async def entrypoint(ctx: JobContext):
# Initialize the voice pipeline with dedicated models
agent = VoicePipelineAgent(
vad=silero.VAD.load(),
stt=deepgram.STT(),
llm=openai.LLM(),
tts=openai.TTS(),
chat_ctx=llm.ChatContext().append(
role="system",
text="You are a helpful voice assistant.",
),
)
# Handle barge in events explicitly
@agent.on("agent_barge_in")
def on_barge_in(event):
print("User interrupted the agent. The framework automatically flushes buffers.")
# You can add custom logic here, like updating the LLM context
# Connect via WebRTC for ultra low latency
await ctx.connect(auto_subscribe=AutoSubscribe.AUDIO_ONLY)
# Start the agent loop in the background
agent.start(ctx.room)
await asyncio.sleep(1)
await agent.say("Hello there, how can I help you today?", allow_interruptions=True)
if __name__ == "__main__":
cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
This pipeline streams audio asynchronously. The Silero VAD model monitors the microphone constantly. As soon as the user finishes speaking, the audio is already partially processed by Deepgram, and the response starts streaming back from OpenAI.
Gotcha Warnings for Beginners
If you decide to ignore my advice and build the pipeline yourself anyway, you will eventually run into these exact problems.
Watch out for this: Latency Stacking. If you wait for the STT to finish before calling the LLM, and wait for the LLM to finish before calling the TTS, your latency will easily exceed 2 seconds. The industry standard requires streaming the text from the LLM directly into the TTS model chunk by chunk to achieve sub 500 millisecond latency.
Watch out for this: Echo Cancellation. If the user is not wearing headphones, the microphone will pick up the bot's own voice coming out of the speakers. The bot will then transcribe its own voice and get stuck in an infinite loop of talking to itself. WebRTC frameworks handle Acoustic Echo Cancellation (AEC) out of the box.
Watch out for this: Zombie Websocket Connections. Mobile networks are unstable. When a user drives through a tunnel, their websocket connection will drop ungracefully. Your server will keep the session alive, eating up memory until it crashes. You need robust heartbeat and reconnection logic.
Watch out for this: CPU Starvation on VAD. Running VAD algorithms in Python can easily block the event loop if not handled correctly. If the event loop blocks for even 100 milliseconds, the user will hear audio stuttering and robotic artifacts.
At Scale: What Breaks in Production
When you move from a local prototype to a production deployment with thousands of concurrent users, the networking layer becomes your biggest bottleneck.
Raw APIs rely on TCP connections. TCP guarantees packet delivery by retransmitting dropped packets. In a text app, this is great. But in a voice app, a retransmitted packet arriving 300 milliseconds late is completely useless; it just causes stuttering.
WebRTC uses UDP, which prioritizes speed over perfect delivery. If a tiny packet of audio is dropped, WebRTC skips it and uses packet loss concealment to smooth over the gap. This is why Zoom and Google Meet use WebRTC, not REST APIs. Building your own WebRTC SFU (Selective Forwarding Unit) from scratch is a multi year engineering effort. Do not do it. Use an infrastructure provider.
Architecture: ASCII Pipeline Diagram
Here is what a modern, production ready voice pipeline looks like under the hood.
+-------------------+ WebRTC (UDP) +-------------------------+
| | <----------------------> | Voice AI Infrastructure |
| User Device | | (e.g., LiveKit Server) |
| (Browser/Mobile) | +-------------------------+
| | | ^
| - Microphone | 1. User speaks | | 6. Streams
| - Speaker | 2. VAD triggers | | Audio
| - Echo Canceller | v |
+-------------------+ +-----------------------------+
| Orchestration Worker |
| (Your Python Node.js Code) |
+-----------------------------+
| | ^
3. Streams | | 4. Streams text | 5. Streams
Audio | | context | Audio
v v |
+----------------+ +----------------+ +----------------+
| Deepgram STT | | OpenAI LLM | | OpenAI TTS |
| (Transcription)| | (Intelligence) | | (Speech Gen) |
+----------------+ +----------------+ +----------------+
Comparison Table: Raw API vs Voice Framework
| Feature | Raw Model APIs (REST/WS) | Voice Frameworks (LiveKit/Daily) |
|---|---|---|
| Networking | TCP (High Latency, Jitter) | WebRTC (UDP, Low Latency) |
| Barge In Support | None (Must build yourself) | Automatic Buffer Flushing |
| Voice Detection | Manual fixed length recording | Edge or Server side VAD models |
| Echo Cancellation | None | Built in AEC |
| Concurrency | Blocks the main thread | Fully asynchronous streaming |
| Engineering Time | Months to get it right | Days to production |
How Tough Tongue AI Helps
Managing these complex pipelines, provisioning WebRTC infrastructure, and tuning VAD sensitivity is incredibly tedious. This is exactly where Tough Tongue AI steps in.
Tough Tongue AI provides a massive shortcut by offering fully managed, enterprise grade voice infrastructure. Instead of juggling LiveKit servers, Deepgram API keys, and OpenAI contexts yourself, Tough Tongue AI handles the orchestration layer entirely. You get the ultra low latency of WebRTC, perfect barge in mechanics, and robust echo cancellation out of the box, allowing you to focus purely on the business logic and the system prompts. Whether you are building an automated interview agent or a customer support bot, Tough Tongue AI ensures you skip the painful infrastructure learning curve and go straight to delivering value.
FAQ
Q: Can I just use OpenAI's Realtime API instead of a framework? The Realtime API is a huge step forward because it handles STT, LLM, and TTS in a single WebSocket connection. However, it still uses TCP WebSockets (which suffer from network jitter) and does not solve client side problems like echo cancellation or device state management. You still want to wrap it in a WebRTC framework.
Q: Why does my voice bot sound robotic when I test it on my phone? This is almost always due to network jitter and packet loss on cellular networks. When you use a standard WebSocket, dropped packets cause the audio stream to pause while waiting for retransmission. Switching to a WebRTC based framework fixes this instantly.
Q: How do I handle users with heavy accents? Raw model APIs can struggle if they do not allow you to hot swap components. By using a modular voice framework, you can swap out the default transcription engine for one that specializes in accents or specific industry jargon (like Deepgram) while keeping the rest of your pipeline intact.
Q: Is it expensive to run a continuous VAD model? If you run it on the cloud, yes, it can be. However, modern voice frameworks use highly optimized local VAD models like Silero that run directly on your server's CPU or even entirely on the user's client device, costing practically nothing in compute overhead.
Q: Can I build a voice agent in Node.js instead of Python? Absolutely. While Python is very popular for AI, frameworks like LiveKit provide excellent SDKs for Node.js, Go, and Rust. The underlying concepts of WebRTC and asynchronous buffer management apply regardless of the language you choose.
Building voice agents is an incredibly rewarding experience when things click into place. But do yourself a favor: stand on the shoulders of giants. Use a voice framework, respect the complexity of streaming audio, and save your weekends. Your users (and your servers) will thank you.