Esegui il rendering delle superfici A2UI nella tua app

Quando utilizzi il renderer da agente a UI (A2UI) di Jetpack Compose, un agente AI invia messaggi che descrivono strutture UI, proprietà dei componenti e aggiornamenti dei dati. Per visualizzare queste interfacce in modo nativo nella tua app, devi ospitare e visualizzare una superficie A2UI all'interno della gerarchia Jetpack Compose della tua app.

Il renderer Compose A2UI coordina l'analisi dei messaggi, la gestione dello stato degli snapshot reattivi e le transizioni di stato delle superfici animate. Sebbene il renderer principale sia indipendente da qualsiasi sistema di progettazione specifico, offre l'integrazione predefinita con Material Design 3 tramite il catalogo di base fornito.

Inizializzare il livello dati basato su Compose

Il renderer A2UI supporta lo stato sensibile agli snapshot nel livello dati della tua app, il che consente all'interfaccia utente della tua app di reagire agli aggiornamenti incrementali dell'agente. Per aggiungere questo supporto, inizializza il parser e il processore utilizzando le funzioni di fabbrica A2uiMessageParser e A2uiMessageProcessor nel file ViewModel, come mostrato nel seguente snippet di codice:

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

Eseguire il rendering delle superfici utilizzando il catalogo di base (Material 3)

Quando esegui il rendering delle superfici utilizzando l'implementazione del catalogo di base fornita (androidx.compose.material3:material3-a2ui), puoi eseguire il rendering di una superficie Material 3 completamente stilizzata, inclusi il supporto integrato per gli indicatori di caricamento, i limiti di errore e le transizioni animate. Per farlo, utilizza il punto di ingresso del componibile 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.
                )
            }
        }
    }
}

Gestire gli stati delle superfici e le transizioni animate

A2uiSurface coordina la risoluzione dello stato del componente radice e applica le transizioni AnimatedContent negli stati di caricamento, errore e riuscito:

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

Rendering di superfici di basso livello con router personalizzati

Puoi osservare lo stato del componente radice della superficie direttamente con observeA2uiComponentState e delegare il rendering 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.
                )
            }
        }
    }
}

Dettagli di implementazione

Il renderer da agente a UI gestisce la risoluzione dello stato della superficie e i limiti di errore difensivi.

Limiti di errore difensivi e gestione delle allucinazioni dell'agente

Poiché le interfacce A2UI sono gestite da agenti LLM generativi, i payload in entrata possono essere malformati o fare riferimento a tipi di componenti sconosciuti.

Il renderer stabilisce i seguenti limiti difensivi per ridurre al minimo la probabilità di arresti anomali:

  • Invio di errori per l'autocorrezione dell'agente: gli errori vengono inviati come messaggi del client in uscita, consentendo all'agente di correggersi nei turni di interazione successivi. Esiste anche un'API che consente alle implementazioni dei componenti di inviare errori all'agente nei casi in cui l'API rileva errori specifici dei componenti al tempo di rendering.
  • Componenti sconosciuti: quando viene rilevato un tipo di componente non riconosciuto, viene intercettato prima di raggiungere l'albero della UI, contrassegnato come stato di errore e segnalato all'agente per l'autocorrezione.
  • Errori di convalida dello schema: i payload vengono convalidati in base agli schemi dei componenti (A2uiSchema). Le proprietà malformate non devono mai raggiungere i layout dell'interfaccia utente di Compose.
  • Protezione da array sparsi: quando vengono ricevuti indici di elenchi molto grandi, il modello di dati passa da un elenco denso a una mappa sparsa adattiva, evitando errori di memoria insufficiente.