Apps inteligentes

Permite que los agentes generen IU en tus apps para Android con A2UI

Bocinas

Acerca de

Acerca de la charla

A2UI está en proceso de transición de componentes centrados en la Web a Android nativo. El nuevo renderizador de Jetpack Compose para A2UI v0.9 permite que los desarrolladores rendericen de forma nativa IU dinámicas generadas por LLM. En lugar de WebViews, aprovecha un framework de actores controlado por corrutinas, ciclos de vida de clases selladas y la integración nativa de Material 3.

En esta sesión, se explica la nueva arquitectura, cómo los árboles de componentes controlados por el servidor se traducen en nodos reactivos de Compose y cómo las transiciones de diseño integradas controlan las actualizaciones estructurales sin problemas y sin bloquear la IU.

Resumen

  • A2UI proporciona un protocolo JSON abierto y declarativo para que los backends de agentes describan interfaces de usuario dinámicas en lugar de respuestas de texto.
  • El protocolo gira en torno a catálogos que definen los componentes y las funciones permitidos, junto con plataformas que representan partes de la IU controladas por el agente.
  • La biblioteca de A2UI de Jetpack Compose renderiza de forma nativa los modelos de plataformas transmitidos con componentes de Material 3 con accesibilidad integrada y renderización progresiva.
  • Los modelos de datos en memoria actualizan los elementos de la IU vinculados de forma instantánea sin comunicarse con el agente, mientras que los eventos salientes enrutan las acciones del usuario de vuelta.
  • Los desarrolladores pueden personalizar los elementos existentes del catálogo o crear componentes personalizados para controlar estrictamente lo que el agente puede mostrar.

Recursos

Gemini Notebook

Temas

  • a2ui
  • generative-ui
  • jetpack-compose
  • android
  • agentes de IA
  • material-design

Transcripción

Descargar transcripción

Introducción a la IU generativa y A2UI

  • Hola a todos. Les damos la bienvenida a esta charla en la que hablaremos sobre cómo generar la IU en tus apps para Android con nuestra nueva biblioteca de A2UI.

  • Mi nombre es Nick Butcher y soy gerente de producto de Jetpack Compose.

  • Hola a todos. Soy Anton, ingeniero del equipo de Compose.

  • Estoy trabajando en este nuevo renderizador de A2UI de Jetpack Compose, del que nos entusiasma mucho hablarte hoy.

  • Sí, ahora con el auge de las capacidades de IA, muchas apps están buscando cómo la IA puede ayudarte a mejorar o crear nuevas experiencias para sus usuarios.

  • Una de las experiencias emocionantes que algunas personas están analizando es la IU generativa, que es cómo, en el entorno de ejecución, los backends de agentes pueden crear IUs completamente nuevas para tus usuarios, lo que creemos que es muy interesante.

  • Pero, lamentablemente, en este momento, muchas respuestas de backends de agentes están atrapadas en bloques de texto, como este.

  • Aquí tienes un ejemplo en el que le pedí a un agente que generara una nueva función para reservar un viaje.

  • Esta es la instrucción.

  • Y, como puedes ver, la respuesta es bastante decente, pero está atrapada en este muro de texto con el que no puedo interactuar fácilmente y no se parece en nada al resto de mi aplicación.

  • En cambio, la IU generativa te permite crear una experiencia como esta, en la que puedes generar una IU nueva sobre la marcha en respuesta a una entrada dinámica, lo que abre posibilidades para una mayor personalización o más experiencias nuevas.

  • Este es un ejemplo de una IU que se creó con componentes nativos estándar.

  • Por lo tanto, puedes crear este tipo de experiencia hoy mismo, pero tendrías que definir tu propio formato para el intercambio con el backend, tu propio tipo de flujo de eventos y funciones, etcétera.

  • A2UI, que significa Agent-to-UI, es un protocolo abierto que es un formato JSON para definir todas estas cosas.

  • Una de las cosas que más me gustan de A2UI es que es un formato declarativo.

  • El agente solo devuelve una representación JSON de la IU que se renderizará.

  • No devuelve ningún código que el cliente deba ejecutar, lo que puede exponerlo a ciertas vulnerabilidades de seguridad.

  • Es solo declarativo.

  • ¿Qué otros conceptos debemos conocer sobre A2UI?

Conceptos básicos: Catálogos y plataformas

  • Solo dos conceptos principales: catálogos y plataformas.

  • El catálogo tiene componentes como texto, botón, fila y columna que el agente puede usar para compilar la IU.

  • Un catálogo también tiene funciones que el agente puede usar para dar formato al texto, validar la entrada del usuario o abrir URLs.

  • Un catálogo es un contrato que define qué componentes y funciones admite tu app, y el agente solo puede compilar con ellos.

  • Esto significa que, como mencionó Nick, el agente nunca ejecuta código arbitrario en tu app.

  • Compone una IU a partir de los componentes básicos que diseñas y en los que confías.

  • Otro concepto importante es la superficie.

  • Una superficie es un elemento de la IU que controla el agente, como este ejemplo de planificador de viajes en la diapositiva.

  • El agente describe la IU de una plataforma en JSON con los mensajes del protocolo A2UI, como createSurface y updateComponents, y con los componentes y las funciones definidos en tu catálogo.

  • Hablando de catálogo, este es un concepto muy fundamental en A2UI, ya que controla qué componentes puede usar el agente para construir una interfaz.

  • La especificación de A2UI define algo llamado catálogo básico, que es un conjunto de componentes comunes que puedes usar para construir la mayoría de las IU.

  • El renderizador de A2UI de Jetpack Compose proporciona una implementación del catálogo básico con componentes de Material 3.

  • La mayoría de los componentes del catálogo están listos para usar de inmediato.

  • Hay tres componentes multimedia: reproductor de imágenes, de video y de audio, en los que debes conectar la biblioteca que elijas.

  • Por ejemplo, puedes elegir Coil para la carga de imágenes y Media3 para el reproductor de video y audio.

  • Pero el verdadero poder de A2UI y nuestra nueva biblioteca de renderizadores es que puedes crear tus propios componentes y catálogos completamente personalizados, y controlar por completo lo que el agente puede usar para compilar la IU.

  • Volveremos a los catálogos más adelante.

  • Bien, veamos cómo se ve esto.

  • Así que, si podemos cambiar a la máquina de demostración, por favor.

  • Esta es nuestra app de demostración, que usaremos, y compilaremos el renderizador de A2UI para mostrarte lo fácil que es.

  • Por lo tanto, es un conjunto de instrucciones, como puedes ver en estos pequeños chips en el medio de la pantalla, que puedes enviar a un agente.

  • Por lo tanto, si hacemos clic en una de las instrucciones, se envía esa instrucción a un backend de agente real y en vivo, así que crucemos los dedos para la demostración y, además de la instrucción, también agregamos algunas instrucciones del sistema.

  • Por lo tanto, le informamos al backend sobre el protocolo de A2UI, sobre el catálogo de componentes, en este caso, el catálogo básico que estamos usando aquí, que puede usar para compilar la IU, y le damos algunos ejemplos de cómo compilar esto.

  • La implementación de la sesión de chat en nuestra app de demostración ya sabe cómo extraer mensajes de A2UI en formato JSON sin procesar de las respuestas del agente.

  • Por lo tanto, en este caso, el agente respondió con un texto seguido de mensajes del protocolo A2UI: createSurface, updateComponents.

  • Sin embargo, por el momento, nuestra app solo muestra este JSON sin procesar y aún no lo renderiza como IU.

  • Antes de comenzar a implementar el código, volvamos a las diapositivas por un segundo para dar un paso atrás y hablar sobre el diseño del sistema que usaremos para implementar esto.

  • Ah, aquí hay uno.

Arquitectura de la aplicación y procesamiento de mensajes

  • Sí, este es el plan.

  • Por lo tanto, el agente de IA transmite una respuesta a nuestra app que recibe la clase ChatSession.

  • La ChatSession extrae mensajes JSON de A2UI sin procesar de las respuestas del agente a medida que se transmiten y los pasa al procesador de mensajes de A2UI.

  • El Message Processor se configura con un analizador que convierte esos mensajes JSON sin procesar en una representación semántica.

  • Luego, el procesador valida los mensajes con la definición del catálogo y los asigna a un conjunto de modelos de plataforma.

  • Los modelos de superficie son los que definen cómo se ve la IU y las partes de la IU que controla el agente.

  • Luego, esos modelos de superficie se renderizan como IU nativa de Compose con el elemento componible de superficie A2UI que proporciona nuestra biblioteca.

  • La comunicación entre el agente y la IU es bidireccional, por lo que, cuando el usuario interactúa con la IU, las acciones y los errores del usuario fluyen de vuelta al agente.

  • En resumen, hay tres componentes principales: el catálogo, el Message Processor y la superficie.

  • Escribamos el código para hacerlo.

  • Sí, construyámoslo si podemos cambiar de máquina.

  • Bien, el primer paso es agregar la dependencia.

  • Aquí puedes ver que ya incluimos la dependencia de A2UI de Material 3 y que ya sincronizamos el proyecto para que no tengamos que esperar a que se ejecute Gradle.

  • Esta es la dependencia de nivel superior que define la implementación del catálogo básico con esos componentes de Material 3, pero hay otras dependencias más detalladas debajo.

  • Por lo tanto, si no quieres usar el catálogo básico, por motivos que describiremos más adelante, puedes usar los más detallados.

  • Pero esto traerá, extraerá el resto de forma transitiva.

  • Bien.

  • Como dijimos, hay tres cosas.

  • El primer paso fue definir un catálogo.

  • Aquí, en nuestra aplicación, crearemos una función de fábrica para ayudar a compilar una instancia de esa implementación de Material 3 del catálogo básico.

  • Por lo tanto, usamos esta función de biblioteca aquí, material A2UI basic catalog, que también tiene versiones.

  • Y, como dijimos, esto proporciona todos los componentes comunes que necesitas, excepto que no queremos obligarte a usar ciertas bibliotecas de carga de imágenes o renderización de contenido multimedia, por lo que debes proporcionarlas tú mismo.

  • Aquí, por ejemplo, Anton ya creó este wrapper alrededor del elemento componible AsyncImage de Coil, que usaremos para cargar imágenes, así como un poco de lógica adicional para que nuestra demostración busque imágenes de Wikipedia.

  • Por lo tanto, podemos conectar esto con mucha facilidad a los componentes del catálogo.

  • Y, de manera similar, tenemos implementaciones similares para la renderización, el video y el audio, en las que usaremos la biblioteca de Media3 para renderizarlos con ExoPlayer.

  • De esta manera tan sencilla, podemos conectar nuestros propios componentes.

  • Lo siguiente que necesita el catálogo es un conjunto de funciones estándar.

  • Estas son funciones que el cliente puede proporcionar.

  • Nuevamente, enviamos una implementación de esto junto con el catálogo básico, pero debemos proporcionarle un contexto, que usarás para realizar ciertas acciones, como abrir un URI si un usuario presiona un elemento.

  • Y así, completamos nuestro primer paso.

  • Creamos un catálogo.

  • El siguiente paso que debemos realizar es configurar el Message Processor.

  • Esto es lo que realmente analiza las respuestas y demás.

  • Por lo tanto, cambiaremos al modelo de vista de nuestra aplicación, donde primero crearemos una instancia de ese catálogo que configuramos anteriormente, ya que lo necesitaremos.

  • Luego, crearemos este objeto Message Parser, Message Processor, que necesita acceso al catálogo para validar las respuestas con los componentes que queremos usar.

  • Otro componente que debemos crear es un analizador, que es el objeto responsable de analizar los mensajes JSON a medida que regresan.

  • Aquí es donde creamos el objeto ChatSession.

  • Como mencioné antes, ChatSession ya extrae los mensajes sin procesar de A2UI de las respuestas del agente.

  • Y cuando cada mensaje nuevo de A2UI está listo, llama a esta devolución de llamada onA2uiMessage.

  • Por lo tanto, todo lo que necesitamos hacer es pasar cada uno de esos mensajes cuando lleguen al procesador.

  • Llamamos a la función processInput y le proporcionamos un analizador y un mensaje.

  • Por lo tanto, la función processInput analiza cada mensaje y, luego, pone en cola el trabajo para que el procesador lo valide con el catálogo y actualice los modelos de superficie.

  • Por lo tanto, el procesador expone el conjunto actual de modelos de superficie activos como StateFlow de activeSurfaces.

  • La ChatSession tiene cierta lógica para agregar los modelos de superficie a los estados de la IU de las respuestas del agente que se renderizarán.

  • Por lo tanto, solo pasamos este StateFlow de activeSurfaces como parámetro a ChatSession.

  • Además, como mencioné, la función processInput pone en cola el trabajo para el procesador.

  • Sin embargo, para comenzar el procesamiento, debemos iniciar una corrutina en un subproceso en segundo plano.

  • Por lo tanto, usaremos un alcance de modelo de vista para eso y estableceremos el despachador predeterminado.

  • Y, en esa corrutina, solo necesitamos llamar a processor.collectMessages.

  • Por lo tanto, mientras esta corrutina esté activa, el procesador controlará los mensajes de la cola de mensajes.

  • Por lo tanto, otra cosa que debemos hacer es que, cuando el usuario borre el historial de chat, también se borren las superficies actuales.

  • Por lo tanto, llamamos a clearSurfaces.

Renderiza modelos de superficie en la IU

  • Bien.

  • Ya completamos los primeros dos pasos.

  • Creamos el catálogo y el procesador para analizar los mensajes.

  • El siguiente paso que debemos realizar es renderizar esas respuestas en nuestra IU.

  • Por lo tanto, recopilamos esas superficies a medida que el analizador las analiza.

  • Y ahora, en nuestra IU, los pasaremos en este objeto de respuesta del agente y simplemente los iteraremos.

  • Luego, usaremos este elemento componible de superficie de A2UI y le pasaremos cada uno de los modelos a medida que los iteramos para renderizar el contenido donde queremos que vaya.

  • Un aspecto que se debe tener en cuenta aquí es que el agente puede optar por usar algo como una lista si decide mostrar, por ejemplo, una gran cantidad de contenido.

  • Por lo tanto, debemos establecer una altura máxima para este componente para asegurarnos de que se renderice correctamente.

  • Y, con eso, implementamos un renderizador básico de A2UI.

  • Ahora, si volvemos a nuestra app, la reiniciamos y volvemos a presionar esa instrucción, con suerte, el agente debería usarla para, sí, aquí vamos, renderizar una IU real.

  • Así que lo programaremos en vivo.

  • No se trata de un truco de magia.

  • Pero sí, usa imágenes reales.

  • Usa pestañas, diálogos, selectores de fecha, controles deslizantes, es decir, todos los componentes estándar de Material con los que, como sabes, crearías tu app en lugar de solo mostrar una respuesta textual.

  • Como esta es una app real que usa componentes de Material reales, ya sabes, hace lo que hacen los componentes reales.

  • Tiene un rendimiento similar al nativo.

  • Por ejemplo, responde al modo oscuro aquí.

  • Estos son los componentes de Material.

  • Por lo tanto, si tienes un elemento componible de tema de Material externo en el que personalizaste los colores, las formas, la tipografía, etcétera, estos componentes los tomarán para que se vean como el resto de tu aplicación.

  • Como mencionó Nick, dado que estos son componentes nativos de Compose, tienen semántica nativa.

  • Esto significa que se admiten todas las funciones de accesibilidad integradas de Compose.

  • Por lo tanto, funciones como TalkBack, el ajuste de escala de fuentes y la navegación con teclado funcionan de inmediato.

  • Ahora, como estás invocando un agente para generar una nueva IU en el entorno de ejecución, puede haber cierta latencia.

  • Es posible que debas esperar a que el agente responda.

  • La biblioteca hace lo posible para minimizar esta latencia.

  • Por lo tanto, si usamos este tipo de, inyectaremos algunas esperas para mostrarte un poco más lentamente lo que sucede y cómo lo minimizamos.

  • Sí, como el agente puede tardar un poco en transmitir la respuesta completa, usamos lo que se conoce como renderización progresiva.

  • Por lo tanto, en cuanto llega el componente raíz, comenzamos a mostrar la IU de la plataforma.

  • Todos los componentes de contenedor muestran marcadores de posición de carga para sus elementos secundarios si estos aún no llegaron.

  • Cuando llegan los componentes secundarios, se animan con el esquema de movimiento de Material 3.

  • Bien.

  • Y si podemos cambiar a la siguiente instrucción solo para mostrar otra función de la biblioteca.

  • Aquí hay otra instrucción en la que reservamos una mesa para cenar.

  • Y aquí el agente elige usar este componente de control deslizante, sí, lo usó.

  • Aquí puedes ver que puedes arrastrar y, además, interactuar con la IU que el agente creó sobre la marcha.

  • Y observa que no solo cambia el número en el componente, sino que también hay una cadena de formato más abajo donde se cuenta.

  • Dime que no hace un viaje de ida y vuelta al agente solo para actualizar eso, ¿verdad?

  • Esta es una vinculación bidireccional.

  • Por lo tanto, el componente del control deslizante está vinculado al modelo de datos en memoria de esta superficie.

  • Todos los componentes que se vinculan a la misma ruta de acceso en el modelo de datos se actualizan automáticamente cuando cambia el valor.

  • Por lo tanto, el control deslizante lee el valor que se mostrará y, cuando lo arrastro, vuelve a escribirlo en el modelo para actualizarlo.

  • Por lo tanto, todo esto sucede de forma local sin un viaje de ida y vuelta al agente.

  • Pero también podemos pedirle al agente que actualice los datos.

  • Así que hagámoslo con 6 personas.

  • Presta atención al control deslizante cuando el agente responda.

  • Acaba de saltar a 6.

  • Veamos qué recibimos del agente.

  • En lugar de enviar una descripción completamente nueva de la IU, el agente solo envió un pequeño mensaje updateDataModel que actualizó la ruta del control deslizante.

  • Como el control deslizante está vinculado a esa ruta de acceso, se actualizó en el lugar.

  • Por lo tanto, el modelo de datos de cada superficie se basa en el estado de la instantánea de Compose, y cada propiedad de componente vinculada a una ruta de acceso es un estado derivado.

  • Por lo tanto, cada vez que el usuario o el agente actualizan el valor en el modelo de datos, todos los componentes vinculados al mismo valor se vuelven a componer automáticamente.

  • Bien.

  • Supongamos que queremos tomar medidas y presionar el botón de reserva. ¿Qué sucederá aquí, Anton?

  • No pasa nada en este momento.

  • El botón envía una acción, pero aún no la enviamos al agente.

  • Así que vamos a corregirlo.

Cómo controlar las acciones del usuario y los flujos de eventos

  • Bien, así como tuvimos que conectarnos al procesador para procesar las entradas, también necesitaremos conectarnos a cualquier salida, cualquier acción que el usuario realice en la IU que creó el agente.

  • Para ello, usamos el flujo outboundEvents que expone el procesador y recopilamos datos en él.

  • Así que esta es tu oportunidad para conectar cualquier lógica que desees en función de, ya sabes, cuando el usuario presiona una acción o realiza algo con la IU.

  • Aquí, enviamos las acciones de vuelta al agente para que decida qué hacer, pero también puedes decidir si llamas a tu propio backend o navegas a una pantalla diferente en tu aplicación, tú decides.

  • Aquí Anton lo conectó, y ahora, cuando presionamos la acción de confirmar la reserva, se enviará esa acción que realizaste de vuelta al agente.

  • Y aquí es donde, ya sabes, podrías en el backend incluso decidir invocar alguna funcionalidad.

  • Veamos qué le enviamos al agente.

  • Este es el mensaje de acción de A2UI.

  • Tiene un contexto, y el contexto contiene un conjunto de propiedades que el agente solicitó que se enviaran cuando se realizara esta acción.

  • Por lo tanto, cuando hago clic en el botón, estas propiedades particulares se resuelven en ese momento en función de los valores actuales del modelo de datos, y eso es lo que enviamos al agente.

  • Bien, hasta ahora el agente ha sido bastante cooperativo con nosotros, pero estamos hablando de IA, y la IA puede cometer errores.

  • Veamos qué sucede si el agente devuelve un error en la IU que nos describe.

  • En esta última instrucción, le pedimos al agente que cometa un error para poder mostrarte cómo funciona.

  • Veamos qué sucede.

  • Sí.

  • Como puedes ver, uno de los componentes está marcado con un error.

  • Y ahora se solucionó.

  • Este es el bucle de autocorrección del agente.

  • Antes de que cada componente llegue a la IU, validamos ese mensaje con el esquema del catálogo.

  • Si el componente no coincide con el esquema, se marca como un error.

  • El renderizador de la implementación básica del catálogo muestra ese error aislado en ese componente específico, mientras que el resto de la IU se sigue mostrando.

  • Tras bambalinas, el renderizador envía un mensaje de error al agente para informarle qué salió mal.

  • En este caso, enviamos un mensaje que decía que el tipo de componente desconocido que no existe no se encuentra en el catálogo.

  • Luego, el agente tuvo la oportunidad de enviar una respuesta de seguimiento que actualizó la IU existente y la IU se corrigió en su lugar.

  • Sí.

  • En la implementación actual del catálogo básico de Material, mostramos los errores de inmediato, pero en tu componente personalizado, puedes decidir seguir mostrando un indicador de carga para un componente si hay un error mientras esperas que el agente lo corrija.

  • De esta manera, el error puede quedar completamente oculto para el usuario.

Crea componentes personalizados y extiende catálogos

  • Y hablando de catálogos y componentes personalizados, hasta ahora solo hemos usado el catálogo básico tal como se envía de forma predeterminada, pero, por supuesto, puedes personalizarlo según tus propias necesidades.

  • Como puedes ver, cuando usamos la función de fábrica para crear el catálogo, se establecen valores predeterminados para todos los componentes.

  • Por lo tanto, puedes conectar fácilmente tus propios componentes si lo deseas.

  • Por ejemplo, si queremos conectar nuestro propio sistema de diseño en nuestra app y su propio botón, podemos hacerlo fácilmente.

  • Por lo tanto, solo anulamos este parámetro cuando creamos una instancia del catálogo y crearemos esta definición de componente.

  • Si creamos una clase nueva para implementar eso, puedes ver que hay una interfaz aquí, el catálogo básico de A2UI de tipo botón.

  • Por lo tanto, si implementamos esto, podemos conectar nuestro propio componente.

  • Sí.

  • Como anularemos un componente en el catálogo básico, debemos implementar esta interfaz de botón del catálogo básico de A2UI.

  • Por lo tanto, esta interfaz ya tiene información sobre el componente, como el nombre, la descripción y el esquema de propiedades.

  • Por lo tanto, todo lo que debemos hacer para implementar nuestra propia versión es implementar esta función de componibilidad.

  • Toma algunos parámetros.

  • Por lo tanto, estos parámetros son valores que se reciben del agente después de que se validan.

  • Por lo tanto, estos son elementos como el ID secundario, que hace referencia a un componente que será el contenido del botón.

  • También tiene una variante que describe el tipo de botón, la definición de la acción y algunos otros parámetros.

  • En la implementación de producción del botón, tendríamos que usar todos estos parámetros, pero para nuestra demostración, solo necesitamos el elemento secundario y la acción.

  • Implementemos este elemento componible.

  • Bien, en esta función de componibilidad, emitiremos nuestro propio tipo de botón Me gusta personalizado aquí.

  • Como puedes ver, podemos usar los parámetros que proporcionó y validó A2UI.

  • Por ejemplo, en la función onClick, podemos enviar esa acción.

  • Podemos usar este tipo de biblioteca DispatchAction y pasarle la acción que definió A2UI.

  • Por lo tanto, usaremos nuestro propio botón, pero queremos renderizar el contenido secundario, la etiqueta o lo que el agente haya decidido usar.

  • Para ello, usamos la función observeA2uiComponentState y le pasamos el childId para obtener un controlador del contenido secundario que se debe renderizar.

  • Luego, usaremos esta función de componibilidad A2uiComponent y le pasaremos el estado del componente secundario para renderizarlo.

  • Y aquí puedes ver que solo renderizamos el contenido secundario si se cargó correctamente, pero también puedes tener tu propia lógica aquí para que, si se está cargando o si hay un estado de error, puedas decidir qué quieres mostrar.

  • Si volvemos a uno de nuestros ejemplos de instrucciones en nuestra app de demostración y ahora reemplazamos el botón usando, ya sabes, el botón estándar de Material.

  • Es tan sutil que espero que puedas verlo.

  • Como puedes ver, también usamos un botón muy personalizado aquí.

  • Genial, así es como puedes, ya sabes, reemplazar algunos de los tipos de biblioteca, pero ¿qué pasa si quieres extender la biblioteca con tus propios tipos personalizados?

  • Podemos hacerlo con mucha facilidad agregando contenido al catálogo.

  • Anton creará una clase nueva aquí para implementar un componente de calificación por estrellas.

  • Por lo tanto, agregaremos algo a nuestra aplicación para que podamos calificar restaurantes mientras estamos en este viaje hipotético.

  • Por lo tanto, como no reemplazaremos un componente existente, usaremos esta interfaz más genérica, esta interfaz de componente A2UI, que, como puedes ver, tiene algunos parámetros que debemos anular.

  • Los dos primeros son el nombre y la descripción.

  • Por lo tanto, esto describe qué es y qué hace nuestro componente.

  • Esto es muy importante porque, cuando serializamos el catálogo y lo enviamos al backend para indicarle qué puede usar para compilar una IU, esto es lo que el agente usará para decidir si usar o no este componente cuando responda.

  • Por lo tanto, debes ser bastante descriptivo y decirle a la IA que sabes exactamente lo que hace.

  • La siguiente propiedad que debemos anular es esta propiedad properties, que básicamente indica qué datos maneja este componente.

  • Por lo tanto, crearemos una barra de calificación por estrellas para que puedas calificar un restaurante, y necesitaremos dos propiedades.

  • Será el valor, como la cantidad de estrellas que seleccionó el usuario, que será una propiedad mutable con la que el usuario puede interactuar y cambiar, y la cantidad máxima de estrellas.

  • Aquí podemos ver que Anton usa esta propiedad dinámica de tipo numérico para el valor.

  • Esto es como, ya sabes, la cantidad real de estrellas que se seleccionan.

  • que, a su vez, tiene su propio tipo de descripción que le indica al agente para qué sirve este valor.

  • Y, luego, tenemos una propiedad de tipo estático, por lo que solo usamos esta propiedad A2UI de tipo número para definir la cantidad máxima de estrellas que puede mostrar este componente.

  • Y, una vez que definimos esas dos propiedades, solo debemos devolverlas.

  • Por lo tanto, anulando esta propiedad de propiedades, haremos una lista de ellas y las devolveremos.

  • Así es como le dices al agente de qué se trata este componente.

  • Sí.

  • Como mencionó Nick, estas definiciones de propiedades se usan para enviarse como parte del esquema del catálogo para que el agente sepa cómo usar este componente.

  • Sin embargo, estas mismas definiciones de propiedades también se usan para extraer valores con tipo en el entorno de ejecución.

  • Por lo tanto, hay una fuente única de información.

  • Veamos qué debemos hacer para implementar esto.

  • Primero, para acceder a la propiedad max, como es una propiedad estática, significa que el agente envía el valor de forma intercalada en el objeto de propiedades.

  • Por lo tanto, para acceder a ella, solo debemos obtenerla del objeto de propiedades haciendo referencia a ella con el objeto de propiedad max adecuado como clave.

  • Como esta propiedad se define como un número, también debemos convertirla en un int y establecer el valor predeterminado.

  • Lo siguiente es la propiedad value.

  • Como la propiedad value es dinámica, significa que hace referencia a algo en el modelo de datos.

  • Por lo tanto, para acceder a él, debemos usar la API properties.bind y pasar el objeto de propiedad como una clave como parámetro.

  • Esto devuelve el valor actual almacenado en el modelo de datos para esta propiedad y también se suscribe a futuras actualizaciones.

  • Por lo tanto, cada vez que se actualice el modelo de datos, este componente se volverá a componer automáticamente.

  • De manera similar, también lo convertimos en un int y establecimos un valor predeterminado.

  • Otra cosa que debemos hacer es configurar la vinculación bidireccional para que podamos cambiar la calificación.

  • Para ello, usamos la API de bindUpdater.

  • Pasamos el mismo objeto de propiedad que define la propiedad y, como resultado, obtenemos que bindUpdater devuelve una expresión lambda que nos permite mutar un valor para esta propiedad en el modelo de datos.

  • Ahora estamos listos para emitir nuestro componente de calificación por estrellas.

  • Bien.

  • Ahora que definimos nuestro propio componente personalizado, debemos agregarlo al catálogo que estamos usando.

  • Anteriormente, solo usábamos el catálogo básico tal como estaba.

  • Ahora, lo que debemos hacer es crear un catálogo nuevo, que tomará todo el catálogo básico y agregará nuestro componente personalizado.

  • Por lo tanto, un catálogo se define como un conjunto de tres elementos.

  • Es un ID, un conjunto de componentes y un conjunto de funciones.

  • Para el ID, usaremos un URI para definir, ya sabes, un catálogo que se identifique de forma única, así como la versión con la que estamos trabajando.

  • Y, para los componentes, tomaremos todos los componentes del catálogo básico y, luego, usaremos el operador add para agregar ese componente personalizado que acabamos de crear.

  • Y, para las funciones, solo usaremos las del catálogo básico.

  • Y así, ampliamos el catálogo básico para agregar nuestro nuevo tipo de componente personalizado.

  • Como ves, es muy fácil crear un catálogo personalizado.

  • Por lo tanto, el agente aprende a usar el catálogo a partir de las instrucciones del sistema que le enviamos.

  • Como ahora creamos un catálogo personalizado, también actualicemos las instrucciones del sistema.

  • Por lo tanto, pasaremos el objeto del catálogo a nuestra función que compila las instrucciones del sistema.

  • Aquí está.

  • Hasta ahora, enviábamos una definición estática del esquema para el catálogo básico, ya que usábamos este catálogo estándar.

  • Ahora que tenemos un catálogo personalizado, podemos generar el esquema de forma dinámica con la función toJsonSchemaString.

  • Esto creará una representación JSON completa de este catálogo, incluido el componente de calificación por estrellas que acabamos de agregar.

  • Las instrucciones del sistema también incluyen algunas respuestas de ejemplo para el agente.

  • Por lo tanto, también cambiamos el ID del catálogo aquí para que los ejemplos hagan referencia al catálogo correcto.

  • Bien.

  • Con eso, extendimos el catálogo básico para agregar nuestros propios tipos personalizados.

  • Por lo tanto, si volvemos a implementar eso y probamos una de estas instrucciones, con suerte, obtendremos una IU de calificación.

  • Luego, el agente responde.

  • Y aquí, puedes ver que este componente de estrella es un componente nuevo que el agente nunca había visto hasta que lo implementamos en este momento.

  • Esto no forma parte del catálogo básico, lo acabamos de crear.

  • Cuando hago clic en la estrella, se llama a la lambda bindUpdater y se actualiza el valor en el modelo de datos.

  • Si enviamos las calificaciones, podemos ver la acción, y el contexto de la acción incluirá los valores que acabamos de establecer.

  • Bien, ahora volvamos a las diapositivas.

  • Revisamos mucha información con bastante rapidez, pero esperamos haberte mostrado cómo puedes pasar fácilmente de agentes que responden con un bloque de texto a agentes que crean IU nativas reales.

Conclusiones y recursos clave

  • Esperamos haberte demostrado que la integración es bastante sencilla.

  • Hay tres componentes principales que debes crear: un catálogo de componentes, implementar cierta lógica de análisis y, luego, renderizarlo en tu IU.

  • Y, con suerte, demostramos que la biblioteca hace mucho por ti de forma predeterminada, como la renderización progresiva o la validación de tu esquema, los bucles de autocorrección o el manejo de errores. Hace mucho.

  • Y, con suerte, hoy demostramos con bastante rapidez que es muy fácil personalizarlo.

  • Mostramos cómo el catálogo básico ya responde a tu tema de Material si usas Material, pero es muy sencillo extender o reemplazar componentes dentro del catálogo.

  • Sin embargo, creo que la mayor parte del poder proviene de crear tu propio catálogo.

  • Si quieres tener el mayor control posible sobre la IU que puede usar el agente, puedes crear componentes completamente personalizados.

  • Si quieres darle, ya sabes, total libertad, puedes darle componentes muy básicos, como etiquetas de texto y botones, y decirle: "Tú creas la IU".

  • Si quieres tener más control, puedes crear componentes más elaborados y decir: "Estas son las únicas cosas que puedes usar".

  • Por lo tanto, tú controlas lo que el agente puede hacer y mostrar a tus usuarios.

  • Esta es la nueva biblioteca de renderizadores de A2UI y la biblioteca de Jetpack que acabamos de lanzar.

    implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")
  • Actualmente, se encuentra en versión alfa, y la versión alfa 02 se lanzó hace poco.

  • Sigue estos vínculos para consultarlos.

  • Tenemos documentación en developer.android.com, así como un par de ejemplos.

  • Tenemos un ejemplo básico que muestra el catálogo básico, así como un ejemplo más elaborado, y esta app de ejemplo de JetPacker que acabamos de crear, que volverás a ver en la presentación de ADS más adelante hoy, que demuestra cómo puedes crear estos componentes más elaborados para controlar lo que ve el usuario.

  • Como dije, actualmente estamos en la fase alfa, por lo que es un buen momento para probarlo y darnos tus comentarios y tus solicitudes de funciones, y decirnos, por ejemplo, cómo te gustaría usarlo o si te gustaría usarlo si pudiera hacer X o Y.

  • Por lo tanto, consulta la biblioteca y envíanos tus comentarios.

  • Y eso es lo que obtuvimos.

  • Y no puedo esperar a ver lo que tú y tus agentes crean con ella.

  • Muchas gracias.

  • Gracias.

Código

Fragmentos de código

Fragmento de secuencia de comandos de Kotlin en el minuto 27:40 de "Key takeaways and resources"
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")