En Alpic, creemos que la próxima generación de productos y servicios se construirá en torno a experiencias centradas en la IA: interfaces en las que los usuarios colaboran con modelos en lugar de seguir flujos de trabajo tradicionales y predefinidos en la interfaz de usuario.
Cuando OpenAI lanzó el Apps SDK, empezamos a desarrollar con él de inmediato. A lo largo de tres meses, desarrollamos dos docenas de Apps de ChatGPT, tanto para uso interno como para nuestros clientes en ámbitos B2B y B2C, como viajes, comercio minorista y SaaS.
Desde el principio descubrimos que crear Apps de ChatGPT es fundamentalmente distinto de crear aplicaciones web o móviles tradicionales. Los patrones que funcionan bien en la web (obtener datos justo cuando se necesitan, gestionar el estado desde la interfaz de usuario, recurrir a la configuración explícita por parte del usuario, etc.) suelen dejar de funcionar o incluso perjudicar la experiencia en un entorno con agentes.
Este artículo resume las 15 lecciones más importantes que aprendimos al crear Apps de ChatGPT para casos reales. Después, contamos cómo las incorporamos en Skybridge, un framework de código abierto para la comunidad, y en una habilidad de Codex para ayudar a los desarrolladores a idear, crear, probar y lanzar Apps mucho más rápido.
El problema de los tres cuerpos
Con las aplicaciones web tradicionales, las cosas eran sencillas: solo había un usuario y una interfaz de usuario. En una App de ChatGPT, entra un tercer cuerpo en el sistema: el modelo.
Una de las partes más difíciles de desarrollar para ChatGPT es gestionar cómo fluye la información entre estos tres participantes. Si un usuario hace clic en un botón “Seleccionar” de tu widget, la interfaz se actualiza visualmente, pero el modelo, el cerebro de la conversación, no se entera a menos que le proporciones ese contexto de forma explícita. Si el usuario luego pide “Dame más detalles sobre este producto” , el modelo no tiene idea de qué está viendo realmente el usuario.
A esto lo llamamos asimetría de contexto : cada cuerpo conoce solo una parte del sistema y ninguno tiene el panorama completo. Crear buenas Apps de ChatGPT no consiste en mantener todo sincronizado, sino en decidir qué información se debe compartir, cuándo compartirla y quién necesita conocerla. Resolver esto marca la diferencia entre una App poco ágil y una experiencia fluida con agentes.
1. No se debe compartir todo el contexto
Nuestro primer impulso fue “simplemente compartir todo en todas partes”. Ese resultó ser uno de nuestros primeros errores.
En la práctica, las distintas partes de una App de ChatGPT suelen necesitar vistas deliberadamente distintas del mismo estado. ¿Por qué?
- Por rendimiento: los widgets de la interfaz suelen requerir muchos más datos de los que el modelo debería necesitar. Por ejemplo, en una App de reservas de viajes, podrían ser imágenes, variantes de precios y opciones precargadas. Enviar todo esto al modelo aumentaría el consumo de tokens, la latencia y el ruido cognitivo.
- Por lógica: cierta información debe mantenerse asimétrica por diseño. En una de nuestras primeras Apps, un juego de misterio llamado Murder in the Valleys , el modelo necesita saber quién es el asesino para interpretar su papel correctamente, mientras que la interfaz y el usuario no deben saberlo. En un juego al estilo de Time’s Up, sucede lo contrario: la interfaz muestra la palabra secreta al usuario, mientras que el modelo debe desconocerla.
La lección no fue “sincronizar siempre todo”, sino decidir explícitamente quién necesita saber qué. Formalizamos esto mediante distintos campos de salida de la herramienta :
| Campo | Propósito | Visible para |
|---|---|---|
| structuredContent | Datos tipados para el widget y el modelo | Tanto el widget como el modelo (mediante las funciones toolOutput y callTool) |
| _meta | Metadatos de la respuesta | Solo el widget; ocultos para el modelo |
Por ejemplo, en el juego Time’s Up, pasábamos la palabra secreta únicamente al widget mediante el campo _meta y dejábamos que el modelo la adivinara a partir de las pistas del usuario.
2. La carga diferida no se adapta bien a las Apps de IA
Como veníamos del desarrollo web, recurrimos por costumbre a la carga diferida: obtener datos cuando el usuario hace clic, cargar detalles bajo demanda y optimizar para reducir al mínimo los datos enviados al inicio.
En ChatGPT, el paradigma se invierte: las llamadas a herramientas implican demoras y suelen tardar varios segundos debido al entorno aislado de seguridad y al razonamiento del modelo.
En la práctica, aprendimos a cargar de antemano todo lo posible: enviar la mayor cantidad de datos posible en la respuesta inicial de la herramienta e hidratar el widget mediante window.openai.toolOutput. Esto casi siempre se tradujo en una experiencia más rápida y con mejor respuesta.
Por supuesto, si el widget puede obtener datos de forma segura desde un punto de acceso de una API pública y no necesita compartir información con el modelo, siempre es posible usar llamadas XHR tradicionales dentro del widget. Sin embargo, la mayoría de las veces querrás que el modelo pueda llamar a herramientas de forma autónoma para mantener una experiencia conversacional.
3. El modelo necesita saber qué ocurre
Surge un problema sutil, pero crucial, cuando el usuario interactúa con un widget (por ejemplo, selecciona un producto específico en una lista) y luego hace una pregunta en el chat. Si el modelo no sabe a qué parte de la interfaz se refiere el usuario, no podrá responder correctamente.
Para esto usamos window.openai.setWidgetState(state), que permite almacenar datos específicos del estado que se agregan al contexto del modelo en la siguiente interacción entre el usuario y el modelo.
A medida que las Apps se volvían más complejas, vimos que agregábamos setWidgetState en muchos lugares para que el modelo pudiera seguir la navegación. Así que decidimos introducir una forma declarativa de describir el contexto de la interfaz. En lugar de actualizar el modelo de forma imperativa en cada interacción, agregamos un atributo data-llm directamente a los componentes:
<div
data-llm={
selectedTab === "details"
? "User is viewing product details"
: "User is viewing reviews"
}
>
Para que esto funcionara de forma interna, creamos un complemento de Vite que extrae estos atributos y actualiza automáticamente widgetState. Desde la perspectiva del modelo, simplemente recibe el contexto pertinente de la interfaz en el momento adecuado, sin que los desarrolladores tengan que sincronizar manualmente cada interacción.
Puedes encontrar este complemento de Vite (y muchos otros consejos que compartimos en este artículo) en el framework de código abierto que creamos para compartir lo que aprendimos con la comunidad.
4. Distintas interacciones requieren distintas API
Las Apps de ChatGPT tienen múltiples vías de interacción entre el widget, el servidor y el modelo. Estas vías no son intercambiables: cada una existe para permitir un tipo de interacción distinto.
Una de las lecciones clave al crear Apps de ChatGPT es definir explícitamente estas vías de comunicación y decidir de forma deliberada qué mecanismo se encarga de cada parte de la experiencia.
Un diagrama de esa vía se vería más o menos así:

Estas lecciones establecen los fundamentos de una App de ChatGPT: cómo se comparte el contexto, cómo el modelo se entera de lo que ocurre y cómo se propagan las distintas interacciones por el sistema. La siguiente sección parte de esta base y se centra en las implicaciones para el diseño de la interfaz de usuario.
Reinventar la interfaz de usuario para la IA
Las Apps de ChatGPT son un entorno completamente nuevo, así que pronto aprendimos a dejar de lado nuestras ideas preconcebidas sobre las interfaces de usuario y a aprovechar al máximo las nuevas capacidades. Esta sección aborda los conceptos de diseño de interfaces que tuvimos que aprender (y desaprender) para crear Apps eficaces.
5. La interfaz debe adaptarse a múltiples modos de visualización y sus limitaciones
Las Apps de ChatGPT no se limitan a una sola disposición visual. Según cómo y cuándo se invoquen, el mismo widget puede mostrarse en tres modos de visualización distintos.
Las Apps pueden aparecer integradas en la conversación, en modo imagen en imagen (PiP) por encima de ella o en pantalla completa cuando se necesita más espacio. Aunque PiP y la pantalla completa permiten interfaces más completas, también introducen elementos superpuestos de la interfaz que el widget no controla. Tener en cuenta las zonas seguras específicas de cada dispositivo, como el botón de cierre persistente en dispositivos móviles, es esencial para evitar que el contenido quede recortado y optimizar las interacciones.
Con el tiempo, identificamos patrones sobre los modos de visualización y cuándo usarlos:
| Cómo se ve | Cuándo usarlo | |
|---|---|---|
| Integrado | Modo de visualización predeterminado. El widget permanece en el historial de la conversación. | para interacciones rápidas |
| Pantalla completa | El widget ocupa toda la pantalla, con la barra de chat en la parte inferior. | si tu widget es complejo y necesita mucho espacio (por ejemplo, mapas) |
| Imagen en imagen | Tiene el mismo tamaño que en el modo integrado, pero el widget permanece por encima de la conversación | si tu widget sigue siendo relevante al continuar la conversación después de la generación |
6. La coherencia de la interfaz importa en un entorno integrado
Al principio, una de nuestras dudas era cuánta libertad visual debía tomarse una App de ChatGPT. Al ser una interfaz nueva para los usuarios, tenía que resultar familiar y coherente, tanto entre nuestras propias Apps como con el ecosistema de ChatGPT que la rodeaba. A diferencia de un producto independiente, un widget vive dentro de una interfaz existente, donde las inconsistencias visuales se notan de inmediato.
Por suerte, el OpenAI Apps SDK UI Kit nos dio un punto de partida claro.
Basado en Tailwind CSS, ofrece componentes listos para usar, íconos y tokens de diseño que se ajustan al sistema de diseño de ChatGPT. Usarlo nos permitió avanzar rápido y asegurarnos de que nuestros widgets se sintieran nativos y fueran visualmente coherentes con el resto de la interfaz, incluso al crear componentes personalizados (por ejemplo, para nuestra integración con Mapbox).
7. Filtrar a partir del lenguaje natural
Los paneles tradicionales se basan en barras laterales llenas de casillas de verificación y controles deslizantes de rango. En las interfaces con agentes, esto suele ser un retroceso. Cuando los usuarios pueden expresar lo que quieren directamente en lenguaje natural, por ejemplo, “Destinos soleados en Europa por menos de $200”, obligarlos a usar múltiples controles de la interfaz añade fricción. Debería bastar con que lo dijeran.
Por eso decidimos optar por un enfoque “sin filtros” para la mayoría de nuestras apps. En lugar de una barra lateral con opciones para filtrar y ordenar, le proporcionamos al modelo una lista de valores (LOV) para los parámetros de nuestras herramientas.
Esto permite que el modelo tome directamente el mensaje del usuario como entrada y evita que “adivine” qué opciones están disponibles. En otras palabras, le permite convertir el lenguaje natural directamente en los valores que requiere la API de nuestro backend. Si un usuario dice “soleado”, el modelo sabe que debe llamar a la herramienta con weather="sunny".
8. Los archivos pueden permitir interacciones más completas
Una lección que surgió al crear apps más complejas es que los archivos no deberían tratarse como entradas secundarias. En las Apps de ChatGPT, los archivos pueden permitir nuevas interacciones. En lugar de comenzar con formularios o filtros, las experiencias pueden partir de algo que el usuario ya tiene.
Por ejemplo, en una app de comercio electrónico, un usuario puede subir la foto de un producto al chat, hacer que el modelo lo identifique y luego continuar buscando productos coincidentes o descubriendo otros directamente en el widget.
Esto es posible al permitir que los archivos circulen por ambos lados del sistema. Del lado del modelo, las herramientas pueden consumir directamente los archivos subidos al chat mediante openai/fileParams, lo que permite al modelo razonar sobre imágenes u otros recursos proporcionados por el usuario. Del lado de la interfaz, los widgets también pueden trabajar directamente con archivos mediante window.openai.uploadFile y window.openai.getFileDownloadUrl, lo que permite solicitar que se suban archivos como parte del flujo de la interfaz o generar archivos que los usuarios puedan descargar y reutilizar.
Pasar a producción
Después, cuando las apps pasan del desarrollo local a otras etapas, entran en juego otras consideraciones sobre seguridad, configuración y herramientas. De eso trata este tercer grupo de lecciones.
9. Las CSP son el nuevo CORS
Por motivos de seguridad, OpenAI renderiza las Apps dentro de un iframe con doble anidamiento. Las políticas de seguridad de contenido (CSP) son un mecanismo nativo de aislamiento de iframes, y esta configuración las aplica estrictamente, lo que suele manifestarse como el clásico síndrome de “funciona en local, pero falla en producción”.
A diferencia del desarrollo web tradicional, donde una política poco restrictiva podría bastar, el Apps SDK exige mucha precisión.
En el archivo de manifiesto de la app, esto significa declarar con cuidado qué dominios están permitidos para cada tipo de interacción:
| Campo | Propósito | Ejemplo | Errores comunes |
|---|---|---|---|
| connectDomains | Solicitudes de API y XHR | https://api.weather.com | Olvidar que la API de preproducción es distinta de la de producción. |
| resourceDomains | Imágenes, fuentes, scripts | https://cdn.jsdelivr.net | Usar una CDN genérica como delivr.net sin incluirla en la lista de dominios permitidos |
| frameDomains | Inserción de iframes | https://www.youtube.com | Insertar un video de YouTube o una instancia de Mapbox sin incluir su dominio en la lista de permitidos. |
| redirectDomains | Enlaces externos que se abren sin advertencias | https://app.alpic.ai | Olvidar el dominio de pago o el de la URL de retorno de OAuth. |
Dar prioridad a la configuración de CSP desde el principio nos ahorró mucho trabajo de depuración en producción más adelante.
10. Los pequeños indicadores de los widgets tienen un gran impacto
Además de las CSP, un pequeño conjunto de ajustes del widget determina cómo se reparte el control entre el widget, el modelo y el entorno del host. Es fácil pasar por alto estos indicadores, pero definen límites críticos para la navegación, el acceso a herramientas y la publicación.
Límites del host y de la navegación
widgetDomaines obligatorio para el envío a revisión. Define la ubicación predeterminada a la que apunta el botón “Abrir en <App>” en modo de pantalla completa y se usa para determinar los orígenes permitidos, ya que los widgets se renderizan bajo<widgetDomain>.web-sandbox.oaiusercontent.com. UsamossetOpenInAppUrlpara dirigir a los usuarios a la ruta adecuada según el contexto.
Límites del modelo y de las herramientas
- Las anotaciones de las herramientas deben cumplir las pautas de publicación. Los indicadores como
readOnly,destructiveHintyopenWorldHintson obligatorios y se validan durante el envío a revisión. - La visibilidad de las herramientas importa: las herramientas que el modelo no deba poder llamar deben marcarse explícitamente como privadas.
Límites de ejecución del widget
widgetAccessiblecontrola si el widget puede llamar a herramientas por su cuenta mediantecallTool.
Por separado, estos ajustes son pequeños, pero en conjunto determinan si una app se comporta correctamente una vez publicada.
Optimizar para iterar rápido
El Apps SDK evoluciona rápidamente, y nos ha entusiasmado crear apps a medida que avanza. Para facilitar un flujo de desarrollo ágil y eficiente, decidimos desarrollar nuestro propio framework de código abierto y compartirlo con la comunidad. Estas son algunas de las lecciones que pueden ayudar a evitar los problemas de experiencia de desarrollo que encontramos al principio.
11. Iterar rápido requiere recarga en caliente
Una de las primeras cosas que abordamos fue la velocidad de iteración. La combinación del almacenamiento de recursos en caché con un TTL largo y el uso de JSON-RPC para reenviar esos recursos hace que la recarga de módulos en caliente estándar (como la de Vite o Next.js) no sea compatible con las Apps de ChatGPT sin modificaciones.
Después de dedicar bastante tiempo a comprender el funcionamiento interno de Vite, creamos un complemento de Vite que permite recargar widgets en vivo directamente dentro de ChatGPT. El complemento intercepta las solicitudes de recursos al servidor MCP e inyecta actualizaciones en tiempo real en el iframe de ChatGPT. Ver un cambio del IDE reflejado de inmediato dentro de ChatGPT acortó enormemente nuestro ciclo de retroalimentación.

12. No todas las pruebas deben hacerse en ChatGPT
Probar en ChatGPT es la referencia ideal, pero en las primeras iteraciones un emulador local puede ayudarte a avanzar más rápido, especialmente cuando trabajas con definiciones de herramientas que requieren recargar la app en el modo de desarrollador.
Para acelerar las primeras iteraciones, creamos un emulador local ligero que simula el entorno del host de ChatGPT, con herramientas de depuración y registros específicos de las apps. Esto nos permitió iterar sobre el estado de React y la disposición de la interfaz en milisegundos, y reservar las pruebas en el entorno real de ChatGPT para validar las interacciones con el modelo y los casos límite.
13. Las pruebas en dispositivos móviles requieren soporte explícito
Las pruebas en dispositivos móviles plantearon otro desafío: aunque es necesario exponer el servidor local a través de un túnel para probar en ChatGPT, el uso predeterminado de localhost en Vite hace que esa misma URL sea inaccesible desde otros dispositivos.
Resolvimos esto ampliando nuestro complemento de Vite para admitir el reenvío de dominios en puertos expuestos mediante túneles. Así pudimos realizar pruebas tanto en dispositivos iOS como Android e incorporar la validación en dispositivos móviles a nuestro flujo de trabajo habitual.
14. Las abstracciones conocidas (como los hooks de React) agilizan el trabajo de frontend
El Apps SDK ofrece capacidades potentes, pero principalmente a través de API de JavaScript de bajo nivel. Como usuarios de React desde hace mucho tiempo, queríamos acercarnos a conceptos que ya dominábamos.
Por eso incorporamos algunas abstracciones adaptadas a React: hooks como useCallTool, useWidgetState y useLocale, además de opciones de gestión de estado más avanzadas, como createStore, basada en Zustand, para flujos de datos complejos. Recuperar patrones conocidos de frontend redujo el código repetitivo e hizo que el desarrollo de widgets se pareciera más a los flujos de trabajo web modernos.
Convertir las lecciones en una habilidad de Codex
15. Convierte las lecciones en herramientas reutilizables
A medida que estos patrones aparecían en distintas apps, quedó claro que redescubrirlos una y otra vez nos estaba frenando. Para que el desarrollo de Apps de ChatGPT fuera más rápido y predecible, decidimos incorporar estas lecciones directamente en nuestras herramientas, no solo para nosotros, sino también para la comunidad.
Esto dio lugar a dos iniciativas complementarias:
- El Skybridge Framework: un framework de React de código abierto que reúne muchos de los patrones descritos en este artículo en componentes reutilizables, incluidos nuestros hooks (
useCallTool,useToolInfo), las herramientas de desarrollo (HMR y el emulador local) y el atributo data-llm. - La habilidad de Codex chatgpt-apps-builder: sobre la base del framework, creamos una habilidad de Codex específica para apoyar todo el ciclo de vida de la aplicación:
- Generación de ideas: explorar cómo hacer que una aplicación esté “basada en agentes” en lugar de ser una simple adaptación de una aplicación web.
- Generación de código: escribir simultáneamente el frontend de React y el backend del servidor MCP, preconfigurados con todos los patrones adecuados de UX y UI.
- Pruebas locales: iniciar servidores de desarrollo y conectar aplicaciones locales a ChatGPT para iterar en tiempo real mediante la recarga en caliente.
- Aseguramiento de la calidad y publicación: ejecutar verificaciones estructuradas según los lineamientos de OpenAI para el envío de aplicaciones, incluidas la validación de CSP, las consideraciones sobre zonas seguras y las pruebas en producción.
- Despliegue de la aplicación: ayudar con los pasos finales necesarios para lanzar una aplicación e iterar sobre ella.
Para instalar y usar la habilidad, simplemente usa el siguiente comando:
npx skills add alpic-ai/skybridge
Conclusión
Crear ChatGPT Apps requiere replantearse cómo fluye el contexto, cómo se comportan las interfaces y cómo colaboran los usuarios y los modelos. Muchas de las lecciones de este artículo surgieron de las diferencias entre los patrones web conocidos y la realidad de los sistemas basados en agentes.
Al compartir estas lecciones e incorporarlas en nuestro framework de código abierto y nuestra habilidad de Codex, esperamos ayudar a los equipos a dedicar menos tiempo a redescubrir los mismos problemas y más a explorar las posibilidades de este nuevo modelo de interacción. Las ChatGPT Apps más atractivas no serán simples adaptaciones de productos existentes, sino experiencias diseñadas deliberadamente en torno a esta nueva forma de interactuar centrada en la IA.