はじめに
こんにちは。Speeda Product Teamの板倉です。
AIエージェントに「直近3年の売上推移を教えて」と聞いたとき、こんな回答が返ってきた経験はないでしょうか。
2022年の売上は120億円で、前年比+8%の成長でした。 2023年は135億円で前年比+12.5%、2024年は128億円で前年比-5.2%となっています。 2023年に大きく伸びた要因としては…(以下、長文が続く)
数字の羅列をテキストで読まされても、傾向はすぐには掴めません。これがテーブルやチャートで返ってきたら、一目で把握できるはずです。あるいは「条件を絞り込みたい」と思っても、テキストの往復で一つずつ指定するしかありません。
AIエージェントの回答がテキストに閉じている限り、こうした体験の壁はつきまといます。
A2UI (Agent to UI) は、まさにそれを実現するための仕組みです。
A2UIとは
A2UI(Agent to UI)は、エージェント駆動型インターフェースのための宣言型UIプロトコルです。エージェントはテキストの代わりに、フォーム、カード、テーブルといったUIの構造をJSONで出力します。フロントエンドはそのJSONを受け取り、アプリのネイティブなコンポーネントとしてレンダリングします。
iframeで外部のHTMLを埋め込むのではありません。アプリ自身のUIフレームワークで描画されるため、見た目もアクセシビリティもアプリの一部として自然に馴染みます。
実際の動き: 3ステップで理解する
エージェントがUIを出力する流れは、3つのステップで構成されます。
- 描画面を宣言する (
createSurface) — サーフェス(描画領域)を作成し、使用するカタログを指定する - UIを構成する (
updateComponents) — サーフェス上にコンポーネントツリーを構築する - データを注入する (
updateDataModel) — コンポーネントにバインドするデータを設定・更新する
先ほどのレストラン予約の例で、実際のJSONメッセージを見てみましょう。
まず、エージェントは描画面を宣言します。
{ "version": "v0.9", "createSurface": { "surfaceId": "booking", "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json" } }
次に、その面の上にUIを構成します。
{ "version": "v0.9", "updateComponents": { "surfaceId": "booking", "components": [ { "id": "root", "component": "Text", "text": "予約フォーム", "variant": "h1" }, { "id": "datetime", "component": "DateTimeInput", "value": { "path": "/booking/date" }, "enableDate": true }, { "id": "submit-btn", "component": "Button", "child": "submit-text", "variant": "primary", "action": { "event": { "name": "confirm_booking" } } } ] } }
最後に、データを注入します。
{ "version": "v0.9", "updateDataModel": { "surfaceId": "booking", "path": "/booking", "value": { "date": "2025-12-16T19:00:00Z" } } }
エージェントはUIの「何を表示するか」を宣言するだけで、「どう描画するか」はフロントエンドに委ねられています。この3ステップはストリーミングで順次送られ、フロントエンドはメッセージを受け取るたびにUIを逐次レンダリングします。
A2UIの肝: カタログという設計
ここまで「カタログ」という言葉が何度か出てきました。これがA2UIの設計上最も重要な概念です。
スキーマによる疎結合
カタログ(JSON Schema)がAgentとFrontendの間の唯一の契約です。
graph LR A["Agent (LLM)"] -- "Schemaに準拠した<br/>JSONを出力" --> C["Catalog<br/>(JSON Schema)"] F["Frontend (WebComponents)"] -- "Schemaに準拠した<br/>JSONを解釈してレンダリング" --> C
両者はこのスキーマだけで繋がっており、互いの実装を知りません。AgentのLLMを差し替えてもFrontendには影響せず、Frontendのコンポーネントを作り替えてもAgentには影響しません。
カタログ = LLMの語彙
しかし、カタログの役割は疎結合のためのバリデーションだけではありません。カタログのJSON SchemaがそのままLLMのプロンプトに注入され、LLMの「使えるUIの語彙」になるという点がA2UIの核心です。
- LLMはプロンプト内でカタログを読み、そこに定義されたコンポーネントとプロパティを使ってJSONを出力する
- カスタムカタログに新しいコンポーネントを追加すれば、LLMの再学習なしにその場で「新しいUIパーツの使い方」を覚える
さらに、A2UIのSDKはカタログに加えてExamples(使用例のJSON)もプロンプトに注入する仕組みを備えています。Examplesは「このカタログを使うと、こういうUIが組める」という具体的なパターンをLLMに示すfew-shotの役割を果たします。カタログが「語彙」なら、Examplesは「例文集」です。
つまりA2UIのカタログは、バリデーションの道具であると同時に、LLMへの指示書そのものです。この二重の役割がスキーマ1つで成立しているのがA2UIの設計の肝です。
注意点: カタログとExamplesが出力品質を決める
A2UIは「情報設計をエージェントに任せられる」仕組みですが、魔法のツールではありません。
ここまで見てきたように、LLMが出力できるUIはカタログという「語彙」とExamplesという「例文集」の範囲に制約されます。これは具体的に以下を意味します。
- カタログにないコンポーネントは出力されない — LLMがどれほど賢くても、カタログに
Chartコンポーネントがなければチャートは生成されません。語彙にない単語は使えないのと同じです - Examplesの質がUIの質を左右する — LLMはExamplesのパターンを参考にUIを組み立てます。Examplesが貧弱であれば、LLMの出力も画一的で不自然なものになりがちです
- 情報設計の「正解」を保証するものではない — エージェントはカタログとExamplesに沿った構造を出力するだけで、ユーザーにとって最適なレイアウトやコンポーネントの選択を常に行えるわけではありません
A2UIの導入効果を最大化するには、カタログの設計とExamplesの整備が不可欠です。どのコンポーネントをどのような粒度で定義するか、どんな使用例をLLMに見せるか——この「LLMへの指示書」の品質が、そのままエージェントのUI出力品質に直結します。
まとめ
A2UIは「エージェントがテキストではなくUIを出力する」という一見シンプルな発想ですが、その設計には明確な思想があります。
- カタログ(JSON Schema)が唯一の契約 — AgentとFrontendを疎結合にし、独立した進化を可能にする
- カタログとExamplesがLLMの指示書になる — カタログが「語彙」、Examplesが「お手本」として、LLMのUI出力を方向づける
- 出力品質はカタログとExamplesの品質で決まる — A2UIは魔法のツールではなく、LLMへの指示書の設計が導入効果を左右する
A2UIはJSONメッセージの仕様であり、特定のLLMやUIフレームワークに依存しません。一方で、その力を引き出すには「LLMにどんな語彙とお手本を与えるか」というカタログ設計が不可欠です。AIエージェントの出力がテキストからUIに変わることで、ユーザー体験は根本的に変わります。A2UIはそのための基盤を、JSON Schemaという標準技術の上に築いています。