6月26日、Devin Chasanoffが「Demystifying A2UI: How to Make AI Agents "Speak UI" in Your App」と題した記事を公開した。AIエージェントがUIを動的に生成・操作するためのプロトコル「A2UI」をAngularアプリに組み込む実装方法を、設計思想から具体的なコードまで体系的に解説している。「AIにUIを喋らせる」という発想は新しくないが、フレームワーク非依存のプロトコルとして標準化しようという試みは注目に値する。
A2UIとは何か——「フレームワーク」ではなく「プロトコル」
A2UIは、AIエージェントがリッチでインタラクティブなUIを生成し、Web・モバイル・デスクトップでネイティブにレンダリングするためのプロトコルだ(現在バージョン1.0 RC)。
ここで重要なのが「フレームワークではなくプロトコルである」という点だ。これはLLMエージェントとクライアントアプリケーションがUI情報をどう伝え合うかを定義するものであり、実際のレンダリングは好みのフロントエンド技術に委ねられる。特定スタックへのロックインが生じない。また、任意コードを実行せずにUIを生成できる点も設計上の特徴として挙げられている。
既存アプローチとの位置づけ
※編集部の考察:AIによるUI生成のアプローチはこれが初めてではない。たとえばServer-Driven UI(SDUI)はサーバーがUIの構造を定義してクライアントに送る手法として以前から存在し、NetflixやAirbnbの事例で広く知られている。またOpenAI Function Callingを使ってLLMにUI定義JSONを返させる実装も開発者の間で試みられてきた。A2UIがこれらと異なるのは、「AIエージェントとクライアント間の通信仕様」をプロトコルとして明示的に標準化し、カタログ・アクション・スキルといった概念を体系化している点にある。SDUIが「サーバーの人間開発者がUIを定義する」仕組みであるのに対し、A2UIは「AIエージェント自身がUIを動的に構成する」ことを前提として設計されている。
A2UIのメッセージはJSON形式で、以下のようなUIの構造定義だ:
{
"version": "v0.9",
"updateComponents": {
"surfaceId": "main",
"components": [
{
"id": "root",
"component": "Column",
"children": ["header", "body"]
},
{
"id": "header",
"component": "Text",
"text": "Welcome"
},
{
"id": "body",
"component": "Card",
"child": "content"
},
{
"id": "content",
"component": "Text",
"text": {"path": "/message"}
}
]
}
}
※本文では「現在バージョン1.0 RC」と記載されているが、サンプルコードのversionフィールドは"v0.9"となっている。元記事のサンプルがそのままの値であるため本稿でも原文に従って掲載するが、実際の実装時は公式ドキュメントで最新仕様を確認していただきたい。
サーバー(エージェント側)がこのJSONを動的に生成し、クライアント(Angularアプリ等)がそれを読み取ってUIを構築する。
サーバー側(エージェント側)に用意するもの
A2UIをアプリに組み込むには、サーバー側とクライアント側それぞれにいくつかのデータ・設定を用意する必要がある。まずサーバー側から整理する。
カタログ(使えるUIの部品一覧)
カタログは、エージェントが参照できるUIコンポーネントの辞書だ。JSON Schemaで記述され、各要素の説明・バリアント・子要素の定義などを含む。A2UIチームは汎用的な「ベーシックカタログ」をオープンソースで公開しており、テキスト・行・列・カードなど基本的な要素が揃っている。独自コンポーネント(グラフ・マップ等)が必要な場合はカスタムカタログを定義する。
UIの自然言語説明とFew-Shot例
エージェントに対して「どのUIをいつ使うか」を自然言語で説明する。加えて、Few-Shot PromptingでJSON構造の具体例を渡すことで、エージェントが期待通りのUIを組み立てやすくなる。たとえばレストラン予約フォームの場合、「予約フォーム」という単一コンポーネントは存在しないが、入力・列・ボタンを組み合わせる例示をすることで正しいJSONが生成される。
アクションマッピング
動的なUIを「使える」アプリにするには、ボタンクリックやフォーム送信などのユーザー操作への対応が必要だ。アクションマッピングはその仕組みで、ユーザーイベントに対して処理を紐づける。
重要なのは、ここでデータベース書き込みやリダイレクトロジックをハードコードしないことだ。代わりに、構造化されたパラメータを新しい自然言語クエリとしてエージェントに送り返す。エージェントはAIなので、そのクエリに基づいてツールを呼び出すなど適切な処理を行う。サンプルコードから引用する:
elif action == "submit_booking":
restaurant_name = ctx.get("restaurantName", "Unknown Restaurant")
party_size = ctx.get("partySize", "Unknown Size")
reservation_time = ctx.get("reservationTime", "Unknown Time")
dietary_reqs = ctx.get("dietary", "None")
image_url = ctx.get("imageUrl", "")
query = (
f"User submitted a booking for {restaurant_name} for {party_size} people at"
f" {reservation_time} with dietary requirements: {dietary_reqs}. The image"
f" URL is {image_url}"
)
このパターンはUIロジックとビジネスロジックの境界を明確に保つ設計思想を反映している。
エージェントツールとスキル
ツールはエージェントがAPIやDBと対話するための関数で、A2UI固有の概念ではないが重要な役割を担う。スキルはユーザーが実行できる機能単位を定義するもので、ツールやアクションと組み合わせて使う。
クライアント側(Angularアプリ)に用意するもの
クライアント側でボイラープレート以外に用意するのは主に2つだ。
カスタムコンポーネントカタログの登録
ベーシックカタログだけで済む場合は、以下のようにAngularのapp.configに登録するだけでよい:
{
provide: A2UI_RENDERER_CONFIG,
useFactory: () => {
const injector = inject(Injector);
return {
catalogs: [new BasicCatalog()],
actionHandler: (action: A2uiClientAction) => injector.get(Client).handleAction(action),
};
},
},
チャートなどカスタムコンポーネントが必要な場合は、Angularコンポーネントの実装を含む独自カタログを作成し、同様に登録する。
ここで絶対に守るべき点として記事は強調している:
クライアント側のコンポーネント定義は、サーバー側に渡したカタログスキーマや例示と完全に一致していなければならない。ズレが生じると、エージェントがクライアントでレンダリングできないJSONを生成してしまう。
テーマ設定
エージェントが生成したUIをアプリのデザインに馴染ませるため、CSSカスタムプロパティによるテーマ設定が可能だ。
まとめ——A2UIが解こうとしている問題
A2UIのアーキテクチャを整理すると、開発者が用意するのはサーバー側が「カタログ・UI説明・Few-Shot例・スキル・アクションマッピング・ツール」、クライアント側が「コンポーネント実装とテーマ」に集約される。プロトコルが間を取り持つことで、LLMが生成したJSONがそのままネイティブUIとして描画される。
このアプローチが解こうとしている本質的な問題は、AIエージェントの「判断」と「表示」の間に存在するギャップだ。エージェントがどれだけ高度な推論を行っても、それをユーザーに届けるUIが固定されていれば体験は制限される。A2UIはその橋渡しをプロトコルとして定義することで、フレームワークを問わず再利用可能な共通基盤を目指している。現時点ではまだ1.0 RCの段階であり、エコシステムの広がりはこれからだが、Angular公式ブログで取り上げられた点は注目に値する。
詳細はDemystifying A2UI: How to Make AI Agents "Speak UI" in Your Appを参照していただきたい。




