[A2UI] AIエージェントとUIをつなぐプロトコル A2UIを試してみる
こんにちは、サントリーこと大橋です。
本記事は Google Developer Experts Advent Calendar 2025 の 2025/12/18 の記事です。
一応私は Google Workspace の GDE なのですが、今年はほぼほぼ ADK (Agent Development Kit) を触って、ADK 周りで色々やった一年でした。
最近は GDE の数も増えて、活動内容も多岐にわたるようになってきましたが、私は引き続き ADK のような新しいフレームワークやサービスの盛り上げを頑張っていきたいと思います。(ぶっちゃけ飽きやすいだけかもしれない)
ということで、今回は 2025/12/15 に Google からリリースされた、オープンソースのエージェント駆動型インターフェースのプロトコル A2UI について、調査とサンプルを触って動きを確かめていきたいと思います。
対象
- AI エージェントの UI について悩んだ方
- これから AI エージェントを作ろうと思っている方
- ADK を触ったことがある方
時間がない人向け
ぶっちゃけ Notebook LM が作った以下の画像が多分一番分かりやすいです。

課題: テキストベースのやり取りの限界
これまで、AI エージェント(LLM)との対話といえば、基本的には「チャット」形式、つまりテキストベースのやり取りが主流でした。
しかし、このテキストベースのやり取りには、特定のタスクにおいて「時間がかかり非効率」という課題がありました。
例えば、ユーザーが「明日の 19 時に 2 名でレストランを予約したい」と考えたとします。
テキストベースの場合、以下のようなやり取りが発生します。
- User: 「レストランの予約をお願い」
- Agent: 「かしこまりました。いつがよろしいですか?」
- User: 「明日」
- Agent: 「何時でしょうか?」
- User: 「19 時くらい」
-
Agent: 「その時間は満席です。18 時か 20 時なら空いています。」
...
このように、何度も往復が発生してしまい、ユーザーにとってもストレスですし、時間もかかります。
雑にやりとりできるという点では便利ですが、何度もやり取りするという点では非効率です。
もしここで、エージェントがテキストで返す代わりに、「日付ピッカー」「時間セレクタ」「人数入力」「予約ボタン」がセットになった予約フォームをシュッと表示してくれたらどうでしょうか?
ユーザーは直感的に操作でき、一回の対話で要件を満たすことができます。
しかし、従来の仕組みでは、エージェント(サーバーサイドやリモート)から、セキュアかつ柔軟にクライアント(Web やスマホアプリ)のネイティブ UI を制御するのは困難でした。iframe を使う手もありましたが、デザインの統合やセキュリティ面で課題がありました。
またクライアントサイド用のコードを生成して、それを Code Execution の仕組みや eval などを用いるなどの方法もありますが、これはセキュリティ面で課題がありました。
そこで登場したのが A2UI です。
A2UI とは?
A2UI (Agent to UI) は、AI エージェントがリッチな UI を生成し、クライアント側でネイティブにレンダリングするためのプロトコルです。
A2UI の概要
A2UI は、「AI エージェントが UI を話せるようにする」ための共通言語のようなものです。
具体的には、エージェントは HTML や JavaScript を送るのではなく、UI コンポーネントの構造とデータを記述した JSON メッセージを送信します。
クライアント側(Web アプリ、Flutter アプリなど)は、その JSON を受け取り、事前に定義された自前のネイティブコンポーネントを使って UI を描画します。
これにより、以下のメリットが生まれます。
- セキュリティ: エージェントは「ボタンを表示して」という指示を送るだけで、実行可能なコード(JS など)は送りません。クライアントは自分のカタログにある安全なコンポーネントだけを使うため、UI インジェクションなどのリスクを回避できます。
- ネイティブな体験: クライアント側の UI フレームワーク(React, Angular, Flutter など)で描画されるため、アプリ本来のデザインや操作感を損ないません。iframe のような違和感がありません。
- LLM フレンドリー: A2UI のメッセージ形式はフラットな JSON 構造で、LLM がストリーミング生成しやすいように設計されています。
詳しくは以下の公式ドキュメントも参照してください。
A2UI のコンセプト
A2UI にはいくつか重要なコンセプトがあります。
- Surface (サーフェス): UI コンポーネントを描画するキャンバスのような領域(ダイアログ、サイドバー、メインビューなど)。
- Component (コンポーネント): ボタン、テキストフィールド、カードなどの UI 要素。
- Data Model (データモデル): アプリケーションの状態。コンポーネントはこのデータモデルにバインドされます。
- Catalog (カタログ): 利用可能なコンポーネントのリスト。
-
Message (メッセージ):
surfaceUpdateやdataModelUpdateなどの JSON オブジェクト。
エージェントは surfaceUpdate(UI 構造の更新)や dataModelUpdate(データの更新)といったメッセージを送信し、クライアントはそれに従って画面を構築・更新します。
A2UI のデータフロー
A2UI の基本的なデータフローは以下のようになります。
- User Action: ユーザーがメッセージを送る。
- Agent Processing: エージェント(A2A Agent など)がリクエストを受け取り、Gemini などの LLM を使って A2UI メッセージ(JSON)を生成する。
- Streaming: 生成された JSON メッセージがストリーミングでクライアントに送られる。
- Rendering: クライアント(A2UI Renderer)がメッセージを解析し、ネイティブコンポーネントとして描画する。
- Interaction: ユーザーが UI を操作すると、そのアクションが再びエージェントに送られる。
実際に触ってみる
論より証拠、ということで実際に公式のクイックスタートを試してみましょう。
このデモでは、テキストベースの指示からレストラン予約フォームを生成するエージェントを動かします。
1. リポジトリのクローン
まずは A2UI のリポジトリをクローンします。
git clone https://github.com/google/a2ui.git
cd a2ui
2. API Key の設定
Gemini API を利用するため、API Key を環境変数に設定します。
export GEMINI_API_KEY="your_gemini_api_key_here"
3. クライアントアプリの準備と実行
サンプルには Lit や Angular を使った Web クライアントが含まれていますが、今回は Angular のサンプルを使ってみます。
以下のコマンドで、依存関係のインストールから .env の修正、サーバーの起動まで行います。
cd samples/client/angular
echo "export GEMINI_API_KEY=your_gemini_api_key" >> .env
cp .env ../../agent/adk/restaurant_finder/.env
npm install
npm run demo:restaurant
コマンドが完了すると、ブラウザで http://localhost:4200 が開きます。
4. 試してみる
画面が開くと、チャットインターフェースが表示されます。

ここで、デフォルトで入力されている「Top 5 Chinese restaurants in New York.」のままEnterキーを押下してみましょう。
すると、エージェントが考え始め、テキストの返答ではなく、5店舗の中華レストランの情報が表示されます。
ここで大事なのはこの表示されたUIはフロントエンド側で事前に構築されたUIではなく、
AIエージェントから返却されたA2UIプロトコルのJSONを元に作成されたUIだということです。
実際にAIエージェントから返却されたJSONは以下です。

AIエージェントから返却されたデータ
[
{
"kind": "text",
"text": "Here are the top 5 Chinese restaurants in New York:"
},
{
"data": {
"beginRendering": {
"surfaceId": "default",
"root": "root-column",
"styles": {
"primaryColor": "#FF0000",
"font": "Roboto"
}
}
},
"kind": "data",
"metadata": {
"mimeType": "application/json+a2ui"
}
},
{
"data": {
"surfaceUpdate": {
"surfaceId": "default",
"components": [
{
"id": "root-column",
"component": {
"Column": {
"children": {
"explicitList": [
"title-heading",
"item-list"
]
}
}
}
},
{
"id": "title-heading",
"component": {
"Text": {
"usageHint": "h1",
"text": {
"path": "title"
}
}
}
},
{
"id": "item-list",
"component": {
"List": {
"direction": "vertical",
"children": {
"template": {
"componentId": "item-card-template",
"dataBinding": "/items"
}
}
}
}
},
{
"id": "item-card-template",
"component": {
"Card": {
"child": "card-layout"
}
}
},
{
"id": "card-layout",
"component": {
"Row": {
"children": {
"explicitList": [
"template-image",
"card-details"
]
}
}
}
},
{
"id": "template-image",
"weight": 1,
"component": {
"Image": {
"url": {
"path": "imageUrl"
}
}
}
},
{
"id": "card-details",
"weight": 2,
"component": {
"Column": {
"children": {
"explicitList": [
"template-name",
"template-rating",
"template-detail",
"template-link",
"template-book-button"
]
}
}
}
},
{
"id": "template-name",
"component": {
"Text": {
"usageHint": "h3",
"text": {
"path": "name"
}
}
}
},
{
"id": "template-rating",
"component": {
"Text": {
"text": {
"path": "rating"
}
}
}
},
{
"id": "template-detail",
"component": {
"Text": {
"text": {
"path": "detail"
}
}
}
},
{
"id": "template-link",
"component": {
"Text": {
"text": {
"path": "infoLink"
}
}
}
},
{
"id": "template-book-button",
"component": {
"Button": {
"child": "book-now-text",
"primary": true,
"action": {
"name": "book_restaurant",
"context": [
{
"key": "restaurantName",
"value": {
"path": "name"
}
},
{
"key": "imageUrl",
"value": {
"path": "imageUrl"
}
},
{
"key": "address",
"value": {
"path": "address"
}
}
]
}
}
}
},
{
"id": "book-now-text",
"component": {
"Text": {
"text": {
"literalString": "Book Now"
}
}
}
}
]
}
},
"kind": "data",
"metadata": {
"mimeType": "application/json+a2ui"
}
},
{
"data": {
"dataModelUpdate": {
"surfaceId": "default",
"path": "/",
"contents": [
{
"key": "title",
"valueString": "Top 5 Chinese restaurants in New York"
},
{
"key": "items",
"valueMap": [
{
"key": "item1",
"valueMap": [
{
"key": "name",
"valueString": "Xi'an Famous Foods"
},
{
"key": "rating",
"valueString": "★★★★☆"
},
{
"key": "detail",
"valueString": "Spicy and savory hand-pulled noodles."
},
{
"key": "infoLink",
"valueString": "[More Info](https://www.xianfoods.com/)"
},
{
"key": "imageUrl",
"valueString": "http://localhost:10002/static/shrimpchowmein.jpeg"
},
{
"key": "address",
"valueString": "81 St Marks Pl, New York, NY 10003"
}
]
},
{
"key": "item2",
"valueMap": [
{
"key": "name",
"valueString": "Han Dynasty"
},
{
"key": "rating",
"valueString": "★★★★☆"
},
{
"key": "detail",
"valueString": "Authentic Szechuan cuisine."
},
{
"key": "infoLink",
"valueString": "[More Info](https://www.handynasty.net/)"
},
{
"key": "imageUrl",
"valueString": "http://localhost:10002/static/mapotofu.jpeg"
},
{
"key": "address",
"valueString": "90 3rd Ave, New York, NY 10003"
}
]
},
{
"key": "item3",
"valueMap": [
{
"key": "name",
"valueString": "RedFarm"
},
{
"key": "rating",
"valueString": "★★★★☆"
},
{
"key": "detail",
"valueString": "Modern Chinese with a farm-to-table approach."
},
{
"key": "infoLink",
"valueString": "[More Info](https://www.redfarmnyc.com/)"
},
{
"key": "imageUrl",
"valueString": "http://localhost:10002/static/beefbroccoli.jpeg"
},
{
"key": "address",
"valueString": "529 Hudson St, New York, NY 10014"
}
]
},
{
"key": "item4",
"valueMap": [
{
"key": "name",
"valueString": "Mott 32"
},
{
"key": "rating",
"valueString": "★★★★★"
},
{
"key": "detail",
"valueString": "Upscale Cantonese dining."
},
{
"key": "infoLink",
"valueString": "[More Info](https://mott32.com/newyork/)"
},
{
"key": "imageUrl",
"valueString": "http://localhost:10002/static/springrolls.jpeg"
},
{
"key": "address",
"valueString": "111 W 57th St, New York, NY 10019"
}
]
},
{
"key": "item5",
"valueMap": [
{
"key": "name",
"valueString": "Hwa Yuan Szechuan"
},
{
"key": "rating",
"valueString": "★★★★☆"
},
{
"key": "detail",
"valueString": "Famous for its cold noodles with sesame sauce."
},
{
"key": "infoLink",
"valueString": "[More Info](https://hwayuannyc.com/)"
},
{
"key": "imageUrl",
"valueString": "http://localhost:10002/static/kungpao.jpeg"
},
{
"key": "address",
"valueString": "40 E Broadway, New York, NY 10002"
}
]
}
]
}
]
}
},
"kind": "data",
"metadata": {
"mimeType": "application/json+a2ui"
}
}
]
次に元の画面に戻り、以下のように入力してみましょう。
Xi'an Famous Foodsで予約したい。
すると今度は日時選択や人数選択ができる予約フォームがレンダリングされました。
日付を選んで「SUBMIT RESERVATION」ボタンを押すと、その結果がまたエージェントに送られ、予約完了画面が表示されます。

これらの画面はAngularで事前に定義されたものではなく、AIエージェントがA2UIプロトコルに従って作成したJSONを元に動的にクライアントサイドで作成されたものです。
実際に動かしてみると、テキストだけのチャットよりも圧倒的にスムーズで、かつ「アプリの一部」として自然に機能している感覚がありました。
まとめ
今回は A2UI の概要を調べつつ、付属のサンプルを触って動きを確かめてみました。
これまで AI エージェントが抱えていた課題の一つである「UIの表示」に対する、かなり具体的かつ実用的な解決策だと感じました。
特に「セキュリティ」と「ネイティブなルック&フィール」を両立させている設計思想は、今後のエージェント開発においてスタンダードになっていく可能性があります。
まだ対応しているフレームワークやライブラリは発展途上ですが、今後様々な UI フレームワークから簡単に利用できるようになると、一気に流行りそうですね。個人的にも ADK と組み合わせて使っていきたいです。
今回はサンプルを動かしただけなので、実際にどのように実装していくかについては触れなかったので、
次回以降の記事で実際にA2UIを使ったADKエージェントシステムを構築していきたいと思います。
お知らせ/宣伝
ADK 開発者が集う日本語の Discord コミュニティがあります。ADK に関する情報交換や議論に興味がある方は、ぜひご参加ください!
また、ADK の最新のコミットログやリリースノートを分かりやすく解説する Podcast を、月・水・金に配信しています。ADK の動向を追いかけたい方は、ぜひ聴いてみてください。クリスマス期間中は毎日配信しています。
Discussion