Intelligente Anwendungen

Mit A2UI können KI-Agenten in Ihren Android-Apps Benutzeroberflächen generieren

Lautsprecher

Info

Über den Vortrag

A2UI wird von webzentrierten Komponenten auf natives Android umgestellt. Mit dem neuen Jetpack Compose-Renderer für A2UI v0.9 können Entwickler dynamische, von LLMs generierte Benutzeroberflächen nativ rendern. Anstelle von WebViews werden ein Coroutine-gesteuertes Actor-Framework, Sealed-Class-Lebenszyklen und eine native Material 3-Integration verwendet.

In dieser Session wird die neue Architektur vorgestellt und erklärt, wie servergesteuerte Komponentenbäume in reaktive Compose-Knoten übersetzt werden und wie integrierte Layoutübergänge strukturelle Updates reibungslos verarbeiten, ohne die Benutzeroberfläche zu blockieren.

Zusammenfassung

  • A2UI bietet ein deklaratives, offenes JSON-Protokoll für agentische Backends, um dynamische Benutzeroberflächen anstelle von Textantworten zu beschreiben.
  • Das Protokoll basiert auf Katalogen, die zulässige Komponenten und Funktionen definieren, sowie auf Oberflächen, die von Agenten gesteuerte UI-Elemente darstellen.
  • Die A2UI-Bibliothek von Jetpack Compose rendert gestreamte Oberflächenmodelle nativ mit Material 3-Komponenten mit integrierter Bedienungshilfen und progressivem Rendering.
  • In-Memory-Datenmodelle aktualisieren gebundene UI-Elemente sofort, ohne den Agenten zu kontaktieren. Ausgehende Ereignisse leiten Nutzeraktionen zurück.
  • Entwickler können vorhandene Katalogelemente anpassen oder benutzerdefinierte Komponenten erstellen, um genau zu steuern, was der Agent anzeigen kann.

Ressourcen

Gemini Notebook

Themen

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

Transkript

Einführung in generative UI und A2UI

  • Hallo zusammen und herzlich willkommen zu diesem Vortrag. Wir sprechen heute darüber, wie ihr mit unserer brandneuen A2UI-Bibliothek Benutzeroberflächen in euren Android-Apps generieren könnt.

  • Mein Name ist Nick Butcher und ich bin Produktmanager für Jetpack Compose.

  • Hallo zusammen, ich bin Anton, ein Entwickler im Compose-Team.

  • Ich arbeite an diesem neuen Jetpack Compose-A2UI-Renderer, den wir Ihnen heute vorstellen möchten.

  • Ja, mit dem Aufkommen von KI-Funktionen prüfen viele App-Entwickler, wie KI ihnen helfen kann, die Nutzererfahrung zu verbessern oder neue Funktionen für ihre Nutzer zu entwickeln.

  • Eine der spannenden Funktionen, die sich einige Nutzer ansehen, ist die generative UI. Dabei können agentische Backends zur Laufzeit brandneue Benutzeroberflächen für Ihre Nutzer erstellen, was wir sehr interessant finden.

  • Leider sind die Antworten vieler agentischer Backends in Echtzeit in Textblöcken wie diesem gefangen.

  • Hier ist ein Beispiel, in dem ich einen KI-Agenten gebeten habe, eine neue Funktion für die Buchung einer Reise zu generieren.

  • Hier ist der Prompt:

  • Wie Sie sehen, ist die Antwort eigentlich ganz gut, aber sie ist in diesem Textblock gefangen, mit dem ich nicht wirklich einfach interagieren kann, und sie sieht ganz anders aus als der Rest meiner Anwendung.

  • Mit generativer UI können Sie stattdessen eine solche Umgebung erstellen, in der Sie spontan eine neue UI als Reaktion auf dynamische Eingaben generieren können. Das eröffnet Möglichkeiten für mehr Anpassung oder neue Funktionen.

  • Hier ist ein Beispiel für eine Benutzeroberfläche, die mit standardmäßigen, nativen Komponenten erstellt wurde.

  • Sie können diese Art von Umgebung also heute selbst erstellen, müssen aber ein eigenes Format für den Austausch mit dem Backend, einen eigenen Ereignisstream, eigene Funktionen usw. definieren.

  • A2UI steht für Agent-to-UI und ist ein offenes Protokoll im JSON-Format, mit dem all diese Dinge definiert werden.

  • Eines der Dinge, die mir an A2UI besonders gefallen, ist, dass es sich um ein deklaratives Format handelt.

  • Der KI-Agent gibt nur eine JSON-Darstellung der zu rendernden Benutzeroberfläche zurück.

  • Es wird kein Code zurückgegeben, den Ihr Client dann ausführen muss, was zu bestimmten Sicherheitslücken führen kann.

  • Es ist nur deklarativ.

  • Welche anderen Konzepte müssen wir also über A2UI wissen?

Wichtige Konzepte: Kataloge und Oberflächen

  • Es gibt nur zwei Hauptkonzepte: Kataloge und Oberflächen.

  • Der Katalog enthält Komponenten wie Text, Schaltfläche, Zeile und Spalte, mit denen der Agent die Benutzeroberfläche erstellen kann.

  • Ein Katalog enthält auch Funktionen, mit denen der Agent Text formatieren, Nutzereingaben validieren oder URLs öffnen kann.

  • Ein Katalog ist ein Vertrag, der definiert, welche Komponenten und Funktionen Ihre Anwendung unterstützt. Der Agent kann nur mit diesen Komponenten und Funktionen arbeiten.

  • Wie Nick bereits erwähnt hat, führt der Agent also nie beliebigen Code in Ihrer App aus.

  • Es erstellt eine Benutzeroberfläche aus den Bausteinen, die Sie entwerfen und denen Sie vertrauen.

  • Ein weiteres wichtiges Konzept ist die Oberfläche.

  • Eine Oberfläche ist ein Teil der Benutzeroberfläche, den der KI-Agent steuert, wie im Beispiel des Reiseberaters auf der Folie.

  • Der Agent beschreibt die Benutzeroberfläche für eine Oberfläche in JSON mithilfe der A2UI-Protokollnachrichten, z. B. createSurface und updateComponents, und mithilfe der in Ihrem Katalog definierten Komponenten und Funktionen.

  • Der Katalog ist ein sehr grundlegendes Konzept in A2UI, da er steuert, welche Komponenten der Agent zum Erstellen einer Oberfläche verwenden kann.

  • In der A2UI-Spezifikation wird der sogenannte Basiskatalog definiert. Das ist eine Reihe gängiger Komponenten, mit denen Sie die meisten Benutzeroberflächen erstellen können.

  • Der Jetpack Compose A2UI-Renderer bietet eine Implementierung des grundlegenden Katalogs mit Material 3-Komponenten.

  • Die meisten Katalogkomponenten sind sofort einsatzbereit.

  • Es gibt drei Medienkomponenten: Bild, Video und Audioplayer. Hier müssen Sie die gewünschte Bibliothek einbinden.

  • Sie können beispielsweise Coil für das Laden von Bildern und Media3 für den Video- und Audioplayer auswählen.

  • Die wahre Stärke von A2UI und unserer neuen Renderer-Bibliothek liegt jedoch darin, dass Sie Ihre eigenen, vollständig benutzerdefinierten Komponenten und Kataloge erstellen und vollständig steuern können, was der KI-Agent zum Erstellen der Benutzeroberfläche verwenden kann.

  • Wir kommen später auf Kataloge zurück.

  • Sehen wir uns das einmal an.

  • Können wir bitte zum Demogerät wechseln?

  • Das ist unsere Demo-App, die wir verwenden werden. Wir werden den A2UI-Renderer erstellen, um Ihnen zu zeigen, wie einfach das ist.

  • Es handelt sich also um eine Reihe von Prompts, die Sie an einen KI-Agenten senden können, wie Sie in diesen kleinen Chips in der Mitte des Bildschirms sehen können.

  • Wenn wir auf einen der Prompts klicken, wird er an ein echtes Live-Agent-Backend gesendet. Drücken wir also die Daumen für die Demo. Zusätzlich zum Prompt hängen wir auch einige Systemanweisungen an.

  • Wir informieren das Backend über das A2UI-Protokoll und den Katalog der Komponenten, in diesem Fall den grundlegenden Katalog, den wir hier verwenden, um die UI zu erstellen. Außerdem geben wir ihm einige Beispiele dafür, wie das geht.

  • Die Implementierung der Chatsitzung in unserer Demo-App kann bereits rohe JSON-A2UI-Nachrichten aus den Antworten des KI-Agenten extrahieren.

  • In diesem Fall hat der Agent mit Text geantwortet, gefolgt von A2UI-Protokollnachrichten: createSurface, updateComponents.

  • Derzeit zeigt unsere App jedoch nur diese JSON-Rohdaten an und rendert sie noch nicht als UI.

  • Bevor wir den Code implementieren, wechseln wir noch einmal kurz zu den Folien, um einen Schritt zurückzugehen und Ihnen das Systemdesign zu zeigen, das wir für die Implementierung verwenden werden.

  • Ah, da sind sie.

Anwendungsarchitektur und Nachrichtenverarbeitung

  • Das ist der Plan.

  • Der KI-Agent streamt also eine Antwort in unsere Anwendung, die von der Klasse „ChatSession“ empfangen wird.

  • Die ChatSession extrahiert unformatierte A2UI-JSON-Nachrichten aus den Antworten des KI-Agenten, während sie gestreamt werden, und übergibt sie an den A2UI Message Processor.

  • Der Message Processor ist mit einem Parser konfiguriert, der diese JSON-Rohnachrichten in eine semantische Darstellung konvertiert.

  • Der Prozessor validiert die Nachrichten dann anhand der Katalogdefinition und ordnet sie einer Reihe von Oberflächenmodellen zu.

  • Die Oberflächenmodelle definieren, wie die Benutzeroberfläche aussieht und welche Teile der Benutzeroberfläche der Agent steuert.

  • Diese Oberflächenmodelle werden dann mithilfe des von unserer Bibliothek bereitgestellten A2UI-Oberflächen-Composable als native Compose-UI gerendert.

  • Die Kommunikation zwischen dem KI-Agenten und der Benutzeroberfläche ist bidirektional. Wenn der Nutzer also mit der Benutzeroberfläche interagiert, werden die Nutzeraktionen und Fehler an den KI-Agenten zurückgegeben.

  • Zusammenfassend gibt es also drei Hauptbausteine: den Katalog, den Message Processor und die Oberfläche.

  • Schreiben wir den Code dafür.

  • Ja, lass uns das bauen, wenn wir die Maschinen wechseln können.

  • Der allererste Schritt ist das Hinzufügen der Abhängigkeit.

  • Hier sehen Sie, dass wir die Material 3-A2UI-Abhängigkeit bereits eingefügt und das Projekt synchronisiert haben, damit wir nicht auf Gradle warten müssen.

  • Dies ist die übergeordnete Abhängigkeit, die die Implementierung des grundlegenden Katalogs mit diesen Material 3-Komponenten definiert. Darunter gibt es jedoch weitere, detailliertere Abhängigkeiten.

  • Wenn Sie den einfachen Katalog nicht verwenden möchten, können Sie diese detaillierteren Kataloge verwenden. Warum Sie den einfachen Katalog möglicherweise nicht verwenden möchten, wird später erläutert.

  • Aber dadurch wird der Rest transitiv einbezogen.

  • Okay.

  • Wir haben also drei Dinge genannt.

  • Der erste Schritt bestand darin, einen Katalog zu definieren.

  • In unserer Anwendung erstellen wir eine Factory-Funktion, um eine Instanz der Material 3-Implementierung des einfachen Katalogs zu erstellen.

  • Wir verwenden hier also diese Bibliotheksfunktion, den Material-A2UI-Basiskatalog, der ebenfalls versioniert ist.

  • Wie bereits erwähnt, bietet diese Bibliothek alle gängigen Komponenten, die Sie benötigen. Wir möchten Sie jedoch nicht dazu zwingen, bestimmte Bibliotheken zum Laden von Bildern oder Rendern von Medien zu verwenden. Diese müssen Sie also selbst bereitstellen.

  • Hier hat Anton zum Beispiel bereits einen Wrapper um das AsyncImage-Composable von Coil erstellt, mit dem wir Bilder laden, sowie ein wenig zusätzliche Logik für unsere Demo, um Bilder von Wikipedia abzurufen.

  • Und so können wir das ganz einfach in die Katalogkomponenten einfügen.

  • Ähnliche Implementierungen gibt es für das Rendering, Video und Audio, wobei wir die Media3-Bibliothek verwenden, um sie mit ExoPlayer zu rendern.

  • So können wir ganz einfach unsere eigenen Komponenten einbinden.

  • Als Nächstes benötigt der Katalog eine Reihe von Standardfunktionen.

  • Das sind also Funktionen, die der Client bereitstellen kann.

  • Auch hier liefern wir eine Implementierung zusammen mit dem grundlegenden Katalog, müssen ihr aber einen Kontext bereitstellen, den Sie für bestimmte Aktionen verwenden, z. B. das Öffnen eines URI, wenn ein Nutzer auf ein Element tippt.

  • Damit ist der erste Schritt abgeschlossen.

  • Wir haben einen Katalog erstellt.

  • Als Nächstes müssen wir den Message Processor einrichten.

  • Das ist also das, was die Antworten und so weiter parst.

  • Wir wechseln also zum Ansichtsmodell unserer Anwendung, wo wir zuerst den zuvor eingerichteten Katalog instanziieren, da wir ihn benötigen werden.

  • Anschließend erstellen wir das Message Parser- und Message Processor-Objekt, das Zugriff auf den Katalog benötigt, um die Antworten anhand der Komponenten zu validieren, die wir verwenden möchten.

  • Eine weitere Komponente, die wir erstellen müssen, ist ein Parser. Das ist das Objekt, das für das Parsen der JSON-Nachrichten verantwortlich ist, wenn sie zurückgegeben werden.

  • Hier erstellen wir das ChatSession-Objekt.

  • Wie bereits erwähnt, extrahiert die ChatSession bereits die unbearbeiteten A2UI-Nachrichten aus den Antworten des KI-Agenten.

  • Und wenn eine neue A2UI-Nachricht bereit ist, wird dieser onA2uiMessage-Callback aufgerufen.

  • Wir müssen also nur jede dieser Nachrichten an den Prozessor weiterleiten, wenn sie eingehen.

  • Wir rufen die Funktion processInput auf und übergeben ihr einen Parser und eine Nachricht.

  • Die Funktion processInput parst also jede Nachricht und stellt die Arbeit für den Prozessor in die Warteschlange, um sie mit dem Katalog abzugleichen und die Oberflächenmodelle zu aktualisieren.

  • Der Prozessor stellt also die aktuellen aktiven Oberflächenmodelle als activeSurfaces-StateFlow bereit.

  • Die ChatSession enthält eine Logik, mit der die Oberflächenmodelle den UI-Zuständen der Agentenantworten hinzugefügt werden, um sie zu rendern.

  • Wir übergeben diesen activeSurfaces-StateFlow also einfach als Parameter an die ChatSession.

  • Wie bereits erwähnt, stellt die Funktion processInput die Arbeit für den Prozessor in die Warteschlange.

  • Um die Verarbeitung tatsächlich zu starten, müssen wir eine Koroutine in einem Hintergrundthread starten.

  • Dafür verwenden wir einen ViewModelScope und legen den Standard-Dispatcher fest.

  • In dieser Koroutine müssen wir nur „processor.collectMessages“ aufrufen.

  • Während diese Koroutine aktiv ist, verarbeitet der Prozessor also die Nachrichten aus der Nachrichtenwarteschlange.

  • Wenn der Nutzer den Chatverlauf löscht, müssen wir auch die aktuellen Oberflächen löschen.

  • Wir rufen also „clearSurfaces“ auf.

Oberflächenmodelle in der Benutzeroberfläche rendern

  • Okay.

  • Die ersten beiden Schritte haben wir also erledigt.

  • Wir haben den Katalog und den Prozessor zum Parsen der Nachrichten erstellt.

  • Als Nächstes müssen wir diese Antworten in unserer Benutzeroberfläche rendern.

  • Wir erfassen also diese Oberflächen, während sie vom Parser geparst werden.

  • In unserer Benutzeroberfläche übergeben wir diese nun in diesem Antwortobjekt des KI-Agenten und iterieren einfach darüber.

  • Dann verwenden wir das A2UI-Surface-Composable und übergeben ihm die einzelnen Modelle, während wir sie durchlaufen, um den Inhalt an der gewünschten Stelle zu rendern.

  • Der Agent kann sich dafür entscheiden, eine Liste zu verwenden, wenn er eine große Menge an Inhalten anzeigen möchte.

  • Daher müssen wir für diese Komponente eine maximale Höhe festlegen, damit sie richtig gerendert wird.

  • Damit haben wir einen einfachen A2UI-Renderer implementiert.

  • Wenn wir jetzt zu unserer App zurückkehren, sie neu starten und den Prompt noch einmal eingeben, sollte der KI-Agent diese Informationen verwenden, um eine echte Benutzeroberfläche zu rendern.

  • Wir programmieren das also live.

  • Das ist keine Täuschung.

  • Aber ja, es werden echte Bilder verwendet.

  • Es werden Tabs, Dialoge, Datumsauswahl, Schieberegler und alle Standard-Material-Komponenten verwendet, die Sie für die Erstellung Ihrer App verwenden würden, anstatt nur eine Textantwort anzuzeigen.

  • Da es sich um eine echte App mit echten Material-Komponenten handelt, macht sie die Dinge, die echte Komponenten tun.

  • Sie bietet eine Leistung, die der einer nativen App ähnelt.

  • Sie reagiert beispielsweise auf den dunklen Modus.

  • Das sind die Material-Komponenten.

  • Wenn Sie also ein äußeres Material Theme-Composable haben, bei dem Sie die Farben, Formen, Typografie usw. angepasst haben, werden diese Komponenten übernommen, sodass es wie der Rest Ihrer Anwendung aussieht.

  • Wie Nick bereits erwähnt hat, haben diese nativen Compose-Komponenten eine native Semantik.

  • Das bedeutet, dass alle integrierten Bedienungshilfen von Compose unterstützt werden.

  • So funktionieren TalkBack, Schriftgrößenanpassung und Tastaturnavigation sofort.

  • Da Sie einen KI-Agenten aufrufen, um zur Laufzeit eine neue Benutzeroberfläche zu generieren, kann es zu Latenz kommen.

  • Möglicherweise müssen Sie warten, bis der KI-Agent antwortet.

  • Die Bibliothek tut alles, um diese Latenz zu minimieren.

  • Wenn wir diese Art von Wartezeiten einfügen, können wir Ihnen etwas langsamer zeigen, was passiert und wie wir das minimieren.

  • Ja, da es einige Zeit dauern kann, bis der KI-Agent die vollständige Antwort streamt, verwenden wir das sogenannte progressive Rendering.

  • Sobald die Stammkomponente eintrifft, wird die Benutzeroberfläche für die Oberfläche angezeigt.

  • Alle Containerkomponenten zeigen Ladeplatzhalter für ihre untergeordneten Elemente an, wenn diese noch nicht geladen wurden.

  • Wenn die untergeordneten Komponenten eintreffen, werden sie mit dem Material 3-Bewegungsschema animiert.

  • Okay.

  • Wechseln wir zum nächsten Prompt, um eine weitere Funktion der Mediathek zu zeigen.

  • Hier ist ein weiterer Prompt, mit dem wir einen Tisch für das Abendessen reservieren.

  • Und hier entscheidet sich der KI-Agent, diese Schieberegler-Komponente zu verwenden. Ja, er hat sie verwendet.

  • Hier sehen Sie, dass Sie die Benutzeroberfläche, die der KI-Agent spontan erstellt hat, verschieben und mit ihr interagieren können.

  • Beachten Sie, dass sich nicht nur die Zahl in der Komponente ändert, sondern auch der Formatstring weiter unten, in dem die Zahl gezählt wird.

  • Like tell me that's not doing a round trip back to the agent just to update that, right?

  • Dies ist eine bidirektionale Bindung.

  • Die Schiebereglerkomponente ist also an das In-Memory-Datenmodell dieser Oberfläche gebunden.

  • Alle Komponenten, die an denselben Pfad im Datenmodell gebunden sind, werden automatisch aktualisiert, wenn sich der Wert ändert.

  • Der Schieberegler liest also den anzuzeigenden Wert und schreibt ihn beim Ziehen des Schiebereglers zurück in das Modell, um es zu aktualisieren.

  • All das geschieht lokal, ohne dass der Agent kontaktiert werden muss.

  • Wir können den Agenten aber auch bitten, Daten zu aktualisieren.

  • Sagen wir also 6 Personen.

  • Achten Sie auf den Schieberegler, wenn der KI-Agent antwortet.

  • Es ist gerade auf 6 gesprungen.

  • Sehen wir uns an, was wir vom KI-Agenten erhalten haben.

  • Anstatt eine komplett neue Beschreibung der Benutzeroberfläche zu senden, hat der Agent nur eine kleine Nachricht updateDataModel gesendet, die den Pfad für den Schieberegler aktualisiert hat.

  • Da der Schieberegler an diesen Pfad gebunden ist, wurde er direkt aktualisiert.

  • Das Datenmodell jeder Oberfläche basiert also auf dem Compose-Snapshot-Status und jede an einen Pfad gebundene Komponenteneigenschaft ist ein abgeleiteter Status.

  • Wenn der Nutzer oder der Agent den Wert im Datenmodell aktualisiert, werden alle Komponenten, die an denselben Wert gebunden sind, automatisch neu zusammengesetzt.

  • Okay.

  • Angenommen, wir möchten aktiv werden und auf die Schaltfläche „Reservieren“ klicken. Was passiert dann, Anton?

  • Derzeit passiert nichts.

  • Die Schaltfläche löst zwar eine Aktion aus, diese wird aber noch nicht an den KI-Agenten gesendet.

  • Das können wir ändern.

Nutzeraktionen und Ereignisabläufe verarbeiten

  • Okay, so wie wir uns mit dem Prozessor verbinden mussten, um die Eingaben zu verarbeiten, müssen wir uns auch mit allen Ausgaben verbinden, also mit allen Aktionen, die der Nutzer auf der vom Agenten erstellten Benutzeroberfläche ausführt.

  • Dazu verwenden wir den outboundEvents-Flow, der vom Prozessor bereitgestellt wird, und erfassen die Daten.

  • Hier können Sie also jede gewünschte Logik einfügen, die auf den Aktionen des Nutzers basiert, z. B. wenn er auf eine Aktion tippt oder etwas mit der UI macht.

  • Hier senden wir alle Aktionen zurück an den Agenten, der dann entscheidet, was zu tun ist. Sie können an dieser Stelle aber auch entscheiden, ob Sie Ihr eigenes Backend aufrufen oder zu einem anderen Bildschirm in Ihrer Anwendung wechseln möchten. Das bleibt Ihnen überlassen.

  • Anton hat die Aktion „Reservierung bestätigen“ ausgewählt. Diese Aktion wird nun an den KI-Agenten zurückgesendet.

  • Und hier können Sie im Backend sogar entscheiden, eine Funktion aufzurufen.

  • Sehen wir uns an, was wir tatsächlich an den Agenten senden.

  • Dies ist die A2UI-Aktionsnachricht.

  • Sie hat einen Kontext, der eine Reihe von Attributen enthält, die der Agent bei der Ausführung dieser Aktion senden soll.

  • Wenn ich also auf den Button klicke, werden diese bestimmten Attribute in diesem Moment anhand der aktuellen Werte im Datenmodell aufgelöst und an den KI-Agenten gesendet.

  • Okay, bisher war der KI-Agent ziemlich kooperativ, aber wir sprechen hier über KI und KI kann Fehler machen.

  • Sehen wir uns an, was passiert, wenn der Agent einen Fehler in der Benutzeroberfläche macht, die er uns beschreibt.

  • Mit diesem letzten Prompt bitten wir den KI-Agenten, einen Fehler zu machen, damit wir Ihnen das zeigen können.

  • Mal sehen, was passiert.

  • Ja.

  • Wie Sie sehen, ist eine der Komponenten mit einem Fehler gekennzeichnet.

  • Und jetzt wurde es behoben.

  • Das ist die Selbstkorrekturschleife des KI-Agenten.

  • Bevor die einzelnen Komponenten die Benutzeroberfläche erreichen, wird die Nachricht anhand des Katalogschemas validiert.

  • Wenn die Komponente nicht mit dem Schema übereinstimmt, wird sie als Fehler markiert.

  • Der Renderer der grundlegenden Katalogimplementierung zeigt, dass der Fehler auf diese bestimmte Komponente beschränkt ist, während der Rest der Benutzeroberfläche weiterhin angezeigt wird.

  • Im Hintergrund sendet der Renderer eine Fehlermeldung an den Agent, um ihn darüber zu informieren, was schiefgelaufen ist.

  • In diesem Fall haben wir eine Nachricht gesendet, dass der Komponententyp „unbekannter Typ, der nicht existiert“ nicht im Katalog gefunden wurde.

  • Der Agent hatte dann die Möglichkeit, eine Folgeantwort zu senden, die die vorhandene Benutzeroberfläche aktualisierte, und die Benutzeroberfläche wurde korrigiert.

  • Ja.

  • In der aktuellen Implementierung des Material Basic-Katalogs werden die Fehler sofort angezeigt. In Ihrer benutzerdefinierten Komponente können Sie jedoch festlegen, dass bei einem Fehler weiterhin eine Ladeanzeige für eine Komponente angezeigt wird, während der Agent versucht, den Fehler zu beheben.

  • So kann der Fehler vollständig vor dem Nutzer verborgen werden.

Benutzerdefinierte Komponenten erstellen und Kataloge erweitern

  • Apropos Kataloge und benutzerdefinierte Komponenten: Bisher haben wir nur den Standardkatalog verwendet, aber Sie können ihn natürlich an Ihre eigenen Anforderungen anpassen.

  • Wenn wir also die Factory-Funktion zum Erstellen des Katalogs verwenden, werden für alle verschiedenen Komponenten Standardwerte verwendet.

  • Sie können also bei Bedarf ganz einfach Ihre eigenen Komponenten einbinden.

  • Wenn wir also beispielsweise unser eigenes Designsystem in unserer App und eine eigene Schaltfläche haben, können wir diese ganz einfach einbinden.

  • Wir überschreiben diesen Parameter also einfach, wenn wir eine Instanz des Katalogs erstellen, und erstellen diese Komponentendefinition.

  • Wenn wir eine neue Klasse erstellen, um das zu implementieren, sehen Sie hier eine Schnittstelle, den A2UI-Basiskatalog vom Typ „Button“.

  • Wenn wir das also implementieren, können wir unsere eigene Komponente einbinden.

  • Ja.

  • Da wir eine Komponente im Basiskatalog überschreiben, müssen wir diese A2UI-Basiskatalog-Button-Schnittstelle implementieren.

  • Diese Oberfläche enthält also bereits einige Informationen zur Komponente, z. B. den Namen, die Beschreibung und das Eigenschaftsschema.

  • Um unsere eigene Version zu implementieren, müssen wir also nur diese zusammensetzbare Funktion implementieren.

  • Dafür sind einige Parameter erforderlich.

  • Diese Parameter sind also Werte, die nach der Validierung vom KI-Agenten empfangen werden.

  • Dazu gehören Dinge wie die untergeordnete ID, die auf eine Komponente verweist, die den Inhalt der Schaltfläche darstellt.

  • Außerdem gibt es eine Variante, die den Typ einer Schaltfläche, die Aktionsdefinition und einige andere Parameter beschreibt.

  • In der Produktionsimplementierung der Schaltfläche müssten wir alle diese Parameter verwenden, aber für unsere Demozwecke benötigen wir nur das untergeordnete Element und die Aktion.

  • Implementieren wir also diese zusammensetzbare Funktion.

  • In dieser zusammensetzbaren Funktion geben wir unseren eigenen benutzerdefinierten Like-Button-Typ aus.

  • Wie Sie sehen, können wir die Parameter verwenden, die von A2UI bereitgestellt und validiert wurden.

  • In der onClick-Funktion können wir diese Aktion beispielsweise auslösen.

  • Wir können den Bibliothekstyp „DispatchAction“ verwenden und ihm die von A2UI definierte Aktion übergeben.

  • Wir verwenden also unsere eigene Schaltfläche, möchten aber trotzdem den untergeordneten Inhalt, das Label oder was auch immer der Agent verwendet hat, rendern.

  • Dazu verwenden wir die Funktion „observeA2uiComponentState“ und übergeben ihr die „childId“, um einen Handle für den untergeordneten Inhalt zu erhalten, der gerendert werden soll.

  • Anschließend verwenden wir die zusammensetzbare Funktion „A2uiComponent“ und übergeben ihr den Status der untergeordneten Komponente, um sie zu rendern.

  • Hier sehen Sie, dass wir die untergeordneten Inhalte nur rendern, wenn sie erfolgreich geladen wurden. Sie können aber auch Ihre eigene Logik dafür festlegen, was angezeigt werden soll, wenn die Inhalte geladen werden oder ein Fehlerstatus vorliegt.

  • Kehren wir zu einem der Beispiel-Prompts in unserer Demo-App zurück und ersetzen den Button durch den Standard-Material-Button.

  • So subtil, dass Sie es hoffentlich sehen können.

  • Auch hier verwenden wir einen sehr individuellen Button.

  • So können Sie also einige der Bibliothekstypen ersetzen. Aber was ist, wenn Sie die Bibliothek um Ihre eigenen benutzerdefinierten Typen erweitern möchten?

  • Das können wir ganz einfach tun, indem wir den Katalog selbst erweitern.

  • Anton erstellt hier eine neue Klasse, um eine Sternebewertungskomponente zu implementieren.

  • Wir fügen unserer Anwendung also etwas hinzu, mit dem wir Restaurants während dieser hypothetischen Reise bewerten können.

  • Da wir keine vorhandene Komponente ersetzen, verwenden wir diese allgemeinere Schnittstelle, die A2UI-Komponentenschnittstelle, die, wie Sie sehen, einige Parameter hat, die wir überschreiben müssen.

  • Die ersten beiden sind der Name und die Beschreibung.

  • Damit wird beschrieben, was unsere Komponente ist und was sie tut.

  • Diese sind sehr wichtig, denn wenn wir den Katalog serialisieren und an das Backend senden, um ihm mitzuteilen, was es zum Erstellen einer Benutzeroberfläche verwenden kann, entscheidet der Agent anhand dieser Informationen, ob er diese Komponente bei der Antwort verwenden soll oder nicht.

  • Sie müssen also sehr genau beschreiben, was die KI tun soll.

  • Die nächste Property, die wir überschreiben müssen, ist „properties“. Sie definiert im Grunde, welche Daten diese Komponente verarbeitet.

  • Wir erstellen eine Leiste für die Sternebewertung, mit der Sie ein Restaurant bewerten können. Dafür benötigen wir zwei Properties.

  • Das ist der Wert, also wie viele Sterne der Nutzer ausgewählt hat. Das ist eine änderbare Eigenschaft, mit der der Nutzer interagieren und die er ändern kann. Außerdem ist es die maximale Anzahl von Sternen.

  • Hier sehen wir, dass Anton diese dynamische Property vom Typ „Zahl“ für den Wert verwendet.

  • So sehen Sie, wie viele Sterne ausgewählt sind.

  • Dieser hat wiederum eine eigene Beschreibung, die dem Agenten mitteilt, wofür dieser Wert gedacht ist.

  • Und dann haben wir eine statische Eigenschaft. Wir verwenden also einfach diese A2UI-Eigenschaft vom Typ „Zahl“, um die maximale Anzahl von Sternen zu definieren, die diese Komponente anzeigen kann.

  • Nachdem wir diese beiden Attribute definiert haben, müssen wir sie nur noch zurückgeben.

  • Wir überschreiben also die Eigenschaft „properties“ und geben eine Liste zurück.

  • So teilen Sie dem KI-Agenten mit, worum es bei dieser Komponente geht.

  • Ja.

  • Wie Nick bereits erwähnt hat, werden diese Attributdefinitionen als Teil des Katalogschemas gesendet, damit der Agent weiß, wie er diese Komponente verwenden soll.

  • Aber diese Property, dieselben Property-Definitionen werden auch verwendet, um typisierte Werte zur Laufzeit zu extrahieren.

  • Es gibt also eine einzige verlässliche Quelle.

  • Sehen wir uns an, was wir tun müssen, um das zu implementieren.

  • Um auf das Attribut „max“ zuzugreifen, das ein statisches Attribut ist, sendet der Agent den Wert dafür inline im Objekt „properties“.

  • Um darauf zuzugreifen, müssen wir es also nur aus dem Objekt „properties“ abrufen, indem wir es mit dem Objekt „proper-max“ als Schlüssel referenzieren.

  • Da dieses Attribut als Zahl definiert ist, müssen wir es auch in einen Ganzzahlwert konvertieren und den Standardwert festlegen.

  • Als Nächstes kommt das Attribut „value“.

  • Da die Property „value“ eine dynamische Property ist, verweist sie auf etwas im Datenmodell.

  • Für den Zugriff müssen wir also die API „properties.bind“ verwenden und das Attributobjekt als Schlüssel als Parameter übergeben.

  • Dadurch wird der aktuelle Wert zurückgegeben, der im Datenmodell für dieses Attribut gespeichert ist, und es werden zukünftige Aktualisierungen abonniert.

  • Wenn das Datenmodell aktualisiert wird, wird diese Komponente automatisch neu zusammengesetzt.

  • Ähnlich haben wir auch in einen Int-Wert konvertiert und einen Standardwert festgelegt.

  • Wir müssen noch eine bidirektionale Bindung einrichten, damit wir die Bewertung ändern können.

  • Dazu verwenden wir die bindUpdater API.

  • Wir übergeben dasselbe Property-Objekt, das die Property definiert, und erhalten als Ergebnis: „bindUpdater“ gibt also eine Lambda-Funktion zurück, mit der wir einen Wert für diese Property im Datenmodell ändern können.

  • Jetzt können wir die Sternebewertungskomponente ausgeben.

  • Okay.

  • Nachdem wir unsere eigene benutzerdefinierte Komponente definiert haben, müssen wir sie dem von uns verwendeten Katalog hinzufügen.

  • Bisher haben wir den Basiskatalog so verwendet, wie er ist.

  • Jetzt müssen wir einen neuen Katalog erstellen, der den Basiskatalog übernimmt und unsere benutzerdefinierte Komponente hinzufügt.

  • Ein Katalog besteht aus drei Dingen.

  • Es ist eine ID, eine Reihe von Komponenten und eine Reihe von Funktionen.

  • Für die ID verwenden wir einen URI, um einen Katalog eindeutig zu identifizieren, sowie die Version, mit der wir arbeiten.

  • Für die Komponenten nehmen wir alle Komponenten aus dem Basiskatalog und fügen dann mit dem Operator „add“ die benutzerdefinierte Komponente hinzu, die wir gerade erstellt haben.

  • Für die Funktionen verwenden wir einfach die Funktionen aus dem Basiskatalog.

  • So haben wir den Standardkatalog um unseren neuen benutzerdefinierten Komponententyp erweitert.

  • Es ist also ganz einfach, einen benutzerdefinierten Katalog zu erstellen.

  • Der KI-Agent lernt also aus den Systemanweisungen, die wir ihm senden, wie er den Katalog verwendet.

  • Da wir jetzt einen benutzerdefinierten Katalog erstellt haben, aktualisieren wir auch die Systemanweisungen.

  • Wir übergeben das Katalogobjekt also an unsere Funktion, die die Systemanweisungen erstellt.

  • Hier ist es.

  • Bisher haben wir eine statische Definition des Schemas für den Basiskatalog gesendet, da wir diesen Standardkatalog verwendet haben.

  • Da wir jetzt einen benutzerdefinierten Katalog haben, können wir das Schema mit der Funktion „toJsonSchemaString“ dynamisch generieren.

  • Dadurch wird eine vollständige JSON-Darstellung dieses Katalogs erstellt, einschließlich der gerade hinzugefügten Sternebewertungskomponente.

  • Die Systemanweisungen enthalten auch einige Beispielantworten für den Agenten.

  • Daher ändern wir auch hier die Katalog-ID, damit die Beispiele auf den richtigen Katalog verweisen.

  • Okay.

  • Damit haben wir den Basiskatalog um unsere eigenen benutzerdefinierten Typen erweitert.

  • Wenn wir die Anwendung also noch einmal bereitstellen und einen dieser Prompts ausprobieren, sollten wir eine Benutzeroberfläche für die Bewertung erhalten.

  • Und dann antwortet der KI-Agent.

  • Und hier sehen Sie, dass diese Sternkomponente eine brandneue Komponente ist, die der KI-Agent noch nie gesehen hat, bis wir sie gerade implementiert haben.

  • Dies ist nicht Teil des Standardkatalogs, wir haben es gerade erstellt.

  • Wenn ich auf den Stern klicke, wird die bindUpdater-Lambda-Funktion aufgerufen und der Wert im Datenmodell aktualisiert.

  • Wenn wir die Bewertungen senden, können wir uns die Aktion ansehen. Der Kontext der Aktion enthält die Werte, die wir gerade festgelegt haben.

  • Okay, können wir jetzt bitte wieder zu den Folien wechseln?

  • Das war jetzt ziemlich viel auf einmal, aber wir hoffen, dass wir Ihnen zeigen konnten, wie Sie von KI-Agenten, die nur Text ausgeben, zu KI-Agenten gelangen, die echte native Benutzeroberflächen erstellen.

Wichtige Erkenntnisse und Ressourcen

  • Wir hoffen, wir konnten Ihnen zeigen, dass die Integration ziemlich einfach ist.

  • Es gibt drei Hauptkomponenten, die Sie erstellen müssen: einen Katalog von Komponenten, eine Parsing-Logik und die Darstellung in Ihrer Benutzeroberfläche.

  • Und hoffentlich haben wir gezeigt, dass die Bibliothek viele Dinge für Sie erledigt, wie das progressive Rendering oder die Validierung Ihres Schemas, die Selbstkorrekturschleifen oder die Fehlerbehandlung.

  • Und hoffentlich haben wir heute schnell gezeigt, dass es sehr einfach ist, es anzupassen.

  • Wir haben gezeigt, wie der Basiskatalog bereits auf Ihr Material-Design reagiert, wenn Sie Material verwenden. Es ist jedoch sehr einfach, Komponenten im Katalog zu erweitern oder zu ersetzen.

  • Aber ich denke, dass es am besten ist, wenn Sie Ihren eigenen Katalog erstellen.

  • Wenn Sie die größtmögliche Kontrolle darüber haben möchten, welche Benutzeroberfläche der Agent tatsächlich verwenden kann, können Sie vollständig benutzerdefinierte Komponenten erstellen.

  • Wenn Sie ihm völlig unbeschränkten Spielraum lassen möchten, können Sie ihm sehr einfache Komponenten wie Textlabels und Schaltflächen geben und sagen: „Erstelle die Benutzeroberfläche.“

  • Wenn Sie mehr Kontrolle wünschen, können Sie komplexere Komponenten erstellen und festlegen, dass nur diese verwendet werden dürfen.

  • Sie haben also die Kontrolle darüber, was der Agent tun und Ihren Nutzern zeigen kann.

  • Das sind die neue A2UI-Renderer-Bibliothek und die Jetpack-Bibliothek, die wir gerade eingeführt haben.

    implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")
  • Es befindet sich derzeit in der Alphaphase. Alpha 02 wurde erst vor Kurzem veröffentlicht.

  • Über diese Links kannst du dir die Videos ansehen.

  • Wir haben eine Dokumentation auf developer.android.com sowie einige Beispiele.

  • Wir haben ein einfaches Beispiel, das den grundlegenden Katalog zeigt, sowie ein komplexeres Beispiel und diese JetPacker-Beispiel-App, die wir gerade erstellt haben. Sie werden sie später in der ADS-Keynote noch einmal sehen. Dort wird gezeigt, wie Sie diese komplexeren Komponenten erstellen können, um zu steuern, was der Nutzer sieht.

  • Wie gesagt, wir befinden uns derzeit in der Alpha-Phase. Es ist also ein guter Zeitpunkt, sich das Ganze anzusehen und uns Feedback und Feature Requests zu geben. Sagen Sie uns, wie Sie das Tool verwenden möchten oder was es können müsste, damit Sie es verwenden würden.

  • Sehen Sie sich die Bibliothek also an und geben Sie uns Feedback.

  • Und das ist das Ergebnis.

  • Ich bin schon gespannt, was Sie und Ihre Agenten damit entwickeln werden.

  • Vielen Dank.

  • Vielen Dank.

Code

Code-Snippets

Kotlin-Skript-Snippet bei 27:40 in „Wichtige Erkenntnisse und Ressourcen“
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")