MODULE 14  ·  Real-World Architectures

How Voice Assistants Work: STT → LLM → TTS Pipeline

You speak. Two seconds later, a voice responds with the weather. Three AI systems chained together make this work.

📅 Mar 2026
10 min read
🎯 Episode 96 of 98
Voice AssistantsSTTTTSPipeline
In this episode

"Hey Siri, what's the weather?"

Two seconds later: "It's 32 degrees and humid in Mumbai."

That two-second journey is a miracle of engineering. Your voice becomes text. Text becomes understanding. Understanding becomes speech. All while you're holding a ₹50,000 computer in your hand that runs on a battery.

Voice assistants aren't magic. They're a pipeline. And each stage has its own challenges, latency budgets, and failure modes.

Let's trace the journey from sound wave to spoken response.

The Pipeline Overview

snippet
code
Voice Input → Wake Word → ASR/STT → NLU → LLM → TTS → Audio Output

(you) (local) (cloud) (cloud) (cloud) (cloud) (speaker)

Stage What Happens Typical Latency

Wake WordDetect activation phrase50-100ms (local)
ASR/STTSpeech to text200-500ms
LLMGenerate response500-2000ms
TTSText to speech100-300ms
TotalEnd-to-end1-3 seconds

Stage 1: Wake Word Detection

Your device is always listening. But it can't send everything to the cloud — bandwidth, privacy, battery.

So it runs a tiny model locally:

python

Simplified wake word detection

import tensorflow as tf

Model is <1MB, runs on device CPU

snippet
code
interpreter = tf.lite.Interpreter(model_path="hey_siri.tflite")

interpreter.allocate_tensors()

def process_audio_chunk(audio_chunk):

example
code
# MFCC feature extraction
    features = extract_mfcc(audio_chunk)
example
code
# Run inference
    interpreter.set_tensor(input_index, features)
    interpreter.invoke()
    prediction = interpreter.get_tensor(output_index)
example
code
# If confidence > threshold, wake word detected
    return prediction[0] > 0.8

Key Constraints

Constraint Why It Matters

Tiny modelMust fit in device memory (<5MB)
Low powerAlways-on can't drain battery
Low latencyDetect within 100ms of speech
No false positivesDon't wake on similar sounds

Popular wake word engines: Picovoice Porcupine, Snowboy, TensorFlow Lite.

🔧

Stage 2: Automatic Speech Recognition (ASR/STT)

Once the wake word triggers, the real work begins. Your speech needs to become text.

OpenAI Whisper

The current gold standard:

python

import whisper

snippet
code
model = whisper.load_model("base")  # tiny, base, small, medium, large
result = model.transcribe("audio.mp3")
print(result["text"])  # "What's the weather like today?"

How Whisper Works

snippet
code
Audio (30s chunks) → Log-Mel Spectrogram → Encoder → Decoder → Text Tokens

Model Parameters Speed Word Error Rate

tiny39M~10x realtime~18%
base74M~7x realtime~15%
small244M~4x realtime~10%
medium769M~2x realtime~8%
large-v31550M~1x realtime~5%

Streaming ASR

For real-time assistants, you can't wait for the user to finish speaking:

python

Streaming transcription

import speech_recognition as sr

snippet
code
recognizer = sr.Recognizer()
microphone = sr.Microphone()

def callback(recognizer, audio):

example
code
try:
        # Send chunk to ASR API
        text = recognizer.recognize_whisper(audio, model="base")
        print(f"Heard: {text}")
    except sr.UnknownValueError:
        pass

Background listening

snippet
code
stop_listening = recognizer.listen_in_background(microphone, callback)

📊

Stage 3: Natural Language Understanding

Now we have text. What does the user want?

"What's the weather like today?"

example
code

Intent: weather_inquiry

Entities: { "time": "today", "location": "current" }

Intent Classification

python

Simple intent classifier

snippet
code
INTENTS = ["weather", "timer", "reminder", "music", "general_knowledge"]
def classify_intent(text: str) -> str:
# Can use small model or even regex for simple cases
    if any(word in text for word in ["weather", "temperature", "rain"]):
        return "weather"
    elif any(word in text for word in ["timer", "countdown"]):
        return "timer"
    # ... fallback to LLM
    return "general"

Modern Approach: Just Use an LLM

With modern LLMs, you can skip explicit NLU:

python

snippet
code
response = openai.chat.completions.create(
model="gpt-4",
    messages=[
        {"role": "system", "content": "You are a helpful voice assistant."},
        {"role": "user", "content": user_text}
    ]

)

The LLM handles intent, entities, and response generation in one shot.

Stage 4: Large Language Model

This is the brain. See Episode 4 for how inference works.

For voice assistants, LLM selection is a latency vs quality tradeoff:

Model Latency Quality Use Case

GPT-4 2-3s Highest Complex queries

GPT-3.5-turbo 0.5-1s High General purpose

Claude Haiku 0.3-0.5s Good Simple queries

Self-hosted 7B 0.2-0.5s Moderate Privacy-critical

Streaming Responses

Voice assistants can't wait for the full response:

python

Stream tokens as they're generated

snippet
code
stream = openai.chat.completions.create(
model="gpt-3.5-turbo",
    messages=messages,
    stream=True  # Enable streaming

)

for chunk in stream:

example
code
if chunk.choices[0].delta.content:
        text_chunk = chunk.choices[0].delta.content
        # Send to TTS immediately
        tts_engine.synthesize(text_chunk)
💡

Stage 5: Text-to-Speech (TTS)

The final step: turn text into natural-sounding speech.

Types of TTS

Type How It Works Quality Speed

Concatenative Stitch recorded audio fragments Robotic Fast

Parametric Generate audio from parameters Better Fast

Neural Deep learning end-to-end Human-like Slower

Modern Neural TTS

python

from TTS.api import TTS

Load model

snippet
code
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2")

Clone voice from sample

tts.tts_to_file(

example
code
text="Hello, this is a test of neural text to speech.",
    speaker_wav="reference_voice.wav",
    language="en",
    file_path="output.wav"

)

Popular TTS Services

Service Quality Latency Cost

ElevenLabsExcellent~500ms$5/1K chars
OpenAI TTSVery good~300ms$15/1M chars
Google Cloud TTSGood~200ms$4/1M chars
Azure TTSGood~200ms$4/1M chars
Coqui XTTS (local)Good~1sFree

🔬

The Latency Challenge

Users expect sub-second responses. Here's how to hit that:

Optimization Strategies

  1. Pipeline Parallelism

ASR chunk 1 → ASR chunk 2 → ASR chunk 3

example
code
↓           ↓           ↓
     LLM start → LLM cont → LLM finish
       ↓           ↓           ↓
     TTS start → TTS cont → Audio play

Start TTS on partial LLM output, don't wait for completion.

  1. Speculative TTS

Pre-generate common responses:

"I'm not sure I understand"

"Let me think about that"

"Here's what I found"

  1. Edge Deployment

python

Run small models locally

Whisper tiny (39M) → GPT-2 (124M) → Piper TTS (local)

Total latency: <500ms, zero cloud cost

🛡️

Try It Yourself

Build a simple voice assistant in Python:

bash

pip install openai whisper TTS pyaudio

python

import whisper

import openai

from TTS.api import TTS

import speech_recognition as sr

class VoiceAssistant:

example
code
def __init__(self):
        self.asr = whisper.load_model("base")
        self.tts = TTS("tts_models/en/ljspeech/tacotron2-DDC")
example
code
def listen(self):
        r = sr.Recognizer()
        with sr.Microphone() as source:
            print("Listening...")
            audio = r.listen(source)
        return audio
example
code
def transcribe(self, audio):
        result = self.asr.transcribe(audio)
        return result["text"]
example
code
def think(self, text):
        response = openai.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[{"role": "user", "content": text}]
        )
        return response.choices[0].message.content
example
code
def speak(self, text):
        self.tts.tts_to_file(text=text, file_path="response.wav")
        # Play audio...
example
code
def run(self):
        audio = self.listen()
        text = self.transcribe(audio)
        response = self.think(text)
        self.speak(response)

📦

Practical Takeaways

snippet
code
Voice = pipeline of 5 stages — Each adds latency, each can fail

Wake word runs locally — Privacy and battery require tiny on-device models

Whisper is the ASR standard — Open source, accurate, multilingual

LLMs can replace NLU — Intent classification + entity extraction in one model

Streaming is essential — Don't wait for full generation, pipeline the stages

Neural TTS is indistinguishable from human — ElevenLabs, OpenAI lead here

Sub-second response requires optimization — Edge deployment, caching, speculation

🚀

What's Next?

Episode 97: How Recommendation Systems Use AI — Netflix, YouTube, Spotify. Collaborative filtering, embeddings, and the AI that decides what you watch next.

← Previous

Ep 95: How Midjourney Works

Next →

Ep 97: How Recommendation Systems Use AI

Next: Episode 97 — How Recommendation Systems Use AI

You open Netflix. The perfect show is already on your home screen. That's not luck — it's a recommendation engine powered by AI.

This is part of a 98-episode series covering AI engineering from tokens to production deployment.

← Previous Ep 95: How Midjourney Works: From Text to Stunning Images