在应用中渲染 A2UI Surface

使用 Jetpack Compose 智能体到界面 (A2UI) 渲染器时,AI 智能体会发送描述界面结构、组件属性和数据更新的消息。如需在应用中以原生方式显示这些界面,您需要在应用的 Jetpack Compose 层次结构中托管并渲染 A2UI Surface。

Compose A2UI 渲染器可协调消息解析、反应式快照状态管理和动画界面状态转换。虽然核心渲染器独立于任何特定的设计系统,但它通过提供的基本目录与 Material Design 3 提供开箱即用的集成。

初始化由 Compose 支持的数据层

A2UI 渲染器支持应用数据层中的快照感知状态,这可让应用界面对来自代理的增量更新做出反应。如需添加此支持,请在 ViewModel 中使用 A2uiMessageParserA2uiMessageProcessor 工厂函数初始化解析器和处理器,如下面的代码段所示:

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

使用基本目录 (Material 3) 渲染 Surface

使用提供的基本目录实现 (androidx.compose.material3:material3-a2ui) 渲染界面时,您可以渲染完全样式化的 Material 3 界面,包括对加载指示器、错误边界和动画过渡效果的内置支持。为此,请使用 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.
                )
            }
        }
    }
}

处理表面状态和动画过渡

A2uiSurface 协调根组件状态解析,并跨加载、错误和成功状态应用 AnimatedContent 过渡:

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

使用自定义路由器进行低级别界面渲染

您可以使用 observeA2uiComponentState 直接观察界面根组件状态,并将渲染委托给 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.
                )
            }
        }
    }
}

实现细节

代理到界面渲染器负责处理界面状态解析和防御性错误边界。

防御性错误边界和智能体幻觉处理

由于 A2UI 界面由生成式 LLM 代理提供支持,因此传入的载荷可能格式错误或引用了未知的组件类型。

渲染器会建立以下防御边界,以尽可能减少崩溃的可能性:

  • 用于智能体自我纠正的错误调度:错误会作为出站客户端消息进行调度,从而使智能体能够在后续互动轮次中进行自我纠正。此外,还有一个 API 可让组件实现向代理分派错误,前提是该 API 在呈现时间检测到特定于组件的错误。
  • 未知组件:当遇到无法识别的组件类型时,系统会在其到达界面树之前对其进行拦截,将其标记为错误状态,并报告给代理以进行自我修正。
  • 架构验证失败:系统会根据组件架构 (A2uiSchema) 验证载荷。格式错误的属性绝不应到达 Compose 界面布局。
  • 稀疏数组保护:当收到非常大的列表索引时,数据模型会从密集列表转换为自适应稀疏映射,从而防止内存不足错误。