--- id: voice-assistants-realtime title: Real-Time Voice Assistants code: RT subtitle: VAD, turn detection, barge-in, latency budgets and full-duplex speech for a voice agent that answers in time. hue: 285 sources: phases/06-speech-and-audio/11-real-time-audio-processing, phases/06-speech-and-audio/12-voice-assistant-pipeline, phases/06-speech-and-audio/14-voice-activity-detection-turn-taking, phases/06-speech-and-audio/15-streaming-speech-to-speech-moshi-hibiki, phases/14-agent-engineering/22-voice-agents-pipecat-livekit, phases/19-capstone-projects/03-realtime-voice-assistant source_hash: bd10481585db3e17 --- ## Cascaded voice pipeline type: architecture from: phases/06-speech-and-audio/12-voice-assistant-pipeline/docs/en.md, phases/19-capstone-projects/03-realtime-voice-assistant/docs/en.md ```arch node | mic | 1,1 | Mic | 16 kHz mono, 20 ms | client node | vad | 2,1 | VAD | Silero, speech gate | process node | stt | 3,1 | Streaming STT | partial transcripts | model node | turn | 4,1 | Turn detector | score above 0.6 | model node | llm | 4,2 | LLM with tools | streams tokens | model node | tool | 3,2 | Tool side channel | filler after 300ms | process node | tts | 2,2 | Streaming TTS | first chunk in 200ms | model node | spk | 1,2 | Speaker | WebRTC jitter buffer | client edge | mic | vad | frames edge | vad | stt | speech edge | stt | turn | partials edge | turn | llm | commit edge | llm | tool | call edge | llm | tts | tokens edge | tts | spk | audio edge | vad | tts | barge-in cancel ``` ## Capstone scheduler, one turn type: flow from: phases/19-capstone-projects/03-realtime-voice-assistant/code/ts/src/orchestrator.ts, phases/19-capstone-projects/03-realtime-voice-assistant/code/main.py 1. **IDLE**: the first speech frame moves the state to LISTENING. 2. **LISTENING**: each silent frame adds 20 ms to `silenceRunMs`. 3. **LISTENING**: at 500 ms of silence, `turnCompletionScore` reads the last partial. 4. **WAITING**: a score of 0.6 or more commits the turn and sets `turnCompleteMs`. 5. **THINKING**: the tool call fires. At 300 ms the agent says "one second, let me check". 6. **THINKING**: the tool returns at 420 ms. The first LLM token comes 140 ms later. 7. **SPEAKING**: the first audio out comes 180 ms after the first LLM token. 8. **Barge-in**: speech in THINKING or SPEAKING cancels TTS and re-arms ASR. ## Latency budget per turn type: defaults from: phases/06-speech-and-audio/11-real-time-audio-processing/docs/en.md, phases/06-speech-and-audio/12-voice-assistant-pipeline/docs/en.md | Setting | Default | Note | |---|---|---| | Mic to buffer | 20 ms | One frame | | VAD | 10 ms | Silero runs under 1 ms per frame | | ASR (streaming) | 150 ms | Partial transcripts | | LLM (first token) | 100 ms | Stream the tokens | | TTS (first chunk) | 100 ms | Start before the full reply | | Render to speaker | 20 ms | One frame | | Total | ~400 ms | Above 500 ms feels robotic | | First TTS audio, laptop CPU | 800 ms | Phase 6 capstone target | ## VAD and turn settings type: defaults from: phases/06-speech-and-audio/14-voice-activity-detection-turn-taking/docs/en.md, phases/06-speech-and-audio/11-real-time-audio-processing/docs/en.md, phases/19-capstone-projects/03-realtime-voice-assistant/docs/en.md | Setting | Default | Note | |---|---|---| | Silero threshold | 0.5 | 0.3 is sensitive. Fewer first-word clips | | Minimum speech | 250 ms | Rejects coughs and chair noise | | Silence hangover | 500-800 ms | Too short cuts the user off | | Pre-roll buffer | 300-500 ms | Keeps the first word | | Energy gate | -40 dBFS | Fires on any noise above it | | Frame size | 20 ms | 320 samples at 16 kHz | | Ring buffer | 32,000 samples | 2 s at 16 kHz | | Turn commit (capstone) | 0.6 | Plus 500ms of VAD silence | ## Silero VAD segments type: annotated-code lang: python from: phases/06-speech-and-audio/14-voice-activity-detection-turn-taking/docs/en.md ```python from silero_vad import load_silero_vad, get_speech_timestamps vad = load_silero_vad() audio = torch.tensor(waveform_16k, dtype=torch.float32) segments = get_speech_timestamps( audio, vad, sampling_rate=16000, threshold=0.5, # 0.3 is sensitive min_speech_duration_ms=250, # drops coughs min_silence_duration_ms=500, # silence hangover speech_pad_ms=300, # pre-roll pad ) for s in segments: print(f"{s['start']/16000:.2f}s - {s['end']/16000:.2f}s") ``` ## Barge-in and the flush trick type: annotated-code lang: python from: phases/06-speech-and-audio/12-voice-assistant-pipeline/docs/en.md, phases/06-speech-and-audio/14-voice-activity-detection-turn-taking/docs/en.md ```python tts_task = asyncio.create_task(tts_loop()) while True: chunk = await mic.get() if vad(chunk): # user speaks during TTS tts_task.cancel() # stop the reply await speaker.stop() await new_turn() # restart STT break def flush_on_end(stt_client, audio_buffer): stt_client.send_audio(audio_buffer) stt_client.send_flush() # forces output now return stt_client.recv_transcript(timeout_ms=150) ``` ## Run the lessons type: command-table from: phases/06-speech-and-audio/11-real-time-audio-processing/code/main.py, phases/06-speech-and-audio/12-voice-assistant-pipeline/docs/en.md, phases/19-capstone-projects/03-realtime-voice-assistant/code/main.py, phases/19-capstone-projects/03-realtime-voice-assistant/code/ts/README.md | Command | What it does | |---|---| | `python3 code/main.py` | Runs the simulator of a lesson | | `python main.py` | Capstone scheduler, clean and barge-in calls | | `pip install silero-vad` | Real VAD in place of the stub | | `npm install` | Installs the TypeScript client | | `npm start` | Two offline sessions and a ws probe, exits 0 | | `npm start -- --serve` | Keeps the ws server up | | `npm test` | node --test runner via tsx | | `npm run typecheck` | Runs `tsc --noEmit` | ## Core terms type: concepts from: phases/06-speech-and-audio/11-real-time-audio-processing/docs/en.md, phases/06-speech-and-audio/14-voice-activity-detection-turn-taking/docs/en.md, phases/19-capstone-projects/03-realtime-voice-assistant/docs/en.md, phases/14-agent-engineering/22-voice-agents-pipecat-livekit/docs/en.md - **Ring buffer**: fixed circular buffer. No allocations in the hot path. - **Jitter buffer**: reorders late packets. Too small gives gaps. Too large adds latency. - **Pre-roll**: audio kept before VAD fires. It prevents the first-word clip. - **Silence hangover**: the wait after VAD returns to 0, before end of turn. - **Semantic endpoint**: a classifier that reads the words, not only the silence. - **Flush trick**: flush the STT at end of speech. 500 ms of look-ahead ends in ~125 ms. - **Filler**: a short phrase when a tool takes more than 300ms. - **DOWNSTREAM**: Pipecat frames from source to sink, audio in and speech out. - **UPSTREAM**: Pipecat control frames, such as cancel, metrics and barge-in. ## Full-duplex Moshi, one 80 ms step type: flow from: phases/06-speech-and-audio/15-streaming-speech-to-speech-moshi-hibiki/docs/en.md, phases/06-speech-and-audio/15-streaming-speech-to-speech-moshi-hibiki/code/main.py 1. **Mimi**: encodes user audio at 12.5 Hz with 8 codebooks. 2. **Temporal Transformer**: 7B parameters. Reads the user tokens and its own last tokens. 3. **Temporal Transformer**: emits the next text token, the inner monologue. 4. **Depth Transformer**: 2 layers. Predicts the 8 codebooks one after the other. 5. **Client**: `decode_audio_mimi` turns the tokens into audio and plays it. 6. **Budget**: 80 ms frame plus 80 ms acoustic delay is 160 ms. 200 ms on one L4 GPU. ## Troubleshooting type: troubleshooting from: phases/06-speech-and-audio/12-voice-assistant-pipeline/docs/en.md, phases/06-speech-and-audio/11-real-time-audio-processing/docs/en.md, phases/06-speech-and-audio/14-voice-activity-detection-turn-taking/docs/en.md | Symptom | Cause | Fix | |---|---|---| | First word is missing | VAD starts a beat too late | Threshold 0.3 and a pre-roll | | Bot talks over the user | LLM keeps going after interrupt | Wire VAD to cancel the LLM | | "Thanks for watching" on silence | Whisper on silent frames | Always VAD-gate | | ASR hears the bot voice | No echo cancellation | WebRTC AEC3 | | Agent cuts user off mid-sentence | Hangover too short | 500-800 ms and a turn detector | | Slow first reply | TTS warm-up on first request | Warm it with a dummy run | | Audible vocoder artifacts | Sub-200 ms TTS chunks | 320 ms chunks | | Glitches under load | GIL starves the audio thread | C-callback audio library | ## Glossary type: glossary from: phases/06-speech-and-audio/11-real-time-audio-processing/docs/en.md, phases/06-speech-and-audio/14-voice-activity-detection-turn-taking/docs/en.md, phases/14-agent-engineering/22-voice-agents-pipecat-livekit/docs/en.md - **AEC**: echo cancellation. Subtracts the speaker to mic path. - **TPR @ 5% FPR**: VAD benchmark point. Silero 87.7%, WebRTC VAD 50.0%. - **MultimodalAgent**: LiveKit class. Audio in, audio out, no text in the middle. - **VoicePipelineAgent**: LiveKit class. STT, LLM, TTS with text-level control. ## Q&A type: qa from: phases/06-speech-and-audio/11-real-time-audio-processing/docs/en.md, phases/06-speech-and-audio/14-voice-activity-detection-turn-taking/docs/en.md, phases/06-speech-and-audio/15-streaming-speech-to-speech-moshi-hibiki/docs/en.md, phases/14-agent-engineering/22-voice-agents-pipecat-livekit/docs/en.md, phases/19-capstone-projects/03-realtime-voice-assistant/docs/en.md - **How fast do humans take turns?** About 230 ms. Above 1500 ms feels broken. - **Pipeline or Moshi?** Use a pipeline for tools. Moshi is not trained for tool calling. - **Why no flush with Whisper streaming?** It is block-based and always waits for chunks. - **What is end-to-end premium?** 450 to 600 ms. 800 to 1200 ms is common. - **What is the capstone bar?** First-audio-out p50 under 800ms, false-cutoff under 3%. - **What does barge-in need in Pipecat?** UPSTREAM cancel frames back to TTS and LLM.