Back to Insights
Technology

Barge-in That Actually Works: Tuning VAD for Phone Audio

Voice activity detection settings tuned on clean studio audio fall apart on an 8 kHz phone line. The two jobs one detector is asked to do, the false barge-in and how to recover from it, and why we moved turn detection back out of the cloud.

Published August 6, 2026
10 min read

Interrupting is not a feature you switch on. It is a judgement your agent makes several times a second, on eight kilohertz audio full of hold music, road noise and other people's conversations, with no ability to ask whether you actually meant to speak. Get that judgement slightly wrong in one direction and the agent talks over your callers. Get it wrong in the other and it cuts them off mid sentence. Most voice agents that feel rude are not badly prompted. They are badly tuned.

One detector, two completely different jobs

Voice activity detection sounds like a single thing, and in most codebases it is a single component. But it is asked to answer two questions that have almost nothing to do with each other, and tuning it as one setting is where the trouble starts.

The first question is: has the caller finished their turn, so may I answer now? That is endpointing, it runs while your agent is quiet, and being wrong costs you either a long awkward gap or a sentence chopped in half. The second question is: is the caller talking right now while I am talking, so should I stop? That is barge-in, it runs while your agent is speaking, and being wrong costs you either an agent that steamrolls people or one that falls silent every time a lorry goes past.

The core confusion

The same audio, two different costs of being wrong

Endpointing errors are measured in dead air. Barge-in errors are measured in rudeness. A single sensitivity setting cannot optimise for both, which is why platforms that expose one slider produce agents that are either sluggish or pushy, and never neither.

Why studio settings fall apart on a phone line

Nearly every published benchmark for a voice activity detector is run on clean, wideband, single-speaker audio recorded through a decent microphone. A phone call is none of those things. Traditional telephony gives you eight kilohertz narrowband, which throws away most of the frequency range that makes consonants distinguishable, then compresses what remains.

On top of that, the caller channel is rarely quiet even when the caller is. There is comfort noise injected deliberately so the line does not sound dead. There is a television, a car, an open-plan office, a toddler. There is hold music from a system the caller is also on. A detector tuned to be helpful in a studio will read a good deal of that as speech, and each false positive either interrupts your agent or convinces it the caller is still talking when they are waiting for an answer.

ConditionWhat it does to detectionPractical response
8 kHz narrowbandSofter consonants and sibilants get lost, so speech onsets are less crisp than the detector expects.Do not port a threshold straight from a wideband benchmark. Re-tune on real call audio.
Comfort noiseA permanently non-silent channel, so a naive energy threshold never sees silence.Use a model-based detector rather than raw energy gating.
Background speechGenuine human speech that is not your caller. Detectors are right to flag it and wrong to act on it.Require a minimum duration before committing to an interruption.
Hold music, ringbackStructured audio that is easy to mistake for voice.Guard the state machine, not just the detector: an agent that is not yet in conversation should not be interruptible.
Filled pauses and thinking noisesThe caller has not finished, but the silence run has started.Give the turn detector a ceiling so an ambiguous pause gets room, rather than a fixed short timer.

The false barge-in, and what to do after one

The single most useful control in this whole area is a minimum interruption duration: a rule that says a burst of detected speech on the caller channel does not count as an interruption unless it lasts long enough to be a word. A cough, a door, a syllable of somebody else's conversation all fail that test and your agent keeps talking.

barge-in-decision.log
agent speaking:  "Your appointment is confirmed for Tues-"
  [vad]   speech detected on caller channel   0.12s
  [turn]  below minimum interruption duration (0.30s)
  [vad]   caller channel silent again          0.19s
  ---> not an interruption. agent keeps speaking.

agent speaking:  "Your appointment is confirmed for Tues-"
  [vad]   speech detected on caller channel   0.34s
  [turn]  interruption committed, agent stops
  [stt]   caller said: "sorry, Wednesday"
  ---> real interruption. agent yields.

On our own pipeline that threshold sits at 300 milliseconds, tuned down from the framework default of 500. Shorter than the default because on a real phone line a genuinely impatient caller starts speaking decisively, and half a second of waiting before yielding is long enough for two people to be talking at once, which is the exact situation barge-in exists to prevent.

The more interesting question is what happens after a false positive slips through anyway, because some always will. Naive implementations stop the agent mid-sentence, wait for a transcript that never arrives because the noise was not speech, and leave both parties in silence until the caller says something. The caller has no idea the agent was ever going to finish that sentence.

false-interruption-recovery.log
[turn]  interruption committed, agent stops mid-sentence
  [stt]   ...no transcript. line noise, not speech.
  [turn]  false interruption timeout reached  (1.5s)
  ---> agent resumes its own sentence instead of standing there

The fix is a timeout with a resume: if an interruption produced no transcript within a second and a half, treat it as false and let the agent continue its own sentence. That turns a dead call into a barely noticeable stumble, and it is the difference between an agent that seems fragile and one that seems composed.

The dial you are actually turning

Once you separate the two jobs, the settings stop looking like a config file and start looking like a product decision. Every one of them trades the same two failure modes against each other, and there is no combination that eliminates both.

Turned up

Eager to yield, quick to answer

  • Feels responsive, close to a human conversational gap
  • Interrupts callers who paused to think or read a number aloud
  • Background speech and line noise stop the agent mid sentence
  • Best for short, transactional calls with a known script
Turned down

Patient, hard to interrupt

  • Lets people finish, tolerates noisy lines and slow speakers
  • Adds dead air to every single exchange in the call
  • An impatient caller has to talk over the agent to be heard
  • Best for open-ended calls, elderly callers, poor connections

The reason this matters more than it looks is that the endpointing floor is a hard lower bound on how fast your agent can possibly reply. Set it high to fight false interruptions and you have quietly capped the responsiveness of the entire product, in a way that no model upgrade will ever recover. We took that argument apart in the latency budget of a phone call, where turn detection turns out to be the largest single block in a one second gap.

A useful default

Bound it, do not fix it

A fixed timer forces one compromise on every turn in every call. A bounded dynamic window lets a confident end of turn commit fast while an ambiguous one gets room to finish. We run a floor of 400 milliseconds and a ceiling of 2 seconds, against framework defaults of 500 milliseconds and 3 seconds.

Who owns the dial: pipeline or provider

There is a structural fork here that catches teams out when they switch architectures. In a classic recognition, model and synthesis pipeline, turn taking is yours: the detector runs on your side and you own every threshold. In a native-audio realtime model, turn taking belongs to the provider, and you get whatever controls they chose to expose.

PipelineRealtime (speech-to-speech)
Who decides a turn endedYour detector, running locally on the audioThe provider, inside their model
What you can tuneThresholds, minimum durations, endpointing windowThe handful of parameters the provider exposes
Per-assistant controlPlatform-wide behaviour, tuned once for phone audioYes, per assistant, within the provider's own ranges
Failure to watch forSettings ported from wideband benchmarksSilently ignored parameters and defaults that differ per model

On the realtime side we expose the provider's turn-detection parameters per assistant rather than hiding them, with the ranges clamped so a typo cannot produce a call that never yields. An out-of-range value logs a warning and keeps the default, because a bad config should degrade to sane behaviour instead of breaking a live call.

Realtime turn detection, per assistant
llm_config.turn_detection = {
  "threshold":           0.5,    // 0.0 - 1.0    how much signal counts as speech
  "prefix_padding_ms":   300,    // 0 - 2000     audio kept from before the onset
  "silence_duration_ms": 200     // 100 - 2000   silence before the turn is closed
}

Two of those parameters are deliberately not exposed at all, and the reason is worth stating plainly: the calls that create and interrupt a model response are what the conversation loop is built out of. Letting a customer disable either one produces an assistant that cannot hold a conversation, which is a support ticket rather than a feature.

Defaults also differ by model in ways that are easy to miss. Where a text-adjacent realtime model may close a turn after two hundred milliseconds of silence, we default a native-audio model to a considerably longer window and the least twitchy sensitivity setting available, precisely so that a background utterance during a pause does not open a turn. Porting one provider's numbers to another is a reliable way to make a working agent feel broken.

Why we took the cloud turn detector out of the call path

There is a fashionable option in this space: hand turn detection to a hosted service that uses a smarter model to predict whether a sentence is semantically finished, not just acoustically quiet. It genuinely works better on ambiguous pauses. We ran it, and in July 2026 we deliberately moved back to running detection locally.

Three reasons, in order of how much they mattered. First, it put a network call from a third-party cloud service directly in the live conversation path, which is the one place in a telephony system where an extra dependency is least welcome. Second, it carried a request quota that was comfortable at our volume and would not survive the scale we are building for, which makes it a ceiling rather than a component. Third, and the one that decided it: when that service was slow to respond, the framework fell back to plain local detection without saying so, which meant our turn-taking behaviour quietly changed depending on someone else's response time.

The principle

Inconsistent is worse than imperfect

A detector that is slightly blunt but behaves identically on every call is easier to build a product on than one that is smarter most of the time and different when a dependency is having a bad afternoon. Silent fallback is the specific failure we design against, because it produces bugs nobody can reproduce.

That is the same reasoning we apply to failover and to configuration generally: a system that degrades loudly is safer than one that degrades quietly. It is also why the local detector settings above are platform-wide rather than per assistant. They describe how our agents listen to a telephone, and that should not vary per customer without a conversation about why.

The other half: when the caller says nothing

Everything so far assumes a caller who talks too much or at the wrong moment. The opposite case is more common than teams expect and is usually handled worse: the caller who says nothing at all. They put the phone down to find a policy number, or they are on speaker in a car, or they simply did not realise it was their turn.

A naive agent waits forever, holding an open line and billing minutes for silence. The shape that works is a short ladder rather than a single timeout: notice quickly, prompt gently, and only then give up. We check in after a few seconds, re-engage on a fixed interval, allow three attempts, and end the call cleanly if nothing comes back rather than leaving the line hanging.

  1. 01

    Separate a pause from an absence

    These are different states with different timers. Treating a two second thinking pause with the same logic as a thirty second absence gets you an agent that nags.
  2. 02

    Re-engage before you escalate

    A single short prompt recovers a surprising share of silent turns. Callers who stepped away come back, and the call continues normally.
  3. 03

    Bound the attempts and end deliberately

    Three tries is enough. Ending with a clear reason gives you a deterministic end state in your reporting instead of an ambiguous hang-up you cannot count.
  4. 04

    Make the outcome visible

    A silence timeout should be its own end reason. If it lands in the same bucket as a caller hanging up, you lose the one signal that tells you your prompts are too slow or your line is one-way.

Barge-in tuning is not a detail you get to after the prompt is right. It is the difference between an agent people talk to and an agent people talk over.

If your agent already sounds correct on paper and still feels wrong on the phone, the problem is usually here rather than in the wording. A related failure worth ruling out first is a media path that was never healthy to begin with, which we covered in reading the SIP logs when calls fail.

Notes on the numbers The minimum interruption duration of 300 milliseconds, the false-interruption timeout of 1.5 seconds with resume, the endpointing floor of 400 milliseconds and ceiling of 2 seconds, the 400 millisecond silence requirement, the activation threshold of 0.4 and the three-attempt silence ladder are the values our own platform runs, along with the framework defaults they were moved away from. The realtime turn-detection parameters and their ranges are the ones published in our API specification. The move away from a hosted turn detector took place in July 2026 for the three reasons described. Narrowband telephony, comfort noise and packetisation behave as they do in any SIP deployment and are not specific to us.