Applications intelligentes

Laissez les agents générer l'UI dans vos applications Android avec A2UI

Haut-parleurs

À propos

À propos de l'intervention

A2UI passe de composants axés sur le Web à Android natif. Le nouveau moteur de rendu Jetpack Compose pour A2UI v0.9 permet aux développeurs d'afficher de manière native une UI dynamique générée par un LLM. Au lieu des vues Web, il exploite un framework d'acteur basé sur des coroutines, des cycles de vie de classes scellées et une intégration native de Material 3.

Cette session présente la nouvelle architecture, en expliquant comment les arbres de composants pilotés par le serveur se traduisent en nœuds Compose réactifs, et comment les transitions de mise en page intégrées gèrent les mises à jour structurelles de manière fluide sans bloquer l'UI.

Résumé

  • A2UI fournit un protocole JSON déclaratif et ouvert permettant aux backends d'agents de décrire des interfaces utilisateur dynamiques au lieu de réponses textuelles.
  • Le protocole s'articule autour de catalogues qui définissent les composants et les fonctions autorisés, ainsi que de surfaces qui représentent des éléments d'interface utilisateur contrôlés par l'agent.
  • La bibliothèque A2UI de Jetpack Compose affiche les modèles de surface diffusés en streaming de manière native à l'aide de composants Material 3 avec accessibilité intégrée et rendu progressif.
  • Les modèles de données en mémoire mettent à jour instantanément les éléments d'interface utilisateur liés sans contacter l'agent, tandis que les événements sortants renvoient les actions de l'utilisateur.
  • Les développeurs peuvent personnaliser les éléments de catalogue existants ou créer des composants personnalisés pour contrôler strictement ce que l'agent peut afficher.

Ressources

Gemini Notebook

Thèmes

  • a2ui
  • generative-ui
  • jetpack-compose
  • android
  • ai-agents
  • material-design

Transcription

Présentation de l'UI générative et d'A2UI

  • Bonjour à tous et bienvenue dans cette présentation. Nous allons voir comment générer une UI dans vos applications Android à l'aide de notre toute nouvelle bibliothèque A2UI.

  • Je m'appelle Nick Butcher et je suis responsable produit pour Jetpack Compose.

  • Bonjour à tous, je m'appelle Anton et je suis ingénieur dans l'équipe Compose.

  • Je travaille sur le nouveau moteur de rendu A2UI de Jetpack Compose, que nous sommes ravis de vous présenter aujourd'hui.

  • Oui, avec l'essor des capacités de l'IA, de nombreuses applications cherchent à l'utiliser pour améliorer ou créer des expériences pour leurs utilisateurs.

  • L'une des expériences passionnantes que certains envisagent est l'UI générative, qui permet aux backends d'agents de créer de toutes nouvelles UI pour vos utilisateurs au moment de l'exécution, ce qui nous semble très intéressant.

  • Malheureusement, à l'heure actuelle, de nombreuses réponses de backends d'agents sont piégées dans des pavés de texte, comme celui-ci.

  • Voici un exemple où j'ai demandé à un agent de générer une nouvelle fonctionnalité pour réserver un voyage.

  • Voici le prompt :

  • Comme vous pouvez le voir, la réponse est plutôt correcte, mais elle est noyée dans un pavé de texte avec lequel je ne peux pas vraiment interagir facilement et qui ne ressemble en rien au reste de mon application.

  • L'UI générative vous permet de créer une expérience comme celle-ci, où vous pouvez générer une nouvelle UI à la volée en réponse à une entrée dynamique, ce qui ouvre la voie à davantage de personnalisation ou de nouvelles expériences.

  • Voici un exemple d'interface utilisateur créée à l'aide de composants standards et natifs.

  • Vous pouvez donc tout à fait créer ce type d'expérience vous-même dès aujourd'hui, mais vous devrez définir votre propre format d'échange avec le backend, votre propre type de flux d'événements, vos propres fonctions, etc.

  • A2UI, qui signifie "Agent-to-UI", est un protocole ouvert qui utilise un format JSON pour définir tous ces éléments.

  • L'un des aspects que j'apprécie vraiment dans A2UI est son format déclaratif.

  • L'agent renvoie simplement une représentation JSON de l'interface utilisateur à afficher.

  • Il ne renvoie aucun code que votre client doit ensuite exécuter, ce qui peut vous exposer à certaines failles de sécurité.

  • Il est simplement déclaratif.

  • Quels autres concepts devez-vous connaître concernant l'A2UI ?

Concepts fondamentaux : catalogues et surfaces

  • Il n'y a que deux concepts principaux : les catalogues et les surfaces.

  • Le catalogue contient des composants tels que du texte, un bouton, une ligne et une colonne, que l'agent peut utiliser pour créer l'UI.

  • Un catalogue comporte également des fonctions que l'agent peut utiliser pour mettre en forme du texte, valider les entrées utilisateur ou ouvrir des URL.

  • Un catalogue est un contrat qui définit les composants et les fonctions compatibles avec votre application. L'agent ne peut créer qu'avec ces éléments.

  • Cela signifie que, comme l'a mentionné Nick, l'agent n'exécute jamais de code arbitraire dans votre application.

  • Il compose une UI à partir des composants de base que vous concevez et auxquels vous faites confiance.

  • Un autre concept important est la surface.

  • Une surface est un élément de l'UI que l'agent contrôle, comme l'exemple de planificateur d'itinéraire sur la diapositive.

  • L'agent décrit l'UI d'une surface en JSON à l'aide des messages du protocole A2UI, comme createSurface, updateComponents, et en utilisant les composants et les fonctions définis dans votre catalogue.

  • En parlant de catalogue, il s'agit d'un concept fondamental dans A2UI, car il contrôle les composants que l'agent peut utiliser pour créer une interface.

  • La spécification A2UI définit ce que l'on appelle le catalogue de base, qui est un ensemble de composants courants que vous pouvez utiliser pour créer la plupart des interfaces utilisateur.

  • Le moteur de rendu A2UI de Jetpack Compose fournit une implémentation du catalogue de base à l'aide de composants Material 3.

  • La plupart des composants du catalogue sont prêts à l'emploi.

  • Il existe trois composants multimédias : image, vidéo et lecteur audio, où vous devez brancher la bibliothèque de votre choix.

  • Par exemple, vous pouvez choisir Coil pour le chargement des images et Media3 pour le lecteur vidéo et audio.

  • Mais le véritable intérêt d'A2UI et de notre nouvelle bibliothèque de moteurs de rendu est que vous pouvez créer vos propres composants et catalogues entièrement personnalisés, et contrôler totalement ce que l'agent peut utiliser pour créer l'UI.

  • Nous reviendrons sur les catalogues plus tard.

  • Voyons à quoi cela ressemble.

  • Passons maintenant à la machine de démonstration.

  • Nous allons utiliser notre application de démonstration et créer le moteur de rendu A2UI pour vous montrer à quel point c'est simple.

  • Il s'agit d'un ensemble de requêtes, comme vous pouvez le voir dans ces petits chips au milieu de l'écran, que vous pouvez envoyer à un agent.

  • Si nous cliquons sur l'un des prompts, il est envoyé à un backend d'agent réel et actif. Croisons les doigts pour la démo. En plus du prompt, nous ajoutons également des instructions système.

  • Nous indiquons donc au backend le protocole A2UI, le catalogue de composants (dans ce cas, le catalogue de base que nous utilisons ici) qu'il peut utiliser pour créer l'UI, et nous lui donnons quelques exemples de création.

  • L'implémentation de la session de chat dans notre application de démonstration sait déjà comment extraire les messages A2UI JSON bruts des réponses de l'agent.

  • Dans ce cas, l'agent a répondu avec du texte suivi de messages de protocole A2UI : createSurface, updateComponents.

  • Mais pour le moment, notre application affiche simplement ce JSON brut et ne l'affiche pas encore en tant qu'UI.

  • Avant de passer à l'implémentation du code, revenons un instant aux diapositives pour vous présenter la conception du système que nous allons utiliser.

  • Voilà.

Architecture de l'application et traitement des messages

  • Voici le plan.

  • L'agent IA diffuse donc une réponse dans notre application, qui est reçue par la classe ChatSession.

  • La ChatSession extrait les messages JSON A2UI bruts des réponses de l'agent au fur et à mesure qu'ils sont diffusés et les transmet au processeur de messages A2UI.

  • Le processeur de messages est configuré avec un analyseur qui convertit ces messages JSON bruts en une représentation sémantique.

  • Le processeur valide ensuite les messages par rapport à la définition du catalogue et les mappe à un ensemble de modèles de surface.

  • Les modèles de surface définissent l'apparence de l'UI et les éléments de l'UI que l'agent contrôle.

  • Ces modèles de surface sont ensuite affichés en tant qu'UI Compose native à l'aide du composable de surface A2UI fourni par notre bibliothèque.

  • La communication entre l'agent et l'UI est bidirectionnelle. Ainsi, lorsque l'utilisateur interagit avec l'UI, les actions et les erreurs de l'utilisateur sont renvoyées à l'agent.

  • En résumé, il existe trois composants principaux : le catalogue, le processeur de messages et la surface.

  • Écrivons le code correspondant.

  • Oui, construisons-le si nous pouvons changer de machine.

  • La toute première étape consiste à ajouter la dépendance.

  • Ici, vous pouvez voir que nous avons déjà inclus la dépendance Material 3 A2UI et que nous avons déjà synchronisé le projet pour ne pas avoir à attendre que Gradle termine.

  • Il s'agit de la dépendance de premier niveau qui définit l'implémentation du catalogue de base à l'aide de ces composants Material 3, mais il existe d'autres dépendances plus précises en dessous.

  • Si vous ne souhaitez pas utiliser le catalogue de base (nous vous expliquerons pourquoi plus tard), vous pouvez utiliser ces catalogues plus précis.

  • Mais cela entraînera l'importation du reste de manière transitive.

  • OK.

  • Nous avons donc dit qu'il y avait trois choses.

  • La première étape consistait à définir un catalogue.

  • Dans notre application, nous allons donc créer une fonction de fabrique pour nous aider à créer une instance de cette implémentation Material 3 du catalogue de base.

  • Nous utilisons donc cette fonction de bibliothèque, le catalogue de base Material A2UI, qui est également versionné.

  • Comme nous l'avons dit, elle fournit tous les composants courants dont vous avez besoin, mais nous ne voulons pas vous forcer à utiliser certaines bibliothèques de chargement d'images ou de rendu multimédia. Vous devez donc les fournir vous-même.

  • Ici, par exemple, Anton a déjà créé ce wrapper autour du composable AsyncImage de Coil, que nous utiliserons pour charger des images, ainsi qu'une logique supplémentaire pour que notre démo recherche des images sur Wikipédia.

  • Nous pouvons donc très facilement l'intégrer aux composants du catalogue.

  • De même, nous avons des implémentations similaires pour l'affichage, la vidéo et l'audio, où nous allons utiliser la bibliothèque Media3 pour les afficher avec ExoPlayer.

  • De cette façon, nous pouvons très simplement y intégrer nos propres composants.

  • Le catalogue a ensuite besoin d'un ensemble de fonctions standards.

  • Il s'agit donc des fonctions que le client peut fournir.

  • Encore une fois, nous fournissons une implémentation de cette interface avec le catalogue de base, mais nous devons lui fournir un contexte, que vous utiliserez pour effectuer certaines actions, comme l'ouverture d'un URI si un utilisateur appuie sur un élément.

  • Et voilà, la première étape est terminée.

  • Nous avons créé un catalogue.

  • L'étape suivante consiste à configurer le processeur de messages.

  • C'est donc ce qui analyse les réponses, etc.

  • Nous allons donc passer au modèle de vue de notre application, où nous allons d'abord instancier le catalogue que nous avons configuré précédemment, car nous en aurons besoin.

  • Nous allons ensuite créer cet objet d'analyseur et de processeur de messages, qui doit avoir accès au catalogue pour valider les réponses par rapport aux composants que nous voulons utiliser.

  • Nous devons également créer un analyseur, qui est l'objet chargé d'analyser les messages JSON lorsqu'ils sont renvoyés.

  • C'est ici que nous créons l'objet ChatSession.

  • Comme je l'ai déjà mentionné, ChatSession extrait déjà les messages A2UI bruts des réponses de l'agent.

  • Et lorsque chaque nouveau message A2UI est prêt, il appelle ce rappel onA2uiMessage.

  • Il nous suffit donc de transmettre chacun de ces messages au processeur lorsqu'ils arrivent.

  • Nous appelons la fonction processInput, et lui transmettons un analyseur et un message.

  • La fonction processInput analyse donc chaque message, puis met en file d'attente le travail du processeur pour le valider par rapport au catalogue et mettre à jour les modèles de surface.

  • Le processeur expose donc l'ensemble actuel de modèles de surface actifs en tant que StateFlow activeSurfaces.

  • La classe ChatSession contient une logique permettant d'ajouter les modèles de surface aux états d'UI des réponses de l'agent à afficher.

  • Nous transmettons donc simplement ce StateFlow activeSurfaces en tant que paramètre à ChatSession.

  • Comme je l'ai mentionné, la fonction processInput met en file d'attente le travail du processeur.

  • Mais pour lancer le traitement, nous devons lancer une coroutine sur un thread d'arrière-plan.

  • Nous allons donc utiliser un champ d'application ViewModel et définir le coordinateur par défaut.

  • Dans cette coroutine, il nous suffit d'appeler processor.collectMessages.

  • Ainsi, tant que cette coroutine est active, le processeur traite les messages de la file d'attente.

  • Nous devons donc également effacer les surfaces actuelles lorsque l'utilisateur efface l'historique des discussions.

  • Nous appelons donc clearSurfaces.

Afficher les modèles de surface dans l'UI

  • OK.

  • Nous avons donc effectué les deux premières étapes.

  • Nous avons créé le catalogue et le processeur pour analyser les messages.

  • L'étape suivante consiste à afficher ces réponses dans notre UI.

  • Nous collectons donc ces surfaces au fur et à mesure qu'elles sont analysées par l'analyseur.

  • Dans notre UI, nous allons maintenant les transmettre dans cet objet de réponse de l'agent et nous allons simplement les itérer.

  • Nous utiliserons ensuite ce composable de surface A2UI et lui transmettrons chaque modèle au fur et à mesure que nous les parcourons, afin d'afficher le contenu là où nous le souhaitons.

  • Notez que l'agent peut choisir d'utiliser une liste s'il décide d'afficher une grande quantité de contenu.

  • Nous devons donc définir une hauteur maximale pour ce composant afin de nous assurer qu'il s'affiche correctement.

  • Nous avons ainsi implémenté un moteur de rendu A2UI de base.

  • Si nous revenons à notre application, que nous la relançons et que nous saisissons à nouveau ce prompt, l'agent devrait l'utiliser pour afficher une véritable UI.

  • Nous allons donc coder cela en direct.

  • Ce n'est pas de la poudre aux yeux.

  • Mais oui, il utilise de vraies images.

  • Il utilise des onglets, des boîtes de dialogue, des sélecteurs de date, des curseurs, etc. (tous les composants Material standards que vous utiliseriez pour créer votre application) plutôt que de simplement afficher une réponse textuelle.

  • Comme il s'agit d'une vraie application utilisant de vrais composants Material, elle fait ce que font les vrais composants.

  • Il offre des performances similaires à celles d'une application native.

  • Il permet par exemple de répondre au mode sombre ici.

  • Il s'agit des composants Material.

  • Ainsi, si vous avez un composable de thématisation Material externe dans lequel vous avez personnalisé les couleurs, les formes, la typographie, etc., ces composants les récupéreront pour qu'ils ressemblent au reste de votre application.

  • Comme Nick l'a mentionné, comme il s'agit de composants Compose natifs, ils ont une sémantique native.

  • Cela signifie que toutes les fonctionnalités d'accessibilité intégrées de Compose sont prises en charge.

  • Ainsi, des fonctionnalités telles que TalkBack, la mise à l'échelle des polices et la navigation au clavier fonctionnent d'emblée.

  • Comme vous appelez un agent pour générer une nouvelle UI au moment de l'exécution, il peut y avoir une certaine latence.

  • Vous devrez peut-être attendre que l'agent réponde.

  • La bibliothèque fait tout son possible pour réduire cette latence.

  • Nous allons donc injecter des temps d'attente pour vous montrer un peu plus lentement ce qui se passe et comment nous minimisons ce problème.

  • Oui, comme l'agent peut mettre un certain temps à diffuser la réponse complète, nous utilisons ce que l'on appelle le rendu progressif.

  • Dès que le composant racine arrive, nous commençons à afficher l'UI de la surface.

  • Tous les composants de conteneur affichent des espaces réservés de chargement pour leurs enfants si ceux-ci ne sont pas encore arrivés.

  • Lorsque les composants enfants arrivent, ils sont animés à l'aide du schéma de mouvement Material 3.

  • OK.

  • Passons à la requête suivante pour montrer une autre fonctionnalité de la bibliothèque.

  • Voici un autre prompt, dans lequel nous réservons une table pour le dîner.

  • Ici, l'agent choisit d'utiliser ce composant de curseur. Oui, il l'a utilisé.

  • Comme vous pouvez le voir, vous pouvez déplacer l'UI créée à la volée par l'agent et interagir avec elle.

  • Notez que le nombre n'est pas seulement modifié sur le composant, mais aussi dans la chaîne de format plus bas, où il est compté.

  • Dites-moi que cela ne nécessite pas un aller-retour vers l'agent juste pour mettre à jour cette information.

  • Il s'agit d'une liaison bidirectionnelle.

  • Le composant de curseur est donc lié au modèle de données en mémoire de cette surface.

  • Tous les composants liés au même chemin d'accès dans le modèle de données sont automatiquement mis à jour lorsque la valeur change.

  • Le curseur lit donc la valeur à afficher et, lorsque je le fais glisser, il l'écrit dans le modèle pour le mettre à jour.

  • Tout cela se passe donc localement, sans aller-retour vers l'agent.

  • Mais nous pouvons également demander à l'agent de mettre à jour des données.

  • Disons donc six personnes.

  • Faites attention au curseur lorsque l'agent répond.

  • Il vient de passer à 6.

  • Voyons ce que nous avons reçu de l'agent.

  • Au lieu d'envoyer une toute nouvelle description de l'UI, l'agent a simplement envoyé un petit message updateDataModel qui a mis à jour le chemin d'accès du curseur.

  • Comme le curseur est lié à ce chemin, il a été mis à jour sur place.

  • Le modèle de données de chaque surface est donc basé sur l'état de l'instantané Compose, et chaque propriété de composant liée à un chemin d'accès est un état dérivé.

  • Ainsi, chaque fois que l'utilisateur ou l'agent met à jour la valeur dans le modèle de données, tous les composants liés à la même valeur sont automatiquement recomposés.

  • OK.

  • Imaginons que nous voulions passer à l'action et appuyer sur le bouton "Réserver". Que va-t-il se passer, Anton ?

  • Rien ne se passe pour le moment.

  • Le bouton distribue une action, mais nous ne l'envoyons pas encore à l'agent.

  • Résolvons ce problème.

Gérer les actions des utilisateurs et les flux d'événements

  • Tout comme nous avons dû nous connecter au processeur pour traiter les entrées, nous devons également nous connecter à toutes les sorties, c'est-à-dire à toutes les actions que l'utilisateur effectue sur l'UI créée par l'agent.

  • Pour ce faire, nous utilisons le flux outboundEvents exposé par le processeur et nous collectons les données.

  • C'est donc l'occasion d'intégrer la logique de votre choix en fonction, par exemple, de l'action de l'utilisateur ou de son interaction avec l'UI.

  • Ici, nous renvoyons toutes les actions à l'agent pour qu'il décide de la marche à suivre, mais vous pouvez également décider à ce stade d'appeler votre propre backend ou d'accéder à un autre écran de votre application. C'est vous qui décidez.

  • Ici, Anton l'a branché. Désormais, lorsque nous cliquons sur l'action "Confirmer la réservation", l'action que vous avez effectuée est renvoyée à l'agent.

  • C'est là que vous pouvez décider d'appeler une fonctionnalité dans le backend.

  • Voyons ce que nous envoyons réellement à l'agent.

  • Il s'agit du message d'action A2UI.

  • Il possède un contexte, et ce contexte contient un ensemble de propriétés que l'agent a demandé d'envoyer lorsque cette action est effectuée.

  • Ainsi, lorsque je clique sur le bouton, ces propriétés spécifiques sont résolues à ce moment-là en fonction des valeurs actuelles du modèle de données, et c'est ce que nous envoyons à l'agent.

  • Jusqu'à présent, l'agent s'est montré plutôt coopératif, mais nous parlons d'IA, et l'IA peut faire des erreurs.

  • Voyons ce qui se passe si l'agent renvoie une mauvaise réponse, par exemple s'il fait une erreur dans l'interface utilisateur qu'il nous décrit.

  • Dans cette dernière requête, nous demandons à l'agent de faire une erreur pour vous montrer comment cela se passe.

  • Voyons ce qui se passe.

  • Oui.

  • Comme vous pouvez le voir, l'un des composants est marqué d'une erreur.

  • Et maintenant, il est corrigé.

  • C'est ce qu'on appelle la boucle d'autocorrection de l'agent.

  • Avant que chaque composant n'atteigne l'UI, nous validons ce message par rapport au schéma du catalogue.

  • Si le composant ne correspond pas au schéma, il est marqué comme une erreur.

  • Le moteur de rendu de l'implémentation du catalogue de base affiche cette erreur isolée dans ce composant spécifique, tandis que le reste de l'UI est toujours affiché.

  • En coulisses, le moteur de rendu envoie un message d'erreur à l'agent pour lui indiquer ce qui n'a pas fonctionné.

  • Dans ce cas, nous avons envoyé un message indiquant que le type de composant "unknown type that does not exist" n'a pas été trouvé dans le catalogue.

  • L'agent a ensuite pu envoyer une réponse de suivi qui a mis à jour l'UI existante, et l'UI a été corrigée.

  • Oui.

  • Dans l'implémentation actuelle du catalogue de base Material, nous affichons les erreurs immédiatement. Toutefois, dans votre composant personnalisé, vous pouvez décider de continuer à afficher un indicateur de chargement pour un composant en cas d'erreur, en attendant que l'agent la corrige.

  • De cette façon, l'erreur peut être complètement masquée pour l'utilisateur.

Créer des composants personnalisés et étendre des catalogues

  • En parlant de catalogues et de composants personnalisés, jusqu'à présent, nous n'avons utilisé que le catalogue de base tel qu'il est fourni, mais vous pouvez bien sûr le personnaliser selon vos besoins.

  • Comme vous pouvez le voir, lorsque nous utilisons la fonction de fabrique pour créer le catalogue, des valeurs par défaut sont utilisées pour tous les composants.

  • Vous pouvez donc facilement intégrer vos propres composants si vous le souhaitez.

  • Par exemple, si nous voulons intégrer notre propre système de conception dans notre application et son propre bouton, nous pouvons facilement le faire.

  • Nous allons donc simplement remplacer ce paramètre lorsque nous créerons une instance du catalogue, et nous créerons cette définition de composant.

  • Si nous créons une classe pour l'implémenter, vous pouvez voir qu'il existe une interface ici, le catalogue de base A2UI de type bouton.

  • Si nous l'implémentons, nous pouvons brancher notre propre composant.

  • Oui.

  • Comme nous remplaçons un composant du catalogue de base, nous devons implémenter cette interface de bouton du catalogue de base A2UI.

  • Cette interface contient donc déjà des informations sur le composant, comme le nom, la description et le schéma de propriété.

  • Pour implémenter notre propre version, il nous suffit donc d'implémenter cette fonction composable.

  • Il nécessite quelques paramètres.

  • Ces paramètres sont donc des valeurs reçues de l'agent après validation.

  • Il s'agit par exemple de l'ID enfant, qui fait référence à un composant qui sera le contenu du bouton.

  • Il comporte également une variante qui décrit le type d'un bouton, la définition de l'action et d'autres paramètres.

  • Dans l'implémentation en production du bouton, nous devrions utiliser tous ces paramètres, mais pour notre démonstration, nous n'avons besoin que de l'enfant et de l'action.

  • Implémentons ce composable.

  • Dans cette fonction composable, nous allons émettre notre propre type de bouton "J'aime" personnalisé.

  • Comme vous pouvez le voir, nous pouvons utiliser les paramètres fournis et validés par A2UI.

  • Par exemple, dans la fonction onClick, nous pouvons distribuer cette action.

  • Nous pouvons utiliser ce type de bibliothèque DispatchAction et lui transmettre l'action définie par A2UI.

  • Nous utilisons donc notre propre bouton, mais nous voulons toujours afficher le contenu enfant, le libellé ou tout autre élément que l'agent a décidé d'utiliser.

  • Pour ce faire, nous utilisons la fonction observeA2uiComponentState et lui transmettons l'ID enfant afin d'obtenir un handle sur le contenu enfant à afficher.

  • Nous utiliserons ensuite cette fonction composable A2uiComponent et lui transmettrons l'état du composant enfant pour l'afficher.

  • Ici, vous pouvez voir que nous n'affichons le contenu enfant que s'il a été chargé correctement. Toutefois, vous pouvez également définir votre propre logique pour décider de ce que vous souhaitez afficher pendant le chargement ou en cas d'erreur.

  • Revenons à l'un des exemples de requêtes de notre application de démonstration et remplaçons le bouton par le bouton Material standard.

  • C'est très subtil, j'espère que vous le voyez.

  • Comme vous pouvez le voir, nous utilisons également un bouton très personnalisé ici.

  • Voilà comment remplacer certains types de la bibliothèque. Mais comment l'étendre avec vos propres types personnalisés ?

  • Pour ce faire, il suffit d'ajouter des éléments au catalogue.

  • Anton va donc créer une classe pour implémenter un composant de notation par étoiles.

  • Nous allons donc ajouter un élément à notre application pour nous permettre de noter les restaurants pendant ce voyage hypothétique.

  • Comme nous ne remplaçons pas un composant existant, nous utilisons cette interface plus générique, l'interface de composant A2UI, qui, comme vous pouvez le voir, comporte quelques paramètres que nous devons remplacer.

  • Les deux premiers sont le nom et la description.

  • Cela décrit ce qu'est notre composant et ce qu'il fait.

  • C'est très important, car lorsque nous sérialisons le catalogue et l'envoyons au backend pour lui indiquer ce qu'il peut utiliser pour créer une UI, c'est ce que l'agent utilisera pour décider d'utiliser ou non ce composant lorsqu'il répondra.

  • Vous devez donc être assez descriptif et indiquer à l'IA que vous savez exactement ce qu'elle fait.

  • La propriété suivante que nous devons remplacer est "properties", qui définit les données que ce composant traite.

  • Nous allons donc créer une barre de notation par étoiles pour que vous puissiez évaluer un restaurant. Nous aurons besoin de deux propriétés.

  • Il s'agit de la valeur, par exemple le nombre d'étoiles sélectionnées par l'utilisateur, qui sera une propriété modifiable avec laquelle l'utilisateur pourra interagir et qu'il pourra modifier, ainsi que le nombre maximal d'étoiles.

  • Ici, nous pouvons voir qu'Anton utilise cette propriété de type numérique dynamique pour la valeur.

  • Cela correspond au nombre d'étoiles sélectionnées.

  • qui possède également sa propre description indiquant à l'agent à quoi sert cette valeur.

  • Nous avons ensuite une propriété de type statique. Nous utilisons donc simplement cette propriété A2UI de type "number" pour définir le nombre maximal d'étoiles que ce composant peut afficher.

  • Une fois ces deux propriétés définies, il nous suffit de les renvoyer.

  • Nous allons donc remplacer cette propriété "properties" par une liste et renvoyer ces propriétés.

  • C'est ainsi que vous indiquez à l'agent ce que ce composant traite.

  • Oui.

  • Comme Nick l'a mentionné, ces définitions de propriétés sont envoyées dans le schéma du catalogue pour que l'agent sache comment utiliser ce composant.

  • Mais cette propriété, les mêmes définitions de propriété sont également utilisées pour extraire des valeurs typées au moment de l'exécution.

  • Il n'existe donc qu'une seule source d'informations.

  • Voyons ce que nous devons faire pour implémenter cela.

  • Pour accéder à la propriété "max", par exemple, comme il s'agit d'une propriété statique, l'agent envoie sa valeur de manière intégrée dans l'objet "properties".

  • Pour y accéder, il nous suffit donc de l'obtenir à partir de l'objet properties en le référençant à l'aide de l'objet proper-max property comme clé.

  • Comme cette propriété est définie en tant que nombre, nous devons également la convertir en entier et définir la valeur par défaut.

  • La propriété suivante est "value".

  • Comme la propriété "value" est une propriété dynamique, cela signifie qu'elle fait référence à un élément du modèle de données.

  • Pour y accéder, nous devons donc utiliser l'API properties.bind et transmettre l'objet de propriété en tant que clé comme paramètre.

  • Cela renvoie la valeur actuelle stockée dans le modèle de données pour cette propriété et s'abonne également aux futures mises à jour.

  • Ainsi, chaque fois que le modèle de données est mis à jour, ce composant se recompose automatiquement.

  • De même, nous avons converti la valeur en entier et défini une valeur par défaut.

  • Nous devons également configurer une liaison bidirectionnelle pour pouvoir modifier la note.

  • Pour ce faire, nous utilisons l'API bindUpdater.

  • Nous transmettons le même objet de propriété qui définit la propriété. Par conséquent, nous obtenons un lambda qui nous permet de modifier une valeur pour cette propriété dans le modèle de données.

  • Nous sommes maintenant prêts à émettre notre composant de notation par étoiles.

  • OK.

  • Maintenant que nous avons défini notre propre composant personnalisé, nous devons l'ajouter au catalogue que nous utilisons.

  • Auparavant, nous utilisions simplement le catalogue de base tel quel.

  • Nous devons maintenant créer un catalogue qui reprendra l'ensemble du catalogue de base et y ajoutera notre composant personnalisé.

  • Un catalogue est donc défini par trois éléments.

  • Il s'agit d'un ID, d'un ensemble de composants et d'un ensemble de fonctions.

  • Pour l'ID, nous utiliserons un URI pour définir un catalogue identifié de manière unique, ainsi que la version avec laquelle nous travaillons.

  • Ensuite, pour les composants, nous allons prendre tous les composants du catalogue de base, puis nous utiliserons l'opérateur d'ajout pour ajouter le composant personnalisé que nous venons de créer.

  • Pour les fonctions, nous utiliserons simplement celles du catalogue de base.

  • Nous avons ainsi étendu le catalogue de base pour y ajouter notre nouveau type de composant personnalisé.

  • Comme vous pouvez le constater, il est assez facile de créer un catalogue personnalisé.

  • L'agent apprend donc à utiliser le catalogue à partir des instructions système que nous lui envoyons.

  • Maintenant que nous avons créé un catalogue personnalisé, mettons également à jour les instructions système.

  • Nous allons donc transmettre l'objet de catalogue à notre fonction qui crée les instructions système.

  • Voici donc la différence :

  • Jusqu'à présent, nous envoyions une définition statique du schéma pour le catalogue de base, car nous utilisions ce catalogue standard.

  • Maintenant que nous disposons d'un catalogue personnalisé, nous pouvons générer dynamiquement le schéma à l'aide de la fonction toJsonSchemaString.

  • Cela va créer une représentation JSON complète de ce catalogue, y compris le composant d'évaluation par étoiles que nous venons d'ajouter.

  • Les instructions système incluent également des exemples de réponses pour l'agent.

  • Nous modifions également l'ID du catalogue ici, afin que les exemples fassent référence au bon catalogue.

  • OK.

  • Nous avons ainsi étendu le catalogue de base pour ajouter nos propres types personnalisés.

  • Si nous redéployons l'application et essayons l'une de ces requêtes, nous devrions obtenir une UI d'évaluation.

  • L'agent répond ensuite.

  • Ici, vous pouvez voir que ce composant d'évaluation est un tout nouveau composant que l'agent n'avait jamais vu avant que nous ne l'implémentions à l'instant.

  • Il ne fait pas partie du catalogue de base, nous venons de le créer.

  • Lorsque je clique sur l'étoile, le lambda bindUpdater est appelé et la valeur est mise à jour dans le modèle de données.

  • Si nous envoyons les notes, nous pouvons examiner l'action. Le contexte de l'action inclura les valeurs que nous venons de définir.

  • Revenons maintenant aux diapositives.

  • Nous avons abordé beaucoup de choses assez rapidement, mais nous espérons vous avoir montré comment passer facilement d'agents qui répondent par un pavé de texte à des agents qui créent de véritables UI natives.

Points clés et ressources

  • Nous espérons vous avoir montré que l'intégration est assez simple.

  • Vous devez effectuer trois opérations principales : créer un catalogue de composants, implémenter une logique d'analyse, puis l'afficher dans votre UI.

  • Nous espérons vous avoir montré que la bibliothèque fait beaucoup de choses pour vous sans configuration préalable, comme l'affichage progressif ou la validation de votre schéma, les boucles d'autocorrection ou la gestion des exceptions. Elle fait beaucoup de choses.

  • Et nous espérons vous avoir démontré rapidement aujourd'hui qu'il est très facile de le personnaliser.

  • Nous avons montré comment le catalogue de base répond déjà à votre thématisation Material si vous utilisez Material, mais il est très simple d'étendre ou de remplacer des composants dans le catalogue.

  • Mais je pense que l'un des aspects les plus intéressants est que vous pouvez créer votre propre catalogue.

  • Si vous souhaitez contrôler au maximum l'UI que l'agent peut utiliser, vous pouvez créer des composants entièrement personnalisés.

  • Si vous voulez lui donner carte blanche, vous pouvez lui fournir des composants très basiques, comme des libellés de texte et des boutons, et lui dire : "Crée l'UI."

  • Si vous souhaitez plus de contrôle, vous pouvez créer des composants plus élaborés et dire : "Voici les seuls éléments que vous pouvez utiliser."

  • Vous contrôlez donc ce que l'agent peut faire et montrer à vos utilisateurs.

  • Voici la nouvelle bibliothèque de moteur de rendu A2UI et la bibliothèque Jetpack que nous venons de lancer.

    implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")
  • Il est actuellement en version alpha, et la version alpha 02 est sortie récemment.

  • Suivez ces liens pour les consulter.

  • Nous avons de la documentation sur developer.android.com, ainsi que quelques exemples.

  • Nous avons un exemple de base montrant le catalogue de base, ainsi qu'un exemple plus élaboré, et cette application exemple JetPacker que nous venons de créer, que vous verrez à nouveau dans la présentation ADS plus tard aujourd'hui, montrant comment vous pouvez créer ces composants plus élaborés pour contrôler ce que l'utilisateur voit.

  • Comme je l'ai dit, nous sommes actuellement en version alpha. C'est donc le moment idéal pour l'essayer et nous faire part de vos commentaires et de vos demandes de fonctionnalités, et nous dire comment vous aimeriez l'utiliser, ou si vous aimeriez l'utiliser s'il pouvait faire X ou Y.

  • N'hésitez pas à consulter la bibliothèque et à nous faire part de vos commentaires.

  • Et c'est ce que nous avons obtenu.

  • J'ai hâte de voir ce que vous et vos agents allez créer avec.

  • Merci beaucoup.

  • Merci.

Code

Extraits de code

Extrait de script Kotlin à 27:40 dans "Points clés à retenir et ressources"
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")