Realtime APIs: GPT-4o Realtime, conversational voice
- ⬜🔊 Text-to-speech: ElevenLabs, OpenAI, Cartesia(Voice, Vision & Multimodal)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Por que pipeline quebra em conversa
Voice agent conversacional vive ou morre na latência. Usuário percebe >500ms como desconforto, >1s como travamento, >2s desliga. Pipeline tradicional (VAD → STT → LLM → TTS) tem custo sequencial: cada etapa precisa da anterior. Na prática, mesmo com Whisper streaming + LLM com SSE + Cartesia Sonic, você raramente desce de 1.2s de round-trip.
Realtime APIs (OpenAI Realtime com GPT-4o, Gemini Live, Claude com audio input em beta) resolvem isso com modelo único que consome e produz áudio. Turn-taking, VAD e geração paralela ficam server-side.
Arquitetura WebRTC vs WebSocket
| Aspecto | Conexão persistente simples | Transporte de mídia em tempo real |
|---|---|---|
| Facilidade | Alta — funciona em qualquer servidor | Menor: exige negociação e servidores auxiliares |
| Perda de pacote | Você trata | Correção nativa |
| Eco e ruído | Você trata | Cancelamento nativo |
| Amortecimento de variação de atraso | Você implementa | Nativo |
| Adequado a | Servidor a servidor, protótipo, ferramenta interna | Navegador e celular com áudio de verdade |
| Custo de errar | Áudio picotado que ninguém sabe explicar | — |
OpenAI Realtime API expõe dois transportes. WebSocket é o mais fácil (backend ou script Node), mas você vira o responsável pelo jitter buffer e pela qualidade do áudio. WebRTC dá peer-to-peer com o modelo, com correção de perda de pacote e echo cancellation nativos — essencial para mobile e chamadas reais.
// Cliente browser — WebRTC direto com o modelo
const pc = new RTCPeerConnection();
// Track de áudio do microfone
const media = await navigator.mediaDevices.getUserMedia({ audio: true });
pc.addTrack(media.getTracks()[0]);
// Receber áudio de volta
pc.ontrack = (ev) => { audioElement.srcObject = ev.streams[0]; };
// Data channel para eventos de controle (tool calls, interrupts)
const dc = pc.createDataChannel('oai-events');
dc.onmessage = (ev) => handleRealtimeEvent(JSON.parse(ev.data));
// SDP offer -> endpoint OpenAI com token efêmero
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const resp = await fetch('https://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + ephemeralToken, // gerado no backend
'Content-Type': 'application/sdp',
},
body: offer.sdp,
});
await pc.setRemoteDescription({ type: 'answer', sdp: await resp.text() });Nunca exponha sua API key no browser. Gere um ephemeral token no backend via POST /v1/realtime/sessions e entregue só esse token ao cliente — ele expira em 60s.
Configurando a sessão
Antes de o áudio fluir, você manda um evento session.update definindo voz, instruções, tools disponíveis e parâmetros de VAD.
{
"type": "session.update",
"session": {
"modalities": ["audio", "text"],
"voice": "sage",
"instructions": "Você é um assistente conciso em PT-BR. Nunca interrompa o usuário.",
"input_audio_transcription": { "model": "whisper-1" },
"turn_detection": {
"type": "server_vad",
"threshold": 0.5,
"prefix_padding_ms": 300,
"silence_duration_ms": 500
},
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "Retorna clima atual",
"parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] }
}
],
"tool_choice": "auto"
}
}Turn-taking e VAD na prática
Com server_vad, o modelo decide sozinho quando o usuário terminou de falar e começa a gerar resposta. Os parâmetros importam muito:
turn_detection:
threshold: 0.5 # sensibilidade (0-1). Em ambiente barulhento suba para 0.7
prefix_padding_ms: 300 # quanto áudio ANTES do VAD disparar enviar ao modelo
silence_duration_ms: 500 # silêncio contínuo para considerar "usuário parou"
# Regras de bolso:
# - call center / barulho alto: threshold 0.7, silence 700ms
# - escritório silencioso: threshold 0.4, silence 400ms
# - usuários idosos (pausas longas): silence 1000-1500msBarge-in: interrompendo o agent
Evento crítico para UX real. Quando o usuário começa a falar enquanto o agent fala, o servidor emite input_audio_buffer.speech_started. Você precisa cortar o áudio sendo tocado E truncar a resposta no histórico, senão o modelo acredita que terminou a frase.
dc.onmessage = (ev) => {
const event = JSON.parse(ev.data);
switch (event.type) {
case 'input_audio_buffer.speech_started':
// Usuário falou em cima: parar saída atual
stopAudioPlayback();
dc.send(JSON.stringify({ type: 'response.cancel' }));
break;
case 'response.audio.delta':
enqueueAudio(event.delta); // base64 PCM16
break;
case 'response.function_call_arguments.done':
handleToolCall(event.name, JSON.parse(event.arguments));
break;
}
};Por que a chave de API nunca pode ir para o navegador numa sessão de voz em tempo real, e qual é a alternativa?
Tool use em voz: o destravamento
Voice agent útil precisa chamar tools (marcar agenda, consultar banco, tocar música). Realtime API entrega tool calls como eventos no data channel, você executa e devolve o resultado como conversation.item.create. O modelo volta a falar incorporando o resultado.
Para tools que demoram (>500ms), instrua o modelo a dizer algo como "só um momento" antes de chamar. Isso é feito no system prompt — sem isso, há silêncio incômodo enquanto a tool executa.
LiveKit Agents: produção séria
Para voice agent em escala (call center, suporte telefônico via SIP), rolar tudo sozinho é tortura. LiveKit Agents oferece framework Python/Node com VAD plugável (Silero), STT/TTS/LLM plugáveis, gravação, métricas e SIP trunk para telefone real.
from livekit.agents import Agent, AgentSession, JobContext
from livekit.plugins import openai, silero
async def entrypoint(ctx: JobContext):
await ctx.connect()
session = AgentSession(
vad=silero.VAD.load(),
stt=openai.STT(model='whisper-1', lang='pt'),
llm=openai.LLM(model='gpt-4o'),
tts=openai.TTS(voice='nova'),
# OU: llm=openai.realtime.RealtimeModel() para end-to-end
)
agent = Agent(instructions='Você é suporte técnico em PT-BR, conciso.')
await session.start(agent=agent, room=ctx.room)Custos reais
GPT-4o Realtime custa ~$100/1M tokens input e ~$200/1M output de áudio (em 2026). Um minuto de conversa vira ~500-700 tokens de áudio cada lado. Ou seja, cada minuto custa na ordem de $0.15-0.20. Caro para call center alto volume — aí pipeline (Whisper + GPT-4o texto + Cartesia) pode ser 5x mais barato ao custo de latência maior.
Operação: o que sempre quebra
Checklist: (1) ephemeral token no backend, nunca key no client; (2) VAD tunado ao ambiente real; (3) barge-in com cancel de áudio E truncate de resposta; (4) timeout para tool calls + fala de espera; (5) fallback para pipeline quando Realtime API falha; (6) gravação + transcrição para eval. Se faltar qualquer um, o agent vira demo, não produto.
Perguntas frequentes
❓ O que uma API de voz em tempo real muda?
❓ Cadeia própria ou API de voz integrada?
❓ Como tratar interrupção do usuário?
Fixando
O usuário começa a falar enquanto o agente ainda está falando. Além de parar o áudio que toca, o que mais precisa ser feito?
Um modelo único de áudio custa cerca de dez a vinte centavos por minuto de conversa. Como isso pesa na escolha de arquitetura?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…