Affordable and efficient Sora video watermark removal. Sign up now and get 1 free credits!
A2A Protocol

2026年完全ガイド:A2UI RizzCharts でインタラクティブなダッシュボードを構築する

MILO
Share
The Complete 2026 Guide: Building Interactive Dashboards with A2UI RizzCharts

2026年完全ガイド:A2UI RizzCharts でインタラクティブなダッシュボードを構築する

🎯 核心要点(TL;DR)

  • RizzCharts は、A2UIA2A Protocol を使用してインタラクティブな ecommerce ダッシュボードを構築する方法を示す本番環境対応のサンプルです
  • 標準 A2UI カタログを超えた Chart と GoogleMap コンポーネントを含むカスタムコンポーネントカタログを作成する方法を学びます
  • リッチな UI をレンダリングするための3メッセージパターンbeginRenderingsurfaceUpdatedataModelUpdate)を理解します
  • データバインディングが UI 構造をアプリケーション状態から分離し、リアクティブな更新を可能にする方法を発見します
  • Google の Agent Development Kit (ADK) と統合して、ネイティブなクロスプラットフォーム UI を生成する AI エージェントを構築します

目次

  1. A2UI RizzCharts とは?
  2. カスタムコンポーネントカタログが重要な理由
  3. アーキテクチャの詳細
  4. 実装ステップバイステップ
  5. カスタムコンポーネント:Chart と GoogleMap
  6. データバインディングとリアクティブ更新
  7. RizzCharts サンプルの実行
  8. ベストプラクティス
  9. よくある質問
  10. まとめ

A2UI RizzCharts とは?

RizzCharts は、A2UI(Agent to UI)プロトコルを使用してAI 駆動の ecommerce ダッシュボードを構築する方法を示す公式サンプルアプリケーションです。AI エージェントがリッチなインタラクティブな可視化を作成し、プラットフォーム間でネイティブにレンダリングする宣言的 UI 生成の力を実証しています。

このサンプルは以下を使用します:

  • Google の Agent Development Kit (ADK) によるエージェントオーケストレーション
  • A2A Protocol によるエージェント間およびエージェントとクライアント間の通信
  • 標準 A2UI コンポーネントを拡張するカスタムコンポーネントカタログ
  • 柔軟な LLM プロバイダー統合(Gemini、OpenAI など)のためのLiteLLM

💡 プロのヒント

RizzCharts は実世界のパターンを示しています:AI エージェントがドメイン固有の可視化(チャート、マップ)を生成し、これらは汎用的なチャット応答ではなく、アプリケーションのネイティブな機能のように感じられます。

主要機能

機能 説明
販売チャート 製品カテゴリ別の販売内訳を示すインタラクティブなドーナツ/円グラフ
地理的地図 店舗の場所とパフォーマンスの外れ値を表示する Google Maps 統合
リアルタイム更新 データが変更されたときにリアクティブに更新されるデータバインドコンポーネント
カスタムカタログ 標準 A2UI コンポーネントを超えた拡張コンポーネントライブラリ

カスタムコンポーネントカタログが重要な理由

標準の A2UI カタログは一般的な UI 要素(Text、Button、TextField、Card など)を提供しますが、実世界のアプリケーションではドメイン固有のコンポーネントが必要になることがよくあります:

  • 金融ダッシュボードの株価ティッカー
  • ヘルスケアアプリケーションの医療チャート
  • エンジニアリングツールのCAD ビューア
  • 位置ベースサービスのインタラクティブマップ

カスタムカタログの仕組み

graph TD
    A[クライアントがカタログを定義] --> B[クライアントがコンポーネントを登録]
    B --> C[クライアントがサポートを発表]
    C --> D[エージェントがカタログを選択]
    D --> E[エージェントが UI を生成]
    E --> F[クライアントがネイティブウィジェットをレンダリング]

フロー:

  1. クライアントがカタログを定義 — 標準コンポーネントとカスタムコンポーネントをリストアップ
  2. クライアントが実装を登録 — コンポーネントタイプをネイティブウィジェットにマッピング
  3. クライアントがサポートを発表 — エージェントにサポートするカタログを通知
  4. エージェントがカタログを選択 — UI サーフェスに適切なカタログを選択
  5. エージェントが UI を生成 — カタログコンポーネントを使用して surfaceUpdate メッセージを作成
  6. クライアントがレンダリング — 任意のコードを実行せずにネイティブウィジェットを表示

アーキテクチャの詳細

プロジェクト構造

samples/agent/adk/rizzcharts/
├── __main__.py                    # エントリーポイント、サーバー設定
├── agent.py                       # LLM 指示付きの RizzchartsAgent
├── agent_executor.py              # セッション管理付きの A2A エグゼキューター
├── component_catalog_builder.py   # カスタムカタログ読み込みロジック
├── tools.py                       # データ取得ツール
├── rizzcharts_catalog_definition.json  # カスタムコンポーネントスキーマ
└── examples/
    ├── rizzcharts_catalog/        # カスタム Chart/GoogleMap を使用する例
    │   ├── chart.json
    │   └── map.json
    └── standard_catalog/          # 標準コンポーネントを使用するフォールバック
        ├── chart.json
        └── map.json

コアコンポーネント

1. RizzchartsAgent (agent.py)

ユーザーリクエストを処理し、A2UI ペイロードを生成するメインエージェントクラス:

class RizzchartsAgent(LlmAgent):
    """ecommerce ダッシュボードを実行するエージェント"""
    
    def __init__(self, model, a2ui_enabled_provider, a2ui_schema_provider):
        super().__init__(
            model=model,
            name="rizzcharts_agent",
            description="販売マネージャーが販売データをリクエストできるエージェント",
            instruction=self.get_instructions,
            tools=[
                get_store_sales,      # 地域/店舗データを取得
                get_sales_data,       # 販売内訳データを取得
                SendA2uiToClientToolset(...)  # クライアントに A2UI JSON を送信
            ],
            planner=BuiltInPlanner(
                thinking_config=types.ThinkingConfig(include_thoughts=True)
            ),
        )

2. Agent Executor (agent_executor.py)

セッション設定と A2UI 拡張機能のアクティベーションを処理:

class RizzchartsAgentExecutor(A2aAgentExecutor):
    def get_agent_card(self) -> AgentCard:
        return AgentCard(
            name="Ecommerce ダッシュボードエージェント",
            description="チャートとマップで ecommerce データを可視化",
            capabilities=AgentCapabilities(
                streaming=True,
                extensions=[get_a2ui_agent_extension(
                    supported_catalog_ids=[STANDARD_CATALOG_ID, RIZZCHARTS_CATALOG_URI]
                )],
            ),
            skills=[
                AgentSkill(id="view_sales_by_category", ...),
                AgentSkill(id="view_regional_outliers", ...),
            ],
        )

3. Component Catalog Builder (component_catalog_builder.py)

コンポーネントスキーマを動的に読み込み、マージ:

class ComponentCatalogBuilder:
    def load_a2ui_schema(self, client_ui_capabilities):
        # クライアントがサポートするカタログを確認
        if RIZZCHARTS_CATALOG_URI in supported_catalog_uris:
            catalog_uri = RIZZCHARTS_CATALOG_URI  # カスタム Chart/GoogleMap を使用
        elif STANDARD_CATALOG_ID in supported_catalog_uris:
            catalog_uri = STANDARD_CATALOG_ID     # 標準コンポーネントにフォールバック
        
        # カタログを A2UI スキーマにマージ
        a2ui_schema_json["properties"]["surfaceUpdate"]
            ["properties"]["components"]["items"]
            ["properties"]["component"]["properties"] = catalog_json
        
        return a2ui_schema_json, catalog_uri

実装ステップバイステップ

ステップ 1:カスタムコンポーネントを定義

カスタムコンポーネントを定義する JSON スキーマを作成します。以下が RizzCharts カタログです:

{
  "components": {
    "$ref": "standard_catalog_definition.json#/components",
    "Canvas": {
      "type": "object",
      "description": "チャットの横のステートフルパネルで UI をレンダリング",
      "properties": {
        "children": {
          "type": "object",
          "properties": {
            "explicitList": {
              "type": "array",
              "items": { "type": "string" }
            }
          }
        }
      },
      "required": ["children"]
    },
    "Chart": {
      "type": "object",
      "description": "階層データを持つインタラクティブチャート",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["doughnut", "pie"]
        },
        "title": {
          "type": "object",
          "properties": {
            "literalString": { "type": "string" },
            "path": { "type": "string" }
          }
        },
        "chartData": {
          "type": "object",
          "properties": {
            "literalArray": { "type": "array" },
            "path": { "type": "string" }
          }
        }
      },
      "required": ["type", "chartData"]
    },
    "GoogleMap": {
      "type": "object",
      "description": "カスタマイズ可能なピンを持つ Google Map",
      "properties": {
        "center": { "type": "object" },
        "zoom": { "type": "object" },
        "pins": { "type": "object" }
      },
      "required": ["center", "zoom"]
    }
  }
}

ステップ 2:データ取得ツールを作成

エージェントがデータを取得するために使用するツールを実装:

def get_sales_data(time_period: str = "year", **kwargs) -> dict:
    """製品カテゴリ別の販売内訳を取得"""
    return {
        "sales_data": [
            {"label": "Apparel", "value": 41, "drillDown": [
                {"label": "Tops", "value": 31},
                {"label": "Bottoms", "value": 38},
                {"label": "Outerwear", "value": 20},
            ]},
            {"label": "Electronics", "value": 28, "drillDown": [...]},
            {"label": "Home Goods", "value": 15},
            {"label": "Health & Beauty", "value": 10},
            {"label": "Other", "value": 6},
        ]
    }

def get_store_sales(region: str = "all", **kwargs) -> dict:
    """販売パフォーマンス付きの店舗の場所を取得"""
    return {
        "center": {"lat": 34, "lng": -118.2437},
        "zoom": 10,
        "locations": [
            {
                "lat": 34.0195, "lng": -118.4912,
                "name": "Santa Monica Branch",
                "description": "高トラフィックの沿岸地域",
                "outlier_reason": "はい、ベースラインを 15% 上回る販売",
                "background": "#4285F4",  # ハイライトされたピン
            },
            {"lat": 34.0488, "lng": -118.2518, "name": "Downtown Flagship"},
            # ... より多くの場所
        ],
    }

ステップ 3:エージェント指示を設定

エージェントは A2UI ペイロードを生成するための詳細な指示を受け取ります:

def get_instructions(self, readonly_context: ReadonlyContext) -> str:
    return f"""
    ### システム指示

    あなたは A2UI Ecommerce ダッシュボード分析の専門家です。主な機能は、ユーザーリクエストを A2UI JSON ペイロードに変換することです。

    **ワークフロー:**
    1. リクエストを分析 - 意図を決定(Chart か Map か)
    2. データを取得 - `get_sales_data` または `get_store_sales` を使用
    3. テンプレートを選択 - CHART または MAP の例をベースとして使用
    4. JSON ペイロードを構築 - 一意の surfaceId を生成し、タイトルを更新
    5. ツールを呼び出す - `send_a2ui_json_to_client` を使用

    **例:**
    - "Q3 のカテゴリ別販売内訳を表示" → Chart
    - "外れ値の店舗はありましたか" → Map

    ---BEGIN CHART EXAMPLE---
    {json.dumps(chart_example)}
    ---END CHART EXAMPLE---

    ---BEGIN MAP EXAMPLE---
    {json.dumps(map_example)}
    ---END MAP EXAMPLE---
    """

ステップ 4:A2UI メッセージペイロードを作成

完全な A2UI ペイロードは 3 つのメッセージで構成されます:

Chart の例

[
  {
    "beginRendering": {
      "surfaceId": "sales-dashboard",
      "root": "root-canvas"
    }
  },
  {
    "surfaceUpdate": {
      "surfaceId": "sales-dashboard",
      "components": [
        {
          "id": "root-canvas",
          "component": {
            "Canvas": {
              "children": { "explicitList": ["chart-container"] }
            }
          }
        },
        {
          "id": "chart-container",
          "component": {
            "Column": {
              "children": { "explicitList": ["sales-chart"] },
              "alignment": "center"
            }
          }
        },
        {
          "id": "sales-chart",
          "component": {
            "Chart": {
              "type": "doughnut",
              "title": { "path": "chart.title" },
              "chartData": { "path": "chart.items" }
            }
          }
        }
      ]
    }
  },
  {
    "dataModelUpdate": {
      "surfaceId": "sales-dashboard",
      "path": "/",
      "contents": [
        { "key": "chart.title", "valueString": "Sales by Category" },
        { "key": "chart.items[0].label", "valueString": "Apparel" },
        { "key": "chart.items[0].value", "valueNumber": 41 },
        { "key": "chart.items[0].drillDown[0].label", "valueString": "Tops" },
        { "key": "chart.items[0].drillDown[0].value", "valueNumber": 31 }
        // ... より多くのデータ
      ]
    }
  }
]

Map の例

[
  {
    "beginRendering": {
      "surfaceId": "la-map-view",
      "root": "root-canvas"
    }
  },
  {
    "surfaceUpdate": {
      "surfaceId": "la-map-view",
      "components": [
        {
          "id": "root-canvas",
          "component": {
            "Canvas": { "children": { "explicitList": ["map-layout-container"] } }
          }
        },
        {
          "id": "map-header",
          "component": {
            "Text": {
              "text": { "literalString": "Points of Interest in Los Angeles" },
              "usageHint": "h2"
            }
          }
        },
        {
          "id": "location-map",
          "component": {
            "GoogleMap": {
              "center": { "path": "mapConfig.center" },
              "zoom": { "path": "mapConfig.zoom" },
              "pins": { "path": "mapConfig.locations" }
            }
          }
        }
      ]
    }
  },
  {
    "dataModelUpdate": {
      "surfaceId": "la-map-view",
      "path": "/",
      "contents": [
        { "key": "mapConfig.center.lat", "valueNumber": 34.0522 },
        { "key": "mapConfig.center.lng", "valueNumber": -118.2437 },
        { "key": "mapConfig.zoom", "valueNumber": 11 },
        { "key": "mapConfig.locations[0].lat", "valueNumber": 34.0135 },
        { "key": "mapConfig.locations[0].name", "valueString": "Google Store Santa Monica" }
        // ... より多くの場所
      ]
    }
  }
]

カスタムコンポーネント:Chart と GoogleMap

Chart コンポーネント

Chart コンポーネントはインタラクティブなドーナツまたは円グラフをレンダリングします:

プロパティ タイプ 説明
type "doughnut" | "pie" チャート可視化タイプ
title {literalString} | {path} チャートタイトル(リテラルまたはデータバインド)
chartData {literalArray} | {path} {label, value, drillDown?} アイテムの配列

DrillDown サポート: 各チャートアイテムは、階層的可視化のためのネストされた drillDown データを持つことができます:

{
  "label": "Apparel",
  "value": 41,
  "drillDown": [
    { "label": "Tops", "value": 31 },
    { "label": "Bottoms", "value": 38 },
    { "label": "Outerwear", "value": 20 }
  ]
}

GoogleMap コンポーネント

GoogleMap コンポーネントは、カスタマイズ可能なピンを持つインタラクティブマップを表示します:

プロパティ タイプ 説明
center {lat, lng} マップの中心座標
zoom number ズームレベル(1-20)
pins array ピンオブジェクトの配列

ピンのプロパティ:

{
  "lat": 34.0195,
  "lng": -118.4912,
  "name": "Santa Monica Branch",
  "description": "高トラフィックの沿岸地域",
  "background": "#4285F4",    // ピンの背景色
  "borderColor": "#FFFFFF",   // ピンの境界色
  "glyphColor": "#FFFFFF"     // ピンのアイコン色
}

データバインディングとリアクティブ更新

A2UI は、データバインディングを通じてUI 構造アプリケーション状態から分離します:

リテラル vs. パス値

// リテラル(固定値)
{"text": {"literalString": "Sales Dashboard"}}

// パス(データバインド、リアクティブ)
{"text": {"path": "chart.title"}}

chart.title のデータが変更されると、コンポーネントは自動的に更新されます—コンポーネントの再生成は不要です。

JSON Pointer パス

A2UI は RFC 6901 JSON Pointer 構文を使用します:

パス 解決先
/user/name オブジェクトプロパティ
/items/0 最初の配列要素
/items/0/price ネストされたプロパティ

テンプレート内のスコープパス

動的リストにテンプレートを使用する場合、パスは各配列アイテムに対してスコープされます:

{
  "id": "location-name",
  "component": {
    "Text": {
      "text": { "path": "name" }  // 現在のアイテムに対して相対的
    }
  }
}

/mapConfig.locations/0 の場合、パス name/mapConfig.locations/0/name に解決されます。


RizzCharts サンプルの実行

前提条件

  • Python 3.9+
  • UV パッケージマネージャー
  • LLM API キー(Gemini、OpenAI など)

セットアップ

# サンプルディレクトリに移動
cd samples/agent/adk/rizzcharts

# 環境ファイルを作成
cp .env.example .env
# API キーで .env を編集

# エージェントサーバーを実行
uv run .

サーバーはデフォルトで http://localhost:10002 で起動します。

環境変数

変数 説明 デフォルト
GEMINI_API_KEY Google AI API キー 必須
GOOGLE_GENAI_USE_VERTEXAI 代わりに Vertex AI を使用 FALSE
LITELLM_MODEL LLM モデル識別子 gemini/gemini-2.5-flash

サンプルクエリでテスト

実行後、次のようなリクエストを送信します:

  • "Q3 の製品カテゴリ別販売内訳を表示" → ドーナツチャートを生成
  • "地域に外れ値の店舗はありましたか?" → ハイライトされたピンを持つマップを生成
  • "前年比の収益トレンドはどうですか?" → チャート可視化を生成

ベストプラクティス

1. 説明的なコンポーネント ID

// ✅ 良い
{"id": "sales-chart-q3-2026"}
{"id": "store-location-map"}

// ❌ 悪い
{"id": "c1"}
{"id": "component"}

2. 構造とデータを分離

動的コンテンツにはデータバインディングを使用:

// ✅ 推奨 - データバインド
{"title": {"path": "chart.title"}}

// ⚠️ 控えめに使用 - リテラル値
{"title": {"literalString": "Static Title"}}

3. 一意の Surface ID を生成

各リクエストは一意の surfaceId を生成する必要があります:

surface_id = f"sales_breakdown_{time_period}_{uuid.uuid4().hex[:8]}"

4. スキーマに対して検証

生成された JSON を常に A2UI スキーマに対して検証:

jsonschema.validate(instance=example_json, schema=a2ui_schema)

5. セキュリティの考慮事項

⚠️ 重要なセキュリティ注意事項

  • エージェントが生成したすべてのコンテンツを信頼できない入力として扱います
  • すべてのプロパティ値に対して入力サニタイゼーションを実装します
  • クライアントレンダラーでコンテンツセキュリティポリシー (CSP) を使用します
  • レンダリング前にデータを厳密に検証します

よくある質問

Q: 標準カタログとカスタムカタログの違いは何ですか?

A: 標準カタログには、すべての A2UI クライアントで機能する一般的な UI コンポーネント(Text、Button、Card、List など)が含まれています。カスタムカタログは、クライアント側の実装を必要とするドメイン固有のコンポーネント(Chart、GoogleMap、StockTicker)でこれを拡張します。RizzCharts は両方のアプローチを示し、フォールバックサポートを提供します。

Q: A2UI は HTML/iframe の送信と比較してどうですか?

A: A2UI宣言的データであり、コードではありません。クライアントは独自のネイティブウィジェットを使用してコンポーネントをレンダリングし、以下を保証します:

  • コード実行リスクなし(セキュリティ)
  • ネイティブなルックアンドフィール(UX)
  • 一貫したスタイリング(デザイン)
  • クロスプラットフォームサポート(移植性)

Q: RizzCharts コンポーネントを他のプロトコルで使用できますか?

A: はい!RizzCharts は A2A Protocol を使用していますが、A2UI メッセージ形式は任意のトランスポートで機能します:SSE、WebSockets、AG UI、または直接 HTTP。AP2 Protocol も、支払い対応のエージェントインターフェース用に A2UI と統合されています。

Q: クライアントでカスタムコンポーネントを実装するにはどうすればよいですか?

A: クライアントフレームワークでコンポーネント実装を登録:

  1. カタログ JSON でコンポーネントスキーマを定義
  2. レンダリングロジックを実装(Lit、Angular、React、Flutter)
  3. A2UI クライアントにカタログを登録
  4. エージェントにサポートするカタログを発表

参照実装については Lit サンプル を参照してください。

Q: クライアントがカタログをサポートしていない場合はどうなりますか?

A: RizzCharts にはフォールバックサポートが含まれています。ComponentCatalogBuilder はクライアントの機能を確認します:

if RIZZCHARTS_CATALOG_URI in supported_catalog_uris:
    catalog_uri = RIZZCHARTS_CATALOG_URI  # カスタムコンポーネント
elif STANDARD_CATALOG_ID in supported_catalog_uris:
    catalog_uri = STANDARD_CATALOG_ID     # 標準コンポーネント

標準カタログの例は、Chart/GoogleMap なしで同じデータを表示するために List と Card コンポーネントを使用します。


まとめ

RizzCharts サンプルは、エージェント駆動のダッシュボードを構築するための A2UI の完全な力を実証しています:

  1. カスタムコンポーネントカタログが A2UI を基本 UI 要素を超えて拡張
  2. データバインディングがリアクティブで効率的な更新を可能に
  3. スキーマ検証が型安全なエージェント出力を保証
  4. フォールバックサポートが優雅な劣化を提供
  5. セキュリティ設計がクライアントを制御下に保つ

次のステップ


最終更新:2026年1月

キーワード:A2UI, Agent to UI, RizzCharts, custom components, declarative UI, A2A Protocol, ecommerce dashboard, data visualization, AI agents, LLM UI generation

Related Articles

Explore more content related to this topic