YouMind
Iniciar sesión

Ingeniería inversa de la Interfaz Inteligente de ChatGPT: así funciona realmente

@rabi_guha
INGLÉS08 oct 2026
206K
2.0K
146
66
3.5K

TL;DR

Este artículo detalla la arquitectura técnica detrás de la recién lanzada Interfaz Inteligente de ChatGPT, explicando el uso de un lenguaje de inferencia personalizado (DIL), la compilación del lado del servidor a JavaScript y JSON, y la ejecución segura del lado del cliente mediante workers aislados.

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.
Rabi Shanker Guha - inline image

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:

text
1## Estimación del plan de equipo
2Arrastra 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):

javascript
1function __dilSafe(evaluate, failureValue) {
2 try { return evaluate(); } catch { return failureValue; }
3}
4
5DIL.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" }));
json
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:

text
1CREATE #1 title SET size = "lg" PLACE under root at 0
2CREATE #2 text "Estimación del plan de equipo" PLACE under #1 at 0
3CREATE #3 text PLACE under root at 1
4CREATE #4 text "Arrastra el control deslizante para ver el " PLACE under #3 at 0
5CREATE #5 bold PLACE under #3 at 1
6CREATE #6 text "precio mensual" PLACE under #5 at 0
7CREATE #7 text " de tu equipo." PLACE under #3 at 2
8CREATE #8 box SET border = true, padding = 3, gap = 2 PLACE under root at 2
9CREATE #9 slider SET min = 1, max = 50, value = 8, onChange = fn#1 PLACE under #8 at 0
10CREATE #10 title SET size = "xl" PLACE under #8 at 1
11CREATE #11 text "$" PLACE under #10 at 0
12CREATE #12 text "232" PLACE under #10 at 1
13CREATE #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:

json
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í:

Rabi Shanker Guha - inline image

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

Rabi Shanker Guha - inline image

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:

text
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!

Rabi Shanker Guha - inline image

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

Guardar con un clic

Lee artículos virales en profundidad con IA en YouMind

Guarda la fuente, haz preguntas concretas, resume el argumento y convierte un artículo viral en notas reutilizables en un único espacio de trabajo con IA.

Explora YouMind
Para creadores

Convierte tu Markdown en un artículo de 𝕏 impecable

Cuando publicas tus propios textos largos, dar formato en 𝕏 a imágenes, tablas y bloques de código es un fastidio. YouMind convierte un borrador completo en Markdown en un artículo de 𝕏 impecable y listo para publicar.

Prueba Markdown a 𝕏

Más patrones por descifrar

Artículos virales recientes

Explorar más artículos virales