AI Engineering Learning Hub

Voice Agents (Speech-to-Text, LLM, Text-to-Speech)

How a voice agent pipeline works: VAD, streaming STT, LLM, streaming TTS, interruptions and latency budgets, built with Pipecat.

What it is

A voice agent is a conversational system you talk to and that talks back. The common architecture is a cascade: a voice activity detector decides when you are speaking, speech-to-text turns audio into words, an LLM decides what to say, text-to-speech turns the reply into audio, and a transport (WebRTC in the browser, or a phone line) carries the audio both ways.

Every stage streams. The transcription arrives word by word, the LLM reply token by token, the speech sentence by sentence, so the user hears the start of the answer before the model has finished writing it. Without streaming the pauses are several seconds long and the conversation feels dead.

I built mine with Pipecat, an open-source Python framework that models the cascade as a pipeline of processors passing audio and text frames. Deepgram does speech-to-text, gpt-4o-mini is the brain, Deepgram Aura or Cartesia does text-to-speech, Silero VAD detects speech, and a peer-to-peer WebRTC transport connects the browser. I chose the cascade over an end-to-end speech model deliberately, because every stage can be measured, swapped and costed separately.

Why it's needed

Voice is the most natural interface and the least forgiving. Humans notice a 700-millisecond pause. They interrupt. They mumble and get transcribed wrong. A text chatbot can hide behind a typing indicator; a voice agent cannot. Building one forces you to deal with latency, streaming, interruption and cost at the same time, which is why it is such a strong demonstration of engineering depth.

Commercially, voice agents are replacing phone trees and first-line support, qualifying leads and booking appointments. The B1 Properties text chatbot I built lifted lead qualification; a voice version of the same thing is the obvious next step for that business.

How it works

  • Transport: the browser captures the microphone and sends audio over WebRTC. Pipecat's SmallWebRTC transport does this without a third-party media server.
  • VAD: Silero runs locally and emits 'user started speaking' and 'user stopped speaking' events. This drives turn-taking and interruption.
  • STT: audio streams to Deepgram, which returns interim and final transcripts. The final transcript becomes the user message.
  • LLM: the transcript goes into the same context, tools and system prompt as a text agent. Tokens stream out as they are generated.
  • TTS: text is cut at sentence boundaries and each sentence is sent to the speech service as soon as it is complete, so audio starts early.
  • Interruption: when VAD detects the user speaking mid-reply, the pipeline cancels the pending LLM and TTS frames and flushes the audio, so the agent stops talking and listens.

Code example

# Simplified Pipecat bot: the real file adds tools, cost guards and the UI.
import os
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.task import PipelineTask
from pipecat.pipeline.runner import PipelineRunner
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
from pipecat.services.deepgram.stt import DeepgramSTTService
from pipecat.services.deepgram.tts import DeepgramTTSService
from pipecat.services.openai.llm import OpenAILLMService

async def run_bot(transport):
    stt = DeepgramSTTService(api_key=os.environ["DEEPGRAM_API_KEY"])
    tts = DeepgramTTSService(api_key=os.environ["DEEPGRAM_API_KEY"])
    llm = OpenAILLMService(api_key=os.environ["OPENAI_API_KEY"], model="gpt-4o-mini")

    context = OpenAILLMContext([{"role": "system", "content":
        "You are the AI voice of Abdur. Keep replies to two short sentences."}])
    agg = llm.create_context_aggregator(context)

    pipeline = Pipeline([
        transport.input(),      # mic audio in (VAD attached to the transport)
        stt,                    # audio -> text
        agg.user(),             # add user turn to context
        llm,                    # text -> streamed reply
        tts,                    # reply -> audio, sentence by sentence
        transport.output(),     # audio out to the browser
        agg.assistant(),        # add assistant turn to context
    ])
    await PipelineRunner().run(PipelineTask(pipeline))

Where I used this

My voice agent is a Pipecat pipeline: Silero VAD, Deepgram speech-to-text, gpt-4o-mini with search_portfolio and show_on_screen tools, Deepgram Aura or Cartesia text-to-speech, and a peer-to-peer WebRTC transport in the browser. It speaks as the AI voice of Abdur, grounded in the same knowledge base as this site, with a hard session timer as a cost guard.

Use cases

  • Phone and web concierges for real estate, clinics and hospitality
  • Lead qualification calls that capture name, budget and intent
  • Hands-free assistants for drivers and field workers
  • Language-practice tutors
  • Accessible interfaces for users who cannot type comfortably

Common mistakes

  • Long LLM replies. A paragraph that reads fine in chat is twenty seconds of speech. The system prompt must demand short answers.
  • Waiting for the full reply before speaking. Stream and cut at sentence boundaries or the latency is unbearable.
  • No interruption handling. Users talk over the agent; if it keeps going, the conversation collapses.
  • No cost guard. A voice session pays for STT, LLM and TTS every second. An open browser tab can run all night.
  • Testing only with your own clear voice. Accents, background noise and short utterances break transcription; collect real sessions and check the transcripts.

Interview questions

Why a cascaded pipeline instead of a speech-to-speech model?
Observability and control. With separate STT, LLM and TTS stages I can measure latency per stage, swap any vendor, apply guardrails on the text in the middle, use tools and keep costs predictable. Speech-to-speech models have lower latency and better prosody but are a black box with fewer controls. For an engineering portfolio the cascade shows more.
What is a realistic latency budget?
Under a second from the user finishing speaking to the agent starting to speak feels natural; two seconds feels slow. Roughly: VAD end-of-speech 200 to 300 milliseconds, STT final transcript 100 to 300, LLM first token 300 to 600, TTS first audio 100 to 300. Streaming everywhere is what makes the sum acceptable.
How do interruptions work?
The VAD fires a speech-started event while the agent is talking. The pipeline cancels the in-flight LLM generation and TTS, flushes the audio buffer so playback stops within a fraction of a second, and the new user audio starts a fresh turn. The context keeps only what the agent actually said before being cut off.
How does RAG work in a voice agent?
The same as in text, but exposed as a tool so retrieval only runs when needed and does not add latency to small talk. In my agent the model calls search_portfolio with a query, gets back the top chunks, and answers from them. Spoken questions are messier, so hybrid retrieval that catches exact names is on the roadmap.
How do you keep a voice demo from costing money?
A hard session cap (180 seconds by default in mine), the cheapest capable LLM, audio that is opt-in rather than auto-started, and vendors with free tiers. Deepgram and Cartesia both offer signup credits, so building and demoing cost me nothing.
What would you measure to prove the agent works?
Per-stage latency percentiles, transcription word error rate on real sessions, retrieval recall on spoken questions, faithfulness of answers, interruption response time and cost per minute. That dashboard is a planned milestone; it is what turns a demo into an engineering artefact.
Ask the AI assistant about this topic

Get new topics by email

One short, practical AI-engineering lesson whenever a new topic is published. No spam, unsubscribe any time.

Need this built for your product?

I build production RAG pipelines, LLM automations and voice agents. Open to freelance projects and full-time roles.