DEV Community

Cover image for OpenAI Realtime API con WebRTC: cómo crear agentes de voz sin filtrar claves ni disparar costes
Khavel
Khavel

Posted on • Originally published at devaisemanal.com

OpenAI Realtime API con WebRTC: cómo crear agentes de voz sin filtrar claves ni disparar costes

Un agente de voz en tiempo real no es solo streaming de audio. Necesita una frontera clara entre navegador, backend, Realtime API, tools, permisos, VAD, logs y costes para no convertirse en una demo peligrosa.

OpenAI Realtime API con WebRTC permite crear agentes de voz de baja latencia donde el navegador envía y recibe audio por una conexión WebRTC, mientras un backend confiable inicializa la sesión, protege la API key real y define tools, permisos, logs y presupuesto.

TL;DR

La keyword principal es OpenAI Realtime API WebRTC. La intención de búsqueda en español es práctica: montar una arquitectura de voz en navegador sin exponer claves, entender cuándo usar tokens efímeros o interfaz unificada, y saber qué controles hacen falta antes de producción.

Mi postura: no empieces por una demo con micro abierto y tools conectadas. Empieza por el límite de confianza. Si no sabes quién crea la sesión, quién ejecuta tools, qué se registra, cuánto cuesta cada minuto y qué acciones requieren aprobación, todavía no tienes un agente de voz: tienes un socket caro con permisos ambiguos.

Qué es Realtime API con WebRTC

Realtime API mantiene una sesión abierta para enviar audio, recibir eventos, actualizar estado y dejar que el modelo responda mientras la conversación sigue viva. WebRTC es la vía recomendada para experiencias de voz en navegador porque mueve audio en tiempo real con menos fricción que intentar hacer streaming manual desde JavaScript.

La diferencia frente a un chatbot normal es importante. En chat puedes tolerar segundos de latencia, reintentos visibles y respuestas largas. En voz, 700 ms extra se sienten como interrupción, una tool lenta rompe el turno y una respuesta prolija parece mala UX aunque sea correcta.

Para developers, la arquitectura mental correcta es esta: el navegador captura audio y reproduce audio; el backend crea o negocia la sesión; Realtime API gestiona el modelo y eventos; tus sistemas internos ejecutan acciones con permisos mínimos; observabilidad y costes se miden por sesión, turno y tool call.

Diagrama de agente de voz con navegador, backend que emite token efímero, conexión WebRTC, canal de datos, modelo realtime, tools, guardrails y registro de costes

La frontera clave no es el audio: es separar cliente, backend confiable, sesión realtime, tools internas y controles de seguridad. El navegador nunca debería llevar la API key real.

Arquitectura recomendada para navegador

En una app web, el navegador no debe contener una API key estándar. Debe pedir a tu backend una sesión o un token de vida corta. Ese backend autentica al usuario, aplica rate limit, define configuración inicial, adjunta un identificador de seguridad si procede y llama a la API de OpenAI con la clave real.

OpenAI documenta dos formas de iniciar WebRTC desde cliente: una interfaz unificada donde el backend crea la llamada con /v1/realtime/calls, y el patrón de token efímero donde el backend emite una credencial temporal y el navegador completa la negociación SDP con Realtime API. La elección depende de cuánto quieras poner al backend en el camino crítico de arranque.

Yo usaría interfaz unificada si quieres control fuerte de sesión, auditoría centralizada y menos lógica sensible en cliente. Usaría token efímero cuando necesitas que el navegador conecte directamente, siempre con TTL corto, rate limit por usuario y configuración cerrada desde servidor.

Flujo paso a paso

  • 1. El usuario abre la UI y concede permisos de micrófono. La app todavía no llama a tools ni abre una sesión privilegiada.
  • 2. El cliente pide a tu backend crear una sesión realtime. El backend autentica al usuario, decide modelo, voz, VAD, herramientas permitidas y presupuesto máximo.
  • 3. El navegador crea un RTCPeerConnection, añade el track de audio local y prepara un canal de datos para eventos.
  • 4. La SDP offer viaja al backend o a Realtime API según el patrón elegido. La respuesta SDP queda como remote description y la sesión empieza.
  • 5. El audio de entrada fluye por WebRTC. El modelo devuelve audio, transcripción, eventos de respuesta y posibles tool calls.
  • 6. Las acciones sensibles pasan por tu servidor o por un MCP remoto con superficie limitada y aprobación. El resultado vuelve a la sesión como output de tool.
  • 7. Al cerrar, guardas métricas: duración, tokens de audio/texto, tool calls, errores, VAD, interrupciones, coste estimado y si hubo aprobación humana.

Código mínimo: backend Node para iniciar sesión

server.js

import express from "express";

const app = express();
app.use(express.text({ type: ["application/sdp", "text/plain"] }));

app.post("/api/realtime/call", async (req, res) => {
  const user = await requireUser(req);
  await enforceRealtimeQuota(user.id);

  const form = new FormData();
  form.set("sdp", req.body);
  form.set("session", JSON.stringify({
    type: "realtime",
    model: "gpt-realtime-2.1",
    audio: {
      input: {
        turn_detection: {
          type: "semantic_vad",
          eagerness: "medium",
          interrupt_response: true
        }
      },
      output: { voice: "ash" }
    },
    instructions: [
      "Eres un asistente tecnico de soporte.",
      "Responde breve en voz.",
      "Confirma antes de ejecutar acciones con impacto externo.",
      "No repitas secretos, tokens ni datos personales."
    ].join("\n"),
    tools: [
      {
        type: "function",
        name: "lookup_ticket",
        description: "Busca un ticket permitido para el usuario autenticado",
        parameters: {
          type: "object",
          properties: { ticket_id: { type: "string" } },
          required: ["ticket_id"],
          additionalProperties: false
        }
      }
    ]
  }));

  const r = await fetch("https://api.openai.com/v1/realtime/calls", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
      "OpenAI-Safety-Identifier": hashUser(user.id)
    },
    body: form
  });

  if (!r.ok) {
    res.status(r.status).send(await r.text());
    return;
  }

  res.type("application/sdp").send(await r.text());
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

¿Te está sirviendo? Hay una dosis cada semana

Te resumo herramientas de IA para devs, agentes, MCP, seguridad y workflows en un email de 5 minutos. En español y sin ruido.

Suscribirme gratis

Lo que conviene comprobar

Este ejemplo deja la API key en servidor, aplica autenticación antes de crear sesión y evita que el cliente decida tools o presupuesto. En producción añadiría CORS estricto, CSRF si aplica, logs por sesión, límites por minuto, cierre explícito de sesiones abandonadas y una lista de tools por rol.

Código mínimo: cliente WebRTC

client.js

const pc = new RTCPeerConnection();
const audio = document.querySelector("audio#assistant");
audio.autoplay = true;

pc.ontrack = (event) => {
  audio.srcObject = event.streams[0];
};

const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
for (const track of stream.getTracks()) {
  pc.addTrack(track, stream);
}

const dc = pc.createDataChannel("oai-events");
dc.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === "response.done") recordTurn(msg);
  if (msg.type.includes("function_call")) queueToolReview(msg);
};

const offer = await pc.createOffer();
await pc.setLocalDescription(offer);

const sdp = await fetch("/api/realtime/call", {
  method: "POST",
  headers: { "Content-Type": "application/sdp" },
  body: offer.sdp
}).then((r) => r.text());

await pc.setRemoteDescription({ type: "answer", sdp });
Enter fullscreen mode Exit fullscreen mode

El cliente debe ser aburrido: capturar audio, negociar WebRTC, reproducir audio y mostrar estado. No debería decidir scopes, modelo caro, credenciales ni tools disponibles. Si necesitas cambiar permisos durante la sesión, hazlo desde servidor con una política verificable.

Tools, MCP y acciones: dónde poner el límite

Realtime puede trabajar con function tools, MCP remoto y conectores. La tentación es conectar CRM, calendario, base de datos y ticketing desde el primer día. Mala idea. Voz reduce la fricción de pedir acciones, así que también reduce el tiempo que tiene el usuario para revisar qué está autorizando.

Para function tools, prefiero que tu aplicación ejecute la lógica y devuelva function\_call\_output. Eso te permite aplicar permisos reales, validar argumentos, registrar payloads y pedir aprobación humana antes de mutaciones. Para MCP remoto, limita allowed\_tools y asume que cualquier dato enviado en una tool call puede ser visto por ese servidor.

La regla operativa: lectura con datos no sensibles puede ser automática; escritura, compra, envío, borrado, cambio de permisos o acceso a datos personales debe tener confirmación visible. En voz, la confirmación debe ser corta pero concreta: acción, destino, identificador y consecuencia.

VAD, interrupciones y experiencia de conversación

Voice Activity Detection decide cuándo empieza y termina el turno del usuario. Si cortas pronto, el agente responde antes de entender. Si esperas demasiado, parece lento. OpenAI documenta server\_vad y semantic\_vad; este último intenta trocear cuando el modelo cree que el usuario terminó la idea, no solo por silencio.

Para soporte técnico, empezaría con semantic\_vad y interrupt\_response: true. Los usuarios interrumpen, corrigen IDs y cambian de objetivo. Si el agente no sabe parar, la experiencia parece una locución, no una conversación.

Mide interrupciones como métrica de producto. Muchas interrupciones pueden indicar que el agente habla demasiado, tarda en reconocer el objetivo o usa preambles molestos. No arregles eso solo subiendo modelo: muchas veces se corrige con prompts más claros y respuestas más cortas.

Prompting para voz: menos literatura, más política

Un prompt de voz necesita estructura. Define rol, idioma, tono, longitud, cuándo usar tools, cuándo pedir datos, cuándo confirmar y cuándo escalar. Sé útil y conciso no basta porque no dice qué hacer ante un número de pedido ambiguo, una tool lenta o una petición de borrar datos.

Con modelos realtime con razonamiento, empieza con reasoning.effort bajo y sube solo si hay tareas que realmente lo necesitan. La voz castiga la latencia. Prefiero un agente que resuelva el 80% de casos simples rápido y escale el resto, antes que uno que piense demasiado en cada saludo.

Los preambles son útiles si son breves: Lo reviso ahora antes de una tool lenta puede mejorar percepción. Pero si el agente rellena cada turno con frases de transición, estás pagando tokens para molestar. Define cuándo hablar mientras trabaja y cuándo quedarse callado.

Costes: lo que debes registrar desde el día uno

El coste de voz no se parece al coste de un prompt textual aislado. Hay audio de entrada, audio de salida, texto, posibles tokens cacheados, tools, reintentos y sesiones largas. Además, una mala UX puede duplicar coste si el usuario repite porque el agente lo interrumpió o contestó tarde.

Registra por sesión: modelo, duración, tokens por modalidad, respuestas canceladas, interrupciones, errores de tool, número de turns, coste estimado y usuario o tenant. No guardes audio completo por defecto salvo que tengas base legal y política clara; muchas veces bastan transcripciones redaccionadas y métricas agregadas.

Realtime soporta prompt caching de forma automática cuando hay coincidencia de tokens entre respuestas, pero no lo trates como garantía de presupuesto. Diseña prompts estables, no metas contexto variable enorme al inicio y resume estado largo si la sesión se alarga.

Seguridad y privacidad específicas de voz

La voz introduce riesgos distintos. Puede contener datos personales que el usuario dice sin pensar, ruido de fondo, nombres de terceros o instrucciones inyectadas por otra persona cerca del micrófono. El agente no debería aceptar una orden sensible solo porque la oyó.

Lo que conviene comprobar

Añade controles simples: autenticación antes de sesión, scopes por usuario, denylist de datos que no se leen en voz, confirmación para acciones externas, timeouts, cierre al cambiar de pestaña si procede, y logs que no creen otra fuga. Para equipos regulados, separa entorno de demo y producción desde el primer prototipo.

La prompt injection indirecta también aplica. Si el agente lee una web, ticket o documento y luego actúa, ese contenido debe tratarse como dato no confiable. Una frase dentro de un ticket no puede autorizar que el agente mande un email, borre un registro o exponga un secreto.

Cuándo usar Agents SDK y cuándo ir directo a Realtime

Si solo necesitas una UI web de voz con una o dos tools, ir directo a Realtime API con WebRTC puede ser más claro. Controlas la negociación, ves los eventos y entiendes bien la frontera cliente-servidor.

Si necesitas handoffs, guardrails, especialistas, sesiones server-side, aprobación o integraciones complejas, mira la capa realtime del Agents SDK. La documentación describe RealtimeAgent, RealtimeRunner, RealtimeSession, handoffs y guardrails específicos para respuestas y function-tool calls.

No lo conviertas en religión de SDK. La pregunta buena es quién orquesta. Si el navegador solo captura audio, tu backend gestiona permisos y el SDK te ayuda a coordinar especialistas, tiene sentido. Si solo añade abstracción antes de entender el flujo, espera.

Checklist de producción

  • API key estándar solo en servidor, nunca en navegador.
  • Sesiones creadas tras autenticar usuario y aplicar cuota.
  • Modelo, voz, VAD, tools y presupuesto definidos en backend.
  • Tools separadas por rol, tenant y tipo de acción.
  • Confirmación explícita para operaciones irreversibles o externas.
  • Logs con IDs, métricas y errores; audio bruto solo si hay necesidad real y política.
  • Evals de conversación con interrupciones, ruido, IDs, acentos y peticiones ambiguas.
  • Monitor de coste por sesión y alertas por duración o reintentos.
  • Fallback textual o humano si falla WebRTC, tool crítica o guardrail.

Conclusión

OpenAI Realtime API con WebRTC ya permite construir agentes de voz muy convincentes, pero la parte difícil no es abrir el micrófono. La parte difícil es hacer que esa conversación tenga permisos, límites, coste predecible y una experiencia que no se rompa cuando el usuario interrumpe.

Mi recomendación: construye primero el esqueleto de confianza. Backend que crea sesiones, cliente tonto, tools estrechas, VAD medido, confirmaciones visibles y coste por sesión. Después mejora voces, handoffs y prompts. Si lo haces al revés, tendrás una demo brillante y una deuda de seguridad desde el primer commit.

Preguntas frecuentes

¿Qué es OpenAI Realtime API con WebRTC?

Es una forma de conectar una app de navegador a modelos realtime mediante WebRTC para enviar audio, recibir audio y manejar eventos de conversación o tools con baja latencia.

¿Puedo usar mi API key de OpenAI en el navegador?

No deberías. La clave estándar debe quedarse en servidor. El navegador debe usar una sesión creada por backend o una credencial efímera de vida corta.

¿Qué diferencia hay entre WebRTC y WebSocket en Realtime API?

WebRTC encaja mejor para audio directo desde navegador. WebSocket suele tener más sentido en pipelines server-side, telephony o cuando tu servidor controla el flujo de audio.

¿Realtime API puede llamar tools o MCP?

Sí. Puede usar function tools, MCP remoto y conectores, pero las acciones sensibles necesitan permisos estrechos, validación y aprobación cuando haya impacto externo.

¿Cómo controlo el coste de un agente de voz?

Mide duración, tokens de audio y texto, turns, reintentos, tools, respuestas canceladas y coste estimado por sesión. Añade cuotas por usuario o tenant desde el backend.

¿Cuándo usar Agents SDK para agentes de voz?

Úsalo cuando necesites handoffs, guardrails, orquestación server-side o especialistas. Para una UI web simple, Realtime API directo puede ser más transparente al principio.

Cómo lanzar un agente de voz con OpenAI Realtime API y WebRTC sin abrir demasiado el sistema

  1. Definir caso de uso. Elige una tarea de voz acotada, con datos permitidos y acciones claras.
  2. Diseñar frontera de confianza. Decide qué vive en navegador, backend, Realtime API y sistemas internos.
  3. Crear endpoint de sesión. Autentica usuario, aplica cuota y crea la sesión con API key solo en servidor.
  4. Conectar WebRTC. Captura micrófono, negocia SDP, reproduce audio y escucha eventos por data channel.
  5. Añadir tools mínimas. Empieza por lectura segura y valida argumentos antes de ejecutar negocio real.
  6. Configurar VAD. Prueba server\_vad y semantic\_vad, mide interrupciones y latencia percibida.
  7. Instrumentar coste. Registra duración, tokens, tools, errores, reintentos y coste estimado por sesión.
  8. Meter guardrails. Bloquea datos sensibles, acciones no autorizadas y contenido externo que intente cambiar instrucciones.
  9. Probar con conversaciones reales. Incluye ruido, acentos, IDs dictados, interrupciones y peticiones ambiguas antes de producción.

Fuentes y referencias

También te puede interesar

Recibe una lectura semanal de herramientas IA para devs

Cada semana te resumo herramientas de IA para devs, agentes, MCP, seguridad y workflows en un email de 5 minutos. En español y sin ruido.

Suscribirme gratis

Top comments (0)