CodeGym /Cursos /ChatGPT Apps /Contexto Voice / Realtime: comportamiento de la App en co...

Contexto Voice / Realtime: comportamiento de la App en conversaciones por voz

ChatGPT Apps
Nivel 8 , Lección 4
Disponible

1. Contexto: qué significa «modo de voz» para ChatGPT App

Para empezar, es importante entender que dentro del ChatGPT Apps SDK no escribes tu propio cliente de audio, no controlas el micrófono ni haces streaming de audio por tu cuenta. De ello se encarga el cliente de ChatGPT (la web o la app móvil).

Suponemos que ya tienes una comprensión básica del widget, callTool y GiftGenius de módulos anteriores: aquí miramos esos mismos elementos a través del prisma del modo de voz.

Desde tu perspectiva como desarrollador de la App, todo se ve así:

  • El usuario habla al micrófono. El cliente de ChatGPT hace el reconocimiento de voz y envía a la modelo el texto ya transcrito.
  • Tú «ves» en el stream lo mismo que si el usuario estuviera escribiendo, solo que los mensajes llegan más rápido y con un tono más conversacional.
  • El modelo responde con texto que el cliente locuta.
  • Al mismo tiempo, el modelo puede invocar tus herramientas (callTool), cambiar el displayMode del widget, actualizar el widgetState y proponer seguimientos, igual que en el modo de texto.

La diferencia clave es que el usuario puede casi no mirar la pantalla, o solo echarle un vistazo de reojo al teléfono. Es decir, tu UI deja de ser el canal principal de interacción y se convierte en un complemento de la voz, y no al revés.

De aquí se derivan dos consecuencias:

  • Todo lo que realmente importa debe entenderse «de oído», mediante las réplicas de GPT.
  • El widget debe ser «vistoso» en el buen sentido: con una mirada rápida se ve al instante el estado y las opciones clave, sin leer texto pequeño.

Para nuestro GiftGenius esto es una pista inmediata: el escenario «voy en coche, elige un regalo para mi madre» no es solo un chat de texto. Es un diálogo multimodal en el que la voz lleva la iniciativa y la UI da respaldo.

2. En qué se diferencia un escenario de voz de uno de texto

Para no caer en la trampa de «es lo mismo, solo que el usuario habla», conviene comparar los modos de texto y voz en varios ejes.

Aspecto Modo de texto Modo de voz
Atención del usuario Mira la pantalla, lee, hace scroll Puede no mirar en absoluto (manos libres)
Forma de las peticiones Más estructurada, la persona edita Conversacional, fragmentos de frases, «ajá», «dale otra»
Tolerancia a pausas 1–2 segundos de silencio es normal El silencio prolongado se percibe peor
Papel de la UI Portador principal de detalles Auxiliar, «tablero» con apoyo visual breve
Errores de entrada Erratas, pero el texto es visible Habla ininteligible, ruido, «sí/no» falsos

De aquí se desprenden varias conclusiones importantes.

  • No puedes confiar en que el usuario «lo leerá en la tarjeta». Las cosas críticas hay que decirlas: qué has entendido, qué vas a hacer, qué resultado has obtenido.
  • La UI debe soportar el escenario «miré 1 segundo». Estado, progreso y elección principal: todo debe estar a la vista con tipografía grande. Los detalles son secundarios.
  • Hay que llenar las pausas. Mientras tu servidor MCP piensa sobre una petición pesada, el modelo debe decir en voz alta qué está pasando, y el widget mostrar el progreso, para evitar la sensación de un asistente bloqueado.

Puedes pensar en el modo de voz como en un audiolibro con ilustraciones: tienes un narrador de voz (GPT) y tienes imágenes (el widget). Deben sincronizarse para complementarse, no duplicarse ni contradecirse.

3. El papel del widget en modo de voz: de «panel de control» a «tablero»

En un escenario de texto, el widget a menudo actúa como una interfaz completa: formularios, tablas, carruseles con filtros, botones de acción. En modo de voz su papel cambia. Las recomendaciones sobre interfaces multimodales y VUI muestran que, en escenarios de voz, la UI se convierte más bien en un tablero informativo (glanceable UI): sirve para comprobaciones y confirmaciones rápidas, no para un trabajo visual intenso.

Para GiftGenius esto significa lo siguiente.

Cuando el usuario sigue un asistente por voz, en pantalla —en el widget inline o a pantalla completa— mostramos:

  • Estado grande: «Paso 2 de 3: presupuesto y tipo de regalo».
  • Mínimo texto, pero etiquetas claras: «Presupuesto hasta 50 $», «Se prefiere regalo digital».
  • Un par de botones CTA grandes, si el escenario de voz permite clics: «Cambiar presupuesto», «Continuar».
  • Una sola barra de progreso o stepper sencillos, no diez indicadores pequeños.

Ejemplo de «tablero» simple en un widget inline para un escenario de voz (TypeScript + React, muy simplificado):

type VoiceUiMode = "default" | "voiceGlance";

interface GiftStepProps {
  step: number;
  totalSteps: number;
  summary: string; // breve descripción de lo ya recopilado
  uiMode: VoiceUiMode;
}

export function GiftVoiceStep(props: GiftStepProps) {
  const fontSize = props.uiMode === "voiceGlance" ? "text-lg" : "text-sm";

  return (
    <div className="rounded-xl border p-3 flex flex-col gap-2">
      <div className={`${fontSize} font-semibold`}>
        Paso {props.step} de {props.totalSteps}
      </div>
      <div className={`${fontSize} text-muted-foreground`}>
        {props.summary}
      </div>
    </div>
  );
}

Aquí no hay nada «de voz» como tal, pero sí una idea clara: cuando uiMode === "voiceGlance" hacemos todo más grande y simple. La señal de que estamos en modo de voz puede venir de varios sitios: desde indicios indirectos hasta una marca explícita que el modelo establece en el widgetState o en la respuesta de la tool.

4. Sincronización de modalidades: lo que dice GPT y lo que muestra la App

El principio clave del voice‑UX para Apps es la sincronización de modalidades: la voz y la UI visual deben contar la misma historia, pero en distintos niveles de detalle.

Un error típico es hacer que el modelo lea en voz alta todo lo que muestra el widget: listas largas de regalos, estructuras JSON con filtros, etc. Esto se vuelve una tortura. La recomendación es: la voz da un breve resumen, y la UI muestra los detalles.

Ejemplo de sincronización correcta para GiftGenius.

Usuario: «Elige un regalo para mi madre, le gusta la jardinería, presupuesto hasta 50 dólares».

Modelo (voz): «He seleccionado varias opciones. La mejor, en mi opinión, es un set de herramientas de jardinería por 45 dólares. En pantalla he mostrado otras dos opciones similares. ¿Quieres que te dé más detalles o pasamos a elegir?»

Widget (inline): muestra tres tarjetas con regalos, una breve descripción y botones CTA «Elegir» / «Mostrar similares».

Representación dialogada en estilo JSON de un paso (no es un protocolo real, sino una ilustración del pensamiento):

{
  "user": "Elige un regalo para mi madre...",
  "assistant_text": "He encontrado varias opciones...",
  "widget": {
    "displayMode": "inline",
    "state": {
      "view": "gift_list",
      "items": [
        { "id": "g1", "title": "Set de herramientas de jardinería", "price": 45 },
        { "id": "g2", "title": "Delantal de jardinería", "price": 30 },
        { "id": "g3", "title": "Set de semillas de flores", "price": 20 }
      ]
    }
  }
}

Detalle importante: en el system prompt puedes indicar explícitamente cómo debe hablar el modelo sobre la UI para no «leer JSON»: «Si muestras una lista de opciones en el widget, no leas cada una completa. Describe brevemente la mejor opción y di que las demás están visibles en pantalla».

En el futuro, cuando trabajes con el Realtime API y tus propios clientes de voz, el principio seguirá siendo el mismo: la UI y el flujo de audio deben estar alineados. Simplemente allí tendrás control directo sobre el streaming.

5. Realtime y latencia: cómo evitar silencios incómodos

Técnicamente, los tool_calls en modo de voz son iguales que en texto: el modelo decide invocar tu herramienta, tú devuelves la respuesta y el widget se actualiza. Pero en voz aparece un nuevo problema de UX: la latencia. Mientras tu servidor MCP llama a APIs externas o calcula un informe complejo, el usuario oye… nada. Y se percibe mucho peor que esperar texto en un chat.

Hay dos niveles de defensa: el de voz y el visual.

  • En el nivel de voz, el system prompt debe permitir (y fomentar) que el modelo verbalice «estoy trabajando» y haga preguntas adicionales mientras la tool aún está procesando. Por ejemplo: «Ahora buscaré regalos, tardará unos segundos. Mientras tanto, cuéntame si hay más restricciones».
  • En el nivel visual, tu widget debe mostrar muy claramente el progreso: loader, estado «Buscando opciones…», paso actual. Sin esto, el usuario pensará que todo se colgó y volverá a hablar, rompiendo el flujo de voz.

En la práctica, esto se resuelve cómodamente con una tarea diferida: la herramienta devuelve inmediatamente el estado "pending" y un jobId, y la selección se realiza en segundo plano. El widget, al ver "pending", muestra progreso, y la voz indica que «está trabajando».

El esquema más simple de una herramienta del lado servidor que devuelve un «placeholder» con el job‑id, en lugar de bloquearse hasta el resultado completo, puede ser así:

// Pseudocódigo del instrumento server-side de GiftGenius
export async function startGiftSearch(params: SearchParams) {
  const jobId = await createBackgroundJob(params); // colocamos la tarea en la cola

  return {
    status: "pending",
    jobId,
    message: "Búsqueda de regalos iniciada"
  };
}

El widget, al ver status: "pending", puede cambiar la UI a modo de progreso:

if (toolOutput.status === "pending") {
  return (
    <div className="p-4 rounded-xl border flex items-center gap-3">
      <Spinner />
      <div className="text-base">
        Estoy buscando regalos… Esto tardará unos segundos.
      </div>
    </div>
  );
}

Y el modelo, en respuesta a ese mismo tool‑output y según las instrucciones, dirá en voz alta algo similar y quizá haga una pregunta aclaratoria adicional. Más tarde, cuando la tarea en segundo plano termine y, por ejemplo, llegue un job.completed vía una notificación MCP, el widget se actualizará con la lista de regalos y la voz locutará su resumen.

Así obtenemos un comportamiento lo más cercano posible al tiempo real, incluso si el backend no responde de inmediato.

6. Seguridad y confirmaciones por voz

La interfaz de voz es muy traicionera cuando se trata de acciones críticas: pago, eliminación de datos, cambios de configuración. El reconocimiento de voz no es perfecto, los usuarios hablan «en movimiento» y un «ajá» puede convertirse fácilmente en «sí, compra». Por eso, en escenarios de voz son especialmente importantes los confirmation flows.

Hay dos patrones básicos.

  • Confirmación de voz explícita (Explicit Voice Confirmation). Para acciones peligrosas exiges una frase concreta. Por ejemplo: «Para confirmar la compra, di: “Confirmo la compra”»; y en el system prompt prohíbes realizar el pago con «ajá», «ok», «dale» ambiguos.
  • Solo confirmación visual (Visual Confirmation Only). El modelo guía por voz al usuario hacia la acción («He preparado el pedido; en pantalla se muestran el importe final y el contenido del carrito»), pero el disparador real es pulsar el botón «Pagar» en el widget. Esto es especialmente relevante en escenarios de comercio; volveremos a ello en el módulo 14.

Para GiftGenius puede verse así.

Modelo: «He encontrado un excelente set para jardinería por 45 dólares. Puedo tramitar la compra a través de ChatGPT. En pantalla se muestran el precio final y la dirección de envío. Para confirmar por voz, di “Confirmo la compra”, o pulsa el botón “Pagar” en la pantalla».

Widget (pantalla completa): muestra el pedido final, resalta en negrita el importe y la dirección, y dos botones destacados: «Pagar» y «Cancelar».

Dentro del widget puedes reflejar el estado de confirmación:

type CheckoutState = "review" | "waiting_voice_confirm" | "confirmed";

if (state.phase === "waiting_voice_confirm") {
  return (
    <div className="space-y-3">
      <h2 className="text-xl font-semibold">Casi listo</h2>
      <p className="text-base">
        Confirma la compra por voz con la frase
        «Confirmo la compra» o pulsa el botón «Pagar».
      </p>
      <Button variant="primary">Pagar</Button>
      <Button variant="ghost">Cancelar</Button>
    </div>
  );
}

De este modo, si el modelo interpreta mal algo en la voz, al usuario le queda una capa visual de «seguridad».

7. Comandos de voz sencillos y diseño de herramientas

El usuario de voz no formulará comandos exactamente como las variables de tu herramienta. Dirá «elige el primero», «muestra más barato», «sin tecnología». Tu tarea es diseñar herramientas y el system prompt para que el modelo pueda mapear fácilmente esas frases a invocaciones de tus herramientas (callTool).

Para GiftGenius puedes prever, por ejemplo, estas acciones:

  • Elegir una de las opciones mostradas por índice o id.
  • Ajustar el presupuesto: «más barato», «hasta 30 dólares».
  • Filtrar por tipo: «solo regalos digitales», «nada que haya que enviar por correo».

Esto se expresa bien mediante una herramienta con un parámetro enum simple action y campos adicionales:

// Pseudoesquema de la herramienta en TypeScript
type VoiceActionInput =
  | { action: "select_item"; itemId: string }
  | { action: "refine_budget"; maxPrice: number }
  | { action: "filter_type"; type: "digital" | "physical" };

export function handleVoiceAction(input: VoiceActionInput) {
  switch (input.action) {
    case "select_item":
      // marcamos el regalo como seleccionado
      break;
    case "refine_budget":
      // recalculamos la selección para el nuevo presupuesto
      break;
    case "filter_type":
      // filtramos la lista existente
      break;
  }
}

En el system prompt describes cómo estas acciones se relacionan con comandos de voz: «Si el usuario dice “elige la primera opción”, invoca la herramienta gift.voiceAction con action="select_item" y el identificador del primer regalo en pantalla», etc.

Desde el punto de vista del UX, esto reduce la carga cognitiva: el usuario no necesita idear formulaciones precisas como «Ajusta los filtros para que solo haya regalos digitales hasta 30$». Habla de forma natural y el modelo lo traduce a una estructura de datos.

8. Escenario de voz para GiftGenius: tres pasos

Juntémoslo todo y diseñemos un flujo completo de voz para GiftGenius, sin entrar aún en el Realtime API de bajo nivel.

Imaginemos al usuario: va en coche y activa el modo de voz de ChatGPT. Dice: «Por favor, elige un regalo para mi madre; le gusta el jardín; presupuesto hasta 50 dólares».

Paso 1. Recopilación de información por voz

Modelo: «Genial, vamos a elegir un regalo. Aclaro un par de cosas: ¿para cuándo lo necesitas, en los próximos días o más adelante? ¿Y hay restricciones, por ejemplo, nada pesado o voluminoso?»

Widget (inline): de momento solo un panel pequeño con el estado «Eligiendo un regalo para: madre, jardinería, hasta 50 $». Tipografías un poco más grandes de lo habitual, para poder leerlo de un vistazo.

El estado del widget puede verse así:

interface GiftSessionState {
  mode: "voice" | "text";
  step: 1 | 2 | 3;
  recipientSummary: string;
  budget?: number;
}

const [state, setState] = useState<GiftSessionState>({
  mode: "voice",
  step: 1,
  recipientSummary: "Mamá, le gusta la jardinería"
});

La parte del servidor, a medida que llegan respuestas del usuario, actualiza recipientSummary y budget, y el widget reacciona.

Paso 2. Búsqueda y espera

Una vez que el modelo ha reunido suficiente información, invoca tu herramienta de búsqueda de regalos. Esta, a su vez, puede iniciar una tarea en segundo plano si la selección es compleja y devolver status: "pending". Mientras el trabajo en segundo plano se ejecuta, el modelo dice: «Ahora buscaré opciones adecuadas; tardará unos segundos. Mientras tanto, ¿prefiere regalos físicos o le valen certificados digitales?»

El widget cambia a un modo «similar a PiP» si el usuario navega a otra parte de la interfaz, o permanece inline con progreso: «Buscando regalos…» y un pequeño indicador.

Paso 3. Resultados y elección

Cuando los resultados están listos, el modelo: «He encontrado tres opciones. La primera: un set de herramientas de jardinería por 45 dólares. La segunda: un delantal de jardinería por 30 dólares. Las he mostrado en pantalla. Di “elige la primera” o “muestra más baratos”».

El widget muestra tres tarjetas grandes con precios y descripciones cortas. Cada tarjeta tiene un CTA «Elegir» y «Similares». Además, un botón aparte «Mostrar más opciones».

Si el usuario dice: «Elige la segunda», el modelo invoca tu herramienta voiceAction con action="select_item" e id del segundo regalo. El widget lo resalta como elegido y el modelo locuta: «Perfecto, hemos elegido el delantal de jardinería por 30 dólares».

Paso 4 opcional. Checkout

Si la App está integrada con pagos (en el futuro, módulo 14), comienza el checkout. El modelo verbaliza las condiciones y pide confirmación por voz o con un botón. El widget pasa a un asistente a pantalla completa con los pasos «Revisión del pedido» → «Dirección de envío» → «Confirmación».

Es importante que en cada paso lo clave se pronuncie por voz y el widget sirva de apoyo visual, especialmente si el usuario ya se ha detenido y mira la pantalla.

9. Notas prácticas de implementación y límites del Apps SDK

Todos los pasos descritos para GiftGenius se implementan dentro de una ChatGPT App normal, sin cliente de audio propio ni WebRTC. Aquí es importante no olvidar los límites del stack.

Es muy fácil «salirse» hacia temas de Realtime API, WebRTC, streaming de audio y montarte tu propia plataforma de voz. Para eso hay un módulo 20 aparte en el curso. En esta lección, recuerda los límites específicos de la ChatGPT App dentro del cliente de ChatGPT.

En la arquitectura actual:

  • El flujo de audio lo gestiona el cliente de ChatGPT. No envías ni recibes bytes de audio en el widget.
  • En el backend sigues viendo invocaciones de tools y mensajes de texto, pero el modelo puede estar en modo de voz y sus respuestas se locutarán.
  • La plataforma puede transmitir indicios indirectos de que ahora mismo es modo de voz (a través del user-agent o campos de entorno). Pero no construyas una dependencia rígida de ello: la API puede cambiar y tu App debe seguir siendo útil también en modo puramente de texto.

Por eso, una buena estrategia de implementación es esta. Primero diseñas un UX que funcione bien tanto para texto como para voz: estados breves, CTAs claras, etapas de progreso comprensibles. Luego añades algunas mejoras para la voz: tipografías ligeramente más grandes en el modo "voiceGlance", progreso más evidente, énfasis en estados como «Paso 2 de 3» y estados obvios como «Esperando confirmación».

Además, en el system prompt describes el comportamiento de voz del modelo: cómo comenta el estado del widget, qué frases usa para confirmaciones y qué evita (por ejemplo, no leer JSON ni detallar cada elemento de una lista).

Si más adelante haces tu propio Custom Voice Client sobre Realtime API, todas estas decisiones de UX «migrarán» sin problemas. La diferencia será el nivel de acceso a eventos y al streaming, no los principios.

10. Errores típicos al trabajar con el contexto Voice / Realtime

Error n.º 1: «Leer el UI en voz alta» en lugar de un resumen.
A veces los desarrolladores diseñan las herramientas de modo que el modelo empiece a leer en voz alta todo el contenido de la respuesta JSON o la lista completa de tarjetas. En modo de voz esto destruye el UX: el usuario pierde el hilo y tú gastas tokens. Es mejor que la voz diga un resumen corto y se centre en una o dos opciones, dejando lo demás en la pantalla.

Error n.º 2: Ausencia total de feedback visual en voz.
Puede parecer tentador pensar: «Si el usuario habla, entonces escucha; la UI no hace falta». En la práctica, el usuario a menudo echa un vistazo a la pantalla o vuelve a ella al cabo de un minuto. Si en ese momento no hay estado, progreso ni un resultado claro, creerá que la App se colgó o no hizo nada. Muestra siempre «Estoy pensando», «Paso 2 de 3», «Resultados listos», etc.

Error n.º 3: Acciones peligrosas sin confirmación fuerte.
En modo de texto ya es arriesgado hacer «Pagar» con un solo botón; en voz es aún más arriesgado ejecutar una compra con un «ajá» ambiguo. Ignorar los confirmation flows explícitos (de voz y/o visuales) lleva a compras incorrectas y a problemas de confianza en la App. Piensa qué acciones requieren doble confirmación y descríbelo claramente en el system prompt y en la UI.

Error n.º 4: Diseñar solo para la vista, no para el oído.
A veces se diseña la App como si el usuario siempre leyera: formulaciones demasiado complejas, botones larguísimos, descripciones sobrecargadas. En modo de voz, además, hay que pronunciar todo eso: se convierte en una «sopa de palabras». Intenta que el sentido clave quepa en frases cortas y sencillas, fáciles de entender al escucharlas.

Error n.º 5: Confundir el Apps SDK con un cliente de voz propio.
Algunos alumnos buscan en el Apps SDK eventos de micrófono, streaming de audio, WebRTC, etc., como en el Realtime API, y se decepcionan al no encontrarlos. Es importante entender: la ChatGPT App vive dentro del cliente de ChatGPT, y la voz la gestiona la plataforma. Tú trabajas con texto, invocaciones de tools y estado del widget, y diseñas el UX para que el modo de voz «simplemente funcione bien». Si necesitas control total sobre la voz, ese ya es otro proyecto más complejo con Realtime API.

Error n.º 6: No tener una estrategia para las demoras.
Si no piensas qué dice el modelo y qué muestra el widget durante operaciones largas, el usuario interrumpirá, hará nuevas preguntas y romperá tu flujo. Las demoras en voz se sienten más que en texto. Usa estados intermedios, procesamiento en segundo plano y frases de voz del tipo «estoy en ello, cuéntame mientras…» para que el silencio no se convierta en un bug.

1
Cuestionario/control
UX e interfaces, nivel 8, lección 4
No disponible
UX e interfaces
UX e interfaces (Inline, Fullscreen, Voice)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION