Part V: Retrieval and Conversation
Chapter 21: Building Conversational AI Systems

Voice & Multimodal Interfaces

The most natural interface is no interface at all, just a voice that understands.

Echo Echo, Softly Spoken AI Agent
Big Picture

Voice is the most natural human interface, and multimodal AI is making it programmable. The convergence of high-quality speech recognition (Whisper, Deepgram), expressive text-to-speech (ElevenLabs, Cartesia), and real-time orchestration frameworks (LiveKit, Pipecat) has made it possible to build voice-first conversational AI that feels responsive and natural. Combined with vision capabilities, these systems can see what users see and respond in real time. This section covers the complete voice and multimodal stack, from individual components to production-ready pipelines. The streaming API patterns from Section 10.1 are essential for achieving the low latency that voice interfaces demand.

Prerequisites

Voice and multimodal conversational interfaces build on the dialogue system architecture from Section 21.1 and the persona design principles in Section 21.2. Understanding the multimodal model landscape from Section 27.1 will help you appreciate how vision and audio capabilities integrate into conversational systems.

1. Speech-to-Text (STT)

Speech-to-text converts spoken audio into text that the LLM can process. The quality of transcription directly impacts the quality of the conversational experience, because every transcription error propagates through the entire pipeline. Modern STT systems offer near-human accuracy for clear speech, but performance degrades with background noise, accents, domain-specific terminology, and overlapping speakers. Domain-specific vocabulary can sometimes be improved by fine-tuning the transcription model on in-domain audio.

Fun Fact

The human brain processes speech with a latency of about 200 milliseconds from ear to comprehension. Users start perceiving voice AI as "laggy" at around 500ms of silence after they stop talking. That 300ms gap is the entire engineering budget for speech recognition, LLM inference, and speech synthesis combined. Code Fragment 21.5.2 below puts this into practice.

STT Provider Comparison

STT Provider Comparison
Provider Model Latency Strengths Best For
OpenAI Whisper whisper-1, whisper-large-v3 Batch (seconds) Multilingual, open-source, strong accuracy Batch processing, self-hosted
Deepgram Nova-2, Nova-3 Streaming (~300ms) Low latency, streaming, keyword boosting Real-time voice AI, call centers
AssemblyAI Universal-2 Near real-time Speaker diarization, sentiment, summarization Meeting transcription, analytics
Google Cloud STT Chirp 2 Streaming (~200ms) 100+ languages, medical/telephony models Enterprise, multilingual
Groq (Whisper) whisper-large-v3-turbo Very fast batch Extremely fast inference on Whisper High-throughput batch transcription
Tip

For real-time voice applications, latency beats accuracy. Choose Deepgram or Google Cloud STT with streaming enabled over Whisper in batch mode, even if Whisper scores slightly higher on word error rate benchmarks. Users tolerate occasional transcription errors far better than they tolerate a 2-second pause after every utterance.

Using Whisper for Transcription

This snippet transcribes audio input to text using OpenAI's Whisper model.


# implement transcribe_audio, transcribe_with_deepgram
# Key operations: results display, API interaction
from openai import OpenAI
from pathlib import Path

client = OpenAI()

def transcribe_audio(audio_path: str, language: str = None) -> dict:
 """Transcribe audio using OpenAI's Whisper API."""
 with open(audio_path, "rb") as audio_file:
 transcript = client.audio.transcriptions.create(
 model="whisper-1",
 file=audio_file,
 language=language, # ISO 639-1 code, e.g., "en"
 response_format="verbose_json",
 timestamp_granularities=["word", "segment"]
 )
 return {
 "text": transcript.text,
 "language": transcript.language,
 "duration": transcript.duration,
 "segments": [
 {
 "text": seg.text,
 "start": seg.start,
 "end": seg.end
 }
 for seg in (transcript.segments or [])
 ]
 }

def transcribe_with_deepgram(audio_path: str) -> dict:
 """Transcribe audio using Deepgram's Nova-2 model."""
 from deepgram import DeepgramClient, PrerecordedOptions

 deepgram = DeepgramClient() # Uses DEEPGRAM_API_KEY env var

 with open(audio_path, "rb") as audio_file:
 payload = {"buffer": audio_file.read()}

 options = PrerecordedOptions(
 model="nova-2",
 smart_format=True, # Adds punctuation and formatting
 utterances=True, # Detects speaker turns
 diarize=True, # Speaker identification
 language="en"
 )

 response = deepgram.listen.rest.v("1").transcribe_file(
 payload, options
 )

 result = response.results
 return {
 "transcript": result.channels[0].alternatives[0].transcript,
 "confidence": result.channels[0].alternatives[0].confidence,
 "words": [
 {
 "word": w.word,
 "start": w.start,
 "end": w.end,
 "confidence": w.confidence,
 "speaker": w.speaker
 }
 for w in result.channels[0].alternatives[0].words
 ]
 }

# Example usage
result = transcribe_audio("user_query.wav")
print(f"Transcription: {result['text']}")
print(f"Language: {result['language']}")
print(f"Duration: {result['duration']:.1f}s")
Transcription: Hi, I'd like to check the status of my order from last week. Language: en Duration: 3.2s
Code Fragment 21.5.1: implement transcribe_audio, transcribe_with_deepgram
Note: Streaming vs. Batch Transcription

For real-time voice AI, streaming transcription is essential. Batch transcription processes the entire audio file at once, introducing latency proportional to the audio length. Streaming transcription processes audio in chunks as it arrives, producing partial transcripts that update in real time. Deepgram and Google Cloud STT offer true streaming; Whisper is primarily batch-oriented, though Groq's accelerated Whisper inference narrows this gap significantly.

2. Text-to-Speech (TTS)

Text-to-speech converts the LLM's text response into spoken audio. The quality bar for TTS has risen dramatically; modern systems produce speech that is nearly indistinguishable from human voice in controlled settings. The key differentiators are naturalness, emotional expressiveness, latency (time to first audio byte), and voice cloning capabilities. Code Fragment 21.5.2 below puts this into practice.

TTS Provider Comparison

TTS Provider Comparison
Provider Latency (TTFB) Voice Quality Key Features
ElevenLabs ~300ms Excellent Voice cloning, emotional control, 32 languages
PlayHT ~200ms Very good Ultra-low latency mode, voice cloning, streaming
Cartesia ~100ms Very good Fastest TTFB, emotion/speed control, streaming
OpenAI TTS ~400ms Good Simple API, 6 built-in voices, affordable
Azure Neural TTS ~200ms Very good SSML support, 400+ voices, enterprise SLAs

# implement text_to_speech_openai
# Key operations: results display, chunking strategy, API interaction
from openai import OpenAI
from pathlib import Path

client = OpenAI()

def text_to_speech_openai(text: str, voice: str = "nova",
 output_path: str = "response.mp3") -> str:
 """Generate speech from text using OpenAI's TTS API."""
 response = client.audio.speech.create(
 model="tts-1-hd", # Higher quality; use "tts-1" for lower latency
 voice=voice, # alloy, echo, fable, onyx, nova, shimmer
 input=text,
 speed=1.0 # 0.25 to 4.0
 )
 response.stream_to_file(output_path)
 return output_path

async def text_to_speech_elevenlabs(
 text: str,
 voice_id: str = "21m00Tcm4TlvDq8ikWAM", # Rachel
 output_path: str = "response.mp3"
) -> str:
 """Generate speech using ElevenLabs with streaming."""
 from elevenlabs import ElevenLabs

 eleven = ElevenLabs() # Uses ELEVEN_API_KEY env var

 audio_generator = eleven.text_to_speech.convert(
 voice_id=voice_id,
 text=text,
 model_id="eleven_turbo_v2_5",
 output_format="mp3_44100_128",
 voice_settings={
 "stability": 0.5,
 "similarity_boost": 0.75,
 "style": 0.3,
 "use_speaker_boost": True
 }
 )

 # Write streaming audio to file
 with open(output_path, "wb") as f:
 for chunk in audio_generator:
 f.write(chunk)

 return output_path

# Simple usage
output = text_to_speech_openai(
 "Hello! I'd be happy to help you check your order status. "
 "Could you provide me with your order number?",
 voice="nova"
)
print(f"Audio saved to: {output}")
Audio saved to: response.mp3
Code Fragment 21.5.2: implement text_to_speech_openai
Key Insight

In voice AI, time-to-first-byte (TTFB) matters more than total generation time. Users perceive a system as fast if audio starts playing quickly, even if the full response takes several seconds to generate. This is why streaming TTS (where audio chunks are sent as they are generated) is critical for real-time voice applications. A system with 100ms TTFB that streams audio progressively feels much faster than a system with 500ms TTFB that delivers the complete audio at once.

3. Real-Time Voice AI Pipelines

A real-time voice AI pipeline connects STT, LLM, and TTS into a seamless flow where the user speaks, the system processes their speech, generates a response, and speaks it back, all with minimal perceptible delay. The total round-trip latency (from when the user finishes speaking to when the first audio of the response plays) is the key performance metric. Users expect sub-second response times for conversational interactions. Figure 21.5.1 shows the real-time voice AI pipeline. Code Fragment 21.5.3 below puts this into practice.

🎤 User Speaks STT Deepgram / Whisper ~200ms text LLM GPT-4o / Claude ~300ms (TTFT) text stream TTS Cartesia / ElevenLabs ~100ms (TTFB) audio 🔊 Audio Plays Total Round-Trip: ~600ms (streaming) to ~2s (batch) Orchestration Layer LiveKit Agents / Pipecat / Vapi / Custom WebSocket VAD (Voice Activity Detection) Turn-Taking Management Interruption Handling WebRTC Transport
Figure 21.5.1: Real-time voice AI pipeline showing the flow from speech input through STT, LLM, and TTS to audio output, with orchestration handling VAD, turn-taking, and transport.

Building a Voice Pipeline with Pipecat

This snippet assembles a real-time voice pipeline using the Pipecat framework for speech-to-text, LLM processing, and text-to-speech.


# Implementation example
# Key operations: prompt construction, API interaction
import asyncio
from pipecat.frames.frames import TextFrame, AudioRawFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.task import PipelineTask
from pipecat.services.openai import OpenAILLMService
from pipecat.services.deepgram import DeepgramSTTService
from pipecat.services.cartesia import CartesiaTTSService
from pipecat.transports.services.daily import DailyTransport

async def create_voice_bot():
 """Create a real-time voice AI bot using Pipecat."""

 # Transport layer (handles WebRTC audio/video)
 transport = DailyTransport(
 room_url="https://your-domain.daily.co/room-name",
 token="your-daily-token",
 bot_name="Assistant"
 )

 # Speech-to-text
 stt = DeepgramSTTService(
 api_key="your-deepgram-key",
 model="nova-2",
 language="en"
 )

 # Language model
 llm = OpenAILLMService(
 api_key="your-openai-key",
 model="gpt-4o",
 system_prompt=(
 "You are a helpful voice assistant. Keep responses "
 "concise (1-2 sentences) since this is a voice conversation. "
 "Be natural and conversational."
 )
 )

 # Text-to-speech
 tts = CartesiaTTSService(
 api_key="your-cartesia-key",
 voice_id="a0e99841-438c-4a64-b679-ae501e7d6091",
 model_id="sonic-english",
 sample_rate=16000
 )

 # Build the pipeline: audio in -> STT -> LLM -> TTS -> audio out
 pipeline = Pipeline([
 transport.input(), # Receive user audio
 stt, # Transcribe to text
 llm, # Generate response
 tts, # Synthesize speech
 transport.output() # Send audio to user
 ])

 task = PipelineTask(pipeline)
 await task.run()

# Run the voice bot
asyncio.run(create_voice_bot())
I can see a Python traceback in your screenshot. The error is a `ModuleNotFoundError: No module named 'torch'`. This means PyTorch is not installed in your current Python environment. To fix this: 1. Open your terminal and run: `pip install torch` 2. If you are using a virtual environment, make sure it is activated before installing. 3. Restart the application after installation completes. If you already installed PyTorch but still see the error, your IDE may be using a different Python interpreter. Check your IDE settings to ensure it points to the correct environment.
Code Fragment 21.5.3: Implementation example
Warning: Voice-Specific Design Constraints

Voice interfaces impose constraints that text-based chat does not. Responses must be concise (users cannot "scan" audio the way they scan text). Latency above 1.5 seconds feels unresponsive. The system needs voice activity detection (VAD) to know when the user has finished speaking. It must handle interruptions (the user speaking while the system is still talking). And the response text must be optimized for spoken delivery: avoid parenthetical asides, complex lists, URLs, or code snippets that work in text but sound terrible when spoken aloud.

4. Voice-Specific Orchestration Challenges

Beyond the basic STT/LLM/TTS pipeline, real-time voice AI requires solving several orchestration challenges that do not arise in text-based chat.

Interruption Handling

Users may interrupt the system while it is speaking. The system needs to detect the interruption, stop the current audio playback, process the new input, and respond without losing context. This requires coordination between the STT, TTS, and transport layers. Code Fragment 21.5.5 below puts this into practice.


# Define InterruptionHandler; implement __init__
# Key operations: chunking strategy
class InterruptionHandler:
 """Manages user interruptions during system speech."""

 def __init__(self):
 self.is_speaking = False
 self.current_utterance: str = ""
 self.spoken_so_far: str = ""

 async def on_speech_started(self, text: str):
 """Called when the system starts speaking."""
 self.is_speaking = True
 self.current_utterance = text
 self.spoken_so_far = ""

 async def on_speech_chunk_played(self, chunk_text: str):
 """Track how much of the response has been spoken."""
 self.spoken_so_far += chunk_text

 async def on_user_interruption(self, user_audio_detected: bool):
 """Handle user interrupting system speech."""
 if not self.is_speaking or not user_audio_detected:
 return None

 self.is_speaking = False

 # Calculate what was and was not heard
 unspoken = self.current_utterance[len(self.spoken_so_far):]

 return {
 "action": "interrupted",
 "spoken_portion": self.spoken_so_far.strip(),
 "unspoken_portion": unspoken.strip(),
 "context_note": (
 f"System was saying: '{self.spoken_so_far.strip()}' "
 f"but was interrupted. The rest ('{unspoken.strip()[:50]}...') "
 "was not heard by the user."
 )
 }

 async def on_speech_completed(self):
 """Called when system finishes speaking without interruption."""
 self.is_speaking = False
 self.spoken_so_far = ""
 self.current_utterance = ""
Code Fragment 21.5.4: Detecting and handling user interruptions during streaming responses, tracking what the user has already seen and resuming gracefully.

5. Vision in Conversations

Multimodal LLMs (GPT-4o, Claude Sonnet 4, Gemini) can process images alongside text, enabling conversational AI systems that can see. Users can share photos, screenshots, documents, or live camera feeds, and the system can discuss what it sees. This capability transforms many use cases: visual troubleshooting ("what is wrong with this error message?"), product identification ("what plant is this?"), accessibility assistance, and interactive tutoring with visual materials. Figure 21.5.2 illustrates the multimodal conversational pipeline. Code Fragment 21.5.5 below puts this into practice.


# Define MultimodalConversation; implement encode_image_to_base64, __init__, send_text
# Key operations: results display, prompt construction, API interaction
import base64
from openai import OpenAI

client = OpenAI()

def encode_image_to_base64(image_path: str) -> str:
 """Read an image file and encode it as base64."""
 with open(image_path, "rb") as f:
 return base64.b64encode(f.read()).decode("utf-8")

class MultimodalConversation:
 """Conversational AI with vision capabilities."""

 def __init__(self, system_prompt: str):
 self.system_prompt = system_prompt
 self.history: list[dict] = []

 def send_text(self, user_message: str) -> str:
 """Send a text-only message."""
 self.history.append({
 "role": "user",
 "content": user_message
 })
 return self._get_response()

 def send_image(self, image_path: str,
 question: str = "What do you see?") -> str:
 """Send an image with an optional question."""
 b64_image = encode_image_to_base64(image_path)

 # Determine MIME type from extension
 ext = image_path.rsplit(".", 1)[-1].lower()
 mime_map = {"jpg": "jpeg", "jpeg": "jpeg",
 "png": "png", "gif": "gif", "webp": "webp"}
 mime_type = f"image/{mime_map.get(ext, 'jpeg')}"

 self.history.append({
 "role": "user",
 "content": [
 {"type": "text", "text": question},
 {
 "type": "image_url",
 "image_url": {
 "url": f"data:{mime_type};base64,{b64_image}",
 "detail": "high"
 }
 }
 ]
 })
 return self._get_response()

 def send_image_url(self, url: str, question: str) -> str:
 """Send an image via URL with a question."""
 self.history.append({
 "role": "user",
 "content": [
 {"type": "text", "text": question},
 {
 "type": "image_url",
 "image_url": {"url": url, "detail": "auto"}
 }
 ]
 })
 return self._get_response()

 def _get_response(self) -> str:
 """Get a response from the multimodal LLM."""
 messages = [
 {"role": "system", "content": self.system_prompt},
 *self.history
 ]
 response = client.chat.completions.create(
 model="gpt-4o",
 messages=messages,
 max_tokens=1000
 )
 assistant_msg = response.choices[0].message.content
 self.history.append({
 "role": "assistant", "content": assistant_msg
 })
 return assistant_msg

# Example: Visual troubleshooting assistant
troubleshooter = MultimodalConversation(
 system_prompt=(
 "You are a technical support assistant that can analyze "
 "screenshots and photos to help users troubleshoot issues. "
 "When shown an image, describe what you see and provide "
 "specific, actionable solutions."
 )
)

# User shares a screenshot of an error
response = troubleshooter.send_image(
 "error_screenshot.png",
 "I keep getting this error when I try to start the application. "
 "What should I do?"
)
print(response)
Code Fragment 21.5.5: Define MultimodalConversation; implement encode_image_to_base64, __init__, send_text
Voice Input Text Input Image / Camera STT (if voice) Input Merger Multimodal LLM GPT-4o / Claude Sonnet 4 Text + Image reasoning TTS Voice out Text Display Conversation History (text + image references) Memory, context management, and session persistence
Figure 21.5.2: Multimodal conversational pipeline accepting voice, text, and image inputs, processing through a multimodal LLM, and producing voice or text outputs.
Note: Emerging Models for Voice

The voice AI landscape is evolving rapidly. OpenAI's GPT-4o natively processes audio without a separate STT step, significantly reducing latency and enabling the model to understand tone, emotion, and non-verbal cues. Google's Gemini 2.0 offers similar native multimodal processing. These "speech-native" models are beginning to replace the traditional STT/LLM/TTS pipeline with a single model that hears, thinks, and speaks. However, the component-based pipeline remains important for customization, cost control, and vendor flexibility.

6. Native Speech-to-Speech Models

A transformative shift in voice AI is the emergence of native speech-to-speech models that process audio directly, without decomposing the task into separate speech-to-text, language model reasoning, and text-to-speech stages. These models represent a fundamental architectural departure from the pipeline approach described above, and they promise lower latency, richer expressiveness, and a more natural conversational experience.

6.1 The Pipeline Problem

The traditional STT/LLM/TTS pipeline introduces several limitations that stem from its cascaded architecture. First, each stage adds latency: 200 to 500ms for STT transcription, 500 to 2000ms for LLM generation, and 200 to 500ms for TTS synthesis, yielding a total round-trip time of 1 to 3 seconds. Second, the text bottleneck between stages loses crucial information. When speech is transcribed to text, paralinguistic signals (tone, emotion, pacing, emphasis, hesitation) are discarded. The LLM reasons over flat text, unable to perceive that the user sounds frustrated, confused, or sarcastic. Third, the TTS stage must synthesize prosody from scratch, since it has no access to the original audio input. The result is responses that sound fluent but lack the contextual expressiveness of natural human conversation.

6.2 Architecture of Speech-Native Models

Native speech-to-speech models replace the three-stage pipeline with an end-to-end architecture that maps audio input directly to audio output. The core idea is to train a single model on speech tokens rather than (or in addition to) text tokens. Audio is encoded into discrete tokens using a neural audio codec (such as EnCodec or SoundStorm), producing a sequence of audio tokens that capture both linguistic content and acoustic properties. The model then generates output audio tokens autoregressively, which are decoded back into a waveform by the codec decoder.

Several architectural variants have emerged:

6.3 Key Models and Platforms

OpenAI GPT-4o Realtime API. Released in late 2024, the Realtime API provides WebSocket-based access to GPT-4o's native audio capabilities. The model accepts audio input and produces audio output without intermediate text conversion, achieving round-trip latencies of 300 to 500ms (compared to 1 to 3 seconds for the pipeline approach). The API supports function calling during audio conversations, allowing the model to invoke tools while maintaining a natural voice interaction. Developers can configure voice presets, control turn detection sensitivity, and implement interruption handling through the WebSocket protocol.

Kyutai Moshi. Moshi is an open-weight speech-to-speech model that introduced the concept of full-duplex conversation. Unlike turn-based systems that alternate between listening and speaking, Moshi maintains simultaneous input and output audio streams. The architecture combines a text language model (Helium, a 7B parameter model) with an audio codec model (Mimi) that compresses speech to 1.1 kbps. Moshi achieves a theoretical latency of 160ms and a practical latency of 200ms, making it one of the fastest conversational AI systems. The model was released with open weights and can run on consumer GPUs.

Google Gemini Live. Gemini 2.0 Flash incorporates native audio understanding and generation as part of its multimodal architecture. Gemini Live enables real-time conversational interactions where the model can see (via camera), hear (via microphone), and respond with synthesized speech. The model natively understands audio context, including environmental sounds, multiple speakers, and emotional tone. Gemini Live's integration with Google's ecosystem (Search, Maps, Home) enables voice-driven agent interactions that span multiple modalities and data sources.

6.4 Pipeline vs. Native: Tradeoffs

6.4 Pipeline vs. Native: Tradeoffs
Dimension STT/LLM/TTS Pipeline Native Speech-to-Speech
Latency 1,000 to 3,000ms round-trip 200 to 500ms round-trip
Expressiveness Limited by text bottleneck Preserves tone, emotion, pacing
Interruption handling Requires explicit VAD logic Natural full-duplex support
Customization Mix-and-match components Limited to model's capabilities
Cost Pay per component, can optimize Single model cost, often higher
Transparency Full transcript at each stage Intermediate text may not exist
Language support Broad (use best STT/TTS per language) Limited to model's training languages
Key Insight

Native speech-to-speech models are not simply faster versions of the pipeline approach; they represent a qualitative shift in what voice AI can do. By reasoning directly over audio, these models can understand and generate paralinguistic cues (sarcasm, emphasis, hesitation, emotional tone) that are fundamentally lost when speech is compressed into text. The practical impact is conversations that feel more natural, more responsive, and more human. However, the pipeline approach retains important advantages in customization, cost control, transparency, and language coverage. Most production systems in 2025 will benefit from a hybrid strategy: use native models for high-value, latency-sensitive interactions, and pipeline approaches for cost-sensitive or highly customized deployments.

6.5 Building with the Realtime API

Working with native speech-to-speech APIs requires a shift from REST-based request/response patterns to persistent WebSocket connections with streaming audio. The developer sends audio chunks as the user speaks, and the model streams audio chunks back as it generates a response. Function calls can be interleaved with audio output, and the model manages turn-taking internally.

Key implementation considerations include: audio format negotiation (PCM 16-bit at 24kHz is common), voice activity detection configuration (sensitivity thresholds, silence duration for turn detection), interruption policy (whether the model should stop speaking when the user starts), and session management (maintaining conversation state across audio turns). Frameworks like LiveKit and Pipecat are adding native support for these APIs, abstracting the WebSocket management and audio buffering into higher-level constructs.

7. Comparing Voice AI Orchestration Frameworks

7. Comparing Voice AI Orchestration Frameworks Intermediate
Framework Type Key Strengths Best For
LiveKit Agents Open-source framework WebRTC transport, plugin system, self-hostable Custom voice bots, self-hosted deployments
Pipecat Open-source framework Composable pipelines, multi-provider, Python-native Rapid prototyping, flexible architectures
Vapi Managed platform Turnkey API, phone integration, low-code setup Phone bots, rapid deployment
Retell AI Managed platform Telephony focus, call analytics, enterprise features Call center automation, enterprise voice
Custom WebSocket DIY Full control, no vendor lock-in Specialized requirements, existing infrastructure
Self-Check
Q1: Why is streaming TTS more important than total TTS generation speed for voice AI?
Show Answer
Users perceive responsiveness based on time-to-first-byte (TTFB), not total generation time. Streaming TTS begins playing audio as soon as the first chunk is synthesized, while the rest generates in parallel. A system with 100ms TTFB that streams feels much faster than one with 500ms total time that delivers everything at once. Since conversational responses are listened to sequentially, the user hears the beginning while the system is still generating the end.
Q2: What is voice activity detection (VAD) and why is it critical for voice AI?
Show Answer
Voice activity detection determines when the user is speaking versus when they have paused or finished their turn. It is critical because the system needs to know when to start processing the user's input. Without good VAD, the system might start responding during a natural pause (cutting the user off), or wait too long after the user finishes (adding unnecessary latency). VAD also helps distinguish meaningful speech from background noise, coughs, or other non-speech audio.
Q3: How does interruption handling work in a voice AI pipeline?
Show Answer
When the user starts speaking while the system is still outputting audio, the interruption handler must: (1) detect the user's speech through VAD, (2) immediately stop the current audio playback, (3) track how much of the response was actually heard by the user versus what was cut off, (4) add this context to the conversation history so the LLM knows the user only heard a partial response, and (5) process the user's new input and generate a response that accounts for the interrupted context.
Q4: What design constraints does voice impose compared to text-based chat?
Show Answer
Voice interfaces require: (1) concise responses since users cannot scan audio, (2) sub-1.5 second latency to feel responsive, (3) text optimized for spoken delivery (no URLs, code snippets, or complex formatting), (4) natural phrasing without parenthetical asides or bullet points, (5) VAD for turn-taking, (6) interruption handling, and (7) filler phrases or acknowledgment sounds to indicate the system is processing. Voice also lacks visual affordances like buttons or typing indicators that text chat uses for interaction cues.
Q5: How do speech-native models (like GPT-4o audio mode) differ from the traditional STT/LLM/TTS pipeline?
Show Answer
Speech-native models process audio directly without a separate STT step, and can generate audio output without a separate TTS step. This eliminates two pipeline stages and their associated latency. More importantly, the model can understand paralinguistic cues (tone, emotion, emphasis, hesitation) that are lost in text transcription. The trade-off is less flexibility: you cannot mix providers (e.g., Deepgram STT with Anthropic LLM with ElevenLabs TTS), customize voices independently, or control costs at the component level. The component pipeline remains valuable for customization and vendor independence.
Key Takeaways
Real-World Scenario: Adding Voice to a Healthcare Appointment Scheduling System

Who: A product manager and a speech engineer at a telehealth platform with 800 clinics

Situation: The clinic's text-based chatbot handled 60% of appointment bookings, but 35% of patients (especially older demographics) preferred calling. The company wanted a voice interface that shared the same backend logic as the text chatbot.

Problem: Speech-to-text errors transformed critical medical terms: "Metformin" became "met for men," "ENT" became "aunt," and "Dr. Patel" was transcribed as "Dr. Patell." These errors cascaded into incorrect slot filling and confused the booking system.

Dilemma: A custom ASR model fine-tuned on medical terms would fix accuracy but cost $150K and 4 months to develop. Using Whisper with a general model and adding post-processing was cheaper but risked missing edge cases in drug and provider names.

Decision: They used Whisper Large-v3 with a domain-specific vocabulary biasing list (2,000 provider names, 500 common medications, specialty terms). A lightweight correction model mapped common ASR errors to intended terms using an edit-distance lookup table.

How: The voice pipeline used WebSocket streaming for real-time transcription, with voice activity detection to segment utterances. Text-to-speech responses used a warm, measured voice profile selected from user testing with patients aged 55+. Latency was kept under 800ms turn-to-turn.

Result: Voice booking completion rate reached 74% (vs. 60% for text). Medical term recognition accuracy improved from 71% to 94% with vocabulary biasing. Patient satisfaction among the 55+ demographic increased from 3.1 to 4.3 out of 5.

Lesson: Voice interfaces for specialized domains need vocabulary biasing and error correction layers rather than general-purpose ASR alone. Testing with your actual user demographic (not just engineers) is critical for voice UX design.

Research Frontier

Real-time speech-to-speech models (e.g., GPT-4o voice mode, Gemini Live) bypass the traditional ASR-LLM-TTS pipeline by processing audio tokens directly, reducing latency and preserving prosodic information. Multimodal conversation agents can see, hear, and respond with text, images, and audio in a unified dialogue flow. Emotion-aware voice interfaces detect user sentiment from vocal cues and adjust response tone accordingly. Research into ambient conversational AI is developing always-listening agents (with appropriate consent) that can proactively offer information or assistance based on overheard context, raising important privacy and consent questions.

Exercises

These exercises cover voice AI, TTS, STT, and multimodal conversational interfaces.

Exercise 21.5.1: Voice pipeline Conceptual

Describe the three main stages of a traditional voice AI pipeline. What is the bottleneck that determines end-to-end latency?

Show Answer

Three stages: (1) Speech-to-Text (STT): convert audio to text. (2) LLM processing: generate a text response. (3) Text-to-Speech (TTS): convert text response to audio. The LLM processing step is typically the latency bottleneck, though TTS quality/latency tradeoffs matter for user experience.

Exercise 21.5.2: Turn-taking Conceptual

Explain why turn-taking is harder in voice conversations than in text chat. What is Voice Activity Detection (VAD) and how does it help?

Show Answer

In text, turn boundaries are explicit (user presses send). In voice, the system must detect when the user has finished speaking, handling pauses, filler words ("um"), and interruptions. VAD analyzes audio energy and spectral features to detect speech vs. silence, signaling when the user has stopped talking.

Exercise 21.5.3: Streaming vs. batch Conceptual

Compare streaming TTS (audio starts playing before generation completes) with batch TTS (wait for full generation). When is each appropriate?

Show Answer

Streaming: lower perceived latency, audio starts immediately, ideal for conversational interfaces. Batch: higher quality (the TTS can plan prosody for the entire utterance), better for pre-generated content like audiobooks. Use streaming for interactive conversations, batch for offline content generation.

Exercise 21.5.4: Emotion detection Discussion

How can a voice AI system detect user frustration from audio alone? What actions should the system take when frustration is detected?

Show Answer

Frustration signals in audio: increased volume, faster speech rate, rising pitch, sighing, repetition of keywords. Actions: acknowledge the frustration ("I understand this is frustrating"), simplify the response, offer to escalate to a human agent, and reduce response verbosity.

Exercise 21.5.5: Speech-to-speech Conceptual

Explain how native speech-to-speech models (like GPT-4o voice) differ from the traditional STT-LLM-TTS pipeline. What advantages do they offer in terms of latency and expressiveness?

Show Answer

Native speech-to-speech models process audio tokens directly without intermediate text conversion. Advantages: lower latency (no STT/TTS overhead), preservation of prosody and emotion from input to output, ability to generate non-verbal sounds (laughter, sighs), and better handling of multilingual or code-switched speech.

Exercise 21.5.6: STT comparison Conceptual

Transcribe 5 audio clips using Whisper (local) and a cloud STT API. Compare accuracy, latency, and handling of background noise.

Exercise 21.5.7: TTS experimentation Conceptual

Generate speech from the same text using 3 different TTS services. Compare naturalness, pronunciation accuracy, and emotional expressiveness. Score each on a 1 to 5 scale.

Exercise 21.5.8: Voice chatbot Coding

Build a simple voice chatbot: record user speech, transcribe with Whisper, generate a response with an LLM, convert to speech with a TTS API, and play the audio. Measure the total round-trip latency.

Exercise 21.5.9: Multimodal conversation Coding

Build a conversation system that accepts both text and image inputs. When the user uploads an image, use a vision model to describe it, then incorporate the description into the conversation context.

What Comes Next

In the next chapter, Chapter 22: AI Agents: Tool Use, Planning & Reasoning, we begin Part VI by exploring AI agents that can use tools, plan actions, and reason about complex tasks.

References & Further Reading

Radford, A. et al. (2023). "Robust Speech Recognition via Large-Scale Weak Supervision." ICML 2023.

The Whisper paper, presenting a speech recognition model trained on 680,000 hours of multilingual data. Achieves near-human accuracy across many languages. Foundational for anyone building voice interfaces.

Paper

OpenAI Realtime API Documentation.

Guide for building real-time voice conversations with OpenAI models. Covers WebSocket connections, audio streaming, and turn-taking. Essential reference for voice-first AI applications.

Tool

Defossez, A. et al. (2024). "Moshi: A Speech-Text Foundation Model for Real-Time Dialogue." arXiv preprint.

Introduces a unified speech-text model capable of real-time spoken dialogue. Processes audio directly without separate ASR and TTS stages. Important for the future of end-to-end voice AI.

Paper

ElevenLabs Text-to-Speech Documentation.

Leading voice synthesis platform with realistic, customizable voices and low latency. Supports voice cloning and multilingual synthesis. Recommended for production-quality text-to-speech integration.

Tool

LiveKit: Open-source WebRTC Infrastructure.

Open-source platform for building real-time audio and video applications. Provides the transport layer for voice AI with support for multiple participants. Essential infrastructure for voice agent deployment.

Tool

Vapi: Voice AI Platform for Developers.

A platform that simplifies building voice AI agents with pre-built telephony and WebRTC integrations. Handles turn-taking, interruptions, and latency optimization. Ideal for teams wanting rapid voice agent prototyping.

Tool