インテリジェントなアプリ

A2UI を使用してエージェントが Android アプリで UI を生成できるようにする

スピーカー

概要

講演について

A2UI は、ウェブ中心のコンポーネントからネイティブ Android に移行しています。A2UI v0.9 の新しい Jetpack Compose レンダラを使用すると、デベロッパーは LLM で生成された動的な UI をネイティブにレンダリングできます。WebView の代わりに、コルーチン駆動のアクター フレームワーク、シールクラスのライフサイクル、ネイティブの Material 3 統合を活用しています。

このセッションでは、新しいアーキテクチャについて説明し、サーバー駆動型のコンポーネント ツリーがリアクティブな Compose ノードにどのように変換されるか、また、組み込みのレイアウト遷移が UI をブロックすることなく構造の更新をスムーズに処理する方法について説明します。

概要

  • A2UI は、エージェント バックエンドがテキスト レスポンスではなく動的なユーザー インターフェースを記述するための、宣言型のオープン JSON プロトコルを提供します。
  • このプロトコルは、許可されたコンポーネントと関数を定義するカタログと、エージェントが制御する UI 要素を表すサーフェスを中心に構成されています。
  • Jetpack Compose の A2UI ライブラリは、組み込みのユーザー補助機能とプログレッシブ レンダリングを備えたマテリアル 3 コンポーネントを使用して、ストリーミングされたサーフェス モデルをネイティブにレンダリングします。
  • インメモリ データモデルは、エージェントに接続することなく、バインドされた UI 要素を即座に更新します。一方、アウトバウンド イベントは、ユーザーのアクションをルーティングして戻します。
  • デベロッパーは、既存のカタログ アイテムをカスタマイズしたり、カスタム コンポーネントを作成したりして、エージェントが表示できる内容を厳密に制御できます。

リソース

Gemini Notebook

トピック

  • a2ui
  • generative-ui
  • jetpack-compose
  • android
  • ai-agents
  • material-design

文字起こし

生成 UI と A2UI の概要

  • 皆さん、こんにちは。このセッションでは、新しい A2UI ライブラリを使用して Android アプリで UI を生成する方法について説明します。

  • Jetpack Compose のプロダクト マネージャーを務める Nick Butcher です。

  • 皆さん、こんにちは。Compose チームのエンジニアの Anton です。

  • 私はこの新しい Jetpack Compose A2UI レンダラに取り組んでいます。本日は、このレンダラについてご紹介します。

  • はい。AI 機能の台頭に伴い、多くのアプリが AI を活用してユーザー エクスペリエンスを向上させたり、新しいエクスペリエンスを構築したりする方法を模索しています。

  • 注目されているエキサイティングなエクスペリエンスの一つが、生成 UI です。これは、実行時にエージェント バックエンドがユーザー向けにまったく新しい UI を作成する方法であり、非常に興味深いものです。

  • しかし残念ながら、現在、多くのエージェント バックエンドのレスポンスは、このようにテキストの壁に閉じ込められています。

  • これは、エージェントに旅行予約の新機能を生成するよう依頼した例です。

  • プロンプトは次のとおりです。

  • ご覧のとおり、かなり適切な回答が得られていますが、このテキストの壁に閉じ込められていて、簡単に操作することはできません。また、アプリケーションの他の部分とはまったく異なる外観になっています。

  • 生成 UI を使用すると、動的な入力に応じて新しい UI をオンザフライで生成できるため、より多くのカスタマイズや新しいエクスペリエンスの可能性が広がります。

  • これは、標準のネイティブ コンポーネントを使用して作成された UI の例です。

  • このようなエクスペリエンスは、現在でも自分で構築できますが、バックエンドとのやり取り、独自のイベント ストリーム、関数などの形式を独自に定義する必要があります。

  • A2UI(Agent-to-UI)は、これらすべてを定義するための JSON 形式のオープン プロトコルです。

  • A2UI の優れた点の 1 つは、宣言型の形式であることです。

  • エージェントは、レンダリングする UI の JSON 表現を返すだけです。

  • クライアントが実行する必要のあるコードは返されないため、特定のセキュリティ脆弱性にさらされる可能性があります。

  • 宣言型です。

  • では、A2UI について他にどのようなコンセプトを知っておく必要があるでしょうか。

基本コンセプト: カタログとサーフェス

  • カタログとサーフェスという 2 つの主要なコンセプトのみ。

  • カタログには、エージェントが UI の構築に使用できるテキスト、ボタン、行、列などのコンポーネントがあります。

  • カタログには、エージェントがテキストの書式設定、ユーザー入力の検証、URL のオープンに使用できる関数もあります。

  • カタログは、アプリがサポートするコンポーネントと機能を定義するコントラクトであり、エージェントはそれらを使用してのみ構築できます。

  • つまり、ニックが言ったように、エージェントがアプリ内で任意のコードを実行することはありません。

  • 設計して信頼できるビルディング ブロックから UI を構成します。

  • もう 1 つの重要なコンセプトはサーフェスです。

  • サーフェスは、エージェントが制御する UI の 1 つです。スライドの旅行プランナーの例がこれに該当します。

  • エージェントは、createSurface、updateComponents などの A2UI プロトコル メッセージと、カタログで定義されているコンポーネントと関数を使用して、サーフェスの UI を JSON で記述します。

  • カタログは、エージェントがインターフェースの構築に使用できるコンポーネントを制御するため、A2UI の非常に基本的なコンセプトです。

  • A2UI 仕様では、基本カタログと呼ばれるものが定義されています。これは、ほとんどの UI の構築に使用できる共通コンポーネントのセットです。

  • Jetpack Compose A2UI レンダラは、マテリアル 3 コンポーネントを使用した基本的なカタログの実装を提供します。

  • カタログ コンポーネントのほとんどは、すぐに使用できます。

  • メディア コンポーネントには、画像、動画、オーディオ プレーヤーの 3 つがあり、選択したライブラリをプラグインする必要があります。

  • たとえば、画像の読み込みには Coil を、動画とオーディオのプレーヤーには Media3 を選択できます。

  • しかし、A2UI と新しいレンダラ ライブラリの真の力は、独自の完全にカスタムなコンポーネントとカタログを作成し、エージェントが UI の構築に使用できるものを完全に制御できることにあります。

  • カタログについては後ほど説明します。

  • では、これがどのようなものか見てみましょう。

  • デモマシンに切り替えてください。

  • これは、これから使用するデモアプリです。A2UI レンダラを構築して、その簡単さを示します。

  • 画面中央の小さなチップに表示されているように、エージェントに送信できる一連のプロンプトです。

  • プロンプトの 1 つをクリックすると、そのプロンプトが実際のライブ エージェント バックエンドに送信されます。デモがうまくいくことを祈りましょう。プロンプトに加えて、システム指示も追加します。

  • そこで、バックエンドに A2UI プロトコルとコンポーネントのカタログ(この場合は、ここで使用している基本カタログ)を伝えます。バックエンドはこれを使用して UI を構築できます。また、構築方法の例もいくつか提供します。

  • デモアプリのチャット セッション実装は、エージェントのレスポンスから未加工の JSON A2UI メッセージを抽出する方法をすでに認識しています。

  • この場合、エージェントはテキストに続けて A2UI プロトコル メッセージ(createSurface、updateComponents)を返しました。

  • しかし、現時点では、アプリはこの未加工の JSON を表示するだけで、UI としてレンダリングしていません。

  • コードの実装に入る前に、スライドに戻って、この実装に使用するシステム設計について説明します。

  • それは、

アプリケーション アーキテクチャとメッセージ処理

  • そこで、計画を立てました。

  • そのため、AI エージェントはレスポンスをアプリにストリーミングし、ChatSession クラスがそれを受信します。

  • ChatSession は、エージェントのレスポンスがストリーミングされる際に、レスポンスから未加工の A2UI JSON メッセージを抽出し、A2UI Message Processor に渡します。

  • Message Processor は、未加工の JSON メッセージをセマンティック表現に変換するパーサーで構成されています。

  • 次に、プロセッサはカタログ定義に照らしてメッセージを検証し、一連のサーフェス モデルにマッピングします。

  • サーフェス モデルは、UI の外観と、エージェントが制御する UI の部分を定義するものです。

  • これらのサーフェス モデルは、ライブラリが提供する A2UI サーフェス コンポーザブルを使用して、ネイティブの Compose UI としてレンダリングされます。

  • エージェントと UI 間の通信は双方向であるため、ユーザーが UI を操作すると、ユーザーのアクションとエラーがエージェントに返されます。

  • まとめると、カタログ、メッセージ プロセッサ、サーフェスという 3 つの主要な構成要素があります。

  • そのためのコードを記述しましょう。

  • はい、マシンを切り替えられるなら、構築しましょう。

  • 最初のステップは、依存関係を追加することです。

  • ここでは、Material 3 A2UI の依存関係がすでに追加されており、プロジェクトの同期も完了しているため、Gradle の処理を待つ必要はありません。

  • これは、それらのマテリアル 3 コンポーネントを使用して基本的なカタログの実装を定義する最上位の依存関係ですが、その下にはよりきめ細かい依存関係があります。

  • そのため、基本カタログを使用したくない場合は(その理由は後で説明します)、よりきめ細かいカタログを使用できます。

  • しかし、これにより、残りの部分が推移的に取り込まれます。

  • 違いますね。

  • 3 つの要素があると言いました。

  • 最初のステップは、カタログを定義することでした。

  • そこで、このアプリケーションでは、基本カタログのマテリアル 3 実装のインスタンスを構築するためのファクトリ関数を作成します。

  • そこで、ここではこのライブラリ関数、material A2UI basic catalog を使用します。これもバージョン管理されています。

  • 前述のとおり、このライブラリは必要な共通コンポーネントをすべて提供しますが、特定の画像読み込みライブラリやメディア レンダリング ライブラリの使用を強制することはないため、それらは自分で提供する必要があります。

  • たとえば、Anton はすでに Coil の AsyncImage コンポーザブルをラップするこのラッパーを作成しています。これを使用して画像を読み込みます。また、デモ用に Wikipedia から画像を検索するロジックも追加しています。

  • そして、非常に簡単に、これをカタログ コンポーネントにプラグインできます。

  • 同様に、レンダリング、動画、音声についても同様の実装があり、Media3 ライブラリを使用して ExoPlayer でレンダリングします。

  • このように、独自のコンポーネントを簡単に組み込むことができます。

  • カタログに必要な次のものは、一連の標準関数です。

  • つまり、これはクライアントが提供できる機能です。

  • この実装も基本カタログとともに提供されますが、コンテキストを提供する必要があります。このコンテキストは、ユーザーがアイテムをタップしたときに URI を開くなど、特定のアクションを実行するために使用します。

  • これで最初のステップが完了しました。

  • カタログを作成しました。

  • 次のステップは、メッセージ プロセッサの設定です。

  • これは、レスポンスなどを実際に解析するものです。

  • そこで、アプリケーションのビューモデルに切り替えます。まず、以前に設定したカタログを実際にインスタンス化します。これは後で必要になるためです。

  • 次に、このメッセージ パーサー、Message Processor オブジェクトを作成します。このオブジェクトは、使用するコンポーネントに対してレスポンスを検証するために、カタログにアクセスする必要があります。

  • 作成する必要があるもう 1 つのコンポーネントは、パーサーです。これは、返される JSON メッセージの解析を行うオブジェクトです。

  • ここで ChatSession オブジェクトを作成します。

  • 前述のように、ChatSession はエージェントのレスポンスから未加工の A2UI メッセージをすでに抽出しています。

  • 新しい A2UI メッセージの準備が整うと、この onA2uiMessage コールバックが呼び出されます。

  • そのため、各メッセージが到着したときにプロセッサに渡すだけで済みます。

  • processInput 関数を呼び出し、パーサーとメッセージを渡します。

  • そのため、processInput 関数は各メッセージを解析し、プロセッサがカタログと照合してサーフェス モデルを更新するための作業をキューに入れます。

  • そのため、プロセッサは現在のアクティブなサーフェス モデルのセットを activeSurfaces StateFlow として公開します。

  • ChatSession には、レンダリングするエージェントの返信の UI 状態にサーフェス モデルを追加するロジックがあります。

  • そのため、この activeSurfaces StateFlow をパラメータとして ChatSession に渡します。

  • また、前述のように、processInput 関数はプロセッサの作業をキューに入れます。

  • ただし、実際に処理を開始するには、バックグラウンド スレッドでコルーチンを起動する必要があります。

  • そのため、ビューモデル スコープを使用して、デフォルトのディスパッチャを設定します。

  • そのコルーチン内で、processor.collectMessages を呼び出すだけです。

  • このコルーチンがアクティブな間、プロセッサはメッセージ キューからメッセージを処理します。

  • もう 1 つ必要なのは、ユーザーがチャット履歴を消去したときに、現在のサーフェスも消去することです。

  • そこで、clearSurfaces を呼び出します。

UI でのサーフェス モデルのレンダリング

  • 違いますね。

  • 最初の 2 つのステップが完了しました。

  • カタログを作成し、メッセージを解析するプロセッサを作成しました。

  • 次のステップでは、これらのレスポンスを UI に実際にレンダリングします。

  • そのため、パーサーによって解析される際に、それらのサーフェスを収集しています。

  • UI では、これらのソースをエージェントの返信オブジェクトで渡し、単純に反復処理します。

  • 次に、この A2UI サーフェス コンポーズ可能を使用し、各モデルを反復処理しながらモデルを渡して、コンテンツを目的の場所にレンダリングします。

  • ここで注意すべき点は、エージェントが大量のコンテンツを表示することを選択した場合、リストのようなものを使用することを選択する可能性があるということです。

  • そのため、このコンポーネントに最大の高さを設定して、適切にレンダリングされるようにする必要があります。

  • これで、基本的な A2UI レンダラを実装できました。

  • アプリに戻って再起動し、プロンプトをもう一度実行すると、エージェントがそれを使用して実際の UI をレンダリングするはずです。

  • そこで、ライブでコーディングします。

  • これは、煙と鏡のようなものではありません。

  • でも、実際の画像を使用しています。

  • タブ、ダイアログ、日付選択ツール、スライダーなど、アプリの構築に使用する標準的なマテリアル コンポーネントをすべて使用しており、テキストによる回答を表示するだけではありません。

  • これは実際のマテリアル コンポーネントを使用した実際のアプリであるため、実際のコンポーネントが行う処理を行います。

  • ネイティブ パフォーマンスのようなものです。

  • たとえば、ダークモードへの対応などです。

  • これらはマテリアル コンポーネントです。

  • そのため、色、シェイプ、タイポグラフィなどをカスタマイズした外側のマテリアル テーマ設定コンポーズ可能がある場合、これらのコンポーネントはそれらを取得して、アプリの他の部分と同じように表示されます。

  • ニックが述べたように、これらはネイティブの Compose コンポーネントであるため、ネイティブのセマンティクスを備えています。

  • つまり、Compose の組み込みのユーザー補助機能がすべてサポートされています。

  • TalkBack、フォント スケーリング、キーボード ナビゲーションなどの機能は、すぐに利用できます。

  • ここで、エージェントを呼び出して実行時に新しい UI を生成しているため、レイテンシが発生する可能性があります。

  • エージェントの応答を待つ必要があるかもしれません。

  • ライブラリは、このレイテンシを最小限に抑えるためにできることを行います。

  • この種のツールを使用すると、待機時間を挿入して、どのようにして最小化するかを少しずつ示すことができます。

  • はい。エージェントが完全なレスポンスをストリーミングするまでに時間がかかることがあるため、プログレッシブ レンダリングと呼ばれる手法を使用しています。

  • そのため、ルート コンポーネントが到着するとすぐに、サーフェスの UI が表示されます。

  • すべての子コンテナがまだ到着していない場合、すべてのコンテナ コンポーネントに子コンテナの読み込みプレースホルダが表示されます。

  • 子コンポーネントが到着すると、マテリアル 3 のモーション スキームを使用してアニメーション化されます。

  • 違いますね。

  • 次のプロンプトに切り替えて、ライブラリの別の機能を見てみましょう。

  • これは、ディナーのテーブルを予約する別のプロンプトです。

  • エージェントがこのスライダー コンポーネントを使用することを選択しています。

  • このように、エージェントが作成した UI をドラッグして操作できます。

  • コンポーネントの数値を変更するだけでなく、その下にあるフォーマット文字列でカウントしていることにも注目してください。

  • つまり、それを更新するためだけにエージェントに往復するわけではないということですよね?

  • これは双方向バインディングです。

  • そのため、スライダー コンポーネントは、このサーフェスのメモリ内データモデルにバインドされます。

  • データモデル内の同じパスにバインドされているすべてのコンポーネントは、値が変更されると自動的に更新されます。

  • スライダーは表示する値を読み取り、スライダーをドラッグすると、モデルに書き戻して更新します。

  • これらはすべてローカルで行われ、エージェントとの往復は発生しません。

  • エージェントにデータの更新を依頼することもできます。

  • 6 人にしましょう。

  • エージェントが返答する際のスライダーに注目してください。

  • 6 に跳ね上がりました。

  • エージェントから何を受け取ったか見てみましょう。

  • エージェントは、UI の新しい説明をすべて送信するのではなく、スライダーのパスを更新する 1 つの小さなメッセージ updateDataModel を送信しました。

  • スライダーはそのパスにバインドされているため、その場で更新されました。

  • そのため、各サーフェスのデータモデルは Compose スナップショット状態に基づいて構築され、パスにバインドされた各コンポーネント プロパティは派生状態になります。

  • そのため、ユーザーまたはエージェントがデータモデルの値を更新すると、同じ値にバインドされているすべてのコンポーネントが自動的に再コンポーズされます。

  • 違いますね。

  • では、実際に予約ボタンを押すとどうなるのでしょうか?

  • 現時点では何も起こりません。

  • ボタンはアクションをディスパッチしますが、エージェントにはまだ送信されません。

  • これを修正しましょう。

ユーザー操作とイベントフローの処理

  • 入力を処理するためにプロセッサにフックする必要があったように、出力、つまりエージェントが作成した UI でユーザーが行うアクションにもフックする必要があります。

  • そのためには、プロセッサが公開する outboundEvents フローを使用して、これを収集します。

  • ユーザーがアクションをタップしたり、UI で何かを実行したりしたときに、必要なロジックをプラグインできます。

  • ここでは、アクションをエージェントに送信して、エージェントが次に何をするかを決定していますが、この時点で、独自のバックエンドを呼び出すか、アプリケーションの別の画面に移動するかを決定することもできます。

  • アントンが接続したので、[予約を確定] アクションをクリックすると、そのアクションがエージェントに送信されます。

  • この時点で、バックエンドで何らかの機能を呼び出すこともできます。

  • では、実際にエージェントに送信する内容を見てみましょう。

  • これが A2UI のアクション メッセージです。

  • このアクションにはコンテキストがあり、コンテキストには、このアクションが実行されたときに送信するようにエージェントが要求した一連のプロパティが含まれています。

  • ボタンをクリックすると、データモデルの現在の値に基づいて、この特定のプロパティがその時点で解決され、エージェントに送信されます。

  • ここまではエージェントがかなり協力的でしたが、AI は間違えることもあります。

  • では、エージェントが UI でエラーを返した場合、つまり、UI でエラーが発生したことをエージェントが報告してきた場合にどうなるかを見てみましょう。

  • この最後のプロンプトでは、デモのために、エージェントにエラーを発生させるよう指示しています。

  • どうなるか見てみましょう。

  • うん

  • ご覧のように、コンポーネントの 1 つにエラーのマークが付いています。

  • この問題は修正されました。

  • これがエージェントの自己修正ループです。

  • 各コンポーネントが UI に到達する前に、メッセージがカタログ スキーマに適合しているかどうかが検証されます。

  • コンポーネントがスキーマと一致しない場合は、エラーとしてマークされます。

  • 基本的なカタログ実装のレンダラは、その特定コンポーネントに限定されたエラーを表示し、UI の残りの部分は引き続き表示されます。

  • バックグラウンドで、レンダラはエージェントにエラー メッセージを送信して、問題の内容を知らせます。

  • この場合、存在しない不明なコンポーネント タイプがカタログで見つからないというメッセージが送信されました。

  • エージェントは、既存の UI を更新するフォローアップ レスポンスを送信する機会を得て、UI が修正されました。

  • うん

  • マテリアル基本カタログの現在の実装では、エラーがすぐに表示されますが、カスタム コンポーネントでは、エラーが発生した場合にエージェントが修正するのを待つ間、コンポーネントの読み込みインジケーターを表示し続けることができます。

  • このようにして、エラーがユーザーに完全に表示されないようにすることができます。

カスタム コンポーネントの作成とカタログの拡張

  • カタログとカスタム コンポーネントについてですが、これまでは、すぐに使える基本的なカタログを使用してきましたが、もちろん、ニーズに合わせてカスタマイズすることもできます。

  • ファクトリ関数を使用してカタログを作成すると、すべてのコンポーネントにデフォルト値が設定されることがわかります。

  • そのため、必要に応じて独自のコンポーネントを簡単にプラグインできます。

  • たとえば、アプリに独自のデザイン システムと独自のボタンがある場合、それを簡単にプラグインできます。

  • そのため、カタログのインスタンスを作成するときにこのパラメータをオーバーライドし、このコンポーネント定義を作成します。

  • これを実装する新しいクラスを作成すると、ここにインターフェース(タイプボタンの A2UI 基本カタログ)があることがわかります。

  • これを実装すれば、独自のコンポーネントをプラグインできます。

  • うん

  • 基本カタログのコンポーネントをオーバーライドするため、この A2UI 基本カタログ ボタン インターフェースを実装する必要があります。

  • このインターフェースには、名前、説明、プロパティ スキーマなど、コンポーネントに関する情報がすでに含まれています。

  • したがって、独自のバージョンを実装するには、このコンポーズ可能な関数を実装するだけで済みます。

  • いくつかのパラメータを受け取ります。

  • これらのパラメータは、検証後にエージェントから受信した値です。

  • たとえば、child ID は、ボタンのコンテンツとなるコンポーネントを参照します。

  • また、ボタンのタイプ、アクション定義、その他のパラメータを記述するバリアントもあります。

  • ボタンの本番環境の実装では、これらのパラメータをすべて使用する必要がありますが、デモでは child と action のみが必要です。

  • では、このコンポーザブルを実装しましょう。

  • このコンポーズ可能な関数では、独自のカスタムの「いいね」ボタンタイプを出力します。

  • ご覧のとおり、A2UI によって提供され、検証されたパラメータを使用できます。

  • たとえば、onClick 関数でそのアクションをディスパッチできます。

  • このライブラリ タイプ DispatchAction を使用して、A2UI が定義したアクションを渡すことができます。

  • 独自のボタンを使用していますが、子コンテンツ、ラベル、またはエージェントが使用することにしたものをレンダリングする必要があります。

  • そのために、この observeA2uiComponentState 関数を使用して、レンダリングする子コンテンツのハンドルを取得するために childId を渡します。

  • 次に、この A2uiComponent コンポーズ可能な関数を使用して、子コンポーネントの状態を渡してレンダリングします。

  • ここでは、正常に読み込まれた場合にのみ子コンテンツをレンダリングしていますが、読み込み中やエラー状態の場合に何を表示するかを決定する独自のロジックをここに記述することもできます。

  • デモアプリのプロンプトの例に戻り、標準のマテリアル ボタンを使用してボタンを置き換えます。

  • 非常に微妙な変化ですが、おわかりいただけるでしょうか。

  • ここでも、非常にカスタムなボタンを使用していることがわかります。

  • ライブラリの型を置き換える方法はわかりました。では、独自のカスタム型でライブラリを拡張するにはどうすればよいでしょうか?

  • これは、カタログ自体に追加することで、非常に簡単に実現できます。

  • そこで、Anton はここに新しいクラスを作成して、星評価コンポーネントを実装します。

  • そこで、この架空の旅行中にレストランを評価できるように、アプリケーションに機能を追加します。

  • 既存のコンポーネントを置き換えるわけではないため、このより一般的なインターフェース、つまり A2UI コンポーネント インターフェースを使用します。ご覧のとおり、オーバーライドする必要があるパラメータがいくつかあります。

  • 最初の 2 つは名前と説明です。

  • これは、コンポーネントの内容と動作を記述したものです。

  • これらは非常に重要です。カタログをシリアル化してバックエンドに送信し、UI の構築に使用できるものを伝えるときに、エージェントはこれを使用して、応答時にこのコンポーネントを使用するかどうかを決定します。

  • そのため、かなり具体的に説明し、これが何をするものなのかを AI に伝える必要があります。

  • 次にオーバーライドする必要があるプロパティは、この properties プロパティです。これは基本的に、このコンポーネントが扱うデータを定義するものです。

  • レストランを評価できるように、星評価バーを作成します。必要なプロパティは 2 つあります。

  • これは、ユーザーが選択した星の数などの値になります。これは、ユーザーが操作して変更できる可変プロパティと、星の最大数です。

  • ここでは、Anton がこの動的な数値型プロパティを値に使用していることがわかります。

  • これは、実際に選択された星の数を示します。

  • ここにも、この値の用途をエージェントに伝える独自の説明があります。

  • 次に、静的タイプのプロパティがあります。このコンポーネントで表示できる星の最大数を定義するために、この A2UI プロパティ(タイプは number)を使用しています。

  • これら 2 つのプロパティを定義したら、それらを返すだけです。

  • この properties プロパティをオーバーライドして、リストを返します。

  • このようにして、このコンポーネントが扱う内容をエージェントに伝えます。

  • うん

  • ニックが述べたように、このプロパティ定義は、エージェントがこのコンポーネントの使用方法を把握できるように、カタログ スキーマの一部として送信されます。

  • しかし、このプロパティ、つまり同じプロパティ定義は、実行時に型付きの値を抽出するためにも使用されます。

  • そのため、信頼できる唯一の情報源が存在します。

  • では、これを実装するために必要なことを確認しましょう。

  • まず、max プロパティにアクセスするには、max プロパティが静的プロパティであるため、エージェントはプロパティ オブジェクトにインラインで値を送信します。

  • したがって、これにアクセスするには、proper-max プロパティ オブジェクトをキーとして参照し、properties オブジェクトから取得するだけです。

  • このプロパティは数値として定義されているため、int に変換してデフォルトを設定する必要もあります。

  • 次に、value プロパティです。

  • value プロパティは動的プロパティであるため、データモデル内の何かを参照していることを意味します。

  • そのため、アクセスするには、properties.bind API を使用し、プロパティ オブジェクトをキーとしてパラメータとして渡す必要があります。

  • この関数は、このプロパティのデータモデルに保存されている現在の値を返すと同時に、今後の更新をサブスクライブします。

  • そのため、データモデルが更新されるたびに、このコンポーネントは自動的に再コンポーズされます。

  • 同様に、int に変換してデフォルトを設定しました。

  • もう 1 つ必要なのは、評価を変更できるように双方向バインディングを設定することです。

  • これを行うには、bindUpdater API を使用します。

  • プロパティを定義する同じプロパティ オブジェクトを渡します。その結果、bindUpdater は、データモデルでこのプロパティの値を変更できるラムダを返します。

  • これで、星評価コンポーネントを出力する準備が整いました。

  • 違いますね。

  • 独自のカスタム コンポーネントを定義したので、使用しているカタログに追加する必要があります。

  • 以前は、基本的なカタログをそのまま使用していました。

  • そこで、今度は実際に新しいカタログを作成する必要があります。このカタログは、基本カタログをすべて取得し、カスタム コンポーネントを追加します。

  • カタログは 3 つの要素で構成されています。

  • ID、一連のコンポーネント、一連の関数です。

  • ID には、URI を使用して、一意に識別されるカタログと、作業対象のバージョンを定義します。

  • コンポーネントについては、基本カタログからすべてのコンポーネントを取得し、add 演算子を使用して、先ほど作成したカスタム コンポーネントを追加します。

  • 関数については、基本カタログの関数を使用します。

  • このようにして、基本的なカタログを拡張し、新しいカスタム コンポーネント タイプを追加しました。

  • カスタム カタログは簡単に作成できます。

  • エージェントは、送信されたシステム指示からカタログの使用方法を学習します。

  • カスタム カタログを作成したので、システム指示も更新しましょう。

  • そこで、カタログ オブジェクトを、システム指示を構築する関数に渡します。

  • こちらがその内容です。

  • これまでは、この標準カタログを使用していたため、基本カタログのスキーマの静的定義を送信していました。

  • カスタム カタログが作成されたので、toJsonSchemaString 関数を使用してスキーマを動的に生成できます。

  • これにより、先ほど追加した star-rating コンポーネントを含む、このカタログの完全な JSON 表現が作成されます。

  • システム指示には、エージェントの回答例も含まれています。

  • そのため、ここではカタログ ID も変更して、サンプルが正しいカタログを参照するようにします。

  • 違いますね。

  • これで、基本カタログを拡張して独自のカスタムタイプを追加できました。

  • そこで、これを再デプロイして、これらのプロンプトのいずれかを試してみましょう。これにより、評価 UI が表示されるはずです。

  • その後、エージェントが返信します。

  • このスター コンポーネントは、エージェントがリアルタイムで実装するまで見たことがなかった、まったく新しいコンポーネントです。

  • これは基本カタログの一部ではなく、作成したばかりのものです。

  • 星をクリックすると、bindUpdater ラムダが呼び出され、データモデルの値が更新されます。

  • 評価を送信すると、アクションを確認できます。アクションのコンテキストには、設定した値が含まれます。

  • では、スライドに戻りましょう。

  • かなり駆け足で説明しましたが、エージェントがテキストの壁として応答する状態から、実際にネイティブ UI を作成する状態に簡単に移行できることをおわかりいただけたかと思います。

重要ポイントとリソース

  • この統合が非常に簡単であることをご理解いただけたと思います。

  • 必要な主要コンポーネントは、コンポーネントのカタログを作成すること、解析ロジックを実装すること、UI でレンダリングすることの 3 つです。

  • また、このライブラリが、プログレッシブ レンダリングやスキーマの検証、自己修正ループ、エラー処理など、多くのことをすぐに実行できることを示しました。

  • また、本日のデモで、カスタマイズが非常に簡単であることをおわかりいただけたかと思います。

  • マテリアルを使用している場合、基本的なカタログはすでにマテリアル テーマに対応していることを説明しましたが、カタログ内のコンポーネントを拡張または置換するのは非常に簡単です。

  • しかし、最も強力なのは、独自のカタログを作成する場合だと思います。

  • エージェントが実際に使用できる UI を最大限に制御したい場合は、完全にカスタムのコンポーネントを作成できます。

  • 完全に自由な裁量を与えたい場合は、テキストラベルやボタンなどの非常に基本的なコンポーネントを与えて、「UI を作成して」と指示します。

  • より細かく制御したい場合は、より複雑なコンポーネントを作成して、「これらのみを使用できる」と指定できます。

  • そのため、エージェントが実行できる操作やユーザーに表示できる内容を制御できます。

  • これが、新たにリリースされた A2UI レンダラ ライブラリと Jetpack ライブラリです。

    implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")
  • 現在はアルファ版で、つい最近アルファ 02 がリリースされました。

  • 以下のリンクからご確認ください。

  • developer.android.com にドキュメントといくつかのサンプルが用意されています。

  • 基本的なカタログを示す基本的なサンプルと、より複雑なサンプル、そして先ほど作成した JetPacker サンプルアプリがあります。このサンプルアプリは、本日後半の ADS 基調講演でも紹介されます。このサンプルアプリでは、ユーザーに表示される内容を制御する、より複雑なコンポーネントを作成する方法が示されています。

  • 先ほど申し上げたように、現在はアルファ版ですので、ぜひお試しいただき、フィードバックや機能リクエストをお寄せください。たとえば、どのように使用したいか、X や Y ができれば使用したい、といったご意見をお待ちしております。

  • ぜひライブラリをチェックして、フィードバックをお寄せください。

  • そして、それが実現しました。

  • 皆様と皆様のエージェントが、この機能を使ってどのようなものを構築されるのか、今から楽しみです。

  • どうぞよろしくお願い申し上げます。

  • ありがとうございました

コード

コード スニペット

「重要なポイントとリソース」の 27:40 にある Kotlin スクリプトのスニペット
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")