
Construa um Agente de Voz na API Realtime da OpenAI: O Tutorial de Produção em 7 Passos (2026)
O nosso agente de teste atendeu uma chamada da Twilio e proferiu a sua primeira palavra 1,1 segundos após o interlocutor parar de falar. Este é o valor p50 de ida e volta, medido em 40 chamadas no gpt-realtime-2 com semantic_vad. Não é magia. A API Realtime da OpenAI realiza conversão de fala para fala dentro de um único socket, pelo que evita o retransmissor STT → LLM → TTS que acumula cerca de 600 ms de sobrecarga. No entanto, as configurações padrão não o levarão a atingir o limite de um segundo. Esta é a implementação em 7 passos que lançámos, com o código e a tabela de latência.
Este é um tutorial de construção, não um explicador de conceitos. Se deseja primeiro uma análise detalhada por camadas, leia o que é realmente um agente de voz de IA e depois regresse. Tudo o que se segue pressupõe que possui uma chave de API da OpenAI e um ambiente de execução Node.
Principais conclusões:
- O gpt-realtime-2 realiza conversão de fala para fala num único socket — sem retransmissão STT/LLM/TTS, poupando ~600 ms.
- Gere chaves efémeras no lado do servidor; nunca envie a sua chave de API padrão para um navegador.
- O fluxo de multimédia da Twilio é de 8 kHz μ-law; faça a reamostragem para PCM16 de 24 kHz para a API Realtime.
- Medimos p50 1,1 s / p95 1,9 s de ida e volta. As interrupções (barge-in) são geridas através de
response.cancel.
O Que Vai Construir em 7 Passos
Este tutorial constrói um agente de voz da API Realtime da OpenAI que atende chamadas telefónicas e responde em menos de 1,5 segundos, chama uma função real durante a conversa e permite que o interlocutor interrompa. O fluxo é curto: um chamador liga para um número de telefone, o áudio flui para o seu servidor, o seu servidor faz a ponte para o gpt-realtime-2 através de um único socket, o modelo fala e pode acionar chamadas de ferramentas, e o áudio flui de volta.
Eis o caminho, podendo parar em qualquer passo que corresponda ao seu caso de uso:
- Gerar uma chave efémera (rota do servidor)
- Abrir e configurar a sessão
- Adicionar chamada de funções
- Fazer a ponte para um número de telefone com a Twilio
- Gerir interrupções (barge-in)
- Ajustar a latência para menos de um segundo
- Implementar e reforçar a segurança
Três transportes carregam o áudio, e a sua escolha depende da origem do áudio. Um navegador captura-o diretamente (WebRTC), o seu servidor já possui um fluxo bruto (WebSocket) ou uma rede telefónica entrega-o (SIP). Utilizaremos WebSocket para a ponte Twilio e mencionaremos os outros onde forem relevantes.
Passo 1: Gerar uma Chave Efémerea (a rota que não pode ignorar)
Nunca exponha a sua chave de API padrão da OpenAI a um navegador ou a um dispositivo cliente. A API Realtime emite chaves efémeras de curta duração exatamente para este fim. O seu servidor chama POST /v1/realtime/client_secrets com a sua chave real, fornece ao cliente um token que expira em cerca de um minuto, e o cliente conecta-se utilizando esse token.
Eis uma rota Express mínima que gera uma:
// server.js
import express from "express";
const app = express();
app.get("/session", async (req, res) => {
const r = await fetch("https://api.openai.com/v1/realtime/client_secrets", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
session: { type: "realtime", model: "gpt-realtime-2" },
}),
});
const data = await r.json();
res.json({ client_secret: data.value, expires_at: data.expires_at });
});
app.listen(3000);O navegador obtém /session, lê o segredo de curta duração e abre a ligação Realtime com ele. Se o seu agente for apenas do lado do servidor (o caso Twilio no Passo 4), pode ignorar a transferência para o cliente e abrir o socket diretamente a partir do seu backend com a chave padrão. O fluxo efémero existe para proteger clientes não confiáveis.
Passo 2: Abrir a Sessão e Configurar o gpt-realtime-2
Abra uma ligação e, em seguida, envie um session.update que define o modelo, o formato de áudio, a voz e a deteção de turnos. A documentação da OpenAI recomenda começar com reasoning.effort definido como low e apenas aumentá-lo se a lógica das suas ferramentas necessitar de maior precisão, uma vez que um esforço mais elevado custa em latência. O áudio funciona como PCM16 de 24 kHz em ambas as direções.
ws.send(JSON.stringify({
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-2",
output_modalities: ["audio"],
audio: {
input: { format: "pcm16", sample_rate: 24000 },
output: { format: "pcm16", sample_rate: 24000, voice: "marin" },
},
instructions: "You are a reservations agent for a restaurant. Be brief.",
reasoning: { effort: "low" },
turn_detection: { type: "semantic_vad" },
},
}));O transporte em que envolve esse socket depende da fonte de áudio:
| Transporte | Utilizar quando | Fonte de áudio |
|---|---|---|
| WebRTC | Navegador ou aplicação móvel captura o microfone diretamente | Dispositivo cliente |
| WebSocket | O seu servidor já detém um fluxo de áudio bruto | Pipeline do servidor |
| SIP | Deseja que a OpenAI gestione a parte telefónica | PSTN / telefonia |
Para a lista completa de campos da sessão e o conjunto de funcionalidades GA, a documentação da API Realtime da OpenAI é a fonte oficial. Utilizaremos WebSocket porque a Twilio nos fornece áudio bruto no Passo 4.
Passo 3: Adicionar Chamada de Funções (para que o agente possa realmente agir)
Um agente de voz que não pode agir é apenas uma narração. A chamada de funções permite que o gpt-realtime-2 pause a meio da conversa, peça ao seu código para executar algo e continue a falar com o resultado. Declara uma ferramenta na sessão, o modelo emite um evento function_call_arguments.done quando a deseja, executa o trabalho e envia o output de volta.
Declare a ferramenta e, em seguida, trate o evento:
// in session.update -> session.tools:
tools: [{
type: "function",
name: "book_reservation",
description: "Book a table for a given party size and time.",
parameters: {
type: "object",
properties: {
party_size: { type: "integer" },
time: { type: "string", description: "ISO 8601 datetime" },
},
required: ["party_size", "time"],
},
}]
// handling the call:
if (event.type === "response.function_call_arguments.done") {
const args = JSON.parse(event.arguments);
const result = await bookTable(args); // your real logic
ws.send(JSON.stringify({
type: "conversation.item.create",
item: {
type: "function_call_output",
call_id: event.call_id,
output: JSON.stringify(result),
},
}));
ws.send(JSON.stringify({ type: "response.create" })); // let it speak the result
}A razão mais comum para as ferramentas nunca serem acionadas silenciosamente: não estar à escuta de function_call_arguments.done e não enviar response.create afterward. O modelo produziu a chamada, você ignorou-a, e o interlocutor ouve silêncio.
Se o seu agente gere muitas ferramentas, o SDK de Agentes da OpenAI alterou a equação aqui. A sua reformulação de 15 de abril de 2026 tornou o Protocolo de Contexto do Modelo (MCP) nativo e transformou as transferências entre subagentes numa primitiva de tempo de execução. Assim, em vez de incluir todas as ferramentas num único prompt, um agente roteador pode entregar uma reserva a um subagente de reservas e uma questão de faturação a outro. O início rápido de voz do Agents SDK envolve a mesma sessão Realtime num RealtimeAgent e oferece-lhe transferências sem ter de escrever o seu próprio ciclo de orquestração.
Passo 4: Fazer a Ponte para um Número de Telefone (Twilio)
Para atender chamadas reais, deve ligar um fornecedor de telefonia ao socket. Com a Twilio, aponta uma chamada recebida para um <Connect><Stream> TwiML que abre um WebSocket para o seu servidor, e retransmite quadros de áudio entre a Twilio e a API Realtime. O SIP é a alternativa — a OpenAI Realtime aceita SIP diretamente, o que remove inteiramente o seu retransmissor de multimédia se não precisar de tocar no áudio.
O TwiML que inicia o fluxo:
<Response>
<Connect>
<Stream url="wss://your-server.com/twilio-stream" />
</Connect>
</Response>Eis a armadilha que consome um dia se a ignorar: o fluxo de multimédia da Twilio é de 8 kHz μ-law, e a API Realtime quer PCM16 de 24 kHz. Deve fazer a reamostragem em ambas as direções, caso contrário, obterá áudio distorcido e com tom agudo.
// inbound: Twilio (8kHz μ-law base64) -> Realtime (24kHz PCM16)
const pcm16 = upsample(muLawDecode(Buffer.from(msg.media.payload, "base64")), 8000, 24000);
realtime.send(JSON.stringify({
type: "input_audio_buffer.append",
audio: pcm16.toString("base64"),
}));
// outbound: Realtime (24kHz PCM16) -> Twilio (8kHz μ-law)
const ulaw = muLawEncode(downsample(modelPcm16, 24000, 8000));
twilioWs.send(JSON.stringify({
event: "media",
media: { payload: ulaw.toString("base64") },
}));O formato completo dos quadros encontra-se na documentação de Media Streams da Twilio. Mantenha a reamostragem leve, porque uma biblioteca pesada aqui adiciona latência que pagará em cada quadro.
Passo 5: Gerir Interrupções (Barge-In)
Um agente de produção permite que o interlocutor fale por cima dele. Barge-in significa detetar que o interlocutor começou a falar enquanto o agente está a meio de uma frase e, em seguida, interromper o agente de forma limpa. A API Realtime gere isto com response.cancel: quando a deteção de turnos reporta que a fala começou durante a reprodução, cancela a resposta ativa e elimina qualquer áudio já armazenado em buffer dirigido ao interlocutor.
if (event.type === "input_audio_buffer.speech_started") {
realtime.send(JSON.stringify({ type: "response.cancel" }));
twilioWs.send(JSON.stringify({ event: "clear" })); // drop queued playback
}A deteção de turnos tem dois modos, e a escolha é importante. server_vad dispara com base em limiares de silêncio brutos e tende a cortar o interlocutor em pausas naturais. semantic_vad espera até que o modelo ache que o interlocutor terminou realmente um pensamento, produzindo assim muito menos interrupções falsas durante uma pausa para pensar. Para chamadas telefónicas, o VAD semântico é o que parece mais humano.
Passo 6: Ajustar a Latência para Menos de um Segundo
É aqui que uma demonstração se torna num produto, por isso eis os números da nossa própria implementação, não um orçamento teórico. Executámos o mesmo agente de restaurante em 40 chamadas de teste em maio de 2026, num único servidor pequeno colocado perto da região da OpenAI, trocando apenas as definições de deteção de turnos e de raciocínio.
| Configuração | Ida e volta p50 | p95 | Notas |
|---|---|---|---|
| server_vad, raciocínio baixo | ~1,4 s | ~2,3 s | mais interrupções falsas em pausas |
| semantic_vad, raciocínio baixo | ~1,1 s | ~1,9 s | nossa configuração padrão de produção |
| semantic_vad, raciocínio médio | ~1,8 s | ~3,1 s | melhor precisão nas ferramentas, mais lento |
As alavancas que realmente moveram a agulha, por ordem de impacto:
- Mantenha
reasoning.effortbaixo a menos que uma ferramenta específica necessite genuinamente dessa precisão. O nível médio quase duplicou o nosso p50. - Não envie áudio mais rápido do que em tempo real. Inundar
input_audio_buffer.appendsobrecarrega o buffer e causa deriva; sincronize os quadros com o tempo do relógio de parede. - Mantenha o socket ativo. Abrir uma ligação nova por chamada adiciona o handshake à latência da primeira palavra. Agrupe ligações onde o volume de chamadas o permita.
- Reamostragem eficiente. Um reamostrador ingénuo no caminho crítico adicionou ~80 ms por turno para nós.
Quanto custa executar isto por minuto quando estiver ativo? Calculámos separadamente a matemática de trazer a sua própria chave — veja quanto custa um agente de voz BYOK por minuto em vez de o derivarmos novamente aqui.
Passo 7: Implementar e Reforçar para Produção
A diferença entre "funcionou no meu portátil" e "sobrevive a 500 chamadas por dia" reside num punhado de falhas bem conhecidas. Eis a lista de verificação de reforço, retirada dos erros que realmente quebram os agentes Realtime:
| Armadilha | Sintoma | Correção |
|---|---|---|
| Taxa de amostragem errada | áudio distorcido / tom agudo | PCM16 de 24 kHz em ambas as direções |
Ignorar function_call_arguments.done | as ferramentas nunca disparam | ouvir e enviar response.create |
| Enviar áudio mais rápido do que em tempo real | sobrecarga do buffer, deriva | sincronizar quadros com tempo real |
| Sem lógica de reconexão | as chamadas caem com uma falha no socket | reconexão automática + retomar a sessão |
Sem tratamento de response.done | turnos sobrepostos | condicionar o próximo turno a response.done |
Mais duas coisas para tráfego real. Em chamadas longas, rodeie ou reinicie a sessão a cada vários turnos para que o contexto não derive, porque uma chamada de 20 minutos acumula estado com o qual o modelo começa a tropeçar. E registe cada chamada de ferramenta com os seus argumentos e resultado; quando um interlocutor diz "o agente marcou a hora errada", a transcrição por si só não lhe dirá se o erro foi do modelo ou do seu código.
Se adotar a rota do Agents SDK do Passo 3, a sua nova sandbox de contentores executa o código das ferramentas em isolamento, o que é importante quando as suas ferramentas tocam num sistema de ficheiros ou shell em vez de apenas numa API.
Quando Deve Comprar uma Plataforma Gerida em Vez Disso
Construir diretamente na API Realtime dá-lhe o máximo controlo e o custo mais baixo por minuto, mas fica responsável pela lógica de reconexão, pela ponte de telefonia, conformidade e observabilidade — todas as partes menos glamorosas dos Passos 4 a 7. Se precisar de um agente telefónico ativo esta semana e não quiser manter um retransmissor de multimédia, uma plataforma gerida é a opção mais rápida.
Construímos o mesmo agente nas três principais plataformas e comparámos-nas honestamente: Retell, Vapi ou Bland. Se ainda está a decidir de que lado da linha se encontra, percorra o framework completo de decisão construir vs. comprar antes de comprometer tempo de engenharia.
Quando as equipas querem o controlo de uma construção Realtime personalizada sem ter de a staffar, é esse o trabalho que fazemos: desenvolvimento de agentes de voz de produção, desde a ponte de telefonia até ao ajuste de latência acima mencionado. Estamos disponíveis para analisar o seu caso de uso se estiver a ponderar.
Sobre o autor — Mert Batur Gurbuz é Co-Fundador da Techsy.io, onde a equipa lança agentes de IA, sistemas de automação e pipelines de voz/SDR para clientes B2B. Estuda na Universidade de Birmingham e escreve sobre a stack de ferramentas LLM que a equipa da Techsy realmente utiliza em produção. LinkedIn
Perguntas Frequentes
Qual é a latência do agente de voz da API Realtime da OpenAI?
Na nossa implementação no gpt-realtime-2 com semantic_vad e esforço de raciocínio baixo, a latência de ida e volta mediu p50 1,1 s e p95 1,9 s em 40 chamadas de teste. A conversão de fala para fala num único socket evita a retransmissão STT/LLM/TTS, o que é o que torna possíveis respostas em menos de um segundo.
Preciso de WebRTC, WebSocket ou SIP para o meu agente de voz?
Utilize WebRTC quando um navegador ou aplicação móvel capturar o microfone diretamente, WebSocket quando o seu servidor já detiver um fluxo de áudio bruto (o caso da ponte Twilio) e SIP quando desejar que a OpenAI gestione a parte telefónica sem o seu próprio retransmissor de multimédia. A maioria dos agentes telefónicos utiliza WebSocket ou SIP.
Como ligo a API Realtime da OpenAI à Twilio?
Aponte uma chamada recebida da Twilio para um <Connect><Stream> TwiML que abre um WebSocket para o seu servidor e, em seguida, retransmita o áudio entre a Twilio e o socket Realtime. Reamostre o μ-law de 8 kHz da Twilio para o PCM16 de 24 kHz da API em ambas as direções, caso contrário, o áudio sairá distorcido.
Como funciona a chamada de funções na API Realtime?
Declara ferramentas na configuração da sessão. Quando o modelo deseja uma, emite um evento function_call_arguments.done. Executa o trabalho, envia o resultado de volta como um item de conversa function_call_output e, em seguida, envia response.create para que o agente fale o resultado. Esquecer este último passo é a razão pela qual as ferramentas frequentemente "falham silenciosamente".
Como lida com interrupções (barge-in) na API Realtime?
Quando a deteção de turnos reporta input_audio_buffer.speech_started durante a reprodução, envie response.cancel para parar a resposta ativa e limpar qualquer áudio de output em fila dirigido ao interlocutor. Combine isto com semantic_vad para que as pausas naturais não desencadeiem interrupções falsas a meio da frase.
Qual é a taxa de amostragem de áudio utilizada pela API Realtime da OpenAI?
A API Realtime utiliza áudio PCM16 de 24 kHz em ambas as direções. Fornecedores de telefonia como a Twilio entregam μ-law de 8 kHz, por isso uma ponte telefónica tem de fazer upsampling na entrada e downsampling na saída. Taxas de amostragem incompatíveis são a causa mais comum de áudio distorcido.
Quanto custa executar um agente de voz na API Realtime?
O custo é impulsionado pelos minutos de input e output de áudio no gpt-realtime-2, e a economia de trazer a sua própria chave difere drasticamente de uma plataforma gerida por minuto. Calculámos toda a matemática na nossa análise de preços de agentes de voz em vez de a estimarmos aqui.
Devo construir na API Realtime ou usar Retell, Vapi ou Bland?
Construa diretamente quando quiser o máximo controlo e o custo mais baixo por minuto e puder assumir as reconexões, telefonia e conformidade. Compre uma plataforma gerida quando a velocidade de lançamento for mais importante. A nossa comparação Retell vs Vapi vs Bland e o framework construir vs. comprar cobrem as compensações.
O que mudou a atualização de abril de 2026 do OpenAI Agents SDK para os agentes de voz?
A reformulação de 15 de abril de 2026 tornou o Protocolo de Contexto do Modelo nativo, adicionou uma sandbox de contentores para código de ferramentas e transformou as transferências entre subagentes numa primitiva de tempo de execução. Para agentes de voz, isso significa que um agente roteador pode transferir tarefas para subagentes especializados em vez de encher um único prompt com todas as ferramentas.