Renderizar superfícies A2UI no seu app

Ao usar o renderizador agente para interface (A2UI) do Jetpack Compose, um agente de IA envia mensagens que descrevem estruturas de interface, propriedades de componentes e atualizações de dados. Para mostrar essas interfaces de forma nativa no app, hospede e renderize uma superfície A2UI na hierarquia do Jetpack Compose do app.

O renderizador A2UI do Compose coordena a análise de mensagens, o gerenciamento de estado de instantâneo reativo e as transições de estado de superfície animadas. Embora o renderizador principal seja independente de qualquer sistema de design específico, ele oferece integração imediata com o Material Design 3 pelo catálogo básico fornecido.

Inicializar a camada de dados com suporte do Compose

O renderizador A2UI oferece suporte a estados com reconhecimento de snapshots na camada de dados do app, o que permite que a interface do app reaja a atualizações incrementais do agente. Para adicionar esse suporte, inicialize o analisador e o processador usando as funções de fábrica A2uiMessageParser e A2uiMessageProcessor no seu ViewModel, conforme mostrado no snippet de código a seguir:

class AgenticUiViewModel : ViewModel() {
    // Create a parser that leverages the built-in JSON parser.
    private val parser = A2uiMessageParser()

    // Create an A2UI message processor with your catalog and optional
    // action interceptor (implementing A2uiActionInterceptor).
    private val processor = A2uiMessageProcessor(
        // You can also use the provided Material catalog instead of
        // a custom one.
        catalogs = listOf(CustomDesignSystemCatalog)
    )

    // Expose active surfaces to the UI as a StateFlow.
    val a2uiSurfaces: StateFlow<List<A2uiSurfaceModel>> =
        processor.activeSurfaces

    init {
        // Collect messages on a background thread tied to the ViewModel lifecycle.
        viewModelScope.launch(Dispatchers.Default) {
            processor.collectMessages()
        }

        // Add support for two-way communication with the agent.
        viewModelScope.launch(start = CoroutineStart.UNDISPATCHED) {
            processor.outboundEvents.collect(::handleOutboundA2uiEvent)
        }
    }

    // Called by your app's networking layer or business logic whenever
    // a new A2UI protocol message arrives from the AI agent.
    fun onNetworkMessage(json: String) {
        processor.processInput(parser, json)
    }
}

Renderizar superfícies usando o catálogo básico (Material 3)

Ao renderizar superfícies usando a implementação do catálogo básico fornecida (androidx.compose.material3:material3-a2ui), é possível renderizar uma superfície do Material 3 totalmente estilizada, incluindo suporte integrado para indicadores de carregamento, limites de erros e transições animadas. Para isso, use o ponto de entrada combinável A2uiSurface:

@Composable
fun AgenticUiScreen(viewModel: AgenticUiViewModel) {
    // Observe active surfaces managed by the data layer.
    val surfaces by viewModel.a2uiSurfaces.collectAsStateWithLifecycle()

    Column(Modifier.fillMaxSize()) {
        surfaces.forEach { surface ->
            key(surface.id) {
                A2uiSurface(
                    surfaceModel = surface,
                    // Add your surface's custom modifiers here.
                )
            }
        }
    }
}

Processar estados de superfície e transições animadas

O A2uiSurface coordena a resolução do estado do componente raiz e aplica transições AnimatedContent em estados de carregamento, erro e sucesso:

@Composable
fun CustomStyledSurface(surface: A2uiSurfaceModel) {
    A2uiSurface(
        surfaceModel = surface,
        modifier = Modifier.fillMaxSize(),
        loadingContent = {
            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                CircularProgressIndicator()
            }
        },
        errorContent = { exception ->
            Text(
                text = "Failed to load: ${exception.message}",
                color = MaterialTheme.colorScheme.error,
                // Add your custom error styling, such as modifiers, here.
            )
        },
        transitionSpec = {
            (fadeIn(animationSpec = tween(600)) togetherWith
                    fadeOut(animationSpec = tween(600)))
                .using(SizeTransform(clip = false))
        },
    )
}

Renderização de superfície de baixo nível com roteadores personalizados

É possível observar o estado do componente raiz da superfície diretamente com observeA2uiComponentState e delegar a renderização a A2uiComponent:

@Composable
fun RawSurfaceCoordinator(surface: A2uiSurfaceModel) {
    // Extract the catalog to provide its readiness evaluator to the
    // composition. This lets components wait for their dynamic data bindings
    // before they're rendered.
    val coreSurface = surface as? A2uiCoreSurfaceModel
        ?: throw IllegalArgumentException(
            "Surface must implement A2uiCoreSurfaceModel")
    val composeCatalog = coreSurface.catalog as? A2uiCatalog
        ?: throw IllegalArgumentException("Catalog must implement A2uiCatalog")
    val readinessEvaluator = remember(composeCatalog) {
        composeCatalog.asReadinessEvaluator() }

    CompositionLocalProvider(
        LocalA2uiReadinessEvaluator provides readinessEvaluator
    ) {
        val rootState = observeA2uiComponentState(surface = surface)
        when (rootState) {
            is A2uiComponentState.Loading -> {
                LoadingSpinner()
            }
            is A2uiComponentState.Error -> {
                ErrorBanner(rootState.exception)
            }
            is A2uiComponentState.Success -> {
                // Delegate component routing to the Compose A2UI router.
                A2uiComponent(
                    component = rootState.component,
                    // Add your custom modifiers here.
                )
            }
        }
    }
}

Detalhes de implementação

O renderizador de agente para interface processa a resolução do estado da superfície e os limites de erros defensivos.

Limites de erros defensivos e tratamento de alucinações do agente

Como as superfícies da A2UI são impulsionadas por agentes de LLM generativos, os payloads recebidos podem estar malformados ou fazer referência a tipos de componentes desconhecidos.

O renderizador estabelece os seguintes limites de defesa para minimizar a probabilidade de falhas:

  • Envio de erros para autocorreção do agente: os erros são enviados como mensagens de cliente de saída, permitindo que o agente se autocorrige em interações subsequentes. Há também uma API que permite que implementações de componentes despachem erros para o agente nos casos em que a API detecta erros específicos do componente no tempo de renderização.
  • Componentes desconhecidos: quando um tipo de componente não reconhecido é encontrado, ele é interceptado antes de chegar à árvore da interface, marcado como um estado de erro e informado de volta ao agente para autocorreção.
  • Falhas na validação de esquema: os payloads são validados em relação aos esquemas de componentes (A2uiSchema). Propriedades malformadas nunca devem chegar aos layouts da UI do Compose.
  • Proteção de matriz esparsa: quando índices de lista muito grandes são recebidos, o modelo de dados faz a transição de uma lista densa para um mapa esparso adaptável, evitando erros de falta de memória.