Renderiza superficies de A2UI en tu app

Cuando se usa el renderizador de agente a IU (A2UI) de Jetpack Compose, un agente de IA envía mensajes que describen estructuras de IU, propiedades de componentes y actualizaciones de datos. Para mostrar estas interfaces de forma nativa en tu app, debes alojar y renderizar una superficie de A2UI dentro de la jerarquía de Jetpack Compose de tu app.

El renderizador de A2UI de Compose coordina el análisis de mensajes, la administración de estados de instantáneas reactivas y las transiciones de estados de superficies animadas. Si bien el renderizador principal es independiente de cualquier sistema de diseño específico, ofrece integración lista para usar con Material Design 3 a través del catálogo básico proporcionado.

Inicializa la capa de datos respaldada por Compose

El renderizador de A2UI admite el estado compatible con instantáneas en la capa de datos de tu app, lo que permite que la IU de tu app reaccione a las actualizaciones incrementales del agente. Para agregar esta compatibilidad, inicializa el analizador y el procesador con las funciones de fábrica A2uiMessageParser y A2uiMessageProcessor en tu ViewModel, como se muestra en el siguiente fragmento de código:

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)
    }
}

Renderiza superficies con el catálogo básico (Material 3)

Cuando renderizas superficies con la implementación de Basic Catalog proporcionada (androidx.compose.material3:material3-a2ui), puedes renderizar una superficie de Material 3 con todos los estilos, incluida la compatibilidad integrada con indicadores de carga, límites de error y transiciones animadas. Para ello, usa el punto de entrada componible 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.
                )
            }
        }
    }
}

Cómo controlar los estados de la superficie y las transiciones animadas

A2uiSurface coordina la resolución del estado del componente raíz y aplica transiciones de AnimatedContent entre los estados de carga, error y éxito:

@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))
        },
    )
}

Renderización de superficie de bajo nivel con routers personalizados

Puedes observar el estado del componente raíz de la superficie directamente con observeA2uiComponentState y delegar la renderización 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.
                )
            }
        }
    }
}

Detalles de la implementación

El renderizador de agente a IU controla la resolución del estado de la superficie y los límites de errores defensivos.

Límites de error defensivos y manejo de alucinaciones del agente

Dado que las interfaces de A2UI se basan en agentes LLM generativos, las cargas útiles entrantes pueden tener un formato incorrecto o hacer referencia a tipos de componentes desconocidos.

El renderizador establece los siguientes límites defensivos para minimizar la probabilidad de fallas:

  • Envío de errores para la autocorrección del agente: Los errores se envían como mensajes salientes del cliente, lo que permite que el agente se autocorriga en los turnos de interacción posteriores. También hay una API que permite que las implementaciones de componentes envíen errores al agente en los casos en que la API detecta errores específicos del componente en el tiempo de renderización.
  • Componentes desconocidos: Cuando se encuentra un tipo de componente no reconocido, se intercepta antes de llegar al árbol de IU, se marca como un estado de error y se informa al agente para que se autocorrija.
  • Errores de validación del esquema: Las cargas útiles se validan en función de los esquemas de componentes (A2uiSchema). Las propiedades con formato incorrecto nunca deben llegar a los diseños de la IU de Compose.
  • Protección de arrays dispersos: Cuando se reciben índices de listas muy grandes, el modelo de datos pasa de una lista densa a un mapa disperso adaptable, lo que evita errores de memoria insuficiente.