Turn-Taking and Interruptions
|
This content was generated with the assistance of AI and should be verified against the official documentation of each tool and framework before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Written on 2026-10-01 against Silero VAD 6.2.3, TEN VAD 1.0, livekit-agents 1.8.3 with
livekit-plugins-turn-detector 1.8.3, the pipecat-ai/smart-turn project with Pipecat 1.12.0, Deepgram Flux
(flux-general-en, flux-general-multi), AssemblyAI streaming, OpenAI GPT-Live and the Realtime API, and the Twilio
ConversationRelay and Media Streams message sets (see the version
baseline). Turn-taking behaviour was not measured on live audio here; the thresholds quoted below are starting points
documented by the projects or common practice, and every one should be tuned on recordings of your own callers.
Turn-taking is the part of a voice agent that callers judge without knowing it exists. An agent that answers too early cuts people off in the middle of a sentence; one that answers too late feels dead. One that keeps talking after the caller says "no, wait" feels rude. This page covers the machinery behind those three impressions.
Voice activity detection
A voice activity detector (VAD) classifies short audio frames as speech or non-speech. It is a small neural network that runs on a CPU in well under a millisecond per frame, so it can run on every call. Two open-source choices are common:
-
Silero VAD (MIT) is the default in Pipecat and LiveKit Agents. It takes 16 kHz (or 8 kHz) audio in small windows and returns a speech probability.
-
TEN VAD (from the TEN Framework) is a low-latency detector aimed at the same job, with a lighter footprint.
A VAD alone gives two decisions, "speech started" and "speech stopped for N milliseconds". Both are parameterised:
| Parameter | Effect |
|---|---|
Speech probability threshold |
Higher values ignore breathing and background noise but miss quiet speakers; lower values do the opposite. |
Minimum speech duration |
How long speech must last before "speech started" fires. Too short and coughs and door slams interrupt the agent. |
Silence (stop) duration |
How long silence must last before "speech stopped" fires. This is the main contributor to in the latency budget. |
Padding |
Audio kept before the detected start, so the first syllable is not clipped when the transcriber receives the segment. |
Telephone audio at 8 kHz with codec artefacts and line noise needs a higher threshold than studio audio, and noisy environments (cars, call centres) benefit from a noise suppression step in front of the VAD; hosted APIs such as Azure Voice Live and the OpenAI APIs offer it as a session option.
Endpointing: when is the turn over?
Detecting that the caller stopped making sound is not the same as detecting that they finished their thought. "My order number is…" followed by a pause to look something up is silence, not a finished turn. There are three levels of sophistication.
-
Silence endpointing. Declare the turn over after a fixed silence, typically a few hundred milliseconds up to about one second. It is simple, adds that wait to every turn, and still cuts off slow speakers.
-
Model-assisted endpointing. The speech recogniser itself reports the end of the utterance. Deepgram Flux emits
EndOfTurnand a speculativeEagerEndOfTurnevent on its/v2/listenendpoint, so the agent can start preparing the answer early and cancel if the caller keeps talking. AssemblyAI’s streaming API marks turn boundaries with anend_of_turnfield on its turn messages. -
Semantic turn detection. A small model reads the transcript (or the audio) and estimates whether the sentence is complete. The LiveKit turn detector (
livekit-plugins-turn-detector) is a language model over the running transcript; Pipecat’s Smart Turn is an audio model that looks at prosody and the last seconds of speech. Both run alongside the VAD and extend the wait when the utterance sounds unfinished.
The trade-off is the false-positive rate (interrupting the caller) against the added delay. A semantic detector typically lets you use a short VAD silence (say 300 to 500 ms) for obviously finished sentences and a longer maximum for unfinished ones, which lowers the average delay without raising the interruption rate. Test on recordings of hesitant speakers, numbers read aloud, and non-native speech, where the models are weakest.
Speech-to-speech APIs make the same choice as a session option. OpenAI exposes server-side VAD with a semantic mode alongside plain silence detection, Gemini Live has automatic activity detection with sensitivity and silence settings, and Azure Voice Live offers semantic end-of-turn detection; see Realtime APIs and check each vendor’s current option names before copying configuration.
from livekit.agents import AgentSession
from livekit.plugins import silero
from livekit.plugins.turn_detector.multilingual import MultilingualModel
session = AgentSession(
vad=silero.VAD.load(),
turn_detection=MultilingualModel(),
allow_interruptions=True,
min_endpointing_delay=0.5,
max_endpointing_delay=3.0,
# stt=..., llm=..., tts=... are configured as shown on the LiveKit Agents page
)
Option names evolve between releases; confirm them against LiveKit: Turn handling and the LiveKit Agents page. For Pipecat, the VAD analyzer and the Smart Turn analyzer are attached to the transport or the user aggregator; the current configuration is in the Smart Turn repository and the Pipecat page.
Barge-in
Barge-in is the caller speaking while the agent is speaking. Handling it well takes four actions, in this order and as fast as possible:
-
Detect it. The VAD fires "speech started" while the agent is in the speaking state. Require a minimum speech duration (200 to 300 ms) or a few recognised words, otherwise a cough or an echo stops the agent.
-
Stop the audio. Cancel the TTS request and flush every audio buffer between the synthesiser and the caller. The buffers are the usual bug: audio already sent to the transport keeps playing unless the transport is told to clear it. Twilio Media Streams has a
clearmessage for exactly this, ConversationRelay raises aninterruptmessage, and WebRTC playback is stopped by the client when it learns of the interruption. -
Cancel the generation. Cancel the running LLM stream, and any tool call that has not started side effects.
-
Truncate the history. The conversation memory must contain what the caller actually heard, not the full answer the LLM had planned. Otherwise the next turn is confused: the caller says "what was the second step?" and the model believes it already spoke all five.
History truncation needs a record of how much audio reached the speaker. Speech-to-speech APIs provide this: the OpenAI
Realtime API accepts a conversation.item.truncate event with the audio end offset in milliseconds, and GPT-Live and
Gemini Live handle interruption inside the session. In a cascaded pipeline you estimate it from the playback position or,
for telephony, from the mark acknowledgements Twilio returns when queued audio reaches the caller.
import asyncio
class TurnState:
def __init__(self) -> None:
self.speaking_task: asyncio.Task | None = None
self.spoken_text = "" # text whose audio was confirmed played
async def on_user_speech_started(state: TurnState, transport, memory, thread_id: str) -> None:
if state.speaking_task and not state.speaking_task.done():
state.speaking_task.cancel() # stops LLM stream and TTS requests
await transport.clear_output_audio() # flush queued audio (Twilio: send "clear")
await memory.replace_last_assistant(thread_id, state.spoken_text + " [interrupted]")
This is a sketch, not a library API: transport and memory stand for whatever your transport and memory classes
provide. The Twilio clear and mark messages are specified in the
Twilio Media Streams WebSocket messages reference.
Echo cancellation
If the caller’s speaker output reaches their microphone, the agent hears itself and interrupts itself. Who removes the echo depends on the transport.
| Transport | Echo handling |
|---|---|
Browser, WebRTC or |
The browser applies acoustic echo cancellation when the constraint |
Phone network (PSTN) |
The carrier and the handset handle echo; the agent usually receives clean far-end audio. Speakerphone use on the caller’s side can still leak the agent’s voice back and needs the stricter minimum-speech rule above. |
SIP and PBX |
Echo can appear between the PBX and the phone; an echo canceller in the PBX or the SIP gateway is configured per channel. See Telephony with Asterisk and SIP. |
Server-side audio capture |
Raw audio sent over a WebSocket has no echo cancellation unless the client applied it. Apply it on the client, or accept that barge-in will need a higher threshold. |
A practical fallback is to suppress barge-in detection during the first few hundred milliseconds of each agent utterance, and to require recognised words, not only energy, before treating speech as an interruption.
DTMF
Phone callers can press keys, and the keys arrive as dual-tone multi-frequency (DTMF) events, not as speech. They are the
reliable way to enter an account number, confirm a choice or escape a loop. Twilio ConversationRelay delivers a dtmf
message to the WebSocket and Media Streams delivers dtmf events; Asterisk raises DTMF events on ARI channels; see
Telephony with Twilio. Treat DTMF as input to the same
ask(conversationId, userId, text) core (for example, as the text "caller pressed 2"), and offer it as an alternative
whenever speech recognition fails twice.
Silence and timeouts
Every state needs a timeout:
-
Caller silent after the agent’s question. Re-prompt once after a few seconds ("Are you still there?"), then offer DTMF, then end the call politely.
-
Caller silent in the first seconds. On outbound calls, silence may mean voicemail or an answering machine; hand over to the voicemail logic rather than speaking into it.
-
Agent thinking too long. If the LLM or a tool has not produced a first token after a threshold, say a short filler ("One moment") so the line does not go dead, and give up with an apology at a hard limit.
-
Whole-call limit. Cap the call duration so a stuck loop cannot run up cost.
Backchannels and fillers
People say "mm-hm", "right" and "okay" while the other party talks, without taking the turn. A VAD treats them as speech and may stop the agent for no reason. Options are to require a minimum number of words before an interruption counts, to list known backchannel words that the pipeline ignores while the agent speaks, or to use a model that classifies the interruption. In the other direction, an agent can emit its own short acknowledgements ("I see", "let me check") to cover tool latency; deliver them as a separate, pre-synthesised clip so they cost no synthesis time.
A tuning checklist
-
Record 50 real calls (with consent; see Production Voice Agents).
-
Replay them with different VAD thresholds and endpointing delays and count false interruptions and cut-offs.
-
Check that barge-in stops audible output within about 300 ms and that the history shows what was heard.
-
Test with a speakerphone, a headset, a noisy room and a bad mobile connection.
-
Re-run after every change of model, codec or transport.