A2UI sta passando da componenti incentrati sul web ad Android nativo. Il nuovo renderer Jetpack Compose per A2UI v0.9 consente agli sviluppatori di eseguire il rendering nativo di UI dinamiche generate da LLM. Anziché le visualizzazioni web, utilizza un framework di attori basato su coroutine, cicli di vita di classi sealed e l'integrazione nativa di Material 3.
Questa sessione illustra la nuova architettura, spiegando come gli alberi dei componenti basati su server si traducono in nodi Compose reattivi e come le transizioni di layout integrate gestiscono gli aggiornamenti strutturali in modo fluido senza bloccare la UI.
Trascrizione
Ciao a tutti e benvenuti a questo intervento in cui parleremo di come generare la UI nelle app per Android utilizzando la nostra nuovissima libreria A2UI.
Mi chiamo Nick Butcher e sono un product manager che lavora su Jetpack Compose.
Ciao a tutti, sono Anton, un ingegnere del team di Compose.
Sto lavorando a questo nuovo renderer Jetpack Compose A2UI di cui siamo molto entusiasti di parlarti oggi.
Sì, ora con l'ascesa delle funzionalità di AI, molte app stanno valutando come l'AI può aiutare a migliorare o creare nuove esperienze per i propri utenti.
Una delle esperienze più interessanti che alcuni stanno esaminando è la UI generativa, ovvero il modo in cui, in fase di runtime, i backend degli agenti possono creare UI completamente nuove per gli utenti, il che ci sembra molto interessante.
Ma purtroppo, al momento, molte risposte dei backend degli agenti sono intrappolate in muri di testo, come questo.
Ecco un esempio in cui ho chiesto a un agente di generare una nuova funzionalità per la prenotazione di un viaggio.
Ecco il prompt.
Come puoi vedere, la risposta è abbastanza buona, ma è intrappolata in questo muro di testo con cui non posso interagire facilmente e non assomiglia affatto al resto della mia applicazione.
La UI generativa ti consente invece di creare un'esperienza come questa, in cui puoi generare una nuova UI al volo in risposta a un input dinamico, aprendo la strada a una maggiore personalizzazione o a nuove esperienze.
Ecco un esempio di UI creata utilizzando componenti nativi standard.
Quindi, puoi assolutamente creare questo tipo di esperienza in autonomia oggi, ma dovresti definire il tuo formato per l'interscambio con il backend, il tuo tipo di stream di eventi, le funzioni e così via.
A2UI, che sta per Agent-to-UI, è un protocollo aperto che utilizza un formato JSON per definire tutti questi elementi.
Una delle cose che mi piacciono di più di A2UI è che si tratta di un formato dichiarativo.
L'agente restituisce semplicemente una rappresentazione JSON della UI da visualizzare.
Non restituisce alcun codice che il client deve poi eseguire, il che può esporre a determinate vulnerabilità di sicurezza.
È solo dichiarativo.
Quali altri concetti dobbiamo conoscere su A2UI?
Solo due concetti principali: cataloghi e piattaforme.
Il catalogo contiene componenti come testo, pulsante, riga e colonna che l'agente può utilizzare per creare la UI.
Un catalogo contiene anche funzioni che l'agente può utilizzare per formattare il testo, convalidare l'input dell'utente o aprire URL.
Un catalogo è un contratto che definisce i componenti e le funzioni supportati dalla tua app e l'agente può creare solo con questi.
Ciò significa che, come ha detto Nick, l'agente non esegue mai codice arbitrario nella tua app.
Compone una UI a partire dai componenti di base che progetti e di cui ti fidi.
Un altro concetto importante è la superficie.
Una superficie è una parte dell'UI controllata dall'agente, come questo esempio di agente di viaggio nella slide.
L'agente descrive l'interfaccia utente di una piattaforma in JSON utilizzando i messaggi del protocollo A2UI, come createSurface, updateComponents, e i componenti e le funzioni definiti nel tuo catalogo.
A proposito di catalogo, il catalogo è un concetto fondamentale in A2UI, perché controlla i componenti che l'agente può utilizzare per creare un'interfaccia.
La specifica A2UI definisce il cosiddetto catalogo di base, ovvero un insieme di componenti comuni che puoi utilizzare per creare la maggior parte delle UI.
Il renderer A2UI di Jetpack Compose fornisce un'implementazione del catalogo di base utilizzando i componenti Material 3.
La maggior parte dei componenti del catalogo è pronta per l'uso.
Esistono tre componenti multimediali: immagine, video e lettore audio, in cui devi collegare la libreria che preferisci.
Ad esempio, puoi scegliere Coil per il caricamento delle immagini e Media3 per il player video e audio.
Ma la vera potenza di A2UI e della nostra nuova libreria di renderer è che puoi creare i tuoi componenti e cataloghi completamente personalizzati e controllare completamente ciò che l'agente può utilizzare per creare la UI.
Torneremo sui cataloghi più avanti.
Ok, diamo un'occhiata a come appare.
Passiamo alla macchina demo, per favore.
Questa è la nostra app demo, che utilizzeremo, e creeremo il renderer A2UI per mostrarti quanto è facile.
Si tratta di un insieme di prompt, come puoi vedere in questi piccoli chip al centro dello schermo, che puoi inviare a un agente.
Quindi, se facciamo clic su uno dei prompt, questo viene inviato a un agente reale, un backend live, quindi incrociamo le dita per la demo e, oltre al prompt, aggiungiamo anche alcune istruzioni di sistema.
Quindi, comunichiamo al backend il protocollo A2UI, il catalogo dei componenti, in questo caso il catalogo di base che stiamo utilizzando qui, che può utilizzare per creare l'UI, e forniamo alcuni esempi di come crearlo.
L'implementazione della sessione di chat nella nostra app demo sa già come estrarre i messaggi A2UI JSON non elaborati dalle risposte dell'agente.
In questo caso, l'agente ha risposto con un testo seguito dai messaggi del protocollo A2UI: createSurface, updateComponents.
Al momento, però, la nostra app mostra solo questo JSON non elaborato e non lo visualizza ancora come UI.
Ok, prima di iniziare a implementare il codice, se possiamo tornare alle slide per un secondo, facciamo un passo indietro e parliamo della progettazione del sistema che utilizzeremo per l'implementazione.
Oh, ci siamo.
Sì, ecco il piano.
Quindi, l'agente AI trasmette una risposta alla nostra app che viene ricevuta dalla classe ChatSession.
ChatSession estrae i messaggi JSON A2UI non elaborati dalle risposte dell'agente durante lo streaming e li passa al processore di messaggi A2UI.
Il processore di messaggi è configurato con un parser che converte i messaggi JSON non elaborati in una rappresentazione semantica.
Il processore convalida quindi i messaggi in base alla definizione del catalogo e li mappa a un insieme di modelli di piattaforma.
I modelli di superficie definiscono l'aspetto della UI e le parti della UI controllate dall'agente.
Questi modelli di superficie vengono poi visualizzati come UI di Compose nativa utilizzando il componibile della superficie A2UI fornito dalla nostra libreria.
La comunicazione tra l'agente e l'interfaccia utente è bidirezionale, quindi quando l'utente interagisce con l'interfaccia utente, le azioni e gli errori dell'utente vengono trasmessi all'agente.
In sintesi, esistono tre componenti principali: il catalogo, il processore di messaggi e la piattaforma.
Scriviamo il codice.
Sì, creiamolo se possiamo cambiare macchina.
Il primo passaggio consiste nell'aggiungere la dipendenza.
Qui puoi vedere che abbiamo già incluso la dipendenza A2UI di Material 3 e abbiamo già sincronizzato il progetto in modo da non dover aspettare Gradle.
Questa è la dipendenza di primo livello che definisce l'implementazione del catalogo di base utilizzando i componenti Material 3, ma esistono altre dipendenze più granulari.
Quindi, se non vuoi utilizzare il catalogo di base, di cui spiegheremo il motivo in seguito, puoi utilizzare quelli più granulari.
Ma questo porterà… attirerà il resto in modo transitivo.
Ok.
Abbiamo detto che ci sono tre cose.
Il primo passaggio è stato definire un catalogo.
Quindi, qui nella nostra applicazione, creeremo una funzione factory per aiutare a creare un'istanza dell'implementazione di Material 3 del catalogo di base.
Quindi utilizziamo questa funzione della libreria, il catalogo di base di Material A2UI, che è anch'esso sottoposto a controllo delle versioni.
Come abbiamo detto, questo fornisce tutti i componenti comuni di cui hai bisogno, ma non vogliamo costringerti a utilizzare determinate librerie di caricamento delle immagini o di rendering dei contenuti multimediali, quindi devi fornirle tu.
In questo caso, ad esempio, Anton ha già creato questo wrapper attorno al composable AsyncImage di Coil, che utilizzeremo per caricare le immagini, oltre a una piccola logica aggiuntiva per la nostra demo per cercare immagini da Wikipedia.
Quindi, molto facilmente, possiamo inserirlo nei componenti del catalogo.
Allo stesso modo, abbiamo implementazioni simili per il rendering, i video e l'audio, dove utilizzeremo la libreria Media3 per eseguirne il rendering con ExoPlayer.
In questo modo, possiamo collegare i nostri componenti.
Il catalogo ha poi bisogno di un insieme di funzioni standard.
Queste sono le funzioni che il client può fornire.
Anche in questo caso, forniamo un'implementazione di questo insieme al catalogo di base, ma dobbiamo fornirgli un contesto, che utilizzerai per eseguire determinate azioni, come l'apertura di un URI se un utente tocca un elemento.
E così abbiamo completato il primo passaggio.
Abbiamo creato un catalogo.
Il passaggio successivo consiste nel configurare l'elaboratore dei messaggi.
Quindi, questo è l'elemento che analizza effettivamente le risposte e così via.
Quindi, passeremo al modello di visualizzazione della nostra applicazione, dove per prima cosa creeremo un'istanza del catalogo che abbiamo configurato in precedenza, perché ne avremo bisogno.
Poi creeremo questo oggetto parser di messaggi, processore di messaggi, che deve accedere al catalogo per convalidare le risposte rispetto ai componenti che vogliamo utilizzare.
Un altro componente che dobbiamo creare è un parser, che è l'oggetto responsabile dell'analisi dei messaggi JSON quando vengono restituiti.
Qui creiamo l'oggetto ChatSession.
Come ho già detto, ChatSession estrae già i messaggi A2UI non elaborati dalle risposte dell'agente.
Quando ogni nuovo messaggio A2UI è pronto, chiama questo callback onA2uiMessage.
Quindi, tutto ciò che dobbiamo fare è passare ciascuno di questi messaggi quando arrivano al processore.
Chiamiamo la funzione processInput, fornendole un parser e un messaggio.
Quindi, la funzione processInput analizza ogni messaggio e poi mette in coda il lavoro per l'elaboratore, che lo convalida rispetto al catalogo e aggiorna i modelli di superficie.
Pertanto, il processore espone l'insieme corrente di modelli di superficie attivi come activeSurfaces StateFlow.
ChatSession ha una logica per aggiungere i modelli di superficie agli stati della UI delle risposte dell'agente da visualizzare.
Quindi, passiamo questo StateFlow activeSurfaces come parametro a ChatSession.
Inoltre, come ho già detto, la funzione processInput mette in coda il lavoro per il processore.
Ma per avviare effettivamente l'elaborazione, dobbiamo lanciare una coroutine su un thread in background.
Quindi, utilizzeremo un ambito del modello di visualizzazione e imposteremo il dispatcher predefinito.
In questa coroutine, dobbiamo solo chiamare processor.collectMessages.
Quindi, mentre questa coroutine è attiva, il processore gestirà i messaggi dalla coda dei messaggi.
Quindi, un'altra cosa che dobbiamo fare è cancellare le superfici attuali quando l'utente cancella la cronologia chat.
Quindi, chiamiamo clearSurfaces.
Ok.
Abbiamo completato i primi due passaggi.
Abbiamo creato il catalogo e il processore per analizzare i messaggi.
Il passaggio successivo consiste nel mostrare queste risposte nella nostra UI.
Quindi raccogliamo queste superfici man mano che vengono analizzate dal parser.
Ora, nella nostra UI, li trasmetteremo in questo oggetto di risposta dell'agente e li ripeteremo semplicemente.
Poi utilizzeremo questo composable di superficie A2UI e gli passeremo ciascuno dei modelli mentre li iteriamo per eseguire il rendering dei contenuti dove vogliamo che vengano visualizzati.
Un aspetto da considerare è che l'agente potrebbe scegliere di utilizzare un elenco se decide di visualizzare una grande quantità di contenuti.
Quindi dobbiamo impostare l'altezza limitata su un'altezza massima per questo componente per assicurarci che venga visualizzato correttamente.
E con questo, abbiamo implementato un renderer A2UI di base.
Ora, se torniamo alla nostra app, la riavviamo e premiamo di nuovo il prompt, l'agente dovrebbe utilizzarlo per, ecco, eseguire il rendering di una UI reale.
Quindi, scriviamo il codice in tempo reale.
Non è un'illusione.
Ma sì, utilizza immagini reali.
Utilizza schede, finestre di dialogo, selettori della data, cursori, ovvero tutti i componenti standard di Material con cui creeresti la tua app, anziché mostrare semplicemente una risposta testuale.
Poiché si tratta di un'app reale che utilizza componenti Material reali, fa ciò che fanno i componenti reali.
Ha prestazioni simili a quelle native.
Ad esempio, risponde alla modalità Buio qui.
Questi sono i componenti Material.
Quindi, se hai un composable del tema Material esterno in cui hai personalizzato i colori, le forme, la tipografia e così via, questi componenti li rileveranno in modo che l'aspetto sia simile al resto della tua applicazione.
Come ha detto Nick, poiché si tratta di componenti Compose nativi, hanno una semantica nativa.
Ciò significa che sono supportate tutte le funzionalità di accessibilità integrate di Compose.
Quindi, elementi come TalkBack, la scalabilità dei caratteri e la navigazione da tastiera funzionano immediatamente.
Poiché stai richiamando un agente per generare una nuova UI in fase di runtime, potrebbe esserci una certa latenza.
Potresti dover attendere la risposta dell'agente.
La libreria fa il possibile per ridurre al minimo questa latenza.
Quindi, se utilizziamo questo tipo di approccio, inseriremo alcune attese per mostrare un po' più lentamente cosa succede e come riduciamo al minimo il problema.
Sì, poiché l'agente potrebbe impiegare un po' di tempo per trasmettere in streaming la risposta completa, utilizziamo il cosiddetto rendering progressivo.
Quindi, non appena arriva il componente principale, iniziamo a visualizzare l'interfaccia utente per la superficie.
Tutti i componenti container mostrano segnaposto di caricamento per i relativi elementi secondari se questi non sono ancora arrivati.
Quando arrivano i componenti secondari, vengono animati utilizzando lo schema di movimento di Material 3.
Ok.
Passiamo al prompt successivo per mostrare un'altra funzionalità della libreria.
Ecco un altro prompt in cui prenotiamo un tavolo per cena.
In questo caso, l'agente sceglie di utilizzare questo componente di scorrimento, sì, lo ha utilizzato.
Qui puoi vedere che puoi trascinare e interagire con la UI creata dall'agente in tempo reale.
E nota come non cambia solo il numero sul componente, ma anche questa stringa di formato più in basso dove viene conteggiato.
Dimmi che non sta facendo un round trip verso l'agente solo per aggiornarlo, giusto?
Si tratta di un'associazione bidirezionale.
Pertanto, il componente del cursore è associato al modello di dati in memoria di questa superficie.
Tutti i componenti che si associano allo stesso percorso nel modello di dati vengono aggiornati automaticamente quando il valore cambia.
Quindi il cursore legge il valore da visualizzare e, quando lo trascino, lo scrive nel modello per aggiornarlo.
Tutto questo avviene localmente senza un round trip all'agente.
Ma possiamo anche chiedere all'agente di aggiornare i dati.
Quindi, facciamo 6 persone.
Presta attenzione allo slider quando l'agente risponde.
È appena salito a 6.
Vediamo cosa abbiamo ricevuto dall'agente.
Anziché inviare una descrizione completamente nuova della UI, l'agente ha inviato un solo piccolo messaggio updateDataModel che ha aggiornato il percorso del cursore.
Poiché il cursore è associato a quel percorso, è stato aggiornato in loco.
Pertanto, il modello di dati di ogni superficie è basato sullo stato dell'istantanea di Compose e ogni proprietà del componente associata a un percorso è uno stato derivato.
Quindi, ogni volta che l'utente o l'agente aggiorna il valore nel modello di dati, tutti i componenti associati allo stesso valore vengono ricomposti automaticamente.
Ok.
Quindi, supponiamo di voler passare all'azione e di voler premere il pulsante Prenota. Cosa succederà qui, Anton?
Non succede nulla al momento.
Il pulsante invia un'azione, ma non la inviamo ancora all'agente.
Risolviamo il problema.
Ok, quindi, proprio come abbiamo dovuto collegarci al processore per elaborare gli input, dovremo anche collegarci a tutti gli output, a tutte le azioni che l'utente esegue sulla UI creata dall'agente.
Per farlo, utilizziamo il flusso outboundEvents esposto dal processore e raccogliamo i dati.
Questa è la tua occasione per inserire qualsiasi logica tu voglia in base, ad esempio, a quando l'utente tocca un'azione o esegue un'operazione con la UI.
In questo caso, inviamo le azioni all'agente, che deciderà cosa fare, ma a questo punto puoi anche decidere se chiamare il tuo backend o passare a un'altra schermata dell'applicazione, a tua scelta.
Qui Anton l'ha collegato e ora, quando premiamo l'azione di conferma della prenotazione, l'azione che hai eseguito viene inviata all'agente.
Ed è qui che, nel backend, potresti anche decidere di richiamare alcune funzionalità.
Vediamo cosa inviamo effettivamente all'agente.
Questo è il messaggio di azione A2UI.
Ha un contesto, che contiene un insieme di proprietà che l'agente ha chiesto di inviare quando viene eseguita questa azione.
Quindi, quando faccio clic sul pulsante, queste proprietà particolari vengono risolte in quel momento in base ai valori correnti nel modello di dati, ed è questo che inviamo all'agente.
Ok, finora l'agente è stato piuttosto collaborativo, ma stiamo parlando di AI e l'AI può commettere errori.
Vediamo cosa succede se l'agente restituisce un errore nella UI che ci descrive.
In questo ultimo prompt, chiediamo all'agente di commettere un errore solo per poterti mostrare questa funzionalità.
Vediamo cosa succede.
Sì.
Come puoi vedere, uno dei componenti è contrassegnato da un errore.
Ora il problema è stato risolto.
Questo è il loop di autocorrezione dell'agente.
Prima che ogni componente raggiunga la UI, convalidiamo il messaggio in base allo schema del catalogo.
Se il componente non corrisponde allo schema, viene contrassegnato come errore.
Il renderer dell'implementazione del catalogo di base mostra l'errore isolato in quel componente specifico, mentre il resto della UI continua a essere visualizzato.
Dietro le quinte, il renderer invia un messaggio di errore all'agente per informarlo del problema.
In questo caso, abbiamo inviato un messaggio che indica che il tipo di componente sconosciuto che non esiste non è stato trovato nel catalogo.
L'agente ha quindi avuto la possibilità di inviare una risposta di follow-up che ha aggiornato la UI esistente e la UI è stata corretta.
Sì.
Nell'implementazione attuale del catalogo di base di Material, mostriamo subito gli errori, ma nel tuo componente personalizzato potresti decidere di continuare a mostrare un indicatore di caricamento per un componente se si verifica un errore in attesa che l'agente lo corregga.
In questo modo, l'errore potrebbe essere completamente nascosto all'utente.
A proposito di cataloghi e componenti personalizzati, finora abbiamo utilizzato solo il catalogo di base così come viene fornito, ma ovviamente puoi personalizzarlo in base alle tue esigenze.
Come puoi vedere, quando utilizziamo la funzione factory per creare il catalogo, i valori predefiniti vengono applicati a tutti i diversi componenti.
In questo modo, se vuoi, puoi collegare facilmente i tuoi componenti.
Ad esempio, se vogliamo inserire il nostro sistema di progettazione nell'app e il relativo pulsante, possiamo farlo facilmente.
Quindi, eseguiamo l'override di questo parametro quando creiamo un'istanza del catalogo e creiamo questa definizione del componente.
Quindi, se creiamo una nuova classe per implementarla, puoi vedere che qui c'è un'interfaccia, il catalogo di base A2UI del tipo button.
Se implementiamo questa interfaccia, possiamo collegare il nostro componente.
Sì.
Poiché stiamo eseguendo l'override di un componente nel catalogo di base, dobbiamo implementare questa interfaccia del pulsante del catalogo di base A2UI.
Questa interfaccia contiene già alcune informazioni sul componente, come il nome, la descrizione e lo schema delle proprietà.
Quindi, per implementare la nostra versione, dobbiamo solo implementare questa funzione componibile.
Accetta alcuni parametri.
Questi parametri sono quindi valori ricevuti dall'agente dopo la convalida.
Questi sono elementi come l'ID secondario, che fa riferimento a un componente che sarà il contenuto del pulsante.
Ha anche una variante che descrive il tipo di pulsante, la definizione dell'azione e alcuni altri parametri.
Nell'implementazione di produzione del pulsante, dovremmo utilizzare tutti questi parametri, ma per i nostri scopi dimostrativi, abbiamo bisogno solo di child e action.
Implementiamo questo composable.
Ok, in questa funzione componibile, emetteremo il nostro tipo di pulsante Mi piace personalizzato qui.
Come puoi vedere, possiamo utilizzare i parametri forniti e convalidati da A2UI.
Ad esempio, nella funzione onClick, possiamo inviare l'azione.
Possiamo utilizzare questo tipo di libreria DispatchAction e passargli l'azione definita da A2UI.
Quindi utilizziamo il nostro pulsante, ma vogliamo comunque eseguire il rendering dei contenuti secondari, dell'etichetta o di qualsiasi altro elemento che l'agente ha deciso di utilizzare.
Per farlo, utilizziamo la funzione observeA2uiComponentState e le passiamo il childId per ottenere un handle per i contenuti secondari di cui eseguire il rendering.
Quindi, utilizzeremo questa funzione componibile A2uiComponent e le passeremo lo stato del componente secondario per eseguirne il rendering.
E qui puoi vedere che eseguiamo il rendering dei contenuti secondari solo se sono stati caricati correttamente, ma puoi anche avere la tua logica qui per decidere cosa mostrare se sono in fase di caricamento o se si verifica uno stato di errore.
Quindi, se torniamo a uno dei nostri prompt di esempio nella nostra app demo e ora sostituiamo il pulsante utilizzando, ad esempio, il pulsante Material standard.
È così impercettibile che spero che tu riesca a vederlo.
Come puoi vedere, anche in questo caso utilizziamo un pulsante molto personalizzato.
Bene, questo è il modo in cui puoi, sai, sostituire alcuni dei tipi di libreria, ma cosa succede se vuoi estendere la libreria con i tuoi tipi personalizzati?
Possiamo farlo molto facilmente aggiungendo elementi al catalogo stesso.
Anton creerà una nuova classe qui per implementare un componente di valutazione a stelle.
Quindi, aggiungeremo qualcosa alla nostra applicazione per consentirci di valutare i ristoranti durante questo viaggio ipotetico.
Quindi, poiché non stiamo sostituendo un componente esistente, utilizziamo questa interfaccia più generica, l'interfaccia del componente A2UI, che, come puoi vedere, ha alcuni parametri di cui dobbiamo eseguire l'override.
I primi due sono il nome e la descrizione.
Questo descrive cosa è e cosa fa il nostro componente.
Questi sono molto importanti perché quando serializziamo il catalogo e lo inviamo al backend per indicare cosa può utilizzare per creare una UI, l'agente utilizzerà questo per decidere se utilizzare o meno questo componente quando risponde.
Quindi, devi essere molto descrittivo e dire all'AI che sai esattamente cosa fa.
La proprietà successiva di cui dobbiamo eseguire l'override è questa proprietà properties, che fondamentalmente indica quali sono i dati gestiti da questo componente.
Quindi, stiamo creando una barra di valutazione a stelle per consentire di valutare un ristorante e avremo bisogno di due proprietà.
Sarà il valore, ad esempio il numero di stelle selezionate dall'utente, che sarà una proprietà modificabile con cui l'utente può interagire e che può modificare, e il numero massimo di stelle.
Qui possiamo vedere che Anton ha utilizzato questa proprietà di tipo numerico dinamico per il valore.
Quindi, questo è il numero effettivo di stelle selezionate.
Anche in questo caso, è presente una descrizione che indica all'agente a cosa serve questo valore.
Poi abbiamo una proprietà di tipo statico, quindi utilizziamo questa proprietà A2UI di tipo numero per definire il numero massimo di stelle che questo componente può mostrare.
Una volta definite queste due proprietà, dobbiamo solo restituirle.
Quindi, eseguendo l'override di questa proprietà, restituiremo un elenco di proprietà.
In questo modo, indichi all'agente che questo è l'argomento di cui si occupa questo componente.
Sì.
Come ha detto Nick, queste definizioni delle proprietà vengono utilizzate per essere inviate come parte dello schema del catalogo, in modo che l'agente sappia come utilizzare questo componente.
Tuttavia, questa proprietà, le stesse definizioni di proprietà vengono utilizzate anche per estrarre i valori tipizzati in fase di runtime.
In questo modo, esiste un'unica fonte attendibile.
Vediamo cosa dobbiamo fare per implementarlo.
Innanzitutto, per accedere, ad esempio, alla proprietà max, poiché si tratta di una proprietà statica, l'agente invia il relativo valore in linea nell'oggetto delle proprietà.
Quindi, per accedervi, dobbiamo solo recuperarlo dall'oggetto delle proprietà facendo riferimento utilizzando l'oggetto della proprietà proper-max come chiave.
Poiché questa proprietà è definita come un numero, dobbiamo anche convertirla in un int e impostare il valore predefinito.
La proprietà successiva è value.
Poiché la proprietà value è una proprietà dinamica, significa che fa riferimento a un elemento nel modello di dati.
Quindi, per accedervi, dobbiamo utilizzare l'API properties.bind e passare l'oggetto della proprietà come chiave come parametro.
In questo modo, viene restituito il valore corrente memorizzato nel modello di dati per questa proprietà e viene eseguita la sottoscrizione per gli aggiornamenti futuri.
Pertanto, ogni volta che il modello di dati viene aggiornato, questo componente verrà ricomposto automaticamente.
Analogamente, abbiamo anche eseguito la conversione in un int e impostato un valore predefinito.
Un'altra cosa che dobbiamo fare è configurare l'associazione bidirezionale in modo da poter modificare la valutazione.
Per farlo, utilizziamo l'API bindUpdater.
Passiamo lo stesso oggetto proprietà che definisce la proprietà e, di conseguenza, otteniamo bindUpdater, che restituisce una funzione lambda che ci consente di modificare un valore per questa proprietà nel modello di dati.
Ora siamo pronti per emettere il nostro componente di valutazione a stelle.
Ok.
Ora che abbiamo definito il nostro componente personalizzato, dobbiamo aggiungerlo al catalogo che stiamo utilizzando.
In precedenza, utilizzavamo il catalogo di base così com'era.
Ora dobbiamo creare un nuovo catalogo, che prenderà tutto il catalogo di base e aggiungerà il nostro componente personalizzato.
Un catalogo è composto da tre elementi.
Si tratta di un ID, un insieme di componenti e un insieme di funzioni.
Per l'ID, utilizzeremo un URI per definire un catalogo identificato in modo univoco e la versione con cui stiamo lavorando.
Per i componenti, prenderemo tutti i componenti dal catalogo di base e poi utilizzeremo l'operatore add per aggiungere il componente personalizzato che abbiamo appena creato.
Per le funzioni, utilizzeremo quelle del catalogo di base.
In questo modo, abbiamo esteso il catalogo di base per aggiungere il nostro nuovo tipo di componente personalizzato.
Come vedi, è piuttosto facile creare un catalogo personalizzato.
In questo modo, l'agente apprende come utilizzare il catalogo dalle istruzioni di sistema che gli inviamo.
Ora che abbiamo creato un catalogo personalizzato, aggiorniamo anche le istruzioni di sistema.
Quindi, passeremo l'oggetto catalogo alla nostra funzione che crea le istruzioni di sistema.
Eccolo qui.
Finora abbiamo inviato una definizione statica dello schema per il catalogo di base, poiché utilizzavamo questo catalogo standard.
Ora che abbiamo un catalogo personalizzato, possiamo generare dinamicamente lo schema utilizzando la funzione toJsonSchemaString.
In questo modo, verrà creata una rappresentazione JSON completa di questo catalogo, incluso il componente di valutazione a stelle che abbiamo appena aggiunto.
Le istruzioni di sistema includono anche alcune risposte di esempio per l'agente.
Quindi, modifichiamo anche l'ID catalogo qui, in modo che gli esempi facciano riferimento al catalogo corretto.
Ok.
In questo modo, abbiamo esteso il catalogo di base per aggiungere i nostri tipi personalizzati.
Quindi, se rifacciamo il deployment e proviamo uno di questi prompt, dovremmo ottenere una UI di valutazione.
L'agente risponde.
E qui puoi vedere che questo componente a stella è un componente completamente nuovo che l'agente non aveva mai visto prima della nostra implementazione.
Questo non fa parte del catalogo di base, l'abbiamo appena creato.
Quando faccio clic sulla stella, viene chiamata la funzione lambda bindUpdater e il valore viene aggiornato nel modello dei dati.
Se inviamo le valutazioni, possiamo dare un'occhiata all'azione e il contesto dell'azione includerà i valori appena impostati.
Ok, ora possiamo tornare alle slide.
Abbiamo visto molte cose in poco tempo, ma spero di aver mostrato come sia facile passare da agenti che rispondono con un muro di testo ad agenti che creano vere e proprie UI native.
Ci auguriamo di averti dimostrato che l'integrazione è piuttosto semplice.
I tre componenti principali che devi creare sono: un catalogo di componenti, l'implementazione di una logica di analisi e il rendering nella UI.
Speriamo di aver dimostrato che la libreria fa molto per te fin da subito, come il rendering progressivo o la convalida dello schema, i loop di autocorrezione o la gestione degli errori. Fa davvero molto.
Ci auguriamo di aver dimostrato rapidamente oggi che è molto facile personalizzarlo.
Abbiamo mostrato come il catalogo di base risponda già al tuo tema di Material se utilizzi Material, ma è davvero semplice estendere o sostituire i componenti all'interno del catalogo.
Tuttavia, penso che la vera forza risieda nella creazione di un catalogo personalizzato.
Se vuoi avere il massimo controllo sulla UI che l'agente può effettivamente utilizzare, puoi creare componenti completamente personalizzati.
Se vuoi dargli, sai, completa libertà, puoi dargli componenti molto semplici, come etichette di testo e pulsanti, e dire: "Crea tu la UI".
Se vuoi un maggiore controllo, puoi creare componenti più elaborati e dire: "Queste sono le uniche cose che puoi utilizzare".
In questo modo, hai il controllo di ciò che l'agente può fare e mostrare ai tuoi utenti.
Quindi, questa è la nuova libreria di rendering A2UI e la libreria Jetpack che abbiamo appena lanciato.
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")Al momento è in versione alpha e la versione alpha 02 è stata rilasciata di recente.
Quindi, segui questi link per effettuare il pagamento.
Abbiamo una documentazione su developer.android.com, oltre a un paio di esempi.
Abbiamo un esempio di base che mostra il catalogo di base, un esempio più elaborato e questa app di esempio JetPacker che abbiamo appena creato, che vedrai di nuovo nel keynote di ADS più tardi oggi, che mostra come puoi creare questi componenti più elaborati per controllare ciò che l'utente vede.
Come ho detto, al momento siamo in fase alpha, quindi è un ottimo momento per provarlo e darci il tuo feedback e le tue richieste di funzionalità, e dirci, ad esempio, come vorresti usarlo o "Mi piacerebbe usarlo se potesse fare X o Y".
Quindi, dai un'occhiata alla libreria e inviaci il tuo feedback.
Ed è quello che abbiamo ottenuto.
Non vedo l'ora di vedere cosa creerai con i tuoi agenti.
Grazie mille.
Grazie.