Os parâmetros do silero.VAD e do LiveKit — o que cada um faz de verdade
O que cada parâmetro muda no barge-in, no áudio que chega ao STT e no atraso percebido
da conversa — e como eles interagem. Semântica verificada no código instalado
(livekit-agents 1.3.10, plugin silero).
1. O modelo mental: duas camadas, duas perguntas
Há dois conjuntos de parâmetros que costumam ser confundidos:
| silero.VAD (detector) | AgentSession (turn-taking) |
| Pergunta que responde |
"há fala neste áudio, de onde até onde?" |
"o usuário terminou o turno? devo interromper o agente?" |
| Parâmetros |
activation/deactivation_threshold, min_speech_duration,
min_silence_duration, prefix_padding_duration,
max_buffered_speech |
turn_detection, min/max_endpointing_delay,
allow_interruptions, min_interruption_duration/words,
false_interruption_timeout |
| Consome | frames de áudio (janelas de 32 ms) | eventos do VAD + transcrições do STT |
O VAD emite três eventos que alimentam tudo: START_OF_SPEECH (começou a falar),
INFERENCE_DONE (a cada 32 ms, com probabilidade e contadores) e
END_OF_SPEECH (parou de falar, carrega o chunk de áudio para o STT).
Toda decisão é quantizada em janelas de 32 ms — nenhum parâmetro tem efeito mais fino que isso.
2. As duas cadeias de latência (o que o cliente sente)
CADEIA DE RESPOSTA — "o robô demora pra responder"
cliente PARA de falar
│ min_silence_duration ······· o VAD só fecha o segmento após esse silêncio (0.5–0.8 s)
▼
END_OF_SPEECH (chunk de áudio pronto)
│ endpointing ················ modo VAD: max(silêncio já contado, min_endpointing_delay)
│ → se min_endpointing ≤ min_silence, custo extra ZERO
▼
turno encerrado
│ whisper-1 (batch!) ········· só começa a transcrever AGORA (o chunk nasce no END)
│ LLM (primeiro token) + TTS (primeiro áudio)
▼
robô responde SILÊNCIO PERCEBIDO ≈ min_silence + STT + LLM + TTS
CADEIA DE BARGE-IN — "falei por cima e o robô não calou"
cliente COMEÇA a falar por cima
│ rampa da probabilidade ····· 2–4 janelas (64–128 ms) até cruzar activation_threshold
│ min_speech_duration ········ piso para confirmar fala (0.05 s ≈ 2 janelas)
▼
START_OF_SPEECH (pub_speech_duration começa a contar)
│ min_interruption_duration ·· checado a CADA janela: speech_duration ≥ 0.5 s?
▼
agente é interrompido ATÉ CALAR ≈ rampa + max(min_speech, min_interruption_duration)
A assimetria importante
O
min_silence_duration é um custo
inevitável e integral na cadeia de resposta —
não existe como saber que o cliente parou sem esperar o silêncio. Já o barge-in NÃO depende do
min_silence: depende da rampa de detecção +
min_interruption_duration.
Por isso mexer no min_silence muda a "lentidão" do robô, e mexer no min_interruption muda a
"teimosia" dele ao ser interrompido — são botões independentes.
3. Parâmetros do silero.VAD, um a um
activation_threshold
default livekit: 0.5 · nosso valor: 0.16 (banda-estreita G729→G711 deprime as probabilidades do Silero)
- O que é
- Probabilidade mínima (suavizada, EMA α=0.35) para uma janela contar como fala e abrir um segmento.
- Barge-in
- Mais baixo = rampa cruza o limiar mais cedo → disparo mais rápido (medimos p90 de onset caindo de 304 → 176 ms ao ir de 0.34 → 0.16). É o parâmetro que mais afeta a cauda da latência de onset.
- STT
- Mais baixo = captura fala baixa/fraca (recall↑), mas ativa mais em fundo/ruído (falsos positivos → chunks-lixo pro whisper, que alucina).
- Atraso percebido
- Não afeta a cadeia de resposta; afeta só o onset.
deactivation_threshold
default livekit: activation − 0.15 · nosso valor: 0.06
- O que é
- Histerese de saída: com o segmento aberto, a janela continua contando como fala enquanto prob > este valor. O silêncio só começa a acumular abaixo dele.
- STT
- Mais baixo = o segmento "segura" caudas fracas de fala (finais de palavra não são cortados), mas também segura fala de fundo — no nosso dataset a intrusão de fundo vem majoritariamente da continuação (deact baixa), não do onset.
- Barge-in
- Indireto: deact mais baixa atrasa o fechamento → atrasa a cadeia de resposta quando há ruído residual.
- Interação
- Deve ficar bem abaixo da activation (histerese); no grid, o eixo deact 0.05–0.10 é quase plano no score — escolha pelo comportamento de cauda.
min_speech_duration
default livekit: 0.05 s · nosso valor: 0.05 s (eixo de grid interessante: 0.10–0.15)
- O que é
- Tempo acumulado de fala (janelas acima do limiar) antes de emitir
START_OF_SPEECH. 0.05 s ≈ 2 janelas.
- Barge-in
- Soma diretamente à latência de onset (é um piso). Subir para 0.15 adiciona ~100 ms ao disparo.
- STT
- Filtra "blips": estalos, cliques e os falsos positivos de 0.1–0.3 s que medimos (inclusive os causados pelo dither do pipeline). Subir este valor é a forma mais barata de matar órfãos.
- Trade-off central
- onset p90 (gate ≤ 0.25 s) vs taxa de órfãos — os dois lados estão nos gates/vetor do avaliador; o grid arbitra.
min_silence_duration
default livekit: 0.55 s · nosso valor: 0.5 s (campeão antigo usava 0.8)
- O que é
- Silêncio acumulado (prob < deactivation) antes de emitir
END_OF_SPEECH e fechar o chunk.
- Atraso percebido
- Sim, é o piso do atraso de resposta — o robô nunca responde antes de min_silence após o cliente calar, por construção. 0.5 s é perceptível mas natural; 0.8 s já soa "lento" somado a STT+LLM+TTS.
- STT / picote
- Controla o picote: pausas do cliente maiores que este valor viram cortes (2 chunks); menores ficam dentro do mesmo chunk. Menor valor = mais picote (mais chunks curtos → mais chamadas ao whisper, respostas a meia-frase se o endpointing deixar); maior valor = menos picote, porém mais "pontes" (emenda turnos distintos) e mais atraso.
- Barge-in
- Não afeta o disparo da interrupção. Afeta quando o turno do cliente é dado como encerrado depois dela.
- Interação com o labeling
- Nossa convenção rotula pausas <0.8 s dentro da mesma fala → min_silence entre 0.5 e 0.8 é a janela coerente: abaixo de 0.5 fragmenta falas rotuladas (pausas internas de 0.5–0.8 s viram corte); acima de 0.8 começa a emendar falas distintas (ponte punida).
prefix_padding_duration
default livekit: 0.5 s · nosso valor: 1.0 s
- O que é
- Áudio retroativo mantido antes do início detectado da fala no chunk emitido. O VAD guarda um buffer circular; quando confirma fala, o chunk já nasce com esse contexto anterior.
- Atraso percebido
- Zero. É retrospectivo — o áudio já estava gravado. Não confundir com espera.
- STT
- É o seguro contra cortar o primeiro fonema: se o onset atrasou 150 ms, o padding cobre o que o detector perdeu. Whisper transcreve visivelmente melhor a primeira palavra com contexto. Custo: payload maior (1 s × nº de chunks) e, na régua
stt_emitted do avaliador, aparece como "spill" — esperado.
- Barge-in
- Nenhum efeito.
max_buffered_speech
default livekit: 60 s · nosso valor: 60 s
- O que é
- Teto do buffer de fala de um segmento. Monólogo mais longo que isso tem o excedente descartado do chunk (com warning no log) — o VAD continua detectando, mas o áudio extra não vai ao STT.
- Quando importa
- Ligações com falas muito longas (nosso dataset tem rótulo de 49 s — perto do teto). Se transcrição integral de monólogos importar, subir; custo é memória.
sample_rate (8000 | 16000) e a janela de 32 ms
nosso valor: 16000 (chega G711 16 kHz do SBC, conteúdo banda-estreita)
- O que é
- Taxa do modelo Silero. A 16 kHz a janela de inferência é 512 amostras = 32 ms; a 8 kHz, 256 amostras = 32 ms também.
- Observação do nosso tráfego
- O conteúdo real é 0–3.4 kHz (G729); alimentar o modo 8 kHz (decimando 2×, sem perda) é um experimento pendente que pode calibrar melhor as probabilidades — ver RELATORIO_V2 §6.
4. Parâmetros do AgentSession (livekit-agents 1.3.10)
turn_detection
default: automático na ordem realtime_llm → vad → stt → manual
- O que é
- A estratégia para decidir "o cliente terminou o turno":
"vad" (eventos do VAD), "stt" (fim-de-fala do provedor de STT), "realtime_llm", "manual", ou um plugin detector de fim-de-turno (modelo EOU).
- No nosso caso
- Com whisper-1 batch não há sinal de endpointing do STT em tempo real → o modo efetivo é "vad": o turno fecha pelos eventos do nosso detector. Ou seja, a qualidade do VAD é a qualidade do turn-taking.
min_endpointing_delay
default: 0.5 s
- O que é
- Tempo mínimo desde a última fala detectada antes de declarar o turno encerrado. Em modo VAD comporta-se como
max(silêncio do VAD, min_endpointing_delay) (docstring do 1.3.10) — o silêncio que o VAD já contou vale para o endpointing.
- Consequência prática
- Se
min_endpointing_delay ≤ min_silence_duration, o endpointing custa zero extra: quando o END_OF_SPEECH chega, a espera já foi cumprida. Com nossos valores (0.5 e 0.5), é o caso. Subir o min_endpointing acima do min_silence adiciona a diferença ao atraso de resposta.
- Detalhe fino
- O evento de "usuário em silêncio" usa
raw_accumulated_silence ≤ min_endpointing_delay/2 para tolerar micro-pausas durante fala ativa — mais um motivo para não deixar esse valor minúsculo.
max_endpointing_delay
default: 3.0 s
- O que é
- Teto de espera quando um detector de fim-de-turno (plugin EOU) acha que o cliente ainda não terminou ("...meu CPF é" → espera mais). Sem plugin EOU, na prática não estica o turno.
- Quando importa
- Se um dia plugarmos um turn detector semântico, este vira o teto do "aguarde, ele vai continuar a frase".
allow_interruptions · min_interruption_duration · min_interruption_words
defaults: True · 0.5 s · 0 palavras
- O que é
- O barge-in. A cada
INFERENCE_DONE (32 ms), se speech_duration ≥ min_interruption_duration, o agente é interrompido (verificado no código do 1.3.10). min_interruption_words exige N palavras transcritas — só funciona com STT streaming; com whisper batch, deixe 0.
- Latência do barge-in
- ≈ rampa de detecção (64–128 ms) +
min_interruption_duration. Com 0.5 s: o robô cala ~0.6 s depois do cliente começar a falar por cima. Baixar para 0.2–0.3 s deixa mais responsivo, mas aí qualquer órfão de 0.3 s cala o robô sozinho — e nossos falsos positivos medem justamente 0.1–0.3 s. O par (min_interruption, taxa de órfãos do VAD) tem que ser calibrado junto.
false_interruption_timeout · resume_false_interruption
defaults: 2.0 s · True
- O que é
- A rede de segurança dos órfãos: se após a interrupção o usuário fica em silêncio e nenhuma transcrição aparece em 2 s, o LiveKit emite
agent_false_interruption e (por default) retoma a fala do agente de onde parou.
- Por que importa aqui
- É a mitigação nativa da nossa gravidade nº 2 no lado do barge-in: um órfão ainda cala o robô por ~2 s (ruim, mas recuperável). O custo real do órfão que sobra é o chunk-lixo indo ao whisper — por isso o pós-filtro por
no_speech_prob proposto no RELATORIO_V2 §6 completa a defesa.
min_consecutive_speech_delay · discard_audio_if_uninterruptible
defaults: 0.0 s · True
- O que é
- Espaçamento mínimo entre falas consecutivas do agente; e se o áudio do usuário é descartado enquanto o agente fala em modo não-interrompível (evita processar "fala represada" depois).
5. Mapa de interações
| Par | Como interagem |
min_silence × min_endpointing_delay |
Em modo VAD o endpointing é max() dos dois → o maior deles define o piso do atraso de resposta. Mantê-los iguais (0.5/0.5) é o ponto sem desperdício: nenhum espera à toa pelo outro. |
min_silence × convenção de labeling (0.8 s) |
Pausas <0.8 s são "a mesma fala" no ground truth. min_silence <0.5 fragmenta o que o rótulo diz ser uma fala só (punido como fragmentação); >0.8 emenda falas distintas (punido como ponte). A janela sã é 0.5–0.8. |
activation × min_speech |
Controlam falsos positivos por caminhos opostos: activation filtra por confiança, min_speech por duração. Com act baixa (0.16, necessário no nosso áudio), min_speech maior (0.10–0.15) recupera a proteção contra blips sem perder fala baixa — trade visível no par (órfãos, onset p90) do avaliador. |
min_speech × min_interruption_duration |
O speech_duration que dispara a interrupção só começa a contar no START (após min_speech). Efetivamente: barge-in ≈ rampa + max(min_speech, min_interruption). Como min_interruption (0.5) ≫ min_speech (0.05), quem manda é o min_interruption. |
min_interruption_duration × taxa de órfãos do VAD |
Interrupção barata (0.2 s) exige VAD com pouquíssimo órfão; interrupção cara (0.5 s) tolera órfãos curtos mas deixa o robô "teimoso". É o mesmo trade-off medido pelos gates (onset p90) e pelo vetor (órfãos) — calibrar junto. |
prefix_padding × activation/min_speech |
O padding perdoa onset atrasado no payload (o áudio perdido volta pelo buffer), mas NÃO perdoa no barge-in (o evento continua atrasado). Por isso o avaliador mede as duas réguas separadas (vad_decision vs stt_emitted) e a latência de onset à parte. |
deactivation × fala de fundo |
Deact baixa mantém o segmento aberto sobre voz de fundo fraca (intrusão via continuação — o modo de falha dominante no nosso dataset). A trava de nível do VAD2 ataca exatamente isso, ao custo de recall. |
6. Perguntas diretas
"min_silence_duration adiciona atraso perceptível à conversa?"
Sim — é o piso estrutural do atraso de resposta (o robô não tem como saber que você parou sem
esperar esse silêncio). Com 0.5 s + whisper batch + LLM + TTS, o silêncio total percebido fica
tipicamente em 1.5–2.5 s. Baixar min_silence reduz esse piso, mas aumenta picote e respostas a
meia-frase. Não é o parâmetro certo para "deixar mais esperto" — é o certo para o ritmo do turno.
"min_silence picota mais o áudio pro STT?"
Quanto
menor, mais picote: toda pausa do cliente maior que o valor vira um corte e um chunk
novo. Quanto maior, menos chunks, porém turnos distintos começam a ser emendados (e a resposta
atrasa). O sinal de picote demais no avaliador é a fragmentação; de picote de menos, a ponte.
"prefix_padding atrasa algo?"
Não. É retroativo (buffer já gravado), custo zero de latência. Só aumenta o tamanho do payload e
melhora a primeira palavra no whisper. O único "efeito colateral" é visual: na régua
stt_emitted ele aparece como spill — esperado e inofensivo.
"o que faz o robô calar rápido quando falo por cima?"
Nesta ordem:
activation_threshold baixo (rampa curta),
min_speech baixo e
min_interruption_duration baixo. O terceiro domina (0.5 s vs 0.05 s dos outros). E a
rede de segurança
false_interruption_timeout (2 s) desfaz interrupções causadas por
falso positivo, retomando a fala.
"e o que faz o robô responder rápido quando termino de falar?"
min_silence_duration (piso),
min_endpointing_delay (só custa algo se for
maior que o min_silence) e, fora do VAD, a latência do whisper batch + LLM + TTS — que no nosso
desenho só começam DEPOIS do END_OF_SPEECH, porque o chunk nasce nele.
7. Valores para o nosso tráfego (e como testar mudanças)
| Parâmetro | Default livekit | Nosso valor | Racional |
| activation_threshold | 0.5 | 0.16 | banda-estreita G729 deprime as probs; sweet spot medido no grid |
| deactivation_threshold | act−0.15 | 0.06 | segura caudas de fala fraca; eixo quase plano 0.05–0.10 |
| min_speech_duration | 0.05 | 0.05 (testar 0.10–0.15) | anti-blip vs onset p90 — o grid + gates arbitram |
| min_silence_duration | 0.55 | 0.5 | janela sã 0.5–0.8 pela convenção de labeling; 0.5 = resposta mais ágil |
| prefix_padding_duration | 0.5 | 1.0 | seguro de primeiro fonema para o whisper; custo zero de latência |
| min_endpointing_delay | 0.5 | 0.5 | ≤ min_silence → custo extra zero (modo VAD usa max()) |
| max_endpointing_delay | 3.0 | 3.0 | só relevante com plugin EOU |
| min_interruption_duration | 0.5 | 0.5 (reavaliar se órfãos → 0) | barge-in ~0.6 s; baixar exige VAD limpo de órfãos |
| min_interruption_words | 0 | 0 | exige STT streaming; whisper é batch |
| false_interruption_timeout | 2.0 | 2.0 | rede de segurança dos falsos positivos de barge-in |
Como testar qualquer hipótese daqui
Os parâmetros do
silero.VAD (thresholds, min speech/silence, padding) são todos eixos do
runner: replay em ~60 ms, grid com gates, e o
/eval mostra
fragmentação/ponte/órfãos/onset por config. Os parâmetros do
AgentSession não passam pelo
avaliador (são do turn-taking, não do detector) — mas as métricas que os alimentam sim: a latência
de onset p90 diz o que esperar do barge-in, e a taxa de órfãos diz se dá para baixar o
min_interruption_duration com segurança.
Fontes: livekit-agents 1.3.10
(voice/agent_session.py, voice/agent_activity.py),
plugin livekit-plugins-silero (espelhado em oracle/vad.py),
medições em RELATORIO_V2.md e na página Metodologia.