Technical Deep Dive•Nov 2025 (8 min read)
Engineering Production AI Voice Agents: Low-Latency STT, Tool Calling & TTS Avatars
Architecting sub-second conversational voice agent interfaces combining Deepgram streaming STT, Gemini 2.5 Flash function calling, and Google TTS.
AIVoice AgentsDeepgramGemini APIRAGWebSockets
A technical walkthrough of designing full-duplex conversational voice agents with streaming speech recognition, real-time tool calling, and low-latency audio synthesis.
1. The Latency Waterfall in Conversational AI
In traditional text chatbots, a 1.5-second time-to-first-token is acceptable. In conversational voice agents, however, any delay above 700ms feels like an awkward pause, destroying the illusion of natural conversation.
The latency waterfall consists of:
1. **User Audio Streaming + STT (Speech-to-Text)**: ~200ms
2. **Context Retrieval / RAG + LLM Inference**: ~300ms
3. **TTS (Text-to-Speech) Audio Synthesis**: ~200ms
4. **Network Round-Trip Time**: ~50ms
To keep the entire loop under 750ms end-to-end, every phase of the pipeline must be streamed concurrently rather than processed sequentially.
Critical Optimization: Do not wait for the LLM to complete its full response. Synthesize the first sentence to TTS as soon as punctuation (period, question mark) is tokenized.
2. The Full-Duplex WebSocket Pipeline
Our production voice agent infrastructure at Aadrila Technologies connected the client browser to a Node.js/FastAPI orchestrator over WebSockets:
- **Audio Capture**: Browser uses `AudioWorkletNode` to capture 16kHz PCM audio and stream binary frames.
- **Deepgram Live Transcriber**: Direct WebSocket pipe returns finalized transcripts within 120ms of speech pause.
- **Gemini 2.5 Flash with Tool Calling**: Ingests transcript, queries vector store or internal APIs if needed, and streams response tokens.
- **Google TTS / Streaming Speech**: Synthesizes audio buffer chunks in real-time, streaming audio back down the same client WebSocket.
voiceAgentOrchestrator.tstypescript
import { WebSocket } from "ws";
import { GoogleGenAI } from "@google/genai";
import { createClient, LiveTranscriptionEvents } from "@deepgram/sdk";
export function handleVoiceSession(clientWs: WebSocket) {
const deepgram = createClient(process.env.DEEPGRAM_API_KEY!);
const dgConnection = deepgram.listen.live({
model: "nova-2",
language: "en-US",
smart_format: true,
interim_results: false,
});
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
// 1. Pipe client mic stream directly into Deepgram STT
clientWs.on("message", (audioData: Buffer) => {
if (dgConnection.getReadyState() === 1) {
dgConnection.send(audioData);
}
});
// 2. On finalized speech transcript, trigger streaming AI inference
dgConnection.on(LiveTranscriptionEvents.Transcript, async (data) => {
const transcript = data.channel.alternatives[0]?.transcript;
if (!transcript || transcript.trim().length === 0) return;
// Send barge-in signal to interrupt any currently playing audio
clientWs.send(JSON.stringify({ type: "INTERRUPT_PLAYBACK" }));
// Stream response from Gemini 2.5 Flash
const responseStream = await ai.models.generateContentStream({
model: "gemini-2.5-flash",
contents: [{ role: "user", parts: [{ text: transcript }] }],
config: {
systemInstruction: "You are a concise, helpful voice assistant. Keep answers brief and conversational.",
},
});
let sentenceBuffer = "";
for await (const chunk of responseStream) {
const text = chunk.text || "";
sentenceBuffer += text;
// As soon as a sentence boundary is reached, trigger TTS chunk
if (/[.?!]\s$/.test(sentenceBuffer)) {
await synthesizeAndStreamAudio(sentenceBuffer, clientWs);
sentenceBuffer = "";
}
}
if (sentenceBuffer.trim()) {
await synthesizeAndStreamAudio(sentenceBuffer, clientWs);
}
});
}3. Barge-In & Voice Activity Detection (VAD)
The most crucial user experience element in voice interfaces is **barge-in interruption**. When a user begins speaking while the AI is responding, the system immediately cancels active TTS synthesis, clears client playback buffers, and switches focus back to listening.
By combining WebRTC audio processing, Deepgram's server-side VAD, and local audio worklets, we cut false-positive interruptions by 94% while providing a seamless conversational flow.
Key Engineering Takeaways
- Achieve full-duplex conversational voice by maintaining persistent bi-directional WebSockets between the client and edge gateway.
- Stream raw PCM audio chunks to Deepgram STT for sub-150ms transcription with interim results.
- Use streaming function calling in Gemini 2.5 Flash to fetch live database records before full model completion.
- Implement Voice Activity Detection (VAD) with aggressive barge-in interruption to make the AI agent feel naturally conversational.