Apps inteligentes

Permita que os agentes gerem a interface nos seus apps Android com a A2UI

Alto-falantes

Sobre

Sobre a palestra

A A2UI está migrando de componentes centrados na Web para o Android nativo. O novo renderizador do Jetpack Compose para A2UI v0.9 permite que os desenvolvedores renderizem de forma nativa uma UI dinâmica gerada por LLM. Em vez de WebViews, ele usa um framework de ator orientado por corrotinas, ciclos de vida de classes seladas e integração nativa com o Material 3.

Esta sessão mostra a nova arquitetura, explicando como as árvores de componentes orientadas pelo servidor se traduzem em nós reativos do Compose e como as transições de layout integradas lidam com atualizações estruturais sem bloquear a IU.

Resumo

  • A A2UI oferece um protocolo JSON aberto e declarativo para que os back-ends de agentes descrevam interfaces do usuário dinâmicas em vez de respostas de texto.
  • O protocolo gira em torno de catálogos que definem componentes e funções permitidos, além de superfícies que representam partes da interface controladas pelo agente.
  • A biblioteca A2UI do Jetpack Compose renderiza modelos de superfície transmitidos de forma nativa usando componentes do Material 3 com acessibilidade integrada e renderização progressiva.
  • Os modelos de dados na memória atualizam os elementos da interface vinculados instantaneamente sem entrar em contato com o agente, enquanto os eventos de saída encaminham as ações do usuário de volta.
  • Os desenvolvedores podem personalizar os itens do catálogo ou criar componentes personalizados para controlar estritamente o que o agente pode mostrar.

Recursos

Gemini Notebook

Tópicos

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

Transcrição

Introdução à interface generativa e à A2UI

  • Olá, pessoal. Sejam bem-vindos a esta palestra, em que vamos falar sobre como gerar uma interface nos seus apps Android usando nossa nova biblioteca A2UI.

  • Meu nome é Nick Butcher, sou gerente de produtos e trabalho no Jetpack Compose.

  • Olá, pessoal. Meu nome é Anton, sou engenheiro na equipe do Compose.

  • Estou trabalhando no novo renderizador de interface do usuário do Android Auto do Jetpack Compose, e estamos muito felizes em falar sobre ele hoje.

  • Sim, agora, com o surgimento dos recursos de IA, muitos apps estão analisando como a IA pode ajudar a melhorar ou criar novas experiências para os usuários.

  • Uma das experiências interessantes que algumas pessoas estão analisando é a interface generativa, que é como, no momento da execução, os back-ends de agentes podem criar interfaces totalmente novas para os usuários, o que achamos muito interessante.

  • Mas, infelizmente, no momento, muitas respostas de back-ends de agentes estão presas em paredes de texto, como esta.

  • Aqui está um exemplo em que pedi a um agente para gerar um novo recurso para reservar uma viagem.

  • Este é o comando.

  • Como você pode ver, a resposta é bem razoável, mas está presa em um bloco de texto com o qual não consigo interagir facilmente e que não se parece em nada com o restante do meu aplicativo.

  • A interface generativa permite criar uma experiência como essa, em que é possível gerar uma nova interface em tempo real em resposta a entradas dinâmicas, abrindo possibilidades para mais personalização ou novas experiências.

  • Este é um exemplo de uma interface criada com componentes nativos padrão.

  • Então, você pode criar esse tipo de experiência hoje mesmo, mas teria que definir seu próprio formato para intercâmbio com o back-end, seu próprio tipo de fluxo de eventos e funções, e assim por diante.

  • O A2UI, que significa Agent-to-UI, é um protocolo aberto que usa o formato JSON para definir tudo isso.

  • Uma das coisas que eu gosto na A2UI é que ela é um formato declarativo.

  • O agente está apenas retornando uma representação JSON da interface para renderização.

  • Ele não retorna nenhum código que o cliente precise executar, o que pode expor você a certas vulnerabilidades de segurança.

  • É apenas declarativo.

  • Então, quais outros conceitos precisamos conhecer sobre a A2UI?

Conceitos básicos: catálogos e plataformas

  • Apenas dois conceitos principais: catálogos e plataformas.

  • O catálogo tem componentes como texto, botão, linha e coluna que o agente pode usar para criar a interface.

  • Um catálogo também tem funções que o agente pode usar para formatar texto, validar a entrada do usuário ou abrir URLs.

  • Um catálogo é um contrato que define quais componentes e funções o app aceita, e o agente só pode criar com eles.

  • Isso significa que, como Nick mencionou, o agente nunca executa código arbitrário no app.

  • Ele compõe uma interface com base nos elementos básicos que você cria e confia.

  • Outro conceito importante é a superfície.

  • Uma superfície é uma parte da interface que o agente controla, como este exemplo de planejador de viagens no slide.

  • O agente descreve a interface de uma plataforma em JSON usando as mensagens do protocolo A2UI, como createSurface, updateComponents, e os componentes e as funções definidos no seu catálogo.

  • Falando em catálogo, ele é um conceito fundamental no A2UI, porque controla quais componentes o agente pode usar para criar uma interface.

  • A especificação da A2UI define o catálogo básico, que é um conjunto de componentes comuns que podem ser usados para criar a maioria das interfaces.

  • O renderizador A2UI do Jetpack Compose oferece uma implementação do catálogo básico usando componentes do Material 3.

  • A maioria dos componentes do catálogo está pronta para uso.

  • Há três componentes de mídia: imagem, vídeo e player de áudio, em que você precisa conectar a biblioteca de sua escolha.

  • Por exemplo, você pode escolher o Coil para carregar imagens e o Media3 para o player de vídeo e áudio.

  • Mas o verdadeiro poder da A2UI e da nossa nova biblioteca de renderização é que você pode criar seus próprios componentes e catálogos totalmente personalizados e controlar totalmente o que o agente pode usar para criar a interface.

  • Vamos voltar aos catálogos mais tarde.

  • Vamos ver como isso funciona.

  • Então, se pudermos mudar para a máquina de demonstração, por favor.

  • Este é o app de demonstração que vamos usar. Vamos criar o renderizador da A2UI para mostrar como é fácil.

  • É um conjunto de comandos, como você pode ver nesses pequenos ícones no meio da tela, que podem ser enviados a um agente.

  • Então, se clicarmos em um dos comandos, ele será enviado para um back-end de agente real e ativo. Vamos torcer para que a demonstração dê certo. Além do comando, também adicionamos algumas instruções do sistema.

  • Então, informamos ao back-end sobre o protocolo A2UI, sobre o catálogo de componentes, neste caso, o catálogo básico que estamos usando aqui, que ele pode usar para criar a IU, e damos alguns exemplos de como fazer isso.

  • A implementação da sessão de chat no nosso app de demonstração já sabe como extrair mensagens JSON brutas da A2UI das respostas do agente.

  • Nesse caso, o agente respondeu com um texto seguido por mensagens do protocolo A2UI: createSurface, updateComponents.

  • No momento, nosso app mostra apenas esse JSON bruto e ainda não o renderiza como uma interface.

  • Antes de começarmos a implementar o código, vamos voltar aos slides por um segundo para falar sobre o design do sistema que vamos usar para essa implementação.

  • Vamos lá.

Arquitetura de aplicativos e processamento de mensagens

  • Então, este é o plano.

  • Assim, o agente de IA transmite uma resposta para o app, que é recebida pela classe ChatSession.

  • A ChatSession extrai mensagens JSON brutas da A2UI das respostas do agente enquanto elas são transmitidas e as transmite para o processador de mensagens da A2UI.

  • O processador de mensagens é configurado com um analisador que converte essas mensagens JSON brutas em uma representação semântica.

  • Em seguida, o processador valida as mensagens em relação à definição do catálogo e as mapeia para um conjunto de modelos de superfície.

  • Os modelos de superfície definem a aparência da interface e as partes dela que o agente controla.

  • Esses modelos de superfície são renderizados como uma interface nativa do Compose usando o elemento combinável de superfície A2UI fornecido pela nossa biblioteca.

  • A comunicação entre o agente e a interface é bidirecional. Assim, quando o usuário interage com a interface, as ações e os erros dele são enviados de volta ao agente.

  • Em resumo, há três componentes principais: o catálogo, o processador de mensagens e a plataforma.

  • Vamos escrever o código para isso.

  • Sim, vamos construir se pudermos trocar de máquina.

  • A primeira etapa é adicionar a dependência.

  • Aqui, você pode ver que já incluímos a dependência do Material 3 A2UI e já sincronizamos o projeto para não precisar ficar esperando o Gradle.

  • Essa é a dependência de nível superior que define a implementação do catálogo básico usando esses componentes do Material 3, mas há outras dependências mais detalhadas abaixo.

  • Portanto, se você não quiser usar o catálogo básico, vamos explicar o motivo mais adiante, use os mais detalhados.

  • Mas isso vai trazer o restante de forma transitiva.

  • Ok.

  • Então, dissemos que há três coisas.

  • A primeira etapa foi definir um catálogo.

  • Então, no nosso aplicativo, vamos criar uma função de fábrica para ajudar a criar uma instância dessa implementação do Material 3 do catálogo básico.

  • Por isso, usamos essa função de biblioteca aqui, o catálogo básico do Material A2UI, que também tem controle de versões.

  • Como dissemos, ele oferece todos os componentes comuns de que você precisa, mas não queremos forçar você a usar determinadas bibliotecas de carregamento de imagens ou renderização de mídia. Por isso, você precisa fornecer essas bibliotecas.

  • Aqui, por exemplo, o Anton já criou esse wrapper em torno do elemento combinável AsyncImage do Coil, que vamos usar para carregar imagens, além de uma lógica extra para nossa demonstração procurar imagens na Wikipédia.

  • Assim, podemos conectar isso facilmente aos componentes do catálogo.

  • Da mesma forma, temos implementações semelhantes para renderização, vídeo e áudio, em que vamos usar a biblioteca Media3 para renderizá-los com o ExoPlayer.

  • De forma simples, podemos conectar nossos próprios componentes a ele.

  • Em seguida, o catálogo precisa de um conjunto de funções padrão.

  • Portanto, essas são funções que o cliente pode fornecer.

  • Novamente, enviamos uma implementação disso junto com o catálogo básico, mas precisamos fornecer um contexto, que você vai usar para realizar determinadas ações, como abrir um URI se um usuário tocar em um item.

  • E assim, concluímos nossa primeira etapa.

  • Criamos um catálogo.

  • A próxima etapa é configurar o processador de mensagens.

  • Então, é isso que analisa as respostas e assim por diante.

  • Vamos mudar para o modelo de visualização do aplicativo, onde vamos instanciar o catálogo que configuramos anteriormente, porque vamos precisar dele.

  • Em seguida, vamos criar esse analisador de mensagens, um objeto processador de mensagens, que precisa de acesso ao catálogo para validar as respostas em relação aos componentes que queremos usar.

  • Outro componente que precisamos criar é um analisador, que é o objeto responsável por analisar as mensagens JSON que retornam.

  • Aqui é onde criamos o objeto ChatSession.

  • Como mencionei antes, a ChatSession já extrai as mensagens brutas da A2UI das respostas do agente.

  • E, quando cada nova mensagem da A2UI estiver pronta, ele vai chamar esse callback onA2uiMessage.

  • Portanto, basta transmitir cada uma dessas mensagens quando elas chegarem ao processador.

  • Chamamos a função processInput, fornecemos um analisador e uma mensagem.

  • Portanto, a função processInput analisa cada mensagem e, em seguida, coloca o trabalho em fila para que o processador o valide em relação ao catálogo e atualize os modelos de superfície.

  • Portanto, o processador expõe o conjunto atual de modelos de superfície ativos como StateFlow activeSurfaces.

  • A ChatSession tem uma lógica para adicionar os modelos de superfície aos estados de interface das respostas do agente a serem renderizadas.

  • Portanto, basta transmitir esse StateFlow activeSurfaces como um parâmetro para a ChatSession.

  • Além disso, como mencionei, a função processInput coloca o trabalho em fila para o processador.

  • Mas para realmente iniciar o processamento, precisamos iniciar uma corrotina em uma linha de execução em segundo plano.

  • Por isso, vamos usar um escopo de modelo de visualização e definir o agente padrão.

  • Nessa corrotina, basta chamar processor.collectMessages.

  • Portanto, enquanto essa corrotina estiver ativa, o processador vai processar as mensagens da fila.

  • Então, mais uma coisa que precisamos fazer é limpar as plataformas atuais quando o usuário limpar o histórico de conversas.

  • Por isso, chamamos clearSurfaces.

Como renderizar modelos de superfície na interface

  • Ok.

  • Já concluímos as duas primeiras etapas.

  • Criamos o catálogo e o processador para analisar as mensagens.

  • A próxima etapa é renderizar essas respostas na interface.

  • Então, estamos coletando essas superfícies conforme elas são analisadas pelo analisador.

  • Agora, na nossa interface, vamos transmitir esses dados no objeto de resposta do agente e iterar sobre eles.

  • Em seguida, vamos usar esse elemento combinável de superfície da A2UI e transmitir cada um dos modelos conforme iteramos neles para renderizar o conteúdo onde queremos que ele vá.

  • Uma observação aqui é que o agente pode optar por usar algo como uma lista se quiser exibir, por exemplo, uma grande quantidade de conteúdo.

  • Por isso, precisamos definir uma altura máxima para esse componente para garantir que ele seja renderizado corretamente.

  • E, com isso, implementamos um renderizador básico de A2UI.

  • Agora, se voltarmos ao app, reiniciarmos e enviarmos o comando de novo, o agente vai usar isso para, sim, aqui está, renderizar uma interface real.

  • Então, vamos programar isso ao vivo.

  • Não é como fumaça e espelhos.

  • Mas sim, ele usa imagens reais.

  • Ele usa guias, caixas de diálogo, seletores de data, controles deslizantes, ou seja, todos os componentes padrão do Material Design que você usaria para criar seu app, em vez de apenas mostrar uma resposta textual.

  • Como esse é um app real que usa componentes reais do Material Design, ele faz o que os componentes reais fazem.

  • Ele tem um desempenho nativo.

  • Ele faz coisas como responder ao modo escuro aqui.

  • Esses são os componentes do Material Design.

  • Portanto, se você tiver um elemento de composição externo do tema do Material Design em que personalizou as cores, formas, tipografia etc., esses componentes vão usar essas personalizações para que fiquem parecidos com o restante do aplicativo.

  • Como o Nick mencionou, como esses são componentes nativos do Compose, eles têm semântica nativa.

  • Isso significa que todos os recursos de acessibilidade integrados do Compose são compatíveis.

  • Assim, recursos como o TalkBack, o dimensionamento de fontes e a navegação pelo teclado funcionam sem problemas.

  • Como você está invocando um agente para gerar uma nova interface em tempo de execução, pode haver alguma latência.

  • Talvez você tenha que esperar o agente responder.

  • A biblioteca faz o possível para minimizar essa latência.

  • Então, se usarmos esse tipo de, vamos injetar algumas esperas para mostrar um pouco mais devagar o que acontece e como minimizamos isso.

  • Sim, como pode levar algum tempo para o agente transmitir a resposta completa, usamos algo chamado renderização progressiva.

  • Assim que o componente raiz chega, começamos a mostrar a interface da plataforma.

  • Todos os componentes de contêiner mostram marcadores de posição de carregamento para os filhos se eles ainda não tiverem chegado.

  • Quando os componentes filhos chegam, eles são animados usando o esquema de movimento do Material 3.

  • Ok.

  • E se pudermos mudar para o próximo comando, só para mostrar outro recurso da biblioteca.

  • Aqui está outro comando em que estamos reservando uma mesa para o jantar.

  • E aqui o agente está escolhendo usar esse componente de controle deslizante, sim, ele foi usado.

  • Aqui, você pode arrastar e interagir com a interface que o agente criou em tempo real.

  • E observe que não é apenas o número no componente que está mudando, há também essa string de formato mais abaixo, onde ele está sendo contado.

  • Isso não está fazendo uma ida e volta ao agente só para atualizar isso, certo?

  • Essa é uma vinculação bidirecional.

  • Portanto, o componente do controle deslizante está vinculado ao modelo de dados na memória dessa superfície.

  • Todos os componentes que se vinculam ao mesmo caminho no modelo de dados são atualizados automaticamente quando o valor muda.

  • Assim, o controle deslizante lê o valor a ser exibido e, quando eu o arrasto, ele grava o valor de volta no modelo para atualizá-lo.

  • Tudo isso acontece localmente, sem uma ida e volta ao agente.

  • Mas também podemos pedir que o agente atualize os dados.

  • Vamos colocar 6 pessoas.

  • Preste atenção ao controle deslizante quando o agente responder.

  • Acabou de pular para 6.

  • Vamos ver o que recebemos do agente.

  • Em vez de enviar uma descrição totalmente nova da interface, o agente enviou apenas uma pequena mensagem updateDataModel que atualizou o caminho do controle deslizante.

  • Como o controle deslizante está vinculado a esse caminho, ele foi atualizado no local.

  • Portanto, o modelo de dados de cada superfície é criado com base no estado do snapshot do Compose, e cada propriedade de componente vinculada a um caminho é um estado derivado.

  • Assim, sempre que o usuário ou o agente atualiza o valor no modelo de dados, todos os componentes vinculados ao mesmo valor são recompostos automaticamente.

  • Ok.

  • Digamos que queremos entrar em ação e clicar no botão de reserva. O que vai acontecer aqui, Anton?

  • Nada acontece no momento.

  • O botão envia uma ação, mas ainda não a enviamos para o agente.

  • Vamos corrigir isso.

Como processar ações do usuário e fluxos de eventos

  • Assim como tivemos que nos conectar ao processador para processar as entradas, também precisamos nos conectar a todas as saídas, todas as ações que o usuário realiza na interface criada pelo agente.

  • Para isso, usamos o fluxo outboundEvents que o processador expõe e coletamos nele.

  • Essa é sua chance de conectar qualquer lógica que você quiser com base em, por exemplo, quando o usuário toca em uma ação ou realiza algo com a interface.

  • Aqui, estamos enviando as ações de volta ao agente para que ele decida o que fazer, mas você também pode decidir se quer chamar seu próprio back-end ou navegar para uma tela diferente no aplicativo. Você decide.

  • Aqui, o Anton conectou o webhook. Agora, quando clicamos na ação de confirmação da reserva, ela é enviada de volta ao agente.

  • E é aqui que, no back-end, você pode decidir invocar alguma funcionalidade.

  • Vamos ver o que realmente enviamos ao agente.

  • Esta é a mensagem de ação do A2UI.

  • Ele tem um contexto, que contém um conjunto de propriedades que o agente pediu para serem enviadas quando essa ação é realizada.

  • Assim, quando clico no botão, essas propriedades específicas são resolvidas naquele momento com base nos valores atuais no modelo de dados, e é isso que enviamos ao agente.

  • Até agora, o agente tem sido bem cooperativo, mas estamos falando de IA, e ela pode cometer erros.

  • Vamos ver o que acontece se o agente cometer um erro na interface que ele descreve para nós.

  • Neste último comando, pedimos ao agente para cometer um erro, só para demonstrar isso para você.

  • Vamos ver o que acontece.

  • Sim.

  • Como você pode ver, um dos componentes está marcado com um erro.

  • E agora foi corrigido.

  • Esse é o loop de autocorreção do agente.

  • Antes de cada componente chegar à interface, validamos essa mensagem em relação ao esquema do catálogo.

  • Se o componente não corresponder ao esquema, ele será marcado como um erro.

  • O renderizador da implementação básica do catálogo mostra que o erro está isolado nesse componente específico, enquanto o restante da interface ainda é exibido.

  • Nos bastidores, o renderizador envia uma mensagem de erro ao agente para informar o que deu errado.

  • Nesse caso, enviamos uma mensagem informando que o tipo de componente desconhecido que não existe não foi encontrado no catálogo.

  • O agente teve a chance de responder com uma mensagem de acompanhamento que atualizou a interface, e ela foi corrigida.

  • Sim.

  • Na implementação atual do catálogo básico do Material, mostramos os erros imediatamente, mas no seu componente personalizado, você pode continuar mostrando um indicador de carregamento para um componente se houver um erro enquanto espera que o agente o corrija.

  • Dessa forma, o erro pode ficar completamente oculto para o usuário.

Como criar componentes personalizados e estender catálogos

  • E por falar em catálogos e componentes personalizados, até agora usamos o catálogo básico como ele é fornecido, mas você pode personalizá-lo de acordo com suas necessidades.

  • Ao usar a função de fábrica para criar o catálogo, os valores padrão são usados para todos os componentes.

  • Assim, você pode conectar seus próprios componentes, se quiser.

  • Por exemplo, se quisermos conectar nosso próprio sistema de design no app e o próprio botão, podemos fazer isso facilmente.

  • Portanto, basta substituir esse parâmetro ao criar uma instância do catálogo, e vamos criar essa definição de componente.

  • Se criarmos uma nova classe para implementar isso, você vai notar que há uma interface aqui, o catálogo básico do A2UI do tipo botão.

  • Se implementarmos isso, poderemos conectar nosso próprio componente.

  • Sim.

  • Como estamos substituindo um componente no catálogo básico, precisamos implementar essa interface de botão do catálogo básico da A2UI.

  • Essa interface já tem algumas informações sobre o componente, como o nome, a descrição e o esquema de propriedades.

  • Portanto, para implementar nossa própria versão, basta implementar essa função combinável.

  • Alguns parâmetros são necessários.

  • Portanto, esses parâmetros são valores recebidos do agente após a validação.

  • Por exemplo, o ID filho, que faz referência a um componente que será o conteúdo do botão.

  • Ele também tem uma variante que descreve o tipo de botão, a definição da ação e alguns outros parâmetros.

  • Na implementação de produção do botão, precisaríamos usar todos esses parâmetros, mas para nossa demonstração, precisamos apenas do filho e da ação.

  • Vamos implementar esse elemento combinável.

  • Nesta função combinável, vamos emitir nosso próprio tipo de botão "Gostei" personalizado.

  • Como você pode ver, podemos usar os parâmetros que foram fornecidos e validados pela A2UI.

  • Por exemplo, na função onClick, podemos enviar essa ação.

  • Podemos usar esse tipo de biblioteca DispatchAction e transmitir a ação definida pela A2UI.

  • Estamos usando nosso próprio botão, mas ainda queremos renderizar o conteúdo filho, o rótulo ou o que o agente decidiu usar.

  • Para isso, usamos a função observeA2uiComponentState e transmitimos o childId para receber um identificador do conteúdo filho que deve ser renderizado.

  • Em seguida, vamos usar essa função combinável A2uiComponent e transmitir o estado do componente filho para renderizá-lo.

  • Aqui, você pode ver que só renderizamos o conteúdo filho se ele for carregado com sucesso, mas você também pode ter sua própria lógica aqui para decidir o que mostrar se ele estiver carregando ou se houver um estado de erro.

  • Se voltarmos a um dos comandos de exemplo no app de demonstração e substituirmos o botão usando o botão padrão do Material Design.

  • É tão sutil que espero que você consiga ver.

  • Também estamos usando um botão bem personalizado aqui.

  • Legal, então é assim que você pode, sabe, substituir alguns dos tipos de biblioteca, mas e se você quiser estender a biblioteca com seus próprios tipos personalizados?

  • Podemos fazer isso facilmente adicionando ao próprio catálogo.

  • Então, Anton vai criar uma nova classe para implementar um componente de classificação por estrelas.

  • Então, vamos adicionar algo ao nosso aplicativo para podermos avaliar restaurantes durante essa viagem hipotética.

  • Como não estamos substituindo um componente, usamos essa interface mais genérica, a A2UI, que tem alguns parâmetros que precisamos substituir.

  • Os dois primeiros são o nome e a descrição.

  • Isso descreve o que é e o que faz o componente.

  • Isso é muito importante porque, quando serializamos o catálogo e o enviamos ao back-end para informar o que ele pode usar para criar uma interface, é isso que o agente vai usar para decidir se deve ou não usar esse componente ao responder.

  • Portanto, você precisa ser bastante descritivo e dizer à IA que sabe exatamente o que isso faz.

  • A próxima propriedade que precisamos substituir é "properties", que basicamente define os dados com que esse componente lida.

  • Estamos criando uma barra de classificação por estrelas para que você possa avaliar um restaurante. Vamos precisar de duas propriedades.

  • Será o valor, como o número de estrelas que o usuário selecionou, que será uma propriedade mutável com que o usuário pode interagir e mudar, e o número máximo de estrelas.

  • Aqui, podemos ver que o Anton usou essa propriedade dinâmica do tipo número para o valor.

  • Isso é como se você soubesse quantas estrelas estão selecionadas.

  • que também tem uma descrição própria informando ao agente para que serve esse valor.

  • Depois, temos uma propriedade de tipo estático. Estamos usando essa propriedade A2UI do tipo número para definir o número máximo de estrelas que esse componente pode mostrar.

  • Depois de definir essas duas propriedades, basta retorná-las.

  • Então, ao substituir essa propriedade, vamos retornar uma lista delas.

  • É assim que você informa ao agente o que esse componente faz.

  • Sim.

  • Como Nick mencionou, essas definições de propriedade são usadas para serem enviadas como parte do esquema do catálogo para que o agente saiba como usar esse componente.

  • Mas as mesmas definições de propriedade também são usadas para extrair valores tipados no ambiente de execução.

  • Portanto, há uma única fonte de verdade.

  • Vamos ver o que precisamos fazer para implementar isso.

  • Primeiro, para acessar, digamos, a propriedade "max", como ela é estática, o agente envia o valor dela inline no objeto de propriedades.

  • Para acessá-lo, basta usar o objeto de propriedades, referenciando-o com o objeto de propriedade proper-max como chave.

  • Como essa propriedade é definida como um número, também precisamos convertê-la em um int e definir o padrão.

  • O próximo item é a propriedade de valor.

  • Como a propriedade de valor é dinâmica, ela faz referência a algo no modelo de dados.

  • Portanto, para acessá-lo, precisamos usar a API properties.bind e transmitir o objeto de propriedade como uma chave como parâmetro.

  • Isso retorna o valor atual armazenado no modelo de dados para essa propriedade e também se inscreve para atualizações futuras.

  • Assim, sempre que o modelo de dados for atualizado, esse componente será recomposto automaticamente.

  • Da mesma forma, também convertemos para um int e definimos um padrão.

  • Precisamos também configurar a vinculação bidirecional para que seja possível mudar a classificação.

  • Para isso, usamos a API bindUpdater.

  • Transmitimos o mesmo objeto de propriedade que define a propriedade e, como resultado, recebemos o bindUpdater, que retorna uma lambda que permite mudar um valor para essa propriedade no modelo de dados.

  • Agora estamos prontos para emitir nosso componente de classificação por estrelas.

  • Ok.

  • Agora que definimos nosso próprio componente personalizado, precisamos adicioná-lo ao catálogo que estamos usando.

  • Antes, usávamos o catálogo básico como ele é.

  • Agora, precisamos criar um novo catálogo, que vai pegar todo o catálogo básico e adicionar nosso componente personalizado.

  • Um catálogo é composto por três elementos.

  • É um ID, um conjunto de componentes e um conjunto de funções.

  • Para o ID, vamos usar um URI para definir um catálogo que identifica exclusivamente, além da versão com que estamos trabalhando.

  • E, para os componentes, vamos pegar todos os componentes do catálogo básico e usar o operador de adição para incluir o componente personalizado que acabamos de criar.

  • E, para as funções, vamos usar as do catálogo básico.

  • E assim, estendemos o catálogo básico para adicionar nosso novo tipo de componente personalizado.

  • Viu como é fácil criar um catálogo personalizado?

  • Assim, o agente aprende a usar o catálogo com as instruções do sistema que enviamos para ele.

  • Agora que criamos um catálogo personalizado, vamos atualizar as instruções do sistema.

  • Então, vamos transmitir o objeto de catálogo para nossa função que cria as instruções do sistema.

  • Então, aqui está.

  • Até agora, enviávamos uma definição estática do esquema para o catálogo básico, já que usávamos esse catálogo padrão.

  • Agora que temos um catálogo personalizado, podemos gerar o esquema de forma dinâmica usando a função toJsonSchemaString.

  • Isso vai criar uma representação JSON completa desse catálogo, incluindo o componente de classificação por estrelas que acabamos de adicionar.

  • As instruções do sistema também incluem alguns exemplos de respostas para o agente.

  • Por isso, também mudamos o ID do catálogo aqui para que os exemplos façam referência ao catálogo correto.

  • Ok.

  • Com isso, estendemos o catálogo básico para adicionar nossos próprios tipos personalizados.

  • Então, se reimplantarmos isso e tentarmos um desses comandos, esperamos que uma interface de classificação seja exibida.

  • Em seguida, o agente responde.

  • E aqui, você pode ver que esse componente de estrela é um componente totalmente novo que o agente nunca tinha visto até que o implementamos em tempo real.

  • Isso não faz parte do catálogo básico, acabamos de criar.

  • Quando clico na estrela, o lambda bindUpdater é chamado, e o valor é atualizado no modelo de dados.

  • Se enviarmos as classificações, poderemos analisar a ação, e o contexto dela vai incluir os valores que acabamos de definir.

  • Agora, vamos voltar para os slides.

  • Foi muita coisa em pouco tempo, mas espero que tenhamos mostrado como é fácil transformar agentes que respondem com um bloco de texto em agentes que criam interfaces nativas reais.

Principais conclusões e recursos

  • Esperamos ter mostrado que a integração é bem fácil.

  • Há três componentes principais que você precisa fazer: criar um catálogo de componentes, implementar uma lógica de análise e renderizá-la na interface.

  • E esperamos ter mostrado que a biblioteca faz muita coisa por você, como renderização progressiva ou validação de esquema, loops de autocorreção ou tratamento de erros. Ela faz muita coisa.

  • E esperamos ter demonstrado rapidamente hoje que é muito fácil personalizá-lo.

  • Mostramos como o catálogo básico já responde ao seu tema do Material Design, mas é muito simples estender ou substituir componentes no catálogo.

  • Mas acho que o maior poder vem de criar seu próprio catálogo.

  • Se você quiser ter o máximo de controle sobre qual interface o agente pode usar, crie componentes totalmente personalizados.

  • Se você quiser dar total liberdade, pode fornecer componentes muito básicos, como rótulos de texto e botões, e dizer: "Crie a interface".

  • Se quiser mais controle, crie componentes mais elaborados e diga: "Estas são as únicas coisas que você pode usar".

  • Assim, você controla o que o agente pode fazer e mostrar aos usuários.

  • Essa é a nova biblioteca de renderização da A2UI e a biblioteca do Jetpack que acabamos de lançar.

    implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")
  • No momento, ele está na versão Alfa, e a Alfa 02 foi lançada recentemente.

  • Então, acesse estes links para conferir.

  • Temos documentação em developer.android.com, além de alguns exemplos.

  • Temos um exemplo básico que mostra o catálogo básico, um exemplo mais elaborado e o app de exemplo JetPacker que acabamos de criar, que você vai ver novamente na palestra do ADS ainda hoje, demonstrando como criar esses componentes mais elaborados para controlar o que o usuário vê.

  • Como eu disse, estamos na fase Alfa, então é um ótimo momento para conferir e nos dar seu feedback e suas solicitações de recursos, além de nos dizer como você gostaria de usar isso ou se gostaria de usar se pudesse fazer X ou Y.

  • Por isso, confira a biblioteca e envie seu feedback.

  • E foi isso que conseguimos.

  • Mal posso esperar para ver o que você e seus agentes vão criar com ele.

  • Agradeço desde já.

  • Nossa equipe agradece.

Código

Snippets de código

Snippet do script do Kotlin aos 27:40 em "Principais conclusões e recursos"
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")