Inteligentne aplikacje

Zezwalanie agentom na generowanie interfejsu w aplikacjach na Androida za pomocą A2UI

Głośniki

Informacje

Informacje o wystąpieniu

A2UI przechodzi z komponentów internetowych na natywne komponenty Androida. Nowy mechanizm renderowania Jetpack Compose dla A2UI w wersji 0.9 umożliwia deweloperom natywne renderowanie dynamicznego interfejsu generowanego przez LLM. Zamiast widoków internetowych wykorzystuje platformę aktorów opartą na współprogramach, cykle życia klas zapieczętowanych i natywną integrację z Material 3.

Podczas tej sesji omówimy nową architekturę i wyjaśnimy, jak drzewa komponentów sterowane przez serwer przekładają się na reaktywne węzły Compose oraz jak wbudowane przejścia układu płynnie obsługują aktualizacje strukturalne bez blokowania interfejsu.

Podsumowanie

  • A2UI udostępnia deklaratywny, otwarty protokół JSON dla backendów agentowych, który umożliwia opisywanie dynamicznych interfejsów użytkownika zamiast odpowiedzi tekstowych.
  • Protokół opiera się na katalogach, które definiują dozwolone komponenty i funkcje, oraz na platformach, które reprezentują elementy interfejsu kontrolowane przez agenta.
  • Biblioteka A2UI w Jetpack Compose renderuje strumieniowe modele platformy natywnie, używając komponentów Material 3 z wbudowanymi ułatwieniami dostępu i renderowaniem progresywnym.
  • Modele danych w pamięci natychmiast aktualizują powiązane elementy interfejsu bez kontaktu z agentem, a zdarzenia wychodzące przekierowują działania użytkownika z powrotem.
  • Deweloperzy mogą dostosowywać istniejące elementy katalogu lub tworzyć niestandardowe komponenty, aby ściśle kontrolować, co agent może wyświetlać.

Zasoby

Gemini Notebook

Tematy

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

Transkrypcja

Pobierz transkrypcję

Wprowadzenie do generatywnego interfejsu i A2UI

  • Cześć wszystkim. Witajcie na tej prezentacji, podczas której omówimy, jak generować interfejs użytkownika w aplikacjach na Androida za pomocą naszej nowej biblioteki A2UI.

  • Nazywam się Nick Butcher i jestem menedżerem produktu pracującym nad Jetpack Compose.

  • Cześć, nazywam się Anton i jestem inżynierem w zespole Compose.

  • Pracuję nad nowym mechanizmem renderowania A2UI w Jetpack Compose, o którym z przyjemnością Wam dzisiaj opowiem.

  • Tak, wraz z rozwojem możliwości AI wiele aplikacji analizuje, jak sztuczna inteligencja może pomóc w ulepszaniu lub tworzeniu nowych funkcji dla użytkowników.

  • Jedną z ciekawych funkcji, którą niektórzy się interesują, jest generatywny interfejs użytkownika. Dzięki niemu w czasie działania backendy agentowe mogą tworzyć zupełnie nowe interfejsy dla użytkowników. Uważamy, że to bardzo interesujące.

  • Niestety obecnie wiele odpowiedzi agentowych backendów jest uwięzionych w ścianach tekstu, takich jak ta.

  • Oto przykład, w którym poprosiłem agenta o wygenerowanie nowej funkcji do rezerwacji podróży.

  • Oto prompt.

  • Jak widać, odpowiedź jest całkiem dobra, ale jest uwięziona w ścianie tekstu, z którą nie mogę łatwo wejść w interakcję, i nie wygląda jak reszta mojej aplikacji.

  • Generatywny interfejs użytkownika pozwala na tworzenie takich doświadczeń, w których można generować nowy interfejs na bieżąco w odpowiedzi na dynamiczne dane wejściowe, co otwiera możliwości większego dostosowania lub nowych doświadczeń.

  • Oto przykład interfejsu utworzonego przy użyciu standardowych komponentów natywnych.

  • Możesz więc już dziś samodzielnie stworzyć takie rozwiązanie, ale musisz zdefiniować własny format wymiany danych z backendem, własny strumień zdarzeń, funkcje itd.

  • A2UI, czyli Agent-to-UI, to otwarty protokół w formacie JSON, który służy do definiowania wszystkich tych elementów.

  • Jedną z rzeczy, które naprawdę lubię w A2UI, jest to, że jest to format deklaratywny.

  • Agent zwraca tylko reprezentację JSON interfejsu, który ma zostać wyrenderowany.

  • Nie zwraca żadnego kodu, który klient musiałby wykonać, co mogłoby narazić Cię na pewne luki w zabezpieczeniach.

  • To tylko deklaracja.

  • Jakie inne pojęcia związane z A2UI musimy znać?

Podstawowe pojęcia: katalogi i platformy

  • Wystarczy, że poznasz 2 główne pojęcia: katalogi i platformy.

  • Katalog zawiera komponenty takie jak tekst, przycisk, wiersz i kolumna, których agent może używać do tworzenia interfejsu.

  • Katalog zawiera też funkcje, których agent może używać do formatowania tekstu, sprawdzania poprawności danych wprowadzonych przez użytkownika lub otwierania adresów URL.

  • Katalog to umowa, która określa, jakie komponenty i funkcje obsługuje Twoja aplikacja. Agent może tworzyć tylko te komponenty i funkcje.

  • Oznacza to, że agent nigdy nie wykonuje dowolnego kodu w aplikacji.

  • Tworzy interfejs z elementów składowych, które zaprojektujesz i którym ufasz.

  • Kolejnym ważnym pojęciem jest powierzchnia.

  • Powierzchnia to jeden element interfejsu, który jest kontrolowany przez agenta, jak ten przykład kreatora podróży na slajdzie.

  • Agent opisuje interfejs użytkownika dla platformy w formacie JSON, używając komunikatów protokołu A2UI, takich jak createSurface, updateComponents, oraz komponentów i funkcji zdefiniowanych w katalogu.

  • Katalog to bardzo podstawowe pojęcie w A2UI, ponieważ określa, jakich komponentów agent może używać do tworzenia interfejsu.

  • Specyfikacja A2UI definiuje tzw. katalog podstawowy, który jest zestawem wspólnych komponentów, których można używać do tworzenia większości interfejsów użytkownika.

  • Mechanizm renderowania A2UI w Jetpack Compose udostępnia implementację podstawowego katalogu z użyciem komponentów Material 3.

  • Większość komponentów katalogu jest gotowa do użycia od razu po zainstalowaniu.

  • Są 3 komponenty multimedialne: obraz, film i odtwarzacz audio, do których musisz podłączyć wybraną bibliotekę.

  • Możesz na przykład wybrać Coil do wczytywania obrazów i Media3 do odtwarzacza filmów i dźwięku.

  • Jednak prawdziwa moc A2UI i naszej nowej biblioteki renderującej polega na tym, że możesz tworzyć własne, całkowicie niestandardowe komponenty i katalogi oraz w pełni kontrolować, czego agent może używać do tworzenia interfejsu użytkownika.

  • Do katalogów wrócimy później.

  • Zobaczmy, jak to wygląda.

  • Przełączmy się na maszynę demonstracyjną.

  • To jest nasza aplikacja demonstracyjna, której użyjemy, i utworzymy moduł renderujący A2UI, aby pokazać, jak łatwo można to zrobić.

  • Jest to zestaw promptów, które możesz wysłać do agenta, jak widać na tych małych elementach na środku ekranu.

  • Jeśli klikniemy jeden z promptów, zostanie on wysłany do prawdziwego agenta w backendzie. Trzymajcie kciuki za demo. Oprócz prompta dołączamy też instrukcje systemowe.

  • Informujemy więc backend o protokole A2UI i katalogu komponentów, w tym przypadku o podstawowym katalogu, którego używamy, aby mógł go użyć do utworzenia interfejsu użytkownika, i podajemy kilka przykładów, jak to zrobić.

  • Implementacja sesji czatu w naszej aplikacji demonstracyjnej już wie, jak wyodrębnić nieprzetworzone wiadomości JSON A2UI z odpowiedzi agenta.

  • W tym przypadku agent odpowiedział tekstem, po którym nastąpiły wiadomości protokołu A2UI: createSurface, updateComponents.

  • Na razie nasza aplikacja wyświetla tylko ten nieprzetworzony kod JSON i nie renderuje go jeszcze jako interfejsu.

  • Zanim przejdziemy do implementacji kodu, wróćmy na chwilę do slajdów, aby omówić projekt systemu, który wykorzystamy do wdrożenia.

  • O, zaczynamy.

Architektura aplikacji i przetwarzanie wiadomości

  • Plan jest taki.

  • Agent AI przesyła odpowiedź do naszej aplikacji, która jest odbierana przez klasę ChatSession.

  • ChatSession wyodrębnia nieprzetworzone wiadomości JSON A2UI z odpowiedzi agenta podczas ich przesyłania strumieniowego i przekazuje je do procesora wiadomości A2UI.

  • Procesor komunikatów jest skonfigurowany z parserem, który konwertuje te nieprzetworzone komunikaty JSON na reprezentację semantyczną.

  • Następnie procesor sprawdza wiadomości pod kątem definicji katalogu i mapuje je na zestaw modeli platform.

  • Modele interfejsu określają, jak wygląda interfejs i które jego elementy kontroluje agent.

  • Te modele platformy są następnie renderowane jako natywny interfejs Compose za pomocą funkcji kompozycyjnej platformy A2UI udostępnianej przez naszą bibliotekę.

  • Komunikacja między agentem a interfejsem jest dwukierunkowa, więc gdy użytkownik wchodzi w interakcję z interfejsem, działania użytkownika i błędy są przekazywane z powrotem do agenta.

  • Podsumowując, istnieją 3 główne elementy składowe: katalog, procesor komunikatów i platforma.

  • Napiszmy kod.

  • Tak, zbudujmy go, jeśli możemy zmienić maszyny.

  • Pierwszym krokiem jest dodanie zależności.

  • Jak widać, uwzględniliśmy już zależność Material 3 A2UI i zsynchronizowaliśmy projekt, więc nie musimy czekać na Gradle.

  • Jest to zależność najwyższego poziomu, która definiuje implementację podstawowego katalogu przy użyciu tych komponentów Material 3, ale poniżej znajdują się inne, bardziej szczegółowe zależności.

  • Jeśli więc nie chcesz używać katalogu podstawowego (później wyjaśnimy dlaczego), możesz użyć tych bardziej szczegółowych.

  • ale spowoduje to przejściowe pobranie pozostałych.

  • OK.

  • Wspomnieliśmy o 3 rzeczach.

  • Pierwszym krokiem było zdefiniowanie katalogu.

  • W naszej aplikacji utworzymy funkcję fabryczną, która pomoże utworzyć instancję implementacji Material 3 podstawowego katalogu.

  • Używamy tutaj tej funkcji biblioteki, podstawowego katalogu Material A2UI, który również jest wersjonowany.

  • Jak już wspomnieliśmy, zawiera on wszystkie potrzebne typowe komponenty, ale nie chcemy zmuszać Cię do korzystania z określonych bibliotek do wczytywania obrazów lub renderowania multimediów, więc musisz je dostarczyć samodzielnie.

  • W tym przypadku Anton utworzył już ten kod wokół funkcji kompozycyjnej AsyncImage biblioteki Coil, której użyjemy do wczytywania obrazów, a także dodatkową logikę na potrzeby naszej wersji demonstracyjnej, która będzie wyszukiwać obrazy w Wikipedii.

  • Możemy więc bardzo łatwo podłączyć to do komponentów katalogu.

  • Podobnie jest w przypadku renderowania, wideo i audio, gdzie używamy biblioteki Media3 do renderowania za pomocą ExoPlayera.

  • W ten prosty sposób możemy podłączyć do niego własne komponenty.

  • Kolejną rzeczą, której potrzebuje katalog, jest zestaw standardowych funkcji.

  • Są to funkcje, które może udostępniać klient.

  • Wraz z podstawowym katalogiem dostarczamy też implementację tego interfejsu, ale musimy podać mu kontekst, którego użyjesz do wykonywania określonych działań, takich jak otwarcie identyfikatora URI, gdy użytkownik kliknie element.

  • W ten sposób ukończyliśmy pierwszy krok.

  • Utworzyliśmy katalog.

  • Następnym krokiem jest skonfigurowanie procesora komunikatów.

  • To jest element, który analizuje odpowiedzi i inne dane.

  • Przejdziemy więc do modelu widoku aplikacji, gdzie najpierw utworzymy instancję skonfigurowanego wcześniej katalogu, ponieważ będzie nam potrzebny.

  • Następnie utworzymy obiekt procesora komunikatów, który potrzebuje dostępu do katalogu, aby sprawdzać odpowiedzi pod kątem komponentów, których chcemy użyć.

  • Innym komponentem, który musimy utworzyć, jest parser, czyli obiekt odpowiedzialny za analizowanie wiadomości JSON, gdy są one zwracane.

  • W tym miejscu tworzymy obiekt ChatSession.

  • Jak już wspominałem, ChatSession wyodrębnia już nieprzetworzone wiadomości A2UI z odpowiedzi agenta.

  • Gdy każda nowa wiadomość A2UI jest gotowa, wywołuje to wywołanie zwrotne onA2uiMessage.

  • Wystarczy więc, że przekażemy każdą z tych wiadomości do procesora, gdy tylko dotrze.

  • Wywołujemy funkcję processInput, przekazując jej parser i wiadomość.

  • Funkcja processInput analizuje każdą wiadomość, a następnie kolejkuje zadania dla procesora, aby zweryfikować je pod kątem katalogu i zaktualizować modele platform.

  • Procesor udostępnia więc bieżący zestaw aktywnych modeli powierzchni jako activeSurfaces StateFlow.

  • ChatSession zawiera logikę dodawania modeli platformy do stanów interfejsu odpowiedzi agenta, które mają być renderowane.

  • Dlatego przekazujemy ten obiekt StateFlow activeSurfaces jako parametr do ChatSession.

  • Jak już wspomniano, funkcja processInput kolejkuje zadania dla procesora.

  • Aby jednak rozpocząć przetwarzanie, musimy uruchomić współprogram w wątku w tle.

  • Dlatego użyjemy zakresu modelu widoku i ustawimy domyślny dyspozytor.

  • W tej współprogramie musimy tylko wywołać metodę processor.collectMessages.

  • Dopóki ta współprocedura jest aktywna, procesor będzie obsługiwać wiadomości z kolejki.

  • Musimy jeszcze zrobić jedną rzecz: gdy użytkownik wyczyści historię czatu, musimy też wyczyścić bieżące platformy.

  • Wywołujemy więc funkcję clearSurfaces.

Renderowanie modeli powierzchni w interfejsie

  • OK.

  • Wykonaliśmy już pierwsze 2 kroki.

  • Utworzyliśmy katalog i procesor do analizowania wiadomości.

  • Następnym krokiem jest wyrenderowanie tych odpowiedzi w interfejsie.

  • Zbieramy więc te powierzchnie, gdy są analizowane przez parser.

  • Teraz w naszym interfejsie przekażemy je w obiekcie odpowiedzi agenta i będziemy je po prostu iterować.

  • Następnie użyjemy tego elementu kompozycyjnego interfejsu A2UI i przekażemy mu każdy z modeli, gdy będziemy je iterować, aby wyrenderować treść tam, gdzie chcemy.

  • Warto zauważyć, że agent może zdecydować się na użycie listy, jeśli chce wyświetlić dużą ilość treści.

  • Aby komponent renderował się prawidłowo, musimy ustawić jego maksymalną wysokość.

  • W ten sposób wdrożyliśmy podstawowy mechanizm renderowania A2UI.

  • Jeśli teraz wrócimy do naszej aplikacji, uruchomimy ją ponownie i jeszcze raz wpiszemy prompta, agent powinien go użyć, aby wyrenderować prawdziwy interfejs.

  • Więc po prostu kodujemy to na żywo.

  • To nie jest iluzja.

  • Ale tak, używa prawdziwych obrazów.

  • Wykorzystuje karty, okna, selektory daty, suwaki – wszystkie standardowe komponenty Material, których używasz do tworzenia aplikacji, zamiast wyświetlać tylko odpowiedź tekstową.

  • Ponieważ jest to prawdziwa aplikacja korzystająca z prawdziwych komponentów Material, wykonuje ona czynności, które wykonują prawdziwe komponenty.

  • Działa jak aplikacja natywna.

  • Może na przykład reagować na tryb ciemny.

  • To są komponenty Material.

  • Jeśli więc masz zewnętrzną funkcję kompozycyjną Material Theme, w której dostosowano kolory, kształty, typografię itp., te komponenty będą je wykorzystywać, aby wyglądać jak reszta aplikacji.

  • Jak wspomniał Nick, ponieważ są to komponenty natywne Compose, mają one semantykę natywną.

  • Oznacza to, że obsługiwane są wszystkie wbudowane ułatwienia dostępu w Compose.

  • Dzięki temu funkcje takie jak TalkBack, skalowanie czcionek czy nawigacja za pomocą klawiatury działają od razu.

  • Ponieważ wywołujesz agenta, aby wygenerować nowy interfejs w czasie działania, mogą wystąpić pewne opóźnienia.

  • Możliwe, że będziesz musiał poczekać na odpowiedź pracownika obsługi klienta.

  • Biblioteka robi, co może, aby zminimalizować to opóźnienie.

  • Jeśli użyjemy tego rodzaju, wprowadzimy pewne opóźnienia, aby pokazać nieco wolniej, co się dzieje i jak to minimalizujemy.

  • Tak, ponieważ agent może potrzebować trochę czasu na przesłanie pełnej odpowiedzi, używamy tzw. renderowania progresywnego.

  • Gdy tylko komponent główny zostanie dostarczony, zaczynamy wyświetlać interfejs użytkownika dla danej platformy.

  • Wszystkie komponenty kontenera wyświetlają obiekty zastępcze ładowania dla swoich elementów podrzędnych, jeśli te elementy nie zostały jeszcze dostarczone.

  • Gdy komponenty podrzędne zostaną dostarczone, są animowane przy użyciu schematu ruchu Material 3.

  • OK.

  • Przejdźmy do następnego prompta, aby pokazać kolejną funkcję biblioteki.

  • Oto kolejny prompt, w którym rezerwujemy stolik na kolację.

  • Tutaj agent decyduje się na użycie tego komponentu suwaka. Tak, użył go.

  • Jak widać, możesz przeciągać elementy i wchodzić w interakcje z interfejsem, który agent utworzył na bieżąco.

  • Zauważ, że nie tylko zmienia się liczba w komponencie, ale też poniżej znajduje się ciąg formatu, w którym jest ona zliczana.

  • Czy to nie oznacza, że nie trzeba wracać do agenta, aby to zaktualizować?

  • Jest to wiązanie dwukierunkowe.

  • Komponent suwaka jest więc powiązany z modelem danych w pamięci tej powierzchni.

  • Wszystkie komponenty powiązane z tą samą ścieżką w modelu danych są automatycznie aktualizowane, gdy wartość ulega zmianie.

  • Suwak odczytuje wartość do wyświetlenia, a gdy go przeciągam, zapisuje ją z powrotem w modelu, aby go zaktualizować.

  • Wszystko to dzieje się lokalnie, bez konieczności komunikacji z agentem.

  • Możemy też poprosić agenta o zaktualizowanie danych.

  • Niech będzie 6 osób.

  • Zwróć uwagę na suwak, gdy agent odpowiada.

  • Właśnie wzrosła do 6.

  • Zobaczmy, co otrzymaliśmy od agenta.

  • Zamiast wysyłać cały nowy opis interfejsu, agent wysłał tylko jedną krótką wiadomość updateDataModel, która zaktualizowała ścieżkę suwaka.

  • Ponieważ suwak jest powiązany z tą ścieżką, został zaktualizowany na miejscu.

  • Dlatego model danych każdej powierzchni jest oparty na stanie migawki Compose, a każda właściwość komponentu powiązana ze ścieżką jest stanem pochodnym.

  • Dlatego gdy użytkownik lub agent aktualizuje wartość w modelu danych, wszystkie komponenty powiązane z tą samą wartością są automatycznie ponownie tworzone.

  • OK.

  • Załóżmy, że chcemy podjąć działanie i kliknąć przycisk rezerwacji. Co się wtedy stanie, Anton?

  • W tej chwili nic się nie dzieje.

  • Przycisk wysyła działanie, ale nie wysyłamy go jeszcze do agenta.

  • Naprawmy to.

Obsługa działań użytkownika i przepływów zdarzeń

  • Tak jak musieliśmy połączyć się z procesorem, aby przetwarzać dane wejściowe, musimy też połączyć się z danymi wyjściowymi, czyli działaniami, które użytkownik wykonuje w interfejsie utworzonym przez agenta.

  • Aby to zrobić, używamy przepływu outboundEvents, który udostępnia procesor, i zbieramy dane.

  • To jest Twoja szansa na podłączenie dowolnej logiki, którą chcesz, na podstawie, wiesz, gdy użytkownik kliknie działanie lub wykona coś z interfejsem użytkownika.

  • W tym miejscu wysyłamy wszystkie działania z powrotem do agenta, który następnie decyduje, co zrobić. Możesz też w tym momencie zdecydować, czy chcesz wywołać własny backend, czy przejść do innego ekranu w aplikacji. To zależy od Ciebie.

  • Anton podłączył go, więc teraz, gdy klikniemy działanie potwierdzenia rezerwacji, wyśle ono działanie, które podjęliśmy, z powrotem do agenta.

  • W tym miejscu możesz nawet zdecydować o wywołaniu jakiejś funkcji w backendzie.

  • Zobaczmy, co faktycznie wysyłamy do agenta.

  • To jest komunikat o działaniu A2UI.

  • Ma on kontekst, który zawiera zestaw właściwości, o których przesłanie poprosił agent podczas wykonywania tego działania.

  • Gdy kliknę przycisk, te konkretne właściwości zostaną w tym momencie rozwiązane na podstawie bieżących wartości w modelu danych i to właśnie wyślemy do agenta.

  • Do tej pory agent był dość pomocny, ale mówimy tu o AI, a AI może popełniać błędy.

  • Zobaczmy, co się stanie, jeśli agent popełni błąd w interfejsie, który nam opisuje.

  • W tym ostatnim prompcie prosimy agenta o popełnienie błędu, abyśmy mogli to zademonstrować.

  • Zobaczmy, co się stanie.

  • Tak.

  • Jak widać, jeden z komponentów jest oznaczony błędem.

  • Teraz problem został rozwiązany.

  • To jest pętla samokorekty agenta.

  • Zanim każdy komponent dotrze do interfejsu, sprawdzamy, czy wiadomość jest zgodna ze schematem katalogu.

  • Jeśli komponent nie pasuje do schematu, jest oznaczany jako błąd.

  • W przypadku podstawowej implementacji katalogu błąd jest izolowany w tym konkretnym komponencie, a reszta interfejsu jest nadal wyświetlana.

  • Za kulisami moduł renderujący wysyła do agenta komunikat o błędzie, aby poinformować go, co poszło nie tak.

  • W tym przypadku wysłaliśmy wiadomość, że nie znaleziono w katalogu typu komponentu o nieznanym typie, który nie istnieje.

  • Agent miał wtedy możliwość wysłania odpowiedzi, która aktualizowała istniejący interfejs, a interfejs został naprawiony na miejscu.

  • Tak.

  • W obecnej implementacji podstawowego katalogu Material błędy są wyświetlane od razu, ale w komponencie niestandardowym możesz zdecydować, aby w przypadku błędu nadal wyświetlać wskaźnik wczytywania, dopóki agent nie rozwiąże problemu.

  • W ten sposób błąd może być całkowicie ukryty przed użytkownikiem.

Tworzenie komponentów niestandardowych i rozszerzanie katalogów

  • Jeśli chodzi o katalogi i komponenty niestandardowe, do tej pory używaliśmy tylko podstawowego katalogu, który jest dostępny od razu, ale oczywiście możesz go dostosować do własnych potrzeb.

  • Jak widać, gdy używamy funkcji fabrycznej do utworzenia katalogu, wszystkie komponenty mają wartości domyślne.

  • Możesz więc łatwo podłączyć własne komponenty, jeśli chcesz.

  • Jeśli na przykład chcemy podłączyć własny system projektowania w aplikacji i jej własny przycisk, możemy to łatwo zrobić.

  • Dlatego podczas tworzenia instancji katalogu zastępujemy ten parametr i tworzymy tę definicję komponentu.

  • Jeśli utworzymy nową klasę, aby to zaimplementować, zobaczysz, że jest tu interfejs, podstawowy katalog A2UI przycisku typu.

  • Jeśli więc to zaimplementujemy, możemy podłączyć własny komponent.

  • Tak.

  • Ponieważ zastępujemy komponent w katalogu podstawowym, musimy zaimplementować ten interfejs przycisku katalogu podstawowego A2UI.

  • Ten interfejs zawiera już pewne informacje o komponencie, takie jak nazwa, opis i schemat właściwości.

  • Aby wdrożyć własną wersję, musimy tylko zaimplementować tę funkcję typu „composable”.

  • Wymaga kilku parametrów.

  • Te parametry to wartości otrzymane od agenta po zweryfikowaniu.

  • Są to na przykład identyfikator elementu podrzędnego, który odnosi się do komponentu będącego treścią przycisku.

  • Ma też wariant, który opisuje typ przycisku, definicję działania i inne parametry.

  • W produkcyjnej implementacji przycisku musielibyśmy użyć wszystkich tych parametrów, ale na potrzeby prezentacji potrzebujemy tylko elementu podrzędnego i działania.

  • Zaimplementujmy ten element kompozycyjny.

  • W tej funkcji typu „composable” wyemitujemy nasz własny niestandardowy typ przycisku „Lubię to”.

  • Jak widać, możemy używać parametrów, które zostały podane i zweryfikowane przez A2UI.

  • Na przykład w funkcji onClick możemy wysłać to działanie.

  • Możemy użyć tego typu biblioteki DispatchAction i przekazać jej działanie zdefiniowane przez A2UI.

  • Używamy więc własnego przycisku, ale nadal chcemy renderować treści podrzędne, etykietę lub cokolwiek, co agent zdecydował się użyć.

  • W tym celu używamy funkcji observeA2uiComponentState i przekazujemy jej childId, aby uzyskać dostęp do treści podrzędnych, które powinny zostać wyrenderowane.

  • Następnie użyjemy funkcji typu „composable” A2uiComponent i przekażemy jej stan komponentu podrzędnego, aby go wyrenderować.

  • Tutaj widać, że renderujemy treści podrzędne tylko wtedy, gdy zostały pomyślnie wczytane. Możesz jednak zastosować własną logikę, aby określić, co ma się wyświetlać, gdy treści są wczytywane lub gdy występuje błąd.

  • Wróćmy do jednego z przykładowych promptów w naszej aplikacji demonstracyjnej i zastąpmy przycisk standardowym przyciskiem Material.

  • Jest bardzo subtelny, mam nadzieję, że go widać.

  • Jak widać, używamy tu również bardzo niestandardowego przycisku.

  • W ten sposób można zastąpić niektóre typy bibliotek, ale co zrobić, jeśli chcesz rozszerzyć bibliotekę o własne typy niestandardowe?

  • Możemy to zrobić bardzo łatwo, dodając je do katalogu.

  • Anton utworzy nową klasę, aby zaimplementować komponent oceny w gwiazdkach.

  • Dodamy więc do naszej aplikacji coś, co pozwoli nam oceniać restauracje podczas tej hipotetycznej podróży.

  • Ponieważ nie zastępujemy istniejącego komponentu, używamy tego bardziej ogólnego interfejsu, czyli interfejsu komponentu A2UI, który, jak widać, ma kilka parametrów, które musimy zastąpić.

  • Pierwsze 2 to nazwa i opis.

  • Ten opis wyjaśnia, czym jest i co robi nasz komponent.

  • Są one bardzo ważne, ponieważ gdy serializujemy katalog i wysyłamy go do backendu, aby poinformować go, czego może użyć do utworzenia interfejsu, agent użyje tych informacji, aby zdecydować, czy użyć tego komponentu podczas odpowiadania.

  • Musisz więc opisać to dość szczegółowo i powiedzieć AI, że dokładnie wiesz, co to robi.

  • Następna właściwość, którą musimy zastąpić, to właściwość properties, która zasadniczo określa, jakimi danymi zajmuje się ten komponent.

  • Tworzymy pasek oceny, aby można było ocenić restaurację. Będziemy potrzebować 2 właściwości.

  • Będzie to wartość, np. liczba gwiazdek wybranych przez użytkownika, która będzie zmienną właściwością, z którą użytkownik może wchodzić w interakcje i którą może zmieniać, oraz maksymalna liczba gwiazdek.

  • Widzimy, że Anton używa tej dynamicznej właściwości typu liczbowego jako wartości.

  • To jest rzeczywista liczba wybranych gwiazdek.

  • Ma on własny opis, który informuje agenta, do czego służy ta wartość.

  • Mamy też właściwość typu statycznego, więc używamy tej właściwości A2UI typu liczbowego, aby określić maksymalną liczbę gwiazdek, które może wyświetlać ten komponent.

  • Po zdefiniowaniu tych 2 właściwości musimy je tylko zwrócić.

  • Zastąpimy więc właściwość properties i zwrócimy listę.

  • W ten sposób informujesz agenta, że ten komponent zajmuje się tymi danymi.

  • Tak.

  • Jak wspomniał Nick, te definicje właściwości są wysyłane jako część schematu katalogu, aby agent wiedział, jak korzystać z tego komponentu.

  • Jednak te same definicje właściwości są też używane do wyodrębniania wartości typowanych w czasie działania.

  • Dzięki temu istnieje jedno źródło wiarygodnych danych.

  • Zobaczmy, co musimy zrobić, aby to wdrożyć.

  • Aby uzyskać dostęp do właściwości max, która jest właściwością statyczną, agent wysyła jej wartość w obiekcie właściwości.

  • Aby uzyskać do niego dostęp, musimy pobrać go z obiektu właściwości, odwołując się do niego za pomocą obiektu właściwości proper-max jako klucza.

  • Ponieważ ta właściwość jest zdefiniowana jako liczba, musimy ją też przekonwertować na liczbę całkowitą i ustawić wartość domyślną.

  • Następna jest właściwość value.

  • Właściwość value jest właściwością dynamiczną, co oznacza, że odwołuje się do czegoś w modelu danych.

  • Aby uzyskać do niego dostęp, musimy użyć interfejsu API properties.bind i przekazać obiekt właściwości jako klucz w parametrze.

  • Zwraca ona bieżącą wartość zapisaną w modelu danych dla tej właściwości, a także subskrybuje przyszłe aktualizacje.

  • Dzięki temu za każdym razem, gdy model danych zostanie zaktualizowany, ten komponent zostanie automatycznie przekomponowany.

  • Podobnie przekonwertowaliśmy na liczbę całkowitą i ustawiliśmy wartość domyślną.

  • Musimy jeszcze skonfigurować wiązanie dwukierunkowe, aby móc zmieniać ocenę.

  • W tym celu używamy interfejsu API bindUpdater.

  • Przekazujemy ten sam obiekt właściwości, który definiuje właściwość, i w rezultacie otrzymujemy… bindUpdater zwraca funkcję lambda, która pozwala nam zmienić wartość tej właściwości w modelu danych.

  • Teraz możemy wyemitować komponent oceny w gwiazdkach.

  • OK.

  • Po zdefiniowaniu własnego komponentu niestandardowego musimy dodać go do katalogu, którego używamy.

  • Wcześniej używaliśmy podstawowego katalogu w obecnej postaci.

  • Teraz musimy utworzyć nowy katalog, który będzie zawierał cały katalog podstawowy i nasz komponent niestandardowy.

  • Katalog składa się z 3 elementów.

  • Jest to identyfikator, zestaw komponentów i zestaw funkcji.

  • W przypadku identyfikatora użyjemy identyfikatora URI, aby zdefiniować jednoznacznie identyfikowany katalog oraz wersję, nad którą pracujemy.

  • Jeśli chodzi o komponenty, weźmiemy wszystkie komponenty z podstawowego katalogu, a następnie użyjemy operatora dodawania, aby dodać utworzony przed chwilą komponent niestandardowy.

  • W przypadku funkcji użyjemy funkcji z katalogu podstawowego.

  • W ten sposób rozszerzyliśmy podstawowy katalog, aby dodać nowy typ komponentu niestandardowego.

  • Jak widzisz, utworzenie katalogu niestandardowego jest bardzo proste.

  • Agent dowiaduje się, jak korzystać z katalogu, z instrukcji systemowych, które mu wysyłamy.

  • Skoro utworzyliśmy katalog niestandardowy, zaktualizujmy też instrukcje systemowe.

  • Przekażemy więc obiekt katalogu do funkcji, która tworzy instrukcje systemowe.

  • Oto on.

  • Do tej pory wysyłaliśmy statyczną definicję schematu dla katalogu podstawowego, ponieważ używaliśmy tego standardowego katalogu.

  • Teraz, gdy mamy katalog niestandardowy, możemy dynamicznie wygenerować schemat za pomocą funkcji toJsonSchemaString.

  • Spowoduje to utworzenie pełnej reprezentacji JSON tego katalogu, w tym dodanego przez nas komponentu oceny w postaci gwiazdek.

  • Instrukcje systemowe zawierają też przykładowe odpowiedzi agenta.

  • Zmieniamy też identyfikator katalogu, aby przykłady odwoływały się do właściwego katalogu.

  • OK.

  • W ten sposób rozszerzyliśmy podstawowy katalog, dodając własne typy niestandardowe.

  • Jeśli więc wdrożymy go ponownie i wypróbujemy jeden z tych promptów, powinniśmy otrzymać interfejs oceniania.

  • Następnie agent odpowiada.

  • Tutaj widzimy, że ten komponent gwiazdki to zupełnie nowy komponent, którego agent nigdy wcześniej nie widział, dopóki go nie wdrożyliśmy.

  • Nie jest on częścią podstawowego katalogu, właśnie go utworzyliśmy.

  • Gdy kliknę gwiazdkę, wywoływana jest funkcja lambda bindUpdater, a wartość jest aktualizowana w modelu danych.

  • Jeśli prześlemy oceny, możemy przyjrzeć się działaniu, a kontekst działania będzie zawierał wartości, które właśnie ustawiliśmy.

  • OK, wróćmy teraz do slajdów.

  • To było sporo informacji, ale mam nadzieję, że udało nam się pokazać, jak łatwo można przejść od agentów odpowiadających ścianą tekstu do tworzenia prawdziwych natywnych interfejsów użytkownika.

Najważniejsze wnioski i zasoby

  • Mamy nadzieję, że udało nam się pokazać, że integracja jest dość prosta.

  • Musisz wykonać 3 główne czynności: utworzyć katalog komponentów, wdrożyć logikę analizy, a następnie wyrenderować ją w interfejsie.

  • Mamy nadzieję, że udało nam się pokazać, że biblioteka wykonuje wiele czynności automatycznie, takich jak renderowanie progresywne, sprawdzanie poprawności schematu, pętle samokorekty czy obsługa błędów.

  • Mamy nadzieję, że udało nam się szybko pokazać, że dostosowanie go jest bardzo proste.

  • Pokazaliśmy, jak podstawowy katalog reaguje na Material Theming, jeśli go używasz, ale rozszerzanie lub zastępowanie komponentów w katalogu jest bardzo proste.

  • Myślę jednak, że największe możliwości daje utworzenie własnego katalogu.

  • Jeśli chcesz mieć największą kontrolę nad tym, z jakiego interfejsu może korzystać agent, możesz utworzyć całkowicie niestandardowe komponenty.

  • Jeśli chcesz dać mu, no wiesz, całkowitą swobodę, możesz dać mu bardzo podstawowe komponenty, takie jak etykiety tekstowe i przyciski, i powiedzieć: „Utwórz interfejs”.

  • Jeśli chcesz mieć większą kontrolę, możesz utworzyć bardziej rozbudowane komponenty i powiedzieć: „To są jedyne rzeczy, których możesz używać”.

  • Dzięki temu masz kontrolę nad tym, co agent może robić i pokazywać użytkownikom.

  • To jest nowa biblioteka renderująca A2UI i biblioteka Jetpack, które właśnie wprowadziliśmy.

    implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")
  • Obecnie jest w wersji alfa, a niedawno wydaliśmy wersję alfa 02.

  • Kliknij te linki, aby dowiedzieć się więcej:

  • Na stronie developer.android.com znajdziesz dokumentację i kilka przykładów.

  • Mamy podstawowy przykład pokazujący podstawowy katalog, a także bardziej rozbudowany przykład oraz przykładową aplikację JetPacker, którą właśnie utworzyliśmy. Zobaczycie ją ponownie podczas prezentacji ADS, która odbędzie się jeszcze dzisiaj. Pokażemy, jak można tworzyć bardziej rozbudowane komponenty, aby kontrolować to, co widzi użytkownik.

  • Jak już wspominałem, obecnie jesteśmy w fazie alfa, więc to świetny moment, aby wypróbować tę funkcję i przekazać nam swoją opinię oraz prośby o dodanie funkcji. Możesz nam powiedzieć, jak chcesz z niej korzystać lub co by Ci się przydało.

  • Dlatego zachęcamy do zapoznania się z biblioteką i przesłania nam opinii.

  • I to właśnie otrzymaliśmy.

  • Z niecierpliwością czekam na to, co Ty i Twoi agenci stworzycie za pomocą tej technologii.

  • Z góry dziękuję.

  • Dziękujemy.

Kod

Fragmenty kodu

Fragment skryptu Kotlin w 27:40 w filmie „Key takeaways and resources”
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")