ai-machine-learning

Crea un agente de voz con la API Realtime de OpenAI: el tutorial de producción en 7 pasos (2026)

Escrito por Mert Batur
Jun 6, 2026
12 lectura
Crea un agente de voz con la API Realtime de OpenAI: el tutorial de producción en 7 pasos (2026)

Crea un agente de voz con la API Realtime de OpenAI: el tutorial de producción en 7 pasos (2026)

Nuestro agente de prueba respondió una llamada de Twilio y pronunció su primera palabra 1,1 segundos después de que quien llamaba dejó de hablar. Es el tiempo de ida y vuelta p50, medido en 40 llamadas con gpt-realtime-2 y semantic_vad. Nada de magia. La API Realtime de OpenAI hace voz a voz en un solo socket, así que te saltas el relevo STT → LLM → TTS que suma unos 600 ms de código de pegamento. Pero los valores por defecto no te llevarán al segundo. Esta es la implementación en 7 pasos que lanzamos, con el código y la tabla de latencia.

Esto es un tutorial de construcción, no una explicación de conceptos. Si quieres primero el desglose por capas, lee qué es realmente un agente de voz con IA y luego vuelve. Todo lo que sigue supone que tienes una clave de API de OpenAI y un entorno Node.

Claves del artículo:

  • gpt-realtime-2 hace voz a voz en un socket, sin relevo STT/LLM/TTS, ~600 ms ahorrados.
  • Genera claves efímeras en el servidor; nunca envíes tu clave de API estándar a un navegador.
  • El flujo de medios de Twilio es de 8 kHz μ-law; reescala a 24 kHz PCM16 para la API Realtime.
  • Medimos p50 1,1 s / p95 1,9 s de ida y vuelta. El barge-in pasa por response.cancel.

Qué vas a construir en 7 pasos

Este tutorial construye un agente de voz con la API Realtime de OpenAI que contesta el teléfono en menos de 1,5 segundos, llama a una función real en plena conversación y deja que quien llama lo interrumpa. El flujo es corto: alguien marca un número, el audio se transmite a tu servidor, tu servidor lo conecta a gpt-realtime-2 por un solo socket, el modelo habla y puede disparar llamadas a herramientas, y el audio vuelve.

Este es el camino, y puedes parar en cualquier paso que encaje con tu caso de uso:

  1. Generar una clave efímera (ruta del servidor)
  2. Abrir y configurar la sesión
  3. Añadir llamadas a funciones
  4. Conectar a un número de teléfono con Twilio
  5. Manejar el barge-in y las interrupciones
  6. Ajustar la latencia por debajo del segundo
  7. Desplegar y endurecer para producción

Tres transportes llevan el audio, y tu elección depende de dónde venga. Un navegador lo captura directo (WebRTC), tu servidor ya tiene un flujo en bruto (WebSocket), o una red telefónica lo entrega (SIP). Usamos WebSocket para el puente con Twilio y señalamos los demás donde encajan.

Paso 1: Generar una clave efímera (la ruta que no puedes saltarte)

Nunca expongas tu clave de API estándar de OpenAI a un navegador o a un dispositivo cliente. La API Realtime emite claves efímeras de corta duración exactamente para esto. Tu servidor llama a POST /v1/realtime/client_secrets con tu clave real, le entrega al cliente un token que caduca en aproximadamente un minuto, y el cliente se conecta con ese en su lugar.

Aquí tienes una ruta mínima de Express que genera una:

javascript
// 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);

El navegador llama a /session, lee el secreto de corta duración y abre la conexión Realtime con él. Si tu agente es solo de servidor (el caso de Twilio del paso 4), puedes saltarte el traspaso al cliente y abrir el socket desde tu backend directamente con la clave estándar. El flujo efímero existe para proteger a clientes no confiables.

Paso 2: Abrir la sesión y configurar gpt-realtime-2

Abre una conexión y luego envía un session.update que fije el modelo, el formato de audio, la voz y la detección de turno. La documentación de OpenAI recomienda empezar con reasoning.effort en low y subirlo solo si tu lógica de herramientas necesita más precisión, ya que un esfuerzo mayor te cuesta latencia. El audio circula como 24 kHz PCM16 en ambas direcciones.

javascript
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: "Eres un agente de reservas para un restaurante. Sé breve.",
    reasoning: { effort: "low" },
    turn_detection: { type: "semantic_vad" },
  },
}));

El transporte con el que envuelves ese socket depende de la fuente de audio:

TransporteUsar cuandoFuente de audio
WebRTCUn navegador o app móvil captura el micrófono directoDispositivo cliente
WebSocketTu servidor ya tiene un flujo de audio en brutoPipeline del servidor
SIPQuieres que OpenAI maneje el tramo telefónicoRTC / telefonía

Para la lista completa de campos de la sesión y el conjunto de funciones GA, la documentación de la API Realtime de OpenAI es la fuente de verdad. Usamos WebSocket porque Twilio nos entrega audio en bruto en el paso 4.

Paso 3: Añadir llamadas a funciones (para que el agente haga cosas de verdad)

Un agente de voz que no puede actuar es una voz en off. Las llamadas a funciones dejan que gpt-realtime-2 haga una pausa en plena conversación, le pida a tu código ejecutar algo y siga hablando con el resultado. Declaras una herramienta en la sesión, el modelo emite un evento function_call_arguments.done cuando la quiere, ejecutas el trabajo y devuelves la salida.

Declara la herramienta y luego maneja el evento:

javascript
// en session.update -> session.tools:
tools: [{
  type: "function",
  name: "book_reservation",
  description: "Reserva una mesa para un número de comensales y una hora.",
  parameters: {
    type: "object",
    properties: {
      party_size: { type: "integer" },
      time: { type: "string", description: "fecha/hora ISO 8601" },
    },
    required: ["party_size", "time"],
  },
}]

// manejo de la llamada:
if (event.type === "response.function_call_arguments.done") {
  const args = JSON.parse(event.arguments);
  const result = await bookTable(args);            // tu lógica real
  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" }));  // que diga el resultado
}

La razón más común de que las herramientas nunca se disparen en silencio: no escuchar function_call_arguments.done y no enviar response.create después. El modelo produjo la llamada, tú la ignoraste, quien llama oye silencio total.

Si tu agente hace malabares con muchas herramientas, el OpenAI Agents SDK cambió las cuentas aquí. Su renovación del 15 de abril de 2026 hizo nativo el Model Context Protocol (MCP) y convirtió los traspasos entre subagentes en una primitiva de ejecución. Así, en lugar de meter cada herramienta en un prompt, un agente enrutador puede pasar una reserva a un subagente de reservas y una pregunta de facturación a otro. El inicio rápido de voz del Agents SDK envuelve la misma sesión Realtime en un RealtimeAgent y te da los traspasos sin escribir tu propio bucle de orquestación.

Paso 4: Conectarlo a un número de teléfono (Twilio)

Para atender llamadas reales, conectas un proveedor de telefonía al socket. Con Twilio, diriges una llamada entrante a un TwiML <Connect><Stream> que abre un WebSocket hacia tu servidor, y retransmites las tramas de audio entre Twilio y la API Realtime. SIP es la alternativa. OpenAI Realtime acepta SIP directamente, lo que elimina por completo tu relevo de medios si no necesitas tocar el audio.

El TwiML que arranca el flujo:

xml
<Response>
  <Connect>
    <Stream url="wss://your-server.com/twilio-stream" />
  </Connect>
</Response>

Aquí está la trampa que te come un día si la pasas por alto: el flujo de medios de Twilio es 8 kHz μ-law, y la API Realtime quiere 24 kHz PCM16. Reescalas en ambas direcciones, o consigues audio distorsionado de voz de ardilla.

javascript
// entrante: 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"),
}));

// saliente: Realtime (24kHz PCM16) -> Twilio (8kHz μ-law)
const ulaw = muLawEncode(downsample(modelPcm16, 24000, 8000));
twilioWs.send(JSON.stringify({
  event: "media",
  media: { payload: ulaw.toString("base64") },
}));

El formato completo de las tramas está en la documentación de Twilio Media Streams. Mantén el reescalado barato, porque una biblioteca pesada aquí añade latencia que pagarás en cada trama.

Paso 5: Manejar el barge-in y las interrupciones

Un agente de producción deja que quien llama hable por encima de él. El barge-in consiste en detectar que la persona empezó a hablar mientras el agente está a media frase, y luego cortar al agente con limpieza. La API Realtime lo maneja con response.cancel: cuando la detección de turno informa de que empezó el habla durante la reproducción, cancelas la respuesta activa y vacías el audio ya almacenado hacia quien llama.

javascript
if (event.type === "input_audio_buffer.speech_started") {
  realtime.send(JSON.stringify({ type: "response.cancel" }));
  twilioWs.send(JSON.stringify({ event: "clear" }));   // descarta la reproducción en cola
}

La detección de turno tiene dos modos, y la elección importa. server_vad se dispara con umbrales de silencio en bruto y tiende a cortar a quien llama en pausas naturales. semantic_vad espera hasta que el modelo cree que la persona realmente terminó una idea, así produce muchas menos interrupciones falsas en una pausa de reflexión. Para llamadas telefónicas, el VAD semántico es el que se siente humano.

Paso 6: Ajustar la latencia por debajo del segundo

Aquí una demo se convierte en producto, así que estos son los números de nuestra propia implementación, no un presupuesto teórico. Pasamos el mismo agente de restaurante por 40 llamadas de prueba en mayo de 2026, en un único servidor pequeño colocalizado cerca de la región de OpenAI, cambiando solo los ajustes de detección de turno y de razonamiento.

ConfiguraciónIda y vuelta p50p95Notas
server_vad, reasoning low~1,4 s~2,3 smás barge-ins falsos en pausas
semantic_vad, reasoning low~1,1 s~1,9 snuestro valor de producción por defecto
semantic_vad, reasoning medium~1,8 s~3,1 smejor precisión de herramientas, más lento

Las palancas que de verdad movieron la aguja, por orden de impacto:

  • Mantén reasoning.effort en low salvo que una herramienta concreta de verdad necesite la precisión. Medium casi duplicó nuestro p50.
  • No empujes el audio más rápido que el tiempo real. Inundar input_audio_buffer.append desborda el búfer y causa deriva; marca las tramas al reloj de pared.
  • Mantén el socket caliente. Abrir en frío una conexión por llamada añade el handshake a tu latencia de la primera palabra. Agrupa conexiones donde el volumen de llamadas lo permita.
  • Reescala con eficiencia. Un reescalador ingenuo en la ruta crítica nos añadió ~80 ms por turno.

¿Cuánto cuesta operarlo por minuto una vez en producción? Hicimos las cuentas del bring-your-own-key por separado; mira cuánto cuesta por minuto un agente de voz BYOK en lugar de rederivarlas aquí.

Paso 7: Desplegar y endurecer para producción

La brecha entre «funcionó en mi portátil» y «aguanta 500 llamadas al día» son un puñado de fallos bien conocidos. Esta es la lista de endurecimiento, sacada de los errores que de verdad rompen los agentes Realtime:

TrampaSíntomaSolución
Frecuencia de muestreo equivocadaaudio distorsionado / voz de ardilla24 kHz PCM16 en ambos sentidos
Ignorar function_call_arguments.donelas herramientas nunca se disparanescuchar y enviar response.create
Empujar audio más rápido que el tiempo realdesbordamiento de búfer, derivamarcar las tramas al tiempo real
Sin lógica de reconexiónlas llamadas caen ante un parpadeo del socketreconexión automática + reanudar sesión
Sin manejo de response.doneturnos superpuestoscondicionar el siguiente turno a response.done

Dos cosas más para tráfico real. En llamadas largas, rota o resiembra la sesión cada pocos turnos para que el contexto no derive, porque una llamada de 20 minutos acumula estado con el que el modelo empieza a tropezar. Y registra cada llamada a herramienta con sus argumentos y resultado; cuando alguien diga «el agente reservó la hora equivocada», la transcripción sola no te dirá si se equivocó el modelo o tu código.

Si adoptas la vía del Agents SDK del paso 3, su nuevo entorno aislado en contenedor ejecuta el código de las herramientas en aislamiento, lo que importa en cuanto tus herramientas tocan un sistema de archivos o una shell en vez de solo una API.

Cuándo deberías comprar una plataforma gestionada en su lugar

Construir directamente sobre la API Realtime te da el mayor control y el menor coste por minuto, pero tú asumes la lógica de reconexión, el puente telefónico, el cumplimiento y la observabilidad, todas las partes poco glamurosas de los pasos 4 a 7. Si necesitas un agente telefónico en línea esta semana y no quieres mantener un relevo de medios, una plataforma gestionada es la opción más rápida.

Creamos el mismo agente en las tres grandes y las comparamos con honestidad: Retell, Vapi o Bland. Si todavía decides en qué lado de la línea estás, recorre el marco de decisión completo build-vs-buy antes de comprometer tiempo de ingeniería.

Cuando los equipos quieren el control de una implementación Realtime a medida sin dotarla de personal, ese es el trabajo que hacemos: desarrollo de agentes de voz para producción, desde el puente telefónico hasta el ajuste de latencia de arriba. Encantados de revisar tu caso de uso si lo estás sopesando.

Sobre el autor — Mert Batur es cofundador de Techsy.io, donde el equipo entrega agentes de IA, sistemas de automatización y pipelines de voz/SDR para clientes B2B. Escribe sobre la pila de herramientas LLM que el equipo de Techsy usa de verdad en producción. LinkedIn

Preguntas frecuentes

¿Cuál es la latencia de un agente de voz con la API Realtime de OpenAI?

En nuestra implementación sobre gpt-realtime-2 con semantic_vad y esfuerzo de razonamiento bajo, la latencia de ida y vuelta midió p50 1,1 s y p95 1,9 s en 40 llamadas de prueba. La voz a voz en un socket evita el relevo STT/LLM/TTS, que es lo que hace posibles las respuestas por debajo del segundo.

¿Necesito WebRTC, WebSocket o SIP para mi agente de voz?

Usa WebRTC cuando un navegador o app móvil captura el micrófono directo, WebSocket cuando tu servidor ya tiene un flujo de audio en bruto (el caso del puente con Twilio), y SIP cuando quieres que OpenAI maneje el tramo telefónico sin tu propio relevo de medios. La mayoría de los agentes telefónicos usan WebSocket o SIP.

¿Cómo conecto la API Realtime de OpenAI con Twilio?

Dirige una llamada entrante de Twilio a un TwiML <Connect><Stream> que abra un WebSocket hacia tu servidor, y luego retransmite el audio entre Twilio y el socket Realtime. Reescala el 8 kHz μ-law de Twilio al 24 kHz PCM16 de la API en ambos sentidos, o el audio sale distorsionado.

¿Cómo funcionan las llamadas a funciones en la API Realtime?

Declaras las herramientas en la configuración de la sesión. Cuando el modelo quiere una, emite un evento function_call_arguments.done. Ejecutas el trabajo, devuelves el resultado como elemento de conversación function_call_output y luego envías response.create para que el agente diga el resultado. Olvidar ese último paso es por qué las herramientas suelen fallar «en silencio».

¿Cómo se manejan las interrupciones (barge-in) en la API Realtime?

Cuando la detección de turno informa de input_audio_buffer.speech_started durante la reproducción, envía response.cancel para detener la respuesta activa y vacía cualquier audio de salida en búfer hacia quien llama. Combínalo con semantic_vad para que las pausas naturales no disparen interrupciones falsas a media frase.

¿Qué frecuencia de muestreo de audio usa la API Realtime de OpenAI?

La API Realtime usa audio 24 kHz PCM16 en ambas direcciones. Los proveedores de telefonía como Twilio entregan 8 kHz μ-law, así que un puente telefónico debe reescalar hacia arriba a la entrada y hacia abajo a la salida. Las frecuencias de muestreo discordantes son la causa más común de audio distorsionado.

¿Cuánto cuesta operar un agente de voz en la API Realtime?

El coste lo impulsan los minutos de audio de entrada y salida en gpt-realtime-2, y la economía del bring-your-own-key difiere mucho de una plataforma gestionada por minuto. Hicimos las cuentas completas en nuestro análisis de precios de agentes de voz en lugar de estimarlas aquí.

¿Debo construir sobre la API Realtime o usar Retell, Vapi o Bland?

Construye directamente cuando quieras máximo control y el menor coste por minuto y puedas asumir reconexiones, telefonía y cumplimiento. Compra una plataforma gestionada cuando importe más la rapidez de lanzamiento. Nuestra comparativa Retell vs Vapi vs Bland y el marco build-vs-buy cubren las concesiones.

¿Qué cambió la actualización del OpenAI Agents SDK de abril de 2026 para los agentes de voz?

La renovación del 15 de abril de 2026 hizo nativo el Model Context Protocol, añadió un entorno aislado en contenedor para el código de herramientas y convirtió los traspasos entre subagentes en una primitiva de ejecución. Para los agentes de voz, eso significa que un agente enrutador puede traspasar a subagentes especializados en lugar de meter cada herramienta en un prompt.

Etiquetas

openai realtime api voice agentgpt-realtime-2function callingtwilio voice agentopenai agents sdkvoice ai tutorial

Compartir este artículo

Inicia Tu Proyecto

¿Listo para construir algo extraordinario?

Convirtamos tu visión en realidad. Nuestro equipo está listo para ayudarte a crear software que marque la diferencia.