Introducción
La conversación en tiempo real establece una conexión persistente mediante WebSocket (un protocolo que mantiene una conexión duradera y bidireccional entre el cliente y el servidor), envía tu audio o texto al modelo de conversación en tiempo real y el modelo devuelve texto y voz de forma incremental. Es adecuada para asistentes de voz, preguntas y respuestas en tiempo real, práctica oral y otros escenarios que requieren interacción de ida y vuelta. Igual que la transcripción en tiempo real, funciona sobre WebSocket, pero sus usos son distintos:
Modelo disponible:
- gpt-realtime-2.1: modelo de conversación por voz, admite entrada de audio y texto y produce respuestas de texto y voz en tiempo real.
Inicio rápido
Punto de conexión
model=gpt-realtime-2.1: obligatorio, el modelo queda fijado al conectar mediante el parámetro de la URL y no puede cambiarse durante la sesión (consulta las restricciones más abajo).- Atención a la diferencia con la transcripción: el punto de conexión de conversación no lleva
intent=transcription.
Autenticación
Envía la clave en una cabecera HTTP estándar durante el handshake:Requisitos de formato de audio
El audio de entrada y de salida solo admite actualmente un formato. Convierte tu audio antes de enviarlo:- Codificación: PCM16 (entero con signo de 16 bits, little-endian)
- Frecuencia de muestreo: 24000 Hz
- Canales: mono
audio/pcm@24000. Declarar otro formato (como G.711/µ-law) en la entrada o la salida se rechaza y cierra la sesión.
A diferencia de la transcripción, las sesiones de conversación sí admiten la detección de actividad de voz (turn_detection / VAD). Al activarla, el modelo determina automáticamente cuándo termina una intervención y desencadena una respuesta; al desactivarla (valor
null), controlas tú cuándo confirmar el audio y cuándo solicitar una respuesta. Elige según lo que necesites.Configuración de sesión (session.update)
Una vez establecida la conexión, el cliente puede enviar una tramasession.update para configurar parámetros de la conversación (voz, instrucciones del sistema, si se activa el VAD). El modelo de la sesión de conversación ya está anclado por la URL de conexión, así que puedes conversar sin enviar session.update; envíala cuando necesites una voz o instrucciones personalizadas.
Parámetros de configuración
string
requerido
Tipo de sesión,
realtime para el escenario de conversación.string
Instrucciones del sistema que definen el rol, el tono y las restricciones de respuesta del modelo.
string[]
Modalidades de salida,
["audio"] o ["text"]: con ["audio"] (predeterminado) el modelo genera voz y el texto de la respuesta llega por el evento response.output_audio_transcript.delta; con ["text"] solo genera texto, que se entrega por response.output_text.delta. Cualquier otra combinación (por ejemplo ["audio", "text"]) se rechaza y devuelve un evento error.object
requerido
Formato de audio de entrada, fijado en
{ "type": "audio/pcm", "rate": 24000 }.object | null
Detección de actividad de voz. Pasa
{ "type": "server_vad" } para activar la detección automática de turnos; pasa null para desactivarla y confirmar el audio y solicitar respuestas manualmente desde el cliente.object
requerido
Formato de audio de salida, fijado en
{ "type": "audio/pcm", "rate": 24000 }.string
Voz de la respuesta de audio. No puede cambiarse una vez iniciada la primera respuesta: cuando la sesión entra en estado de generación, un
voice enviado de nuevo se ignora (el resto de ajustes sí se aplican). Por tanto, si necesitas una voz concreta, defínela antes de solicitar la primera respuesta.Las siguientes capacidades no son compatibles en esta versión y cierran la sesión al configurarlas (código de cierre
1008): activar la transcripción integrada dentro de una sesión de conversación (audio.input.transcription, motivo input_transcription_not_supported), inyectar audio (motivo item_audio_not_supported) o imágenes (motivo image_input_not_supported) mediante elementos de conversación, y cualquier tipo de elemento de contenido distinto de texto (motivo unsupported_content_part). Envía todo el audio por el canal input_audio_buffer.append.Envío de entrada
Envío de audio
Divide el audio PCM16 en fragmentos pequeños (por ejemplo, uno cada 100 ms), codifícalos en base64 y envíalos de forma continua con el eventoinput_audio_buffer.append:
Envío de texto
También puedes inyectar directamente un mensaje de texto y solicitar una respuesta:Recepción de respuestas
El servidor sigue enviando eventos. Tipos de evento clave:event
Confirmación de que la sesión se creó o de que se actualizó su configuración. Puedes empezar a enviar audio y texto al recibir
session.created.event
Un elemento de conversación se ha escrito: cada entrada del usuario y cada respuesta del modelo añaden un elemento.
event
Con la VAD activada, el servidor ha detectado que el usuario empieza o deja de hablar. Un evento
speech_started suele significar que el usuario está interrumpiendo al modelo; consulta Interrupción y truncado.event
Una respuesta ha empezado a generarse.
event
El elemento de salida de la respuesta ha empezado y ha terminado. El campo
item.id del evento added es el ID del elemento de conversación que debes referenciar al truncar el audio más adelante.event
Un fragmento incremental del audio de la respuesta (PCM16 codificado en base64) y su marca de fin, que puedes reproducir a medida que llega.
event
La transcripción incremental que corresponde al audio de la respuesta frase por frase, y su marca de fin; el campo
delta contiene el texto añadido. Cuando la salida incluye audio, toma el texto de la respuesta de este evento, por ejemplo para mostrar subtítulos mientras se reproduce el audio.event
Un fragmento incremental de una respuesta solo de texto y su marca de fin, emitidos únicamente cuando la modalidad de salida es solo texto (
output_modalities: ["text"]).event
Una respuesta ha terminado. Este evento incluye el uso de tokens del turno (
usage), que es la base de la facturación.event
Confirmación de que la solicitud de truncado ha surtido efecto; consulta Interrupción y truncado.
event
Evento de error, con código y descripción. Un problema de la propia solicitud (por ejemplo un valor
output_modalities no válido) devuelve un único evento error y la sesión sigue siendo utilizable; las cuestiones de política (cambio de modelo, saldo agotado) cierran la sesión.Elige bien el evento para el texto de la respuesta. Por defecto (salida con audio) el modelo solo envía
response.output_audio_transcript.delta y no envía response.output_text.delta; si defines la modalidad de salida como solo texto, el texto pasa a response.output_text.delta. Escucha ambos en cualquiera de los dos modos para no perder texto (consulta la salida medida más abajo).Interrupción y truncado
Cuando el usuario empieza a hablar mientras el modelo habla, el contenido ya generado pero aún no reproducido entra en conflicto con la siguiente frase del usuario. En una conexión WebSocket la reproducción la gestiona el cliente, así que el cliente también completa la limpieza tras una interrupción. Con la VAD activada, el servidor envíainput_audio_buffer.speech_started en cuanto detecta que el usuario ha empezado a hablar. Al recibir ese evento, el cliente debe:
- Detener de inmediato la reproducción local y anotar hasta dónde se había reproducido la respuesta (en milisegundos).
- Enviar
conversation.item.truncatepara quitar de la conversación el audio no reproducido, de modo que el modelo no lo trate como dicho en el siguiente turno.
item_id: el ID del elemento de conversación de esta respuesta, tomado deitem.iden el eventoresponse.output_item.added.content_index: el índice de la parte de contenido de audio, siempre0.audio_end_ms: la longitud de audio que se conserva, en milisegundos, según la posición que el cliente haya reproducido realmente.
conversation.item.truncated cuando la solicitud se ha procesado. El truncado solo afecta al audio de esta respuesta y a su transcripción; la sesión no se ve afectada y puedes continuar con el siguiente turno. Con el SDK de OpenAI, llama a conn.conversation.item.truncate(item_id=..., content_index=0, audio_end_ms=...).
Con la VAD desactivada (por ejemplo, pulsar para hablar), la pulsación es la interrupción: envía response.cancel para cancelar la respuesta en curso y trunca como se describe arriba; al soltar, envía input_audio_buffer.append, input_audio_buffer.commit y response.create en ese orden.
Ejemplos completos
A continuación se muestran tres enfoques; elige uno:- SDK oficial de OpenAI (recomendado): no necesitas escribir las tramas WebSocket a mano; apunta
websocket_base_url(el parámetro de URL base de WebSocket del SDK) a la pasarela y reutiliza la biblioteca oficial. - SDK oficial de OpenAI Agents: la forma de voz en tiempo real del framework de agentes oficial; sustituye la
urldemodel_configpor la dirección de la pasarela. - websockets sin procesar: sin SDK, intercambia tramas directamente según el protocolo. Menos dependencias y más fácil de depurar.
¿Por qué pasar el nombre del modelo al conectar? La pasarela de AiHubMix necesita el
model en el momento del handshake WebSocket para seleccionar el proveedor de modelos, autenticar y reservar cupo, mientras que session.update llega después de completarse el handshake. Por eso, con un SDK debes pasar model explícitamente a connect() (el SDK lo inserta en la consulta de la URL); sin él, la pasarela rechaza durante el handshake y no se establece ninguna conexión. A diferencia de la transcripción, el punto de conexión de conversación no necesita intent=transcription.Reutilizar ejemplos oficiales
La mayoría de los ejemplos de conversación en tiempo real que publica OpenAI solo dependen del parámetro de URL base del SDK, así que puedes reutilizarlos apuntando la dirección al punto de conexión de AiHubMix:Salida medida (en producción)
El siguiente es el resultado real del ejemplo del SDK de OpenAI en el entorno de producciónaihubmix.com con el modelo gpt-realtime-2.1. La sesión activa server_vad, y tanto los prompts como el audio están en inglés.
Entrada de texto
Confirmación medida: por defecto (salida con audio) el texto de la respuesta solo llega carácter a carácter por
response.output_audio_transcript.delta, y response.output_text.delta nunca aparece; response.done lleva el usage del turno; el handshake tarda entre 2 y 4 segundos, el coste de la reserva de cupo al establecer la sesión.Facturación
- Facturación por token: una sesión de conversación devuelve el uso de tokens de cada turno (
usage) con el eventoresponse.done, y la facturación se basa en él. El uso se mide por separado según el componente, incluidos entrada de audio, salida de audio, entrada de texto y salida de texto. El precio unitario de cada componente sigue el precio publicado en vivo en la página de detalle del modelo. - Liquidación al vuelo: se trata de una conexión de larga duración, y los cargos se descuentan en tiempo real con cada turno durante la sesión, sin una liquidación única al final. Al establecer la sesión se hace primero una reserva de cupo de aproximadamente un minuto de uso (solo una comprobación de admisión, no un cargo real), y la reserva restante se libera al terminar la sesión. Por tanto, el saldo disponible de tu cuenta debe cubrir al menos un minuto de uso para que la sesión pueda establecerse.
- Cada registro de facturación de conversación en tiempo real puede consultarse uno a uno en Uso y facturación.
Límites y restricciones
- Duración de la sesión: una conexión WebSocket dura como máximo 62 minutos, tras lo cual el servidor la cierra (código de cierre
1000, motivosession_duration_limit); divide en segmentos si necesitas más tiempo. - Desconexión por inactividad: cuando ni el cliente ni el modelo tienen actividad durante 5 minutos, el servidor cierra la sesión (código de cierre
1008, motivoidle_timeout). La actividad de cualquiera de las dos partes reinicia el temporizador, por lo que una respuesta larga que sigue transmitiéndose no se interrumpe. - Saldo insuficiente: al establecer la sesión, si el saldo disponible no cubre la reserva de aproximadamente un minuto, el handshake se rechaza de inmediato (HTTP 403) y no se establece ninguna sesión; durante la sesión, si el saldo se agota, la conexión establecida se cierra de inmediato.
- Solo del lado del servidor: no se admiten conexiones directas desde el navegador (se valida la cabecera
Origin); integra desde tu servidor. - Modelo fijado: el modelo queda fijado en la URL de conexión, y cambiarlo con
session.updatedurante la sesión se rechaza y cierra la sesión. - Formato fijado: el audio de entrada y de salida solo admite
audio/pcm@24000mono; otros formatos se rechazan. - Voz fijada:
voiceno puede cambiarse una vez iniciada la primera respuesta; defínela antes de solicitar la primera respuesta. - No compatible: la transcripción integrada dentro de una sesión de conversación, la inyección de contenido de audio o imagen mediante elementos de conversación y cualquier tipo de elemento de contenido distinto de texto.
- Una respuesta a la vez: una sesión solo permite una respuesta en curso; enviar otro
response.createantes de que termine el turno actual se rechaza y cierra la sesión (código de cierre1008, motivoresponse_already_active).
Errores comunes
Última actualización: 2026-09-21