Phase 06 - Lesson 14

Detecção de Atividade de Voz e Alternância de Turnos — Silero, Cobra e o Truque do Flush

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

Todo agente de voz vive ou morre por duas decisões: o usuário está falando agora e ele terminou? A VAD responde à primeira. A detecção de turno (VAD + carência de silêncio + modelo de endpoint semântico) responde à segunda. Erre qualquer uma delas e seu assistente ou corta o usuário no meio da fala ou nunca para de falar.

Tipo: Build Linguagens: Python Pré-requisitos: Fase 6 · 11 (Áudio em Tempo Real), Fase 6 · 12 (Assistente de Voz) Tempo: ~45 minutos

O Problema

Três decisões distintas que um agente de voz toma a cada bloco de 20 ms:

  1. Este quadro é fala? — VAD. Binária, por quadro.
  2. O usuário começou um novo enunciado? — detecção de início.
  3. O usuário terminou? — end-pointing (fim de turno).

A resposta ingênua (limiar de energia) falha em qualquer ruído — trânsito, teclados, balbúrdia de multidão. A resposta de 2026: Silero VAD (aberta, baseada em deep learning) + um modelo de detecção de turno (endpointing semântico) + uma carência de silêncio calibrada pela VAD.

O Conceito

Cascata de VAD: energia → Silero → detector de turno → truque do flush

A cascata de VAD em três camadas

Camada 1: portão de energia. A mais barata. Aplica limiar de RMS em -40 dBFS. Filtra silêncio óbvio, mas dispara em qualquer ruído acima do limiar.

Camada 2: Silero VAD (2020-2026, MIT). 1 milhão de parâmetros. Treinada em mais de 6000 idiomas. Roda em ~1 ms por bloco de 30 ms em uma única thread de CPU. 87,7% de TPR a 5% de FPR. O padrão de código aberto.

Camada 3: detector semântico de turno. O modelo de detecção de turno da LiveKit (2024-2026) ou seu próprio classificador pequeno. Distingue "pausa no meio da frase" de "terminou de falar". Usa contexto linguístico (entonação + palavras recentes), não apenas silêncio.

Parâmetros-chave e seus padrões

  • Limiar. A Silero produz uma probabilidade; classifique como fala em > 0.5 (padrão) ou > 0.3 (sensível). Limiar mais baixo = menos cortes da primeira palavra, mais falsos positivos.
  • Duração mínima de fala. Rejeite fala mais curta que 250 ms — geralmente tosses ou ruído de cadeira.
  • Carência de silêncio (end-pointing). Após a VAD retornar a 0, espere 500-800 ms antes de declarar o fim do turno. Curta demais → interrompe o usuário. Longa demais → parece lenta.
  • Buffer de pre-roll. Mantenha 300-500 ms de áudio antes de a VAD disparar. Evita que o "ei" seja cortado.

O truque do flush (Kyutai 2025)

Modelos de STT em streaming têm um atraso de look-ahead (500 ms para o Kyutai STT-1B, 2,5 s para o STT-2.6B). Normalmente você esperaria todo esse tempo após o fim da fala para obter a transcrição. Truque do flush: quando a VAD dispara o fim da fala, envie um sinal de flush ao STT que força a saída imediata. O STT processa a ~4× tempo real, então o buffer de 500 ms termina em ~125 ms.

Ponta a ponta: 125 ms de VAD + flush do STT = latência conversacional.

Comparação de VAD em 2026

VAD TPR @ 5% FPR Latência Termos
WebRTC VAD (Google, 2013) 50,0% 30 ms BSD
Silero VAD (2020-2026) 87,7% ~1 ms MIT
Cobra VAD (Picovoice) 98,9% ~1 ms comercial
pyannote segmentation 95% ~10 ms tipo MIT

A Silero é o padrão certo. A Cobra é o upgrade de conformidade / precisão. VAD baseada apenas em energia não tem lugar na produção de 2026.

Construa

Passo 1: o portão de energia

def energy_vad(chunk, threshold_dbfs=-40.0):
    rms = (sum(x * x for x in chunk) / len(chunk)) ** 0.5
    dbfs = 20.0 * math.log10(max(rms, 1e-10))
    return dbfs > threshold_dbfs

Passo 2: Silero VAD em 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,
    min_speech_duration_ms=250,
    min_silence_duration_ms=500,
    speech_pad_ms=300,
)
for s in segments:
    print(f"{s['start']/16000:.2f}s - {s['end']/16000:.2f}s")

Passo 3: máquina de estados de fim de turno

class TurnDetector:
    def __init__(self, silence_hangover_ms=500, min_speech_ms=250):
        self.state = "idle"
        self.speech_ms = 0
        self.silence_ms = 0
        self.silence_hangover_ms = silence_hangover_ms
        self.min_speech_ms = min_speech_ms

    def update(self, is_speech, chunk_ms=20):
        if is_speech:
            self.speech_ms += chunk_ms
            self.silence_ms = 0
            if self.state == "idle" and self.speech_ms >= self.min_speech_ms:
                self.state = "speaking"
                return "START"
        else:
            self.silence_ms += chunk_ms
            if self.state == "speaking" and self.silence_ms >= self.silence_hangover_ms:
                self.state = "idle"
                self.speech_ms = 0
                return "END"
        return None

Passo 4: o esqueleto do truque do flush

def flush_on_end(stt_client, audio_buffer):
    stt_client.send_audio(audio_buffer)
    stt_client.send_flush()
    return stt_client.recv_transcript(timeout_ms=150)

O STT (Kyutai, Deepgram, AssemblyAI) precisa suportar flush para que isso funcione. O Whisper em streaming não suporta — ele é baseado em blocos e sempre espera os blocos.

Use

Situação Escolha de VAD
Aberta, rápida, geral Silero VAD
Call center comercial Cobra VAD
No dispositivo (telefone) Silero VAD ONNX
Pesquisa / diarização pyannote segmentation
Fallback sem dependências WebRTC VAD (legado)
Precisa de qualidade no fim de turno Silero + detector de turno da LiveKit em camadas

Regra prática: nunca coloque em produção VAD baseada apenas em energia, a menos que você realmente não tenha outra opção.

Armadilhas

  • Limiar fixo. Funciona no silêncio, falha no ruído. Calibre no dispositivo ou troque pela Silero.
  • Carência de silêncio curta demais. O agente interrompe no meio da frase. 500-800 ms é o ponto ideal para fala conversacional.
  • Carência longa demais. Parece lenta. Faça testes A/B com usuários-alvo.
  • Sem buffer de pre-roll. Os primeiros 200-300 ms do áudio do usuário se perdem. Sempre mantenha um pre-roll rolante.
  • Ignorar o endpointing semântico. "Hmm, deixa eu pensar..." contém pausas longas. Usuários odeiam ser cortados no meio do pensamento. Use o detector de turno da LiveKit ou similar.

Entregue

Salve como outputs/skill-vad-tuner.md. Escolha o modelo de VAD, o limiar, a carência, o pre-roll e a estratégia de detecção de turno para uma carga de trabalho.

Exercícios

  1. Fácil. Rode code/main.py. Ele simula uma sequência de fala + silêncio + fala + tosses e testa três camadas de VAD.
  2. Médio. Instale o silero-vad, processe uma gravação de 5 min, ajuste o limiar para minimizar tanto os cortes da primeira palavra quanto os disparos falsos. Reporte precisão/recall.
  3. Difícil. Construa um mini detector de turno: Silero VAD + uma MLP de 3 camadas sobre os embeddings das últimas 10 palavras (use sentence-transformers). Treine em um dataset de fim de turno rotulado à mão. Supere a Silero sozinha em 10% de F1.

Termos-Chave

Termo O que as pessoas dizem O que realmente significa
VAD Detector de voz Binário por quadro: isto é fala?
Detecção de turno End-pointing VAD + carência de silêncio + endpoint semântico.
Carência de silêncio Espera após a fala Tempo a esperar antes de declarar o fim do turno; 500-800 ms.
Pre-roll Buffer pré-fala Mantenha 300-500 ms de áudio antes de a VAD disparar.
Truque do flush Hack da Kyutai VAD → flush do STT → 125 ms em vez de 500 ms de atraso.
Endpoint semântico "Eles quiseram parar?" Classificador de ML que olha para as palavras, não apenas o silêncio.
TPR @ FPR 5% Ponto na ROC Benchmark padrão de VAD; 87,7% para a Silero, 50% para o WebRTC.

Leitura Complementar

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).