Novas atualizações para A2UI e o pacote GenUI do Flutter
UI gerativa, ou GenUI para abreviar, é um padrão de experiência do usuário no qual uma agente não apenas gera conteúdo, mas também toma decisões sobre como esse conteúdo deve ser exibido e interagido pelo usuário. Para desenvolvedores do Flutter, implementar o GenUI significa usar A2UI, um protocolo aberto que define uma maneira para agentes e clientes (ou “renderers”) colaborarem na composição e estado de uma interface do usuário. Para aproveitar isso, a equipe do Flutter criou genui, um pacote que usa A2UI para se conectar com um agente e fornecer-lhe um catálogo de widgets para usar, e então apresenta esses widgets ao usuário.
Ambos o pacote genui e o protocolo A2UI recentemente receberam atualizações!
A última versão do pacote genui introduz várias mudanças arquiteturais no quadro. Impulsionado pela adoção da versão 0.9 do protocolo A2UI, essa atualização desloca o genui de uma filosofia “Structured Output First” em que as mensagens A2UI eram transmitidas através de APIs de saída estruturada para um método “Prompt First”, no qual os agentes incluem blocos de JSON como texto em suas respostas. Também decupla a arquitetura, fornecendo mais controle direto sobre como seu aplicativo interage com Modelos Linguísticos Grandes (LLMs).
Se você está migrando um aplicativo da versão 0.7.0 para a versão 0.9.0 do pacote genui, este guia abrange os passos necessários, desde a limpeza de dependências até o encaminhamento dos novos loops de chat.
Decupla da arquitetura
Em versões anteriores, o GenUI confiava em uma série de classes baseadas em ContentGenerator. Essas classes escondiam os detalhes da construção de prompts, chamadas de rede LLM e análise de respostas.
A versão mais recente do pacote:genui remove ContentGenerator. Em vez disso, o quadro agora é dividido em camadas distintas:
- Motor (
SurfaceController): Gerencia o estado e a renderização da sua interface do usuário. - Transporte (
A2uiTransportAdapter): Transmite mensagens entre o agente e o renderizador. - Fachada (
Conversation): Fornece uma API de alto nível para gerenciar os estados da conversa.
Esta decupagem significa que você tem controle sobre o histórico de chat, lógica de retentativa e tratamento de erros. Também significa que você pode configurar sua conexão com seu LLM da maneira que desejar. O framework não envolve mais seu agente com um ContentGenerator, então você está livre para usar o modelo e o provedor de sua preferência, ajustar as configurações de geração, adicionar suas próprias funções e assim por diante, sem precisar passar pelo API do framework.
Como o ContentGenerator foi removido, os pacotes de envoltório específicos do provedor não são mais necessários. Se você puxar a versão mais recente do pacote, verá que nomes como genui_dartantic, genui_google_generative_ai e genui_firebase_ai não aparecem mais na árvore.
Esta é a mudança de código mais significativa na migração. Em vez de passar um ContentGenerator para seu SurfaceController, seu aplicativo é responsável por configurar uma conexão com um agente e passar mensagens de ida e volta através de um TransportAdapter.
A antiga maneira:
// Cria um ContentGenerator que encapsula a interação com
// o agente.
final generator = FirebaseAiContentGenerator(
catalog: CoreCatalogItems.asCatalog(),
systemInstruction: 'Você é um assistente útil.',
);
// Cria uma conversa que vincula o gerador a um GenUiManager
// que gerencia superfícies, atualizações e assim por diante.
final conversation = GenUiConversation(
genUiManager: GenUiManager(catalog: catalog),
contentGenerator: generator,
);A nova maneira:
final catalog = BasicCatalogItems.asCatalog();
// Cria um SurfaceController para gerenciar o estado das superfícies geradas.
final surfaceController = SurfaceController(catalogs: [catalog]);
// Cria uma adaptação de transporte que roteia mensagens do pacote `genui`
// até o agente, então alimenta as respostas de volta para a adaptação via `addChunk`.
late final adapter = A2uiTransportAdapter(
onSend: (ChatMessage msg) async {
// Use um buffer de string para preparar os tokens que serão enviados ao agente.
final buffer = StringBuffer();
// Itere sobre as mensagens criadas pelo pacote `genui`, e
// adicione-as como tokens ao buffer.
for (final part in msg.parts) {
if (part.isUiInteractionPart) {
buffer.write(part.asUiInteractionPart!.interaction);
} else if (part is genui.TextPart) {
buffer.write(part.text);
}
}
// Envie uma solicitação de geração de conteúdo ao agente, incluindo as
// mensagens stringificadas do `genui`.
final response = await myAgentClient.sendRequest(buffer.toString());
// Após receber a resposta do agente, adicione-a ao fluxo de entrada do pacote `genui`
// usando `addChunk`, onde será analisada para mensagens A2UI.
adapter.addChunk(response);
},
);Você pode olhar para esses dois exemplos e pensar, “Espere, as melhorias na API não deveriam significar que eu tenho que escrever menos código em vez de mais?” É verdade que anteriormente essa parte do código de “conexão” com o agente estava incluída no pacote genui, escondida dentro da classe ContentGenerator, mas a nova abordagem tem algumas vantagens concretas:
- Sem a necessidade de um
ContentGenerator, você pode configurar seu agente da maneira que deseja, mantê-lo em memória onde preferir e gerenciar seu ciclo de vida. Você também pode usar quase qualquer fonte de IA que desejar sem esperar por uma atualização do pacote com um novoContentGenerator. - Você não precisa mais “injetar” sua conexão com o agente na API do
genui. Eles são decouplados, com apenas tokens se movendo de um para o outro. - A testagem é mais simples.
genuiaceita tokens diretamente, e eles podem vir de um agente, ou de um agente simulado, ou de um teste codificado manualmente. - Se você gostaria de encapsular sua conexão em uma classe, ainda pode fazê-lo. Na verdade, vários dos exemplos do
genuiadotam essa abordagem.
Adotando a primeira palavra-chave
Em versões anteriores, o pacote genui dependia fortemente das restrições API do provedor de LLM (Modelo de Linguagem Aperfeiçoado) (como “Modo JSON” ou definições complexas de chamadas de função) para obrigar o modelo a gerar estruturas de UI válidas. O esquema era passado ao LLM fora do canal através de parâmetros específicos da API, e o LLM estava efetivamente preso em uma estrutura rígida.
Enquanto isso direcionava os modelos para criar JSON previsíveis e bem formatados, esquemas profundamente aninhados poderiam confundir os modelos ou se opor às suas tendências naturais de geração de texto. Além disso, a dependência em saídas estruturadas impunha limites sobre o tamanho geral e complexidade do catálogo. Também dificultava a depuração porque as restrições viviam inteiramente na camada da rede, em vez de estar em texto simples que você pudesse ler e ajustar facilmente.
A abordagem “prompt first” transfere a verdadeira fonte de volta para onde os LLMs excel: instruções do sistema. Em vez de depender inteiramente de alternâncias estritas da API, o esquema UI e as instruções relacionadas ao A2UI são injetados diretamente no prompt do sistema do LLM como texto simples. O LLM lê as instruções, detalhando exatamente como construir mensagens para o cliente.
Esta abordagem tem algumas vantagens. Primeiro, os LLMs modernos são altamente otimizados para seguir instruções detalhadas do sistema e exemplos. Fornecer o esquema no prompt alinha com como eles “pensam”. Além disso, porque o esquema UI agora é apenas texto simples em seu prompt, você tem a capacidade de alterá-lo conforme necessário para sua aplicação.
Esta mudança significa que agora a responsabilidade por obter o prompt certo no contexto do agente repousa com seu aplicativo. Felizmente, o pacote genui fornece uma nova ferramenta para ajudá-lo a criar o prompt de sistema correto para sua aplicação, PromptBuilder. Dado um catálogo e quaisquer instruções adicionais que você gostaria de fornecer, PromptBuilder criará um prompt do sistema que inclui as definições de esquema e regras que seu LLM precisa para formatar corretamente as mensagens A2UI.
final promptBuilder = PromptBuilder.chat(
catalog: catalog,
systemPromptFragments: ['Você é um assistente útil.'],
);Uma vez configurado, seu aplicativo pode recuperar uma versão de String do prompt usando promptBuilder.systemPrompt, e então passar esse valor para o LLM.
Ajustes de protocolo e esquema
Se seu código constrói manualmente JSON A2UI ou depende de estruturas específicas do payload, note estas mudanças quebradoras a partir da atualização A2UI v0.9:
- Criação de superfície:
beginRenderingagora écreateSurface. - Definições de componentes planos: Em vez de chaves aninhadas (como
{“Text”: {“text”: “Hello”}}), os componentes agora usam um discriminador plano:{“component”: “Text”, “text”: “Hello”}. - Vinculação de dados: Os vinculamentos foram simplificados. Use
{ “path”: “/path/to/var” }para resolução de caminhos.
Renomeações de propriedades:
distribution=>justifyalignment=>alignusageHint=>varianttext(emTextField) =>valueuserAction=>action
Outras coisas foram renomeadas!
Além de algumas pequenas alterações, o prefixo GenUi foi removido da maioria das classes principais:
GenUiConversation=>ConversationGenUiController=>SurfaceControllerGenUiSurface=>SurfaceGenUiHost=>SurfaceHostGenUiContext=>SurfaceContextGenUiTransport=>TransportGenUiFallback=>FallbackWidget
Além disso, CoreCatalogItems foi renomeado para BasicCatalogItems para esclarecer que ele serve como uma implementação de base em vez de uma exigência estrita.
Finalmente, para se alinhar com a terminologia padrão de chamada de função LLM, GenUiFunctionDeclaration e referências a “tools” foram renomeadas para ClientFunction.
O novo pacote genai_primitives
O pacote genui não embarca mais com seus próprios tipos de mensagens. Em vez disso, a equipe GenUI criou o novo pacote genai_primitives com tipos primitivos para funcionalidades comuns necessárias por aplicações GenAI. Este pacote inclui tipos como ChatMessage, MessagePart e ToolDefinition.
Estes novos tipos são usados em toda a API do pacote genui e eles são flexíveis o suficiente para se encaixar em outras aplicações GenAI ou pacotes com os quais você possa estar trabalhando.
E há mais!
Além do que foi mencionado acima, a versão mais recente de A2UI e o pacote genui trazem várias novas funcionalidades como:
- Funções personalizadas, úteis para validar dados no cliente
- Um novo esquema modular
- Melhorias na manipulação de erros
Para mais informações sobre essas atualizações, consulte o post do blog da anúncio v0.9 e visite a2ui.org.
Resumo
A última atualização do genui introduz uma arquitetura que é muito mais idioma, flexível e robusta. Se você ainda não começou a usar GenUI, não há melhor momento do que agora! Visite nosso laboratório de códigos introdutório para obter uma compreensão prática da tecnologia e criar um aplicativo funcional em cerca de 90 minutos.

