Como usar o Jev para um assistente de voz decidir se responde agora ou espera o usuário terminar?

diagrama mostrando o Jev assistente de voz recebendo state e perguntas tipadas para decidir entre responder ou esperar o turno
Resposta rápida

O Jev num assistente de voz não escuta áudio, então ele não decide quando a pessoa parou de falar: quem faz isso é o seu STT em streaming, tipo o Deepgram Flux com eot_threshold. O papel do Jev é o outro, decidir o que fazer com o turno que acabou de chegar: responder agora, esperar, pedir esclarecimento ou escalar. Você manda um state (trecho transcrito, pausa, últimos turnos) e um lote de perguntas tipadas na mesma requisição, lê choice, probabilities e confidence, e roteia por limiar. Nos cookbooks da TypeSafe, lotes assim ficaram na casa de 111 a 114 ms de ida e volta

Assistente de voz que responde cedo demais atropela a pessoa

E assistente que espera demais soa travado, aquele silêncio esquisito onde o usuário já começou a repetir a frase achando que a ligação caiu

Esse é o problema de turn-taking, e ele não é um problema só de áudio: metade dele é decisão

O Jev é o primeiro modelo público da TypeSafe AI e inaugura o que eles chamam de System One models: em vez de gerar texto, ele avalia um state e devolve respostas tipadas com probabilidades

Isso encaixa MUITO bem num pipeline de voz, onde você não tem tempo de esperar um LLM escrever um parágrafo pra depois parsear

Mas já aviso antes de você se empolgar: o Jev não enxerga áudio

Ele decide sobre texto ou JSON, e quem diz "a pessoa parou de falar" continua sendo o seu STT

Bora separar isso direito?

O que o Jev faz (e o que ele não faz) numa conversa por voz

O Jev tem três tipos de pergunta, as primitivas: Choice, Score e Noul

Toda pergunta tem id, tipo e instructions

Choice e Score aceitam criteria (as opções ou os níveis), e o Noul aceita criteria como esclarecimento opcional do que é sim e do que é não

As respostas de Choice e Score carregam um confidence entre 0 e 1, derivado da distribuição de probabilidade da resposta

O Choice ainda devolve a opção escolhida em choice e a probabilidade de cada opção em probabilities, e um Choice aceita até 255 opções

O Noul devolve um valor numérico entre zero e um

A parte que salva sua latência: o state vai no campo state da requisição junto com o mapa questions, e todas as perguntas enxergam o MESMO state

O Jev ingere esse state uma vez e avalia as perguntas em paralelo, em vez de fazer cada resposta depender da anterior

Ou seja, você não precisa de quatro chamadas pra saber quatro coisas do mesmo turno

É a mesma mecânica de lote que aparece quando o Jev é usado pra detectar jailbreak antes do LLM responder, só que aqui o state é uma conversa por voz em andamento

Duas camadas, não uma

Separa mentalmente assim:

  • "A pessoa parou?" é camada de áudio, resolvida por endpointing (Deepgram Flux, Smart Turn e afins)
  • "E agora, o que eu faço?" é camada de decisão, e é aí que o Jev entra

Se você tentar usar o Jev pra adivinhar fim de fala, vai dar ruim: ele não recebe forma de onda, não sabe que a pessoa respirou fundo pra continuar a frase

No projeto de referência que existe hoje pra isso, quem encerra o turno a partir do áudio é o Flux, e o Jev cuida só do ramo da conversa

O que você precisa antes de começar

Antes do código, o checklist:

  • Conta ativa na TypeSafe, e aqui tem um detalhe chato: a empresa abriu o Jev pra todo mundo em 20 de setembro de 2026 e pausou novos cadastros em 22 de setembro de 2026 por excesso de demanda
  • Contas já criadas seguem funcionando, e eles dizem que trabalham pra reabrir, sem data anunciada até agora
  • Se você não tem conta, dá pra chegar no modelo por intermediários: existe página do provider Typesafe no OpenRouter e o Jev também está listado na documentação de modelos da Cloudflare
  • TYPESAFE_API_KEY na variável de ambiente, que é de onde o SDK lê a chave
  • SDK Python com pip install typesafe-sdk (ou uv add typesafe-sdk), exigindo Python 3.10 ou superior, ou SDK JavaScript com npm install @typesafe-ai/sdk, pedindo Node.js 20 ou mais novo
  • Um STT em streaming que emita evento de fim de turno, porque sem isso não existe gatilho pra chamar o Jev
  • Um TTS pra falar a resposta escolhida

O endpoint de avaliação é POST https://api.typesafe.ai/v1/systemone, com Authorization: Bearer e corpo JSON

No campo model você usa o alias jev-latest ou um ID versionado, tipo jev-1.13.0

Se for pelo OpenRouter, dá pra apontar o SDK oficial pra lá ou chamar pela Decisions API, que não é o endpoint de chat completions (não adianta jogar um cliente de chat em cima)

Passo a passo: da fala do usuário à decisão de responder ou esperar

  1. Configure o detector de fim de turno no STT

No Deepgram Flux (endpoint /v2/listen), o parâmetro eot_threshold define a confiança necessária pra disparar o evento EndOfTurn

Ele aceita valores de 0.5 a 0.9 e o padrão é 0.7

Valor mais alto reduz falso positivo (menos atropelo) com um pouquinho mais de latência, e 1.0 suprime a detecção natural de fim de turno

# parâmetros do Flux (/v2/listen)
eot_threshold = 0.8          # faixa 0.5 a 0.9, padrão 0.7, 1.0 desliga a detecção natural
eager_eot_threshold = 0.5    # faixa 0.3 a 0.9, habilita EagerEndOfTurn e TurnResumed

O erro comum deste passo: tentar resolver endpointing com o Jev

Ele não ouve áudio, ponto

  1. Monte o state com o que é decisão, não com o dump da conversa

O state é o retrato do turno que acabou de fechar: trecho transcrito, quanto durou a pausa, qual evento do STT chegou, os últimos turnos e onde você está no roteiro

O orçamento de contexto é de 64k cobrindo state mais todas as perguntas somadas, e de 32k pra state mais a pergunta mais longa

Parece muito, mas histórico bruto de uma ligação de dez minutos come isso rapidinho

O erro comum deste passo: jogar a transcrição inteira da chamada dentro do state

Resuma o histórico e mande só os turnos recentes

  1. Escreva as perguntas em lote, na mesma requisição

Um Choice pro ramo da conversa, dois Nouls (a pessoa respondeu o que foi perguntado? ela quer falar com humano?) e um Score de clareza

Esse é exatamente o desenho do projeto de referência: uma única chamada ao Jev por turno com quatro perguntas

{
  "model": "jev-latest",
  "state": {
    "ultimo_trecho": "quero mudar o endereco de entrega do meu pedido",
    "pausa_ms": 480,
    "evento_stt": "EndOfTurn",
    "turnos_recentes": [
      {"agente": "pode me confirmar o numero do pedido?"},
      {"usuario": "e o 4821, mas antes"}
    ],
    "passo_do_roteiro": "confirmar_pedido",
    "transcricao_suspeita": true
  },
  "questions": {
    "branch": {
      "type": "choice",
      "instructions": "o agente acabou de receber um fim de turno. escolha a proxima acao",
      "criteria": {
        "responder_agora": "o turno esta completo e ja da pra avancar o roteiro",
        "esperar": "a frase parece cortada no meio, a pessoa provavelmente vai continuar",
        "pedir_esclarecimento": "o trecho esta ambiguo ou a transcricao nao faz sentido",
        "escalar_humano": null
      }
    },
    "answered": {
      "type": "noul",
      "instructions": "o usuario respondeu a ultima pergunta do agente?"
    },
    "escalating": {
      "type": "noul",
      "instructions": "o usuario pediu para falar com um atendente humano?"
    },
    "clarity": {
      "type": "score",
      "instructions": "quao clara esta a intencao do usuario neste turno",
      "criteria": {
        "baixa": "transcricao truncada ou sem sentido",
        "media": "da pra entender mas cabe mais de uma leitura",
        "alta": "intencao explicita e sem ambiguidade"
      }
    }
  }
}

O erro comum deste passo: quebrar isso em quatro requisições encadeadas, uma esperando a outra

As perguntas são independentes e rodam em paralelo sobre o mesmo state, encadear só multiplica round-trip

  1. Chame o Jev uma vez por turno
import os
import requests

resp = requests.post(
    "https://api.typesafe.ai/v1/systemone",
    headers={
        "Authorization": f"Bearer {os.environ['TYPESAFE_API_KEY']}",
        "Content-Type": "application/json",
    },
    json=payload,
    timeout=2,
)
answers = resp.json()

Do Choice você lê três coisas: choice (a opção), probabilities (a probabilidade de cada opção) e confidence (0 a 1)

O formato completo da resposta está na doc da API, o que importa aqui é que esses três campos são o seu material de roteamento

  1. Aplique o portão de confiança e roteie

Nada de confiar cegamente no choice

A regra é: se a confiança não bater o limiar, você não executa o ramo, você cai numa fala de esclarecimento

No projeto de referência o limiar do ramo começa em 0.65 e sobe pra 0.75 quando a transcrição vem marcada como suspeita, ou seja, quando alguma palavra do Flux ficou abaixo de GATE_MIN_WORD_CONFIDENCE (0.6)

Quem faz esse acréscimo é o GATE_SUSPECT_PENALTY (0.1)

GATE = 0.65
GATE_SUSPECT_PENALTY = 0.1
GATE_MIN_WORD_CONFIDENCE = 0.6

def rotear(branch, palavras_do_stt):
    limiar = GATE
    if any(p["confidence"] < GATE_MIN_WORD_CONFIDENCE for p in palavras_do_stt):
        limiar += GATE_SUSPECT_PENALTY   # vira 0.75
    if branch["confidence"] < limiar:
        return "pedir_esclarecimento"
    return branch["choice"]

O erro comum deste passo: tratar confidence como enfeite de log

Ele é o botão que decide se o agente age ou pergunta de novo

  1. Use os eventos do STT pra ganhar tempo e pra parar de falar

Definir eager_eot_threshold (valores válidos entre 0.3 e 0.9) habilita os eventos EagerEndOfTurn e TurnResumed

A orientação da Deepgram é rascunhar a resposta no EagerEndOfTurn e cancelar o rascunho quando chegar TurnResumed, que indica que a pessoa voltou a falar

E pra barge-in, o caminho recomendado é o StartOfTurn, porque todo StartOfTurn vem com transcrição não vazia

O erro comum deste passo: rascunhar e esquecer de cancelar

Aí o agente fala por cima da pessoa que só tinha dado uma pausa pra respirar 😅

Como transformar confiança em decisão: limiares que fazem sentido

Os números que dão chão pra isso vêm do jev-voice-agent, um agente de voz em tempo real publicado na conta dg-coreylweathers no GitHub, sem LLM no loop: o Flux encerra o turno, o Jev escolhe a resposta e todas as falas possíveis estão escritas por humano em src/script.ts

Lá, qualquer palavra devolvida pelo Flux com confiança abaixo de GATE_MIN_WORD_CONFIDENCE (0.6) marca a transcrição como suspeita

Transcrição suspeita eleva a barra de confiança do ramo em GATE_SUSPECT_PENALTY (0.1), saindo de 0.65 pra 0.75

Quando o portão não é atendido, o agente cai na fala de esclarecimento escrita no script

Sacou a lógica? Áudio ruim não vira decisão errada, vira pergunta

Por que limiar funciona aqui

O paper JEV-as-a-Judge descreve o Jev como calibrado: quando o modelo diz probabilidade 0.8, o rótulo de referência concorda em torno de 80% das vezes

O mesmo paper descreve uma cascata que aceita o veredito confiante e escala o incerto, mantendo 99% da acurácia do comparador com custo menor

É a mesma ideia de aceitar o confiante e escalar a dúvida que aparece quando o Jev é usado pra qualificar e priorizar leads, só que aqui a "escalada" é falar mais uma frase, não abrir um ticket

Três cenários concretos

  • Alta confiança em "responder_agora": confidence 0.91, nenhuma palavra suspeita, limiar 0.65
  • O agente executa o ramo e o TTS já dispara a fala do script
  • Confiança baixa por transcrição ruim: o Flux entregou duas palavras com confiança 0.4, a transcrição vira suspeita e o limiar sobe pra 0.75
  • O Choice veio com confidence 0.7, ou seja, passaria no portão normal e NÃO passa no portão penalizado
  • Resultado: fala de esclarecimento, e ninguém confirma um endereço errado
  • Ramo de escalar pra humano: o Noul de "quer falar com atendente" veio alto e o Choice escolheu escalar_humano com confiança acima do limiar
  • Transferência, sem mais rodeios

Jev, Deepgram Flux e Smart Turn: quem resolve qual parte

Muita gente lê isso como concorrência, e não é

São camadas diferentes do mesmo pipeline

Peça O que observa O que devolve Onde roda Custo
Deepgram Flux áudio em streaming (/v2/listen) transcrição e eventos de turno (StartOfTurn, EagerEndOfTurn, TurnResumed, EndOfTurn), com eot_threshold de 0.5 a 0.9 serviço da Deepgram cobrança do serviço de STT
Smart Turn v3 forma de onda do áudio, não a transcrição detecção semântica de fim de turno local, inferência em CPU em cerca de 12 ms em processadores modernos open source, pesos em pipecat-ai/smart-turn-v3
Jev state em texto ou JSON respostas tipadas com choice, probabilities e confidence API da TypeSafe, OpenRouter ou Cloudflare US$ 0,042 por 1 milhão de tokens de entrada, saída gratuita

O Smart Turn é mantido no repositório pipecat-ai/smart-turn e usa Whisper Tiny como base com uma camada classificadora linear, com cerca de 8M de parâmetros quantizados em int8

Ele resolve a camada de áudio, não a de decisão

Sobre latência do Jev, os números que existem publicados são os dos cookbooks da própria TypeSafe: uma rodada com 14 perguntas Noul numa única chamada registrou 111 ms de média de ida e volta, e uma rodada com 8 perguntas Choice registrou 114 ms

A home da TypeSafe divulga ainda 193,6x mais rápido e 444,6x mais barato, derivados dos workflows que eles mesmos publicaram, e a própria empresa admite que esses resultados representam a ponta alta dos ganhos reais

Ou seja, trate como claim de fornecedor e meça no SEU pipeline 🙂

Problemas comuns nessa arquitetura e como evitar

Sintoma: o agente atropela a fala do usuário

Causa provável: eot_threshold baixo demais, o turno fecha antes da hora

Como prevenir: suba dentro da faixa 0.5 a 0.9, lembrando que o padrão é 0.7 e que valores mais altos reduzem falso positivo com um pouco mais de latência

Sintoma: o agente demora a responder e a conversa fica robótica

Causa provável: você só começa a trabalhar depois do EndOfTurn

Como prevenir: habilite eager_eot_threshold (0.3 a 0.9), rascunhe a resposta no EagerEndOfTurn e cancele quando vier TurnResumed

Sintoma: decisão errada quando o áudio tá ruim

Causa provável: o Jev recebeu uma transcrição corrompida e decidiu com confiança em cima de lixo

Como prevenir: portão de confiança por palavra, penalizando o limiar do ramo quando existe palavra suspeita, e fallback numa fala de esclarecimento

Sintoma: 429 Too Many Requests

Causa provável: os limites da API são medidos em tokens por segundo E em requisições por minuto, e estourar qualquer um dos dois devolve 429

Como prevenir: enxugue o state e agrupe as perguntas numa chamada só por turno, em vez de uma chamada por pergunta

Sintoma: erro de contexto no meio da ligação

Causa provável: state crescendo turno a turno com histórico bruto

Como prevenir: respeite os 64k de state mais todas as perguntas e os 32k de state mais a pergunta mais longa, cortando histórico antigo e mantendo só o resumo do roteiro

Próximo passo

A ideia central é essa separação: o áudio diz QUANDO o turno acabou, o Jev diz O QUE fazer com ele

Misturar as duas camadas é o caminho mais rápido pra um agente que fala por cima da pessoa ou que congela esperando um modelo escrever texto

Pra começar, o jev-voice-agent é um ponto de partida honesto, porque mostra o pipeline inteiro com as falas escritas por humano e o portão de confiança explícito no código

E se você usa Pipecat, vale acompanhar a integração oficial do TypeSafe no pull request #5792, aberto por jamsea no repositório pipecat-ai/pipecat, que segue em rascunho e ainda não foi mesclado

Minha sugestão de ordem: sobe primeiro um único Choice de ramo com limiar fixo, valida em ligações reais, e só depois adiciona os Nouls e o Score no mesmo lote

Começar com quatro perguntas e três limiares é a receita certa pra você não saber qual deles tá errado quando o agente fizer besteira 😀

até o próximo post!

Perguntas frequentes

O Jev substitui o Deepgram Flux num assistente de voz?

Não, os dois fazem coisas diferentes

O Flux decide que a pessoa parou de falar a partir do áudio, é o endpointing

O Jev entra depois disso, decide o ramo da conversa a partir de texto ou JSON, porque ele não enxerga forma de onda

Dá pra usar o Jev sem ter conta na TypeSafe?

Dá, via intermediário

Existe página do provider Typesafe no OpenRouter e o modelo também está listado na documentação de modelos da Cloudflare

Vale porque a TypeSafe pausou novos cadastros em 22 de setembro de 2026 por excesso de demanda, embora contas já criadas sigam funcionando normalmente

Quantas chamadas ao Jev eu preciso fazer por turno de conversa?

No projeto de referência jev-voice-agent, é uma única requisição por turno com quatro perguntas em lote: o Choice do ramo, um Noul de se a pessoa respondeu, um Noul de escalar pra humano e um Score de clareza

Isso funciona porque o Jev avalia todas as perguntas em paralelo sobre o mesmo state, então não precisa de quatro chamadas separadas

Qual o limite de contexto pra montar o state de um turno de voz?

São dois orçamentos: 64k cobrindo o state somado a todas as perguntas da requisição, e 32k cobrindo o state mais a pergunta mais longa sozinha

Por isso o state deve ser um resumo do turno (trecho, pausa, evento do STT, turnos recentes), não o dump inteiro da ligação

Quanto custa rodar o Jev num agente de voz em produção?

O Jev 1.13 cobra US$ 0,042 por 1 milhão de tokens de entrada, e a saída é gratuita, já que o modelo devolve resposta tipada e não texto gerado

Como cada turno costuma virar uma única requisição em lote, o custo por turno tende a ficar baixo mesmo em volume

O que acontece se o STT transcrever uma palavra com baixa confiança antes de chamar o Jev?

No projeto de referência, qualquer palavra do Flux abaixo de GATE_MIN_WORD_CONFIDENCE (0.6) marca a transcrição como suspeita

Isso eleva a barra de confiança do ramo escolhido pelo Jev via GATE_SUSPECT_PENALTY (0.1), subindo o limiar de 0.65 para 0.75

Se o portão não é atendido, o agente cai numa fala de esclarecimento já escrita no script, em vez de arriscar responder errado



Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Claude Code

Formação Claude Code

Domine Claude Code do absoluto zero até o avançado

  • 120 aulas
  • 4 projetos
  • 9h 48min

Blog | Mais populares