ChatGPT lanzó Generative UI (por fin) y de la noche a la mañana le cambió el nombre a Intelligent UI. Teníamos que ver cómo lo implementaron.
https://x.com/OpenAI/status/2107894997538525580
Este artículo es un análisis profundo de cómo OpenAI implementó su Intelligent UI insignia: las capas, los formatos y el renderizado nativo en Web y Mobile.
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 del lado del 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 design tokens disponibles para el modelo.

Diagrama de arquitectura de 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 común. 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 deriva price a partir de él. El slider está vinculado a seats, así que al moverlo se actualiza el precio.
Se necesita un formato dedicado porque el modelo escribe la interfaz token por token.
- Tiene que ser fácil de escribir de forma confiable, por eso se construye con notaciones que el modelo ya conoce bien.
- Tiene que seguir siendo usable aunque esté escrito a medias. Las instrucciones van en líneas independientes y cualquier elemento abierto puede cerrarse automáticamente. Eso permite que el servidor corte una respuesta parcial en su última estructura completa y aun así pueda compilarla.
Compilación del lado del servidor
El cliente nunca ejecuta la salida del modelo tal como fue escrita. 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 mayor legibilidad):
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 adentro, y sus palabras pasan a la tabla de constantes.
La compilación hace el trabajo que de otro modo cada cliente tendría que repetir:
- Llamadas a funciones simples. El markup se convierte en llamadas a __dil.jsx, para 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, de modo que si una expresión lanza un error solo elimina un elemento en lugar de abortar todo el renderizado.
- Texto en una tabla separada. El texto estático pasa a la tabla de constantes, así que mientras la respuesta se transmite en streaming, el texto que va creciendo 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. Las instrucciones y etiquetas incompletas se descartan, los elementos sin cerrar se cierran, 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 renderer, que dibuja el resultado.
El programa es código escrito por el modelo, así que 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 scope 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. Luego 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 el worker se reinicia.
El runtime es un reconciler pequeño al estilo de React. Renderiza el componente y mantiene el estado de los hooks en slots con clave. Después 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 listan 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 handler del slider se envía únicamente como un identificador (fn#1). Durante la transmisión, las operaciones se codifican como una secuencia binaria de enteros, con los strings guardados en una tabla aparte.
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 markup o estilos arbitrarios. Las excepciones son los valores CSS crudos 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 el identificador del handler y los argumentos al worker. 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 puras de layout y estilos. Elige entre componentes que ChatGPT ya sabe dibujar y les aplica estilos mediante design tokens como padding={3}. Algunas propiedades aceptan valores CSS crudos, como anchos en píxeles o colores hexadecimales, pero se prefieren los design tokens. Como resultado:
- Las interfaces generadas se ven igual que el resto de ChatGPT en todas las plataformas.
- El compilador tiene un esquema contra el cual 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 ícono (un diagnóstico unknown_prop) y gap="1" en un box (un diagnóstico invalid_literal).
El catálogo tiene tres partes:
- Componentes nativos. Alrededor de 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.
- Design tokens para espaciado, radio, color y tamaño.
- Componentes compuestos escritos por OpenAI en DIL y enviados prearmados al sandbox, como los componentes de imagen y de producto. En nuestras capturas, el modelo usó estos componentes, pero nunca definió los suyos.
Streaming
Hacer streaming de texto es simple: cada nuevo token se agrega a lo que ya está en pantalla. Hacer streaming de una interfaz es más difícil, por tres razones:
- Por lo general, la salida todavía no se puede ejecutar. En la mayoría de los momentos es un programa incompleto, con una etiqueta o expresión aún abierta, y no se puede ejecutar tal como está.
- La interfaz tiene que seguir funcionando mientras crece. Los componentes que el usuario ya tocó deben conservar su estado.
- Parte del contenido llega por separado. Datos como las imágenes vienen del servidor, no del texto.
Streaming del lado del servidor
Un stream simple de tokens agregados no puede expresar esto. En su lugar, ChatGPT transmite parches hacia un mensaje estructurado que contiene el texto crudo, el programa compilado y sus datos uno al lado del otro.
La respuesta llega al navegador a través de un stream de eventos enviados por el servidor (POST /backend-api/f/conversation). Cada evento es una actualización al estilo JSON-Patch sobre el mensaje que se está construyendo. Un solo evento suele actualizar el texto DIL crudo y su forma compilada al mismo tiempo. Esta es una actualización de una respuesta capturada, abreviada:
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 estás"}},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 bloque 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 cualquier etiqueta.
El compilador tiene que encargarse de:
- Compilar una respuesta escrita a medias
- Actualizar el texto
- Actualizar la UI
La línea de tiempo se ve más o menos así:

GIF
Streaming del lado del 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 que se agregan 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 nuevas y los elementos de grilla 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 saltar.
Válvula de escape: AppBlock, una app en un iframe

App inline generada por ChatGPT
Algunas solicitudes 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 esos casos, el modelo puede escribir un AppBlock: una web app autocontenida en HTML, CSS y JavaScript, incrustada en la respuesta. Este es el inicio de uno, abreviado:
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 manera distinta a los componentes de Intelligent UI.
Poniendo todo junto
Escribes un prompt. El modelo empieza a escribir la interfaz, el servidor la convierte en algo que ChatGPT pueda ejecutar, y la página la arma pieza por pieza mientras la respuesta llega en streaming. Una vez que está lista, mover un slider o marcar una casilla actualiza la interfaz de forma local, sin volver a consultarle 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 ponen todo en marcha. Cada parte cumple un rol fundamental y, juntas, llevan 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 Intelligent UI de ChatGPT
Metodología
Todas las observaciones provienen de nuestras propias cuentas de ChatGPT, del tráfico que genera la app 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 hecho con la ayuda de Codex y Claude. Escrito con Codex, visualizaciones de Claude.
(Una versión más detallada está en https://www.openui.com/blog/how-chatgpt-intelligent-ui-works





