ChatGPT lanzó por fin la Generative UI y, de la noche a la mañana, le cambió el nombre a Intelligent UI. Teníamos que ver cómo la implementaron.
https://x.com/OpenAI/status/2107894997538525580
Este artículo es un análisis en profundidad de cómo OpenAI implementó su Intelligent UI estrella: las capas, los formatos y el renderizado nativo tanto en Web como en móvil.
Bloques de construcción
La implementación de ChatGPT divide el trabajo entre el modelo, el servidor backend y el cliente:
- Formato de inferencia: el modelo escribe la interfaz en DIL, que combina Markdown con etiquetas tipo JSX y JavaScript.
- Compilación en el servidor: el servidor convierte cada respuesta parcial en un programa JavaScript y un documento JSON con texto y datos.
- Runtime del cliente: un entorno aislado (sandbox) ejecuta el programa y genera operaciones de UI.
- Renderizado: ChatGPT aplica esas operaciones a sus propios componentes nativos.
- Sistema de diseño y catálogo: los componentes, propiedades y tokens de diseño disponibles para el modelo.

Diagrama de arquitectura de la Intelligent UI de ChatGPT
Formato de inferencia
Esto es lo que escribe el modelo. En ChatGPT se trata de un lenguaje que OpenAI llama DIL: Markdown para el texto, etiquetas tipo JSX para los componentes y JavaScript para el estado y la lógica. Vamos a seguir una respuesta pequeña a través de todas las capas:
1## Estimación del plan de equipo2Arrastra el control deslizante para ver el **precio mensual** de tu equipo.3{@body const [seats,setSeats] = DIL.useState(8)}4{@body const price = seats*29}5<box border padding={3} gap={2}>6 <slider min={1} max={50} value={seats} onChange={setSeats}/>7 <title size="xl">${price}/mes</title>8</box>
El encabezado y el párrafo son Markdown normal. Las etiquetas son componentes del catálogo de ChatGPT. Las dos líneas {@body …} son JavaScript: la primera declara un estado, seats, y la segunda calcula price a partir de él. El slider está vinculado a seats, así que al moverlo se actualiza el precio.
Hace falta un formato específico porque el modelo construye la interfaz token por token.
- Tiene que ser fácil de escribir de forma fiable, por eso se basa en notaciones que el modelo ya domina.
- Tiene que seguir siendo usable aunque esté a medio escribir. Cada instrucción va en su propia línea y cualquier elemento abierto puede cerrarse automáticamente. Así, el servidor puede cortar una respuesta parcial en su última estructura completa y compilarla igualmente.
Compilación en el servidor
El cliente nunca ejecuta la salida del modelo tal cual la escribió. El servidor de OpenAI la compila en un programa JavaScript y un documento JSON, que se almacenan junto con el mensaje (como model_dil_v2). La respuesta se compila en esto (formateado para facilitar la lectura):
1function __dilSafe(evaluate, failureValue) {2 try { return evaluate(); } catch { return failureValue; }3}45DIL.render(__dil.jsx(() => {6 const __dilConstants = DIL.useConstants();7 const __dilModelDataBindings = DIL.useAppData((appData) => appData.opGenui?.modelDataBindings ?? {});8 const [seats, setSeats] = DIL.useState(8, { key: "seats" });9 const price = __dilSafe(() => seats * 29, undefined);10 return __dil.jsx(__dil.Fragment, null,11 __dil.jsx("title", { size: "lg" }, __dilConstants["0"]),12 __dil.jsx("text", null, __dilConstants["1"], __dil.jsx("bold", null, __dilConstants["2"]), __dilConstants["3"]),13 __dil.jsx("box", { border: true, padding: 3, gap: 2 },14 __dilSafe(() => __dil.jsx("slider", { min: 1, max: 50, value: seats, onChange: setSeats }), null),15 __dil.jsx("title", { size: "xl" }, __dilConstants["4"], __dilSafe(() => price, null), __dilConstants["5"])));16}, { key: "body:2" }));
1{2 "constants": {3 "0": "Estimación del plan de equipo",4 "1": "Arrastra el control deslizante para ver el ",5 "2": "precio mensual",6 "3": " de tu equipo.",7 "4": "$",8 "5": "/mes"9 },10 "appData": { "opGenui": { "componentResults": {}, "modelDataBindings": {} } }11}
El Markdown se compila en el mismo árbol que los componentes. El encabezado se convierte en un title, el párrafo en un text con un bold dentro, y sus palabras pasan a la tabla de constantes.
La compilación hace un trabajo que, de otro modo, cada cliente tendría que repetir:
- Llamadas a funciones simples. El marcado se transforma en llamadas a __dil.jsx, de modo que un runtime de JavaScript pueda evaluar el programa sin necesidad de un parser para DIL.
- Aislamiento de errores. Las expresiones se envuelven en __dilSafe, para que si una lanza un error solo elimine un elemento en lugar de abortar todo el renderizado.
- Texto en una tabla aparte. El texto estático pasa a la tabla de constantes, de forma que, mientras la respuesta llega en streaming, el texto que crece modifica los datos y no el programa.
- Claves de estado estables. Cada fragmento de estado recibe una clave ({ key: "seats" }), para que su valor sobreviva a cada recompilación.
- Reparación y validación. Se descartan las instrucciones y etiquetas incompletas, se cierran los elementos abiertos y las propiedades que no superan la validación contra el catálogo se eliminan y se registran como diagnósticos.
El documento JSON contiene las constantes de texto y cualquier dato que el servidor resuelva para la respuesta, como resultados de búsqueda de imágenes (ver Datos).
Runtime del cliente
El cliente recibe el programa compilado y el documento JSON. Su trabajo se divide entre un runtime, que ejecuta el programa, y un renderizador, que dibuja el resultado.
Como el programa lo ha escrito el modelo, no se ejecuta directamente en la página de ChatGPT. ChatGPT carga un iframe oculto (runner.html), aislado con allow-scripts y una política de seguridad de contenido default-src 'none', que inicia un Web Worker.
- Lockdown. Antes de evaluar un programa, el worker elimina de su ámbito global el acceso a la red, los temporizadores, la mensajería y la evaluación dinámica de código, y congela los globales restantes.
- Evaluación. Después evalúa el programa con new Function. Los objetos del runtime (DIL, __dil, GenUI) y los componentes compuestos del catálogo se pasan como parámetros.
- Watchdog. Si un programa no responde dentro de un tiempo límite, se pone en cuarentena y se reinicia el worker.
El runtime es un pequeño reconciliador al estilo React. Renderiza el componente y mantiene el estado de los hooks en ranuras con clave. Luego compara el árbol resultante con el anterior y codifica las diferencias como una lista de operaciones. No dibuja nada.
El siguiente ejemplo ilustrativo muestra las operaciones de un primer renderizado, con una línea por nodo. Se omiten las entradas que enumeran los nombres de las propiedades de cada elemento:
1CREATE #1 title SET size = "lg" PLACE under root at 02CREATE #2 text "Estimación del plan de equipo" PLACE under #1 at 03CREATE #3 text PLACE under root at 14CREATE #4 text "Arrastra el control deslizante para ver el " PLACE under #3 at 05CREATE #5 bold PLACE under #3 at 16CREATE #6 text "precio mensual" PLACE under #5 at 07CREATE #7 text " de tu equipo." PLACE under #3 at 28CREATE #8 box SET border = true, padding = 3, gap = 2 PLACE under root at 29CREATE #9 slider SET min = 1, max = 50, value = 8, onChange = fn#1 PLACE under #8 at 010CREATE #10 title SET size = "xl" PLACE under #8 at 111CREATE #11 text "$" PLACE under #10 at 012CREATE #12 text "232" PLACE under #10 at 113CREATE #13 text "/mes" PLACE under #10 at 2
Las funciones nunca salen del worker; el manejador del slider se envía únicamente como un identificador (fn#1). Por la red, las operaciones se codifican como una secuencia binaria de enteros, con las cadenas guardadas en una tabla separada.
Renderizado
La página de ChatGPT aplica las operaciones a su propio árbol de componentes. Cada CREATE instancia un componente nativo del sistema de diseño de ChatGPT, y la página anima los cambios a medida que llegan. La página solo acepta operaciones para tipos de componentes conocidos, por lo que la salida del modelo no puede introducir marcado o estilos arbitrarios. Las excepciones son los valores CSS puros que aceptan algunas propiedades (ver Sistema de diseño y catálogo) y las apps AppBlock, que se ejecutan en un iframe (ver Válvula de escape).
La interacción funciona en sentido contrario. Cuando el usuario arrastra el slider hasta 9, la página envía al worker el identificador del manejador y los argumentos. El worker llama a setSeats(9), vuelve a renderizar y devuelve las operaciones de actualización. No hay ninguna llamada al modelo de por medio.
Sistema de diseño y catálogo
El catálogo define qué puede solicitar el modelo. Es necesario porque el modelo no construye una interfaz a partir de reglas crudas de maquetación y estilo, sino que elige entre componentes que ChatGPT ya sabe dibujar y les aplica estilo mediante tokens de diseño como padding={3}. Algunas propiedades aceptan valores CSS puros, como anchos en píxeles o colores hexadecimales, pero se prefieren los tokens de diseño. Como resultado:
- Las interfaces generadas tienen el mismo aspecto que el resto de ChatGPT en todas las plataformas.
- El compilador cuenta con un esquema contra el que validar la salida. Una propiedad que no existe en un componente, o un literal de tipo incorrecto, se elimina durante la compilación y se registra como diagnóstico.
En una respuesta que capturamos, el compilador eliminó dos propiedades: fill en un icono (un diagnóstico unknown_prop) y gap="1" en un box (un diagnóstico invalid_literal).
El catálogo tiene tres partes:
- Componentes nativos. Unos 70 componentes están definidos en el registro de componentes del código cliente de ChatGPT; 39 de ellos aparecen en las respuestas que capturamos.
- Tokens de diseño para espaciado, radio, color y tamaño.
- Componentes compuestos escritos por OpenAI en DIL y enviados precompilados al sandbox, como los componentes de imagen y de producto. En nuestras capturas, el modelo usaba estos componentes, pero nunca definió los suyos propios.
Streaming
Hacer streaming de texto es sencillo: cada nuevo token se añade a lo que ya está en pantalla. Hacer streaming de una interfaz es más difícil, por tres motivos:
- Normalmente la salida aún no se puede ejecutar. En casi cualquier momento es un programa incompleto, con una etiqueta o expresión todavía abierta, y no se puede ejecutar tal cual.
- La interfaz tiene que seguir funcionando mientras crece. Los componentes con los que el usuario ya ha interactuado deben conservar su estado.
- Parte del contenido llega por separado. Datos como las imágenes vienen del servidor, no del texto.
Streaming en el servidor
Un simple flujo de tokens añadidos no sirve para expresar esto. En su lugar, ChatGPT transmite parches a un mensaje estructurado que mantiene juntos el texto original, el programa compilado y sus datos.
La respuesta llega al navegador a través de un flujo de eventos enviados por el servidor (POST /backend-api/f/conversation). Cada evento es una actualización al estilo JSON-Patch del mensaje que se está construyendo. Normalmente, un único evento actualiza a la vez el texto DIL original y su forma compilada. Esta es una actualización de una respuesta capturada, acortada:
1{"o": "patch", "v": [2 {"p": "/message/content/parts/0", "o": "append", "v": " Asado de cordero del domingo con amigos — comida abundante, …"},3 {"p": "/message/metadata/model_dil_v2/code", "o": "replace", "v": "DIL.render(__dil.jsx(()=>{…"},4 {"p": "/message/metadata/model_dil_v2/constants", "o": "append", "v": {"0": "Aquí tienes un plan para un buen asado de cordero del domingo con amigos — …"}},5 {"p": "/message/metadata/model_dil_v2/constants", "o": "append", "v": {"1": "Como eres"}},6 {"p": "/message/metadata/model_dil_v2/fallbackMarkdown", "o": "append", "v": " Asado de cordero del domingo con amigos — …"}7]}
El servidor no compila de forma incremental. Cada pocos cientos de milisegundos, muy probablemente con cada nuevo fragmento de salida del modelo, recompila todo lo que el modelo ha escrito hasta ese momento y envía el resultado. La compilación empieza con el primer token, antes de que aparezca ninguna etiqueta.
El compilador tiene que encargarse de:
- Compilar una respuesta escrita a medias
- Actualizar el texto
- Actualizar la UI
La línea temporal se parece a esto:

GIF
Streaming en el cliente
La página pasa cada nueva actualización al worker aislado. El worker la evalúa, vuelve a renderizar con el estado existente y envía las operaciones de actualización a la página. El estado conserva sus valores entre recompilaciones gracias a las claves añadidas durante la compilación. Si un programa nuevo falla al evaluarse o renderizarse, el worker se queda con el último que funcionó.
Después, la página anima cada cambio:
- el texto aparece gradualmente en 0,7 s;
- las filas y elementos de cuadrícula nuevos se deslizan en 0,42 s;
- los gráficos se dibujan en 1,8 s;
- las alturas de los contenedores hacen una transición en lugar de cambiar de golpe.
Válvula de escape: AppBlock, una app en un iframe

App integrada generada por ChatGPT
Algunas peticiones requieren cosas para las que los componentes nativos no están diseñados, como una caja de ritmos que sintetice sonido con Web Audio. Para estos casos, el modelo puede escribir un AppBlock: una aplicación web autocontenida en HTML, CSS y JavaScript, incrustada en la respuesta. Este es el inicio de uno, acortado:
1<AppBlock title="Laboratorio de batería" icon="app-chatgpt" variant="inline" app_block_id="drum-lab-01">2<div id="dl" class="w-full min-w-0 space-y-4 text-base">3 <style>4 #dl{color:var(--viz-text)}#dl button{touch-action:manipulation}#dl .panel{background:var(--viz-panel);border:1px solid var(--viz-border);border-radius:15px}…5 </style>6 …7 <button id="dl-play" class="btn" style="background:var(--viz-text);color:var(--viz-card);min-width:100px">▶ Reproducir</button>8 …9</div>10<script>11(function(){12const root=document.getElementById('dl');if(root.dataset.init)return;root.dataset.init="yes";13…14function audioInit(){if(!audio){const C=window.AudioContext||window.webkitAudioContext; if(!C)return false;audio=new C();…15…16})();17</script>18</AppBlock>
Los AppBlocks se renderizan de forma distinta a los componentes de la Intelligent UI.
Poniendo todo junto
Escribes un prompt. El modelo empieza a generar la interfaz, el servidor la convierte en algo que ChatGPT puede ejecutar y la página la construye pieza a pieza mientras la respuesta llega en streaming. Una vez que está ahí, mover un slider o marcar una casilla actualiza la interfaz localmente, sin volver a consultar al modelo.
Un lenguaje específico para el modelo, un paso de compilación en el servidor, renderizadores nativos y un sistema de diseño bien definido lo unen todo. Cada parte cumple una función crítica y, juntas, ofrecen la próxima generación de interfaces nativas de IA a miles de millones de usuarios en todo el mundo. ¡Qué época para estar vivos!

Capturas de pantalla de la Intelligent UI de ChatGPT
Metodología
Todas las observaciones proceden de nuestras propias cuentas de ChatGPT, del tráfico que genera la aplicación web de ChatGPT y del JavaScript que chatgpt.com sirve públicamente. Se realizaron en octubre de 2026 con GPT-6 y GPT-6 Thinking.
Análisis realizado con la ayuda de Codex y Claude. Redactado con Codex, visualizaciones de Claude.
(Hay una versión más detallada en https://www.openui.com/blog/how-chatgpt-intelligent-ui-works





