アプリで A2UI サーフェスをレンダリングする

Jetpack Compose のエージェントから UI(A2UI)へのレンダラを使用する場合、AI エージェントは UI 構造、コンポーネント プロパティ、データ更新を記述するメッセージを送信します。アプリでこれらのインターフェースをネイティブに表示するには、アプリの Jetpack Compose 階層内で A2UI サーフェスをホストしてレンダリングします。

Compose A2UI レンダラは、メッセージの解析、リアクティブ スナップショット状態の管理、アニメーション サーフェス状態の遷移を調整します。コア レンダラは特定のデザイン システムに依存しませんが、提供された Basic Catalog を通じて Material Design 3 との統合をすぐに利用できます。

Compose を基盤とするデータレイヤを初期化する

A2UI レンダラは、アプリのデータレイヤでスナップショットを認識する状態をサポートしています。これにより、アプリの UI はエージェントからの増分更新に対応できます。このサポートを追加するには、次のコード スニペットに示すように、ViewModelA2uiMessageParser ファクトリ関数と A2uiMessageProcessor ファクトリ関数を使用して、パーサーとプロセッサを初期化します。

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

基本カタログ(マテリアル 3)を使用してサーフェスをレンダリングする

提供された基本的なカタログ実装(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.
                )
            }
        }
    }
}

実装の詳細

エージェントから UI へのレンダラは、サーフェス状態の解決と防御エラー境界を処理します。

防御的なエラー境界とエージェントのハルシネーション処理

A2UI サーフェスは生成 LLM エージェントによって駆動されるため、受信ペイロードの形式が正しくないか、不明なコンポーネント タイプを参照している可能性があります。

レンダラは、クラッシュの可能性を最小限に抑えるため、次の防御境界を確立します。

  • エージェントの自己修正のためのエラー ディスパッチ: エラーはアウトバウンド クライアント メッセージとしてディスパッチされ、エージェントは後続のインタラクション ターンで自己修正できます。また、API がレンダリング時にコンポーネント固有のエラーを検出した場合に、コンポーネント実装がエージェントにエラーをディスパッチできる API もあります。
  • 不明なコンポーネント: 認識されないコンポーネント タイプが検出されると、UI ツリーに到達する前にインターセプトされ、エラー状態としてマークされ、自己修正のためにエージェントに報告されます。
  • スキーマ検証の失敗: ペイロードはコンポーネント スキーマ(A2uiSchema)に対して検証されます。形式が正しくないプロパティが Compose UI レイアウトに到達することはありません。
  • スパース配列保護: 非常に大きなリスト インデックスを受信すると、データモデルが密なリストから適応型スパースマップに移行し、メモリ不足エラーを防ぎます。