iT邦幫忙

2026 iThome 鐵人賽

0
Build on Google AI

將考國際證照的應用程式變成開源系列 第 53

使用 ADK 與 A2UI 構建宣告式 AI Agent 介面完整技術手冊

  • 分享至 

  • xImage
  •  

整理一下,我今晚上的上課學習筆記

原始資料來源: https://codelabs.developers.google.com/next26/adk-a2ui

使用 ADK 與 A2UI 構建宣告式 AI Agent 介面完整技術手冊

1. 架構概述與核心概念

多數 AI Agent 應用受限於純文字或 Markdown 輸出,在面對雲端資源監控、資料庫狀態或分散式服務排程等複雜維運場景時,單純文字會造成使用者極大的閱讀與決策負擔。

Google Agent Development Kit (ADK) 結合 Agent-to-User Interface (A2UI) 協定解決了此問題。A2UI 是一種開放的宣告式 UI 協定,內建 18 個基礎元件(Primitives)與 3 種訊息格式。後端 Agent 只需負責產生描述 UI 階層與資料關聯的結構化 JSON,前端客戶端(如 React、Lit、Angular 或 Flutter)即可原生渲染為對應的 UI 元件,無須針對每種對話情境撰寫專屬的前端頁面。

+-------------------------------------------------------------------------------+
|                                 Agent 後端                                    |
|                                                                               |
|  +---------------------+       +---------------------+                        |
|  | Mock 資源 / Tools   | ----> | LLM 推論核心        |                        |
|  | (resources.py)      |       | (gemini-3-flash)    |                        |
|  +---------------------+       +---------------------+                        |
|                                           |                                   |
|                                           v                                   |
|                                +---------------------+                        |
|                                | A2UI Schema Manager |                        |
|                                +---------------------+                        |
|                                           |                                   |
|                                           v                                   |
|                              +-------------------------+                      |
|                              | 後處理回呼 (Callback)   |                      |
|                              | (a2ui_callback)         |                      |
|                              +-------------------------+                      |
+-------------------------------------------|-----------------------------------+
                                            | A2UI 訊息 (JSON / DataPart)
                                            v
+-------------------------------------------------------------------------------+
|                                前端客戶端                                     |
|                                                                               |
|    +-------------------+    +--------------------+    +--------------------+  |
|    |  beginRendering   |    |   surfaceUpdate    |    |  dataModelUpdate   |  |
|    |  (畫布與根節點)   |    |  (扁平化元件樹)    |    |  (響應式資料模型)  |  |
|    +-------------------+    +--------------------+    +--------------------+  |
|                                       |                                       |
|                                       v                                       |
|                      +---------------------------------+                      |
|                      | A2UI 原生渲染引擎               |                      |
|                      | (ADK Dev UI, React, Flutter)    |                      |
|                      +---------------------------------+                      |
|                                       |                                       |
|                                       v                                       |
|                      +---------------------------------+                      |
|                      | 互動式動態儀表板                |                      |
|                      +---------------------------------+                      |
+-------------------------------------------------------------------------------+


2. A2UI 協定規範解析

A2UI 協定主要基於三大訊息類型扁平化元件樹18 個基礎元件

三大核心訊息類型

每一次 A2UI 回應包含由以下三種結構組成的 JSON 陣列:

  1. beginRendering(啟動渲染):宣告渲染畫布(Surface)與根節點 ID。
{"beginRendering": {"surfaceId": "default", "root": "main-column"}}

  1. surfaceUpdate(介面結構更新):傳遞 UI 元件結構。A2UI 採用扁平化列表(Flat List)而非深度巢狀物件,父元件以 ID 參考子元件,能有效降低 LLM Token 消耗並避免解析上限。
{
  "surfaceUpdate": {
    "surfaceId": "default",
    "components": [
      {"id": "main-column", "component": {"Column": {"children": {"explicitList": ["header", "svc-card"]}}}},
      {"id": "header", "component": {"Text": {"text": {"literalString": "雲端資源清單"}, "usageHint": "h1"}}},
      {"id": "svc-card", "component": {"Card": {"child": "svc-name"}}},
      {"id": "svc-name", "component": {"Text": {"text": {"path": "service_name"}}}}
    ]
  }
}

  1. dataModelUpdate(資料模型更新):將動態資料與介面結構抽離。元件透過 {"path": "key"} 綁定欄位,資料變更時僅需推送更新值即可刷新介面。
{
  "dataModelUpdate": {
    "surfaceId": "default",
    "contents": [
      {"key": "service_name", "valueString": "auth-service"}
    ]
  }
}

18 個基礎元件分類

  • 版面配置 (Layout)CardColumnRowListTabsDividerModal
  • 資訊展示 (Display)TextImageIconVideoAudioPlayer
  • 資料輸入 (Input)TextFieldDateTimeInputMultipleChoiceCheckBoxSlider
  • 操作互動 (Action)Button

3. 環境準備與基礎設定(CLI 操作)

執行環境以 Google Cloud Shell 為主,使用專案 test20260902001 與 Vertex AI。

1. 設定環境變數與切換專案

gcloud config set project test20260902001

export GOOGLE_CLOUD_PROJECT=test20260902001
export GOOGLE_CLOUD_LOCATION=global
export GOOGLE_GENAI_USE_VERTEXAI=True

2. 啟用 Vertex AI API

gcloud services enable aiplatform.googleapis.com --project test20260902001

3. 安裝必要套件與設定路徑

pip install -U google-adk a2ui-agent-sdk
export PATH="$HOME/.local/bin:$PATH"

4. 建立工作目錄

mkdir -p ~/test20260902001/a2ui_agent
cd ~/test20260902001


4. 第一階段:建置純文字基準 Agent

首先建立傳統純文字 Agent,用於對比導入 A2UI 前後的輸出差異。

1. 建立資源查詢 Tool (a2ui_agent/resources.py)

cat <<'EOF' > a2ui_agent/resources.py
RESOURCES = [
    {
        "name": "auth-service",
        "type": "Cloud Run",
        "region": "us-west1",
        "status": "healthy",
        "cpu": "2 vCPU",
        "memory": "1 GiB",
        "instances": 3,
        "url": "https://auth-service-abc123.run.app",
        "last_deployed": "2026-04-18T14:22:00Z",
    },
    {
        "name": "events-db",
        "type": "Cloud SQL",
        "region": "us-east1",
        "status": "warning",
        "tier": "db-custom-8-32768",
        "storage": "500 GB SSD",
        "connections": 195,
        "version": "PostgreSQL 16",
        "issue": "Storage usage at 92%",
    },
    {
        "name": "analytics-pipeline",
        "type": "Cloud Run",
        "region": "us-west1",
        "status": "error",
        "cpu": "2 vCPU",
        "memory": "4 GiB",
        "instances": 0,
        "url": "https://analytics-pipeline-ghi789.run.app",
        "last_deployed": "2026-04-10T16:45:00Z",
        "issue": "CrashLoopBackOff: OOM killed",
    },
]

def get_resources() -> list[dict]:
    """Get all cloud resources in the current project."""
    return RESOURCES
EOF

2. 建立純文字 Agent (a2ui_agent/agent.py)

cat <<'EOF' > a2ui_agent/agent.py
from google.adk.agents import Agent
from .resources import get_resources

root_agent = Agent(
    model="gemini-3-flash-preview",
    name="cloud_dashboard",
    description="A cloud infrastructure assistant that reports on project resources.",
    instruction=(
        "You are a cloud infrastructure assistant. When users ask about their "
        "cloud resources, use the get_resources tool to fetch the current state. "
        "Summarize the results clearly in plain text."
    ),
    tools=[get_resources],
)
EOF

3. 啟動與測試

adk web --port 8080 --allow_origins "*" --reload_agents

開啟網頁預覽(通訊埠 8080),發送提示詞 What's running in my project?,觀察純文字條列回傳。測試完畢後在終端機按 Ctrl + C


5. 第二階段:動態生成 A2UI JSON

使用 A2uiSchemaManager 自動組裝 Prompt,讓模型學會 18 個基礎元件並輸出符合格式的 JSON 陣列。

覆寫更新 a2ui_agent/agent.py

cat <<'EOF' > a2ui_agent/agent.py
from google.adk.agents import Agent
from a2ui.schema.manager import A2uiSchemaManager
from a2ui.basic_catalog.provider import BasicCatalog
from .resources import get_resources

schema_manager = A2uiSchemaManager(
    version="0.8",
    catalogs=[BasicCatalog.get_config("0.8")],
)

instruction = schema_manager.generate_system_prompt(
    role_description=(
        "You are a cloud infrastructure assistant. When users ask about "
        "their cloud resources, use the get_resources tool to fetch the "
        "current state."
    ),
    workflow_description=(
        "Analyze the user's request and return structured UI when appropriate."
    ),
    ui_description=(
        "Use cards for resource summaries, rows and columns for comparisons, "
        "icons for status indicators, and buttons for drill-down actions. "
        "Do NOT use markdown formatting in text values. Use the usageHint "
        "property for heading levels instead. "
        "Respond ONLY with the A2UI JSON array. Do NOT include any text "
        "outside the JSON. Put all explanations into Text components."
    ),
    include_schema=True,
    include_examples=True,
)

root_agent = Agent(
    model="gemini-3-flash-preview",
    name="cloud_dashboard",
    description="A cloud infrastructure assistant that renders rich A2UI interfaces.",
    instruction=instruction,
    tools=[get_resources],
)
EOF


6. 第三階段:渲染 A2UI 互動式元件

建立後處理回呼(Callback),將模型輸出的 JSON 字串轉換為前端渲染器可辨識的 DataPart 二進位封包。

1. 建立後處理工具模組 (a2ui_agent/a2ui_utils.py)

cat <<'EOF' > a2ui_agent/a2ui_utils.py
import json
import re
from google.genai import types
from google.adk.agents.callback_context import CallbackContext
from google.adk.models.llm_response import LlmResponse

def _wrap_a2ui_part(a2ui_message: dict) -> types.Part:
    """將 A2UI 訊息封裝為 adk web 可渲染的 DataPart"""
    datapart_json = json.dumps({
        "kind": "data",
        "metadata": {"mimeType": "application/json+a2ui"},
        "data": a2ui_message,
    })
    blob_data = (
        b"<a2a_datapart_json>"
        + datapart_json.encode("utf-8")
        + b"</a2a_datapart_json>"
    )
    return types.Part(
        inline_data=types.Blob(
            data=blob_data,
            mime_type="text/plain",
        )
    )

def a2ui_callback(
    callback_context: CallbackContext,
    llm_response: LlmResponse,
) -> LlmResponse | None:
    """攔截文字 JSON 並轉換為前端渲染結構"""
    if not llm_response.content or not llm_response.content.parts:
        return None

    for part in llm_response.content.parts:
        if not part.text:
            continue
        text = part.text.strip()
        if not text:
            continue
        if not any(k in text for k in ("beginRendering", "surfaceUpdate", "dataModelUpdate")):
            continue

        # 移除 markdown 代碼標記
        if text.startswith("```"):
            text = text.split("\n", 1)[-1]
        if text.endswith("```"):
            text = text[:-3].strip()

        # 尋找 JSON 開頭位置
        json_start = None
        for i, ch in enumerate(text):
            if ch in ("[", "{"):
                json_start = i
                break
        if json_start is None:
            continue

        json_text = text[json_start:]
        try:
            parsed, _ = json.JSONDecoder().raw_decode(json_text)
        except json.JSONDecodeError:
            try:
                fixed = "[" + re.sub(r'\}\s*\{', '},{', json_text) + "]"
                parsed, _ = json.JSONDecoder().raw_decode(fixed)
            except json.JSONDecodeError:
                continue

        if not isinstance(parsed, list):
            parsed = [parsed]

        a2ui_keys = {"beginRendering", "surfaceUpdate", "dataModelUpdate", "deleteSurface"}
        a2ui_messages = [msg for msg in parsed if isinstance(msg, dict) and any(k in msg for k in a2ui_keys)]
        if not a2ui_messages:
            continue

        new_parts = [_wrap_a2ui_part(msg) for msg in a2ui_messages]
        return LlmResponse(
            content=types.Content(role="model", parts=new_parts),
            custom_metadata={"a2a:response": "true"},
        )

    return None
EOF

2. 註冊回呼函式至 Agent (a2ui_agent/agent.py)

cat <<'EOF' > a2ui_agent/agent.py
from google.adk.agents import Agent
from a2ui.schema.manager import A2uiSchemaManager
from a2ui.basic_catalog.provider import BasicCatalog
from .resources import get_resources
from .a2ui_utils import a2ui_callback

schema_manager = A2uiSchemaManager(
    version="0.8",
    catalogs=[BasicCatalog.get_config("0.8")],
)

instruction = schema_manager.generate_system_prompt(
    role_description=(
        "You are a cloud infrastructure assistant. When users ask about "
        "their cloud resources, use the get_resources tool to fetch the "
        "current state."
    ),
    workflow_description=(
        "Analyze the user's request and return structured UI when appropriate."
    ),
    ui_description=(
        "Use cards for resource summaries, rows and columns for comparisons, "
        "icons for status indicators, and buttons for drill-down actions. "
        "Do NOT use markdown formatting in text values. Use the usageHint "
        "property for heading levels instead. "
        "Respond ONLY with the A2UI JSON array. Do NOT include any text "
        "outside the JSON. Put all explanations into Text components."
    ),
    include_schema=True,
    include_examples=True,
)

root_agent = Agent(
    model="gemini-3-flash-preview",
    name="cloud_dashboard",
    description="A cloud infrastructure assistant that renders rich A2UI interfaces.",
    instruction=instruction,
    tools=[get_resources],
    after_model_callback=a2ui_callback,
)
EOF


7. 驗證與多情境互動測試

1. 釋放通訊埠並啟動伺服器

fuser -k 8080/tcp || kill -9 $(lsof -t -i:8080)
adk web --port 8080 --allow_origins "*" --reload_agents

2. 介面測試驗證
在預覽視窗重新整理後點擊 +New Session,依序測試三種不同意圖的提示詞:

  • 全面概覽:輸入 What's running in my project?

  • 呈現包含服務名稱、規格、URL 連結與運行狀態的完整 Card 列表。

  • 告警聚焦:輸入 Does anything need my attention?

  • 自動過濾正常服務,僅針對警告(Cloud SQL 儲存空間達 92%)與錯誤(Cloud Run OOM CrashLoopBackOff)生成醒目的告警排版。

  • 動態表單:輸入 I need to deploy a new service

  • 動態生成包含服務名稱文字欄位、規格下拉選單及部署動作按鈕的互動表單。


8. 常見故障排除

  • 通訊埠衝突 (Errno 98 Address already in use)
    若先前執行的處理程序未正常結束,執行以下指令強制釋放:
fuser -k 8080/tcp || kill -9 $(lsof -t -i:8080)

  • 憑證缺失錯誤 (ValueError: No API key was provided)
    表示環境變數遺失 Vertex AI 旗標或 ADC 尚未建立,依序執行:
export GOOGLE_CLOUD_PROJECT=test20260902001
export GOOGLE_CLOUD_LOCATION=global
export GOOGLE_GENAI_USE_VERTEXAI=True
gcloud auth application-default login --no-launch-browser


9. 正式環境前端整合指南

本實驗使用 adk web 的內建渲染器進行驗證。於實際生產架構中,後端 Agent 程式碼完全不需更動,前端僅需導入對應生態系的官方 A2UI 渲染套件:

平台 / 框架 官方套件名稱 安裝指令
React @a2ui/react npm install @a2ui/react
Lit @a2ui/lit npm install @a2ui/lit
Angular @a2ui/angular npm install @a2ui/angular
Flutter (行動/桌面端) Flutter GenUI SDK 透過 Flutter pub 依賴引入

10. 環境清理

測試完成後可終止本機伺服器與清理雲端專案:

1. 終止伺服器:在 Cloud Shell 終端機按下 Ctrl + C

2. (選用)刪除測試專案

gcloud projects delete test20260902001 --quiet

以下是英文的筆記

Comprehensive Guide to Building Declarative Agentic User Interfaces with ADK and A2UI

1. Executive Summary and Architecture Overview

Modern generative artificial intelligence applications have historically been constrained by the limitations of conversational text interfaces. While Large Language Models (LLMs) excel at reasoning, contextual understanding, and semantic synthesis, delivering output as unformatted plain text or raw markdown places substantial cognitive overhead on human users. In enterprise environments—especially across cloud infrastructure monitoring, database administration, and distributed service management—operational efficiency demands structured, scannable, and actionable visual representations.

The combination of the Agent Development Kit (ADK) and the Agent-to-User Interface (A2UI) protocol bridges this architectural divide. Rather than producing static blocks of prose, autonomous agents leverage declarative UI specifications to compose dynamic, rich, and contextually grounded frontend experiences.

A2UI decouples visualization from business logic by introducing an intermediate layout representation governed by 18 universal primitives and 3 foundational message types. The backend agent remains agnostic of the target rendering environment, emitting structured messages that describe interface hierarchies, component attributes, and reactive data models. The client application—whether executing within a browser via React, Lit, or Angular, or natively on mobile and desktop runtimes via Flutter—interprets this protocol to instantiate native components. Consequently, engineering teams eliminate the need to implement bespoke frontend routes or static templates for every specialized tool query or multi-step agent workflow.

+-------------------------------------------------------------------------------+
|                                 Agent Backend                                 |
|                                                                               |
|  +---------------------+       +---------------------+                        |
|  | Mock Data / Tools   | ----> | LLM Reasoning       |                        |
|  | (resources.py)      |       | (gemini-3-flash)    |                        |
|  +---------------------+       +---------------------+                        |
|                                           |                                   |
|                                           v                                   |
|                                +---------------------+                        |
|                                | A2UI Schema Manager |                        |
|                                +---------------------+                        |
|                                           |                                   |
|                                           v                                   |
|                              +-------------------------+                      |
|                              | Post-Processing / Hook  |                      |
|                              | (a2ui_callback)         |                      |
|                              +-------------------------+                      |
+-------------------------------------------|-----------------------------------+
                                            | A2UI Messages (JSON / DataPart)
                                            v
+-------------------------------------------------------------------------------+
|                               Frontend Client                                 |
|                                                                               |
|    +-------------------+    +--------------------+    +--------------------+  |
|    |  beginRendering   |    |   surfaceUpdate    |    |  dataModelUpdate   |  |
|    |  (Canvas / Root)  |    |  (Component Tree)  |    |  (Reactive State)  |  |
|    +-------------------+    +--------------------+    +--------------------+  |
|                                       |                                       |
|                                       v                                       |
|                      +---------------------------------+                      |
|                      | A2UI Native Renderer Engine     |                      |
|                      | (ADK Dev UI, React, Flutter)    |                      |
|                      +---------------------------------+                      |
|                                       |                                       |
|                                       v                                       |
|                      +---------------------------------+                      |
|                      | Interactive Dynamic Dashboard   |                      |
|                      +---------------------------------+                      |
+-------------------------------------------------------------------------------+


2. Theoretical Framework: The A2UI Protocol

The A2UI specification is built upon three pillars: Message Segregation, Flat Component Graphing, and a Constrained Primitives Catalog. Understanding these mechanics is essential for developing performant, deterministic, and error-tolerant agent interfaces.

The Three Message Archetypes

Every A2UI transmission consists of an array containing one or more declarative payloads belonging to three discrete schemas:

  1. beginRendering
    Defines the viewport lifecycle and declares the top-level structural anchor. It instructs the client renderer to initialize a named surface and designate an entry root node:
{
  "beginRendering": {
    "surfaceId": "default",
    "root": "main-column"
  }
}

  1. surfaceUpdate
    Supplies the layout composition graph. Unlike conventional DOM structures or HTML trees, A2UI avoids recursive JSON nesting. Instead, components are defined in an adjacency list / flat array. Parent components hold reference arrays pointing to child IDs. This prevents deep recursion parse limits, allows selective element re-rendering, and drastically reduces token overhead during LLM generation:
{
  "surfaceUpdate": {
    "surfaceId": "default",
    "components": [
      {
        "id": "main-column",
        "component": {
          "Column": {
            "children": {
              "explicitList": ["header-title", "service-card"]
            }
          }
        }
      },
      {
        "id": "header-title",
        "component": {
          "Text": {
            "text": {"literalString": "Production Workloads"},
            "usageHint": "h1"
          }
        }
      },
      {
        "id": "service-card",
        "component": {
          "Card": {
            "child": "service-label"
          }
        }
      },
      {
        "id": "service-label",
        "component": {
          "Text": {
            "text": {"path": "service_name"}
          }
        }
      }
    ]
  }
}

  1. dataModelUpdate
    Decouples interface hierarchy from volatile runtime state. Visual components bind to reactive references via JSON pointer paths ({"path": "key_name"}). The agent can publish state updates independently of structural updates:
{
  "dataModelUpdate": {
    "surfaceId": "default",
    "contents": [
      {"key": "service_name", "valueString": "auth-service"},
      {"key": "system_status", "valueString": "healthy"}
    ]
  }
}

The 18 Canonical Component Primitives

The A2UI design system enforces layout consistency across heterogeneous renderers by limiting generative assembly to 18 fundamental primitives across four functional categories:

Category Primitives Functional Description & Enterprise Use Case
Layout Card, Column, Row, List, Tabs, Divider, Modal Establishes spatial hierarchy, flexible grids, segmented tab views, and modal overlays. Used for multi-service dashboards, inspection drawers, and split-screen telemetry comparisons.
Display Text, Image, Icon, Video, AudioPlayer Renders typography with semantic usage hints (h1, body, caption), contextual state icons, network architecture schematics, and media telemetry.
Input TextField, DateTimeInput, MultipleChoice, CheckBox, Slider Captures end-user parameters directly within agent chat streams. Used for provisioning filters, replica scaling parameters, and deployment time-windows.
Action Button Exposes event dispatchers back to the agent backend (e.g., executing rollbacks, restarting pods, or drilling into logs).

3. Environment Preparation and Infrastructure Provisioning

Executing this workflow requires an active Google Cloud Platform (GCP) project with Google Cloud Shell or a local Linux environment provisioned with Python 3.12+. The primary cloud integration relies on the Vertex AI platform.

Step 1: Environment Configuration

Within the shell terminal, export the necessary context variables to instruct the Agent Development Kit to authenticate through Google Cloud Vertex AI:

# Set active project and regional configuration
gcloud config set project test20260902001

export GOOGLE_CLOUD_PROJECT=test20260902001
export GOOGLE_CLOUD_LOCATION=global
export GOOGLE_GENAI_USE_VERTEXAI=True

Step 2: Service API Enablement

Enable the Vertex AI (AI Platform) API within your project. This grants the ADK access to high-performance foundation models such as gemini-3-flash-preview:

gcloud services enable aiplatform.googleapis.com --project test20260902001

Step 3: Dependency Installation

Install the Agent Development Kit (google-adk) along with the A2UI Agent SDK (a2ui-agent-sdk). Update the local path variable to ensure installed executable binaries are globally accessible:

pip install -U google-adk a2ui-agent-sdk
export PATH="$HOME/.local/bin:$PATH"

Step 4: Workspace Organization

Establish a dedicated workspace directory and transition into it:

mkdir -p ~/test20260902001/a2ui_agent
cd ~/test20260902001


4. Phase I: Baseline Agent Implementation (Plain-Text Architecture)

To evaluate the paradigm shift introduced by A2UI, you first construct a traditional agent architecture. Traditional conversational agents return operational status as raw, unstructured text strings.

Defining Tools and Enterprise Mock Data

Create the resource telemetry provider at a2ui_agent/resources.py. This script simulates an enterprise monitoring API capturing real-time infrastructure state across Cloud Run serverless containers and Cloud SQL relational instances:

# a2ui_agent/resources.py

RESOURCES = [
    {
        "name": "auth-service",
        "type": "Cloud Run",
        "region": "us-west1",
        "status": "healthy",
        "cpu": "2 vCPU",
        "memory": "1 GiB",
        "instances": 3,
        "url": "https://auth-service-abc123.run.app",
        "last_deployed": "2026-04-18T14:22:00Z",
    },
    {
        "name": "events-db",
        "type": "Cloud SQL",
        "region": "us-east1",
        "status": "warning",
        "tier": "db-custom-8-32768",
        "storage": "500 GB SSD",
        "connections": 195,
        "version": "PostgreSQL 16",
        "issue": "Storage usage at 92%",
    },
    {
        "name": "analytics-pipeline",
        "type": "Cloud Run",
        "region": "us-west1",
        "status": "error",
        "cpu": "2 vCPU",
        "memory": "4 GiB",
        "instances": 0,
        "url": "https://analytics-pipeline-ghi789.run.app",
        "last_deployed": "2026-04-10T16:45:00Z",
        "issue": "CrashLoopBackOff: OOM killed",
    },
]

def get_resources() -> list[dict]:
    """Get all cloud resources in the current project.
    
    Returns a list of cloud infrastructure resources including their
    name, type, region, status, and type-specific details.
    Status is one of: healthy, warning, error. Resources with
    warning or error status include an 'issue' field describing
    the problem.
    """
    return RESOURCES

Implementing the Text-Based Agent

Create a2ui_agent/agent.py to bind the resource tool to an ADK Root Agent powered by the gemini-3-flash-preview model:

# a2ui_agent/agent.py

from google.adk.agents import Agent
from .resources import get_resources

root_agent = Agent(
    model="gemini-3-flash-preview",
    name="cloud_dashboard",
    description="A cloud infrastructure assistant that reports on project resources.",
    instruction=(
        "You are a cloud infrastructure assistant. When users ask about their "
        "cloud resources, use the get_resources tool to fetch the current state. "
        "Summarize the results clearly in plain text."
    ),
    tools=[get_resources],
)

Deploying and Evaluating Plain-Text Behavior

Launch the ADK Dev server from the workspace root:

adk web --port 8080 --allow_origins "*" --reload_agents

Open the web interface via Cloud Shell Web Preview (Port 8080). Select a2ui_agent and submit evaluation prompts:

  • What's running in my project?
  • Does anything need my attention?

The response generates dense paragraphs. While factually accurate, human triage is impeded: error states are buried within prose, URLs require manual copying, and status attributes lack visual categorization.


5. Phase II: Generating Structured A2UI Payloads

To replace prose with declarative interfaces, the LLM must be educated on the A2UI JSON schema without requiring manual prompt engineering of all 18 primitives. The A2uiSchemaManager automates this compilation.

Integrating the A2UI Schema Manager

Update a2ui_agent/agent.py to dynamically construct system prompts that bind schema definitions, catalog constraints, and few-shot formatting rules:

# a2ui_agent/agent.py

from google.adk.agents import Agent
from a2ui.schema.manager import A2uiSchemaManager
from a2ui.basic_catalog.provider import BasicCatalog
from .resources import get_resources

# Initialize schema manager targeting specification version 0.8
schema_manager = A2uiSchemaManager(
    version="0.8",
    catalogs=[BasicCatalog.get_config("0.8")],
)

# Compile dynamic instructions embedding role context and declarative constraints
instruction = schema_manager.generate_system_prompt(
    role_description=(
        "You are a cloud infrastructure assistant. When users ask about "
        "their cloud resources, use the get_resources tool to fetch the "
        "current state."
    ),
    workflow_description=(
        "Analyze the user's request and return structured UI when appropriate."
    ),
    ui_description=(
        "Use cards for resource summaries, rows and columns for comparisons, "
        "icons for status indicators, and buttons for drill-down actions. "
        "Do NOT use markdown formatting in text values. Use the usageHint "
        "property for heading levels instead. "
        "Respond ONLY with the A2UI JSON array. Do NOT include any text "
        "outside the JSON. Put all explanations into Text components."
    ),
    include_schema=True,
    include_examples=True,
)

root_agent = Agent(
    model="gemini-3-flash-preview",
    name="cloud_dashboard",
    description="A cloud infrastructure assistant that renders rich A2UI interfaces.",
    instruction=instruction,
    tools=[get_resources],
)

Inspecting Raw JSON Emission

With hot-reloading active (--reload_agents), return to the ADK Dev UI, initiate a +New Session, and re-submit:

What's running in my project?

The output transforms into a validated JSON array containing the triad of A2UI messages (beginRendering, surfaceUpdate, and dataModelUpdate). While the structural blueprint is complete, the ADK browser client defaults to rendering raw JSON text because it has not yet received the MIME-typed metadata required to invoke its native visual renderer.


6. Phase III: Intercepting and Rendering Native UI Components

To render A2UI payloads as interactive frontend components, output emitted by the model must be intercepted, cleansed of formatting fences, and wrapped inside an A2A (Agent-to-Agent / Agent-to-App) application/json+a2ui DataPart envelope.

Building the Interception Utility

Create a2ui_agent/a2ui_utils.py. This module defines an ADK after_model_callback pipeline that inspects model responses, extracts JSON constructs, handles malformed concatenations, and wraps elements into binary DataPart blobs:

# a2ui_agent/a2ui_utils.py

import json
import re
from google.genai import types
from google.adk.agents.callback_context import CallbackContext
from google.adk.models.llm_response import LlmResponse

def _wrap_a2ui_part(a2ui_message: dict) -> types.Part:
    """Encapsulates an A2UI message inside an ADK-compliant binary datapart."""
    datapart_json = json.dumps({
        "kind": "data",
        "metadata": {"mimeType": "application/json+a2ui"},
        "data": a2ui_message,
    })
    blob_data = (
        b"<a2a_datapart_json>"
        + datapart_json.encode("utf-8")
        + b"</a2a_datapart_json>"
    )
    return types.Part(
        inline_data=types.Blob(
            data=blob_data,
            mime_type="text/plain",
        )
    )

def a2ui_callback(
    callback_context: CallbackContext,
    llm_response: LlmResponse,
) -> LlmResponse | None:
    """Transforms raw textual A2UI JSON into structured, rendered components."""
    if not llm_response.content or not llm_response.content.parts:
        return None

    for part in llm_response.content.parts:
        if not part.text:
            continue
        text = part.text.strip()
        if not text:
            continue
        
        # Verify presence of core A2UI lifecycle keys
        if not any(k in text for k in ("beginRendering", "surfaceUpdate", "dataModelUpdate")):
            continue

        # Strip markdown syntax fences if accidentally produced by LLM
        if text.startswith("```"):
            text = text.split("\n", 1)[-1]
        if text.endswith("```"):
            text = text[:-3].strip()

        # Locate valid JSON opening delimiters
        json_start = None
        for i, ch in enumerate(text):
            if ch in ("[", "{"):
                json_start = i
                break
        if json_start is None:
            continue

        json_text = text[json_start:]
        
        # Resilient JSON deserialization with delimiter error recovery
        try:
            parsed, _ = json.JSONDecoder().raw_decode(json_text)
        except json.JSONDecodeError:
            try:
                fixed = "[" + re.sub(r'\}\s*\{', '},{', json_text) + "]"
                parsed, _ = json.JSONDecoder().raw_decode(fixed)
            except json.JSONDecodeError:
                continue

        if not isinstance(parsed, list):
            parsed = [parsed]

        a2ui_keys = {"beginRendering", "surfaceUpdate", "dataModelUpdate", "deleteSurface"}
        a2ui_messages = [msg for msg in parsed if isinstance(msg, dict) and any(k in msg for k in a2ui_keys)]
        
        if not a2ui_messages:
            continue

        # Convert each A2UI message into an ADK rendering part
        new_parts = [_wrap_a2ui_part(msg) for msg in a2ui_messages]
        return LlmResponse(
            content=types.Content(role="model", parts=new_parts),
            custom_metadata={"a2a:response": "true"},
        )

    return None

Registering the Callback with Root Agent

Update a2ui_agent/agent.py to attach a2ui_callback to the after_model_callback event hook:

# a2ui_agent/agent.py

from google.adk.agents import Agent
from a2ui.schema.manager import A2uiSchemaManager
from a2ui.basic_catalog.provider import BasicCatalog
from .resources import get_resources
from .a2ui_utils import a2ui_callback

schema_manager = A2uiSchemaManager(
    version="0.8",
    catalogs=[BasicCatalog.get_config("0.8")],
)

instruction = schema_manager.generate_system_prompt(
    role_description=(
        "You are a cloud infrastructure assistant. When users ask about "
        "their cloud resources, use the get_resources tool to fetch the "
        "current state."
    ),
    workflow_description=(
        "Analyze the user's request and return structured UI when appropriate."
    ),
    ui_description=(
        "Use cards for resource summaries, rows and columns for comparisons, "
        "icons for status indicators, and buttons for drill-down actions. "
        "Do NOT use markdown formatting in text values. Use the usageHint "
        "property for heading levels instead. "
        "Respond ONLY with the A2UI JSON array. Do NOT include any text "
        "outside the JSON. Put all explanations into Text components."
    ),
    include_schema=True,
    include_examples=True,
)

root_agent = Agent(
    model="gemini-3-flash-preview",
    name="cloud_dashboard",
    description="A cloud infrastructure assistant that renders rich A2UI interfaces.",
    instruction=instruction,
    tools=[get_resources],
    after_model_callback=a2ui_callback,
)


7. Interactive Verification and Intent Adaptation

With the callback pipeline operational, relaunch or refresh the ADK Web interface to observe dynamic interface generation across distinct user prompts.

Test Case 1: General Infrastructure Survey

User Query:

What's running in my project?

Interface Result:
The agent synthesizes a multi-column card layout. Each cloud workload (auth-service, events-db, analytics-pipeline) is presented as an encapsulated Card primitive containing:

  • System title styled with usageHint: "h2".
  • Visual Icon representations mapping healthy, warning, or error conditions.
  • Resource configuration rows detailing allocated vCPUs, RAM, and instance counts.
  • Interactive Button components linking directly to service endpoint URLs.

Test Case 2: Filtered Exception Reporting

User Query:

Does anything need my attention?

Interface Result:
The agent filters out healthy workloads and alters layout composition. Instead of a general catalog, it displays an alert banner followed by priority containers focusing on:

  • Cloud SQL Alert: events-db highlighting 92% storage capacity limits.
  • Cloud Run CrashLoop: analytics-pipeline highlighting OOM termination status.
  • Interactive remediation action buttons (e.g., "Increase Disk Size", "Restart Container").

Test Case 3: Interactive Provisioning Workflow

User Query:

I need to deploy a new service

Interface Result:
Without requiring changes to backend Python logic, the agent composes a provisioning form using input primitives (TextField for service name, MultipleChoice for region selection, Slider for concurrency limits, and a primary submit Button). This demonstrates the power of the 18 primitives: UI layouts adapt dynamically to user intent.


8. Troubleshooting and Operational Best Practices

When operating ADK and A2UI services inside cloud shell environments, several common system errors may occur:

Error: address already in use (Port 8080 Conflict)

Occurs when an orphaned background process retains the bind port:

fuser -k 8080/tcp || kill -9 $(lsof -t -i:8080)

Error: ValueError: No API key was provided

Occurs when SDK client libraries fall back to Google AI Studio authentication instead of Vertex AI:

# Explicitly enforce Vertex AI routing
export GOOGLE_GENAI_USE_VERTEXAI=True
export GOOGLE_CLOUD_PROJECT=test20260902001
export GOOGLE_CLOUD_LOCATION=global

# Refresh Application Default Credentials
gcloud auth application-default login --no-launch-browser

Component Formatting Constraints

  • Markdown Avoidance: A2UI renderers expect plain strings within Text primitives. Applying markdown (e.g., # Header, **bold**) results in unparsed markdown literals. Always instruct the model to use the usageHint attribute (h1, h2, body, caption) for typographic hierarchy.
  • Flat ID Cross-Referencing: Ensure child identifiers referenced in explicitList match exact unique IDs defined within the components array.

9. Production Architecture: Integrating Enterprise Client Renderers

While adk web provides a rapid local environment for development and prototyping, production deployment involves rendering A2UI streams within enterprise frontend frameworks.

The agent architecture remains completely unchanged; production clients consume the same A2UI JSON payloads using official client packages:

+-----------------------------------------------------------------------------------+
|                           Target Production Ecosystem                             |
|                                                                                   |
|  +--------------------+  +--------------------+  +--------------------+  +------+ |
|  |    React (Web)     |  |     Lit (Web)      |  |   Angular (Web)    |  |Flutter| |
|  |  @a2ui/react       |  |  @a2ui/lit         |  |  @a2ui/angular     |  |GenUI | |
|  +--------------------+  +--------------------+  +--------------------+  +------+ |
+-----------------------------------------------------------------------------------+

Package Ecosystem

  • React Platforms:
npm install @a2ui/react

Provides native React functional components that deserialize surfaceUpdate graphs directly into React DOM trees.

  • Lit Web Components:
npm install @a2ui/lit

Delivers lightweight, framework-agnostic custom elements for embedding within existing micro-frontends.

  • Angular Enterprise Applications:
npm install @a2ui/angular

Offers declarative Angular modules with native reactive form bindings for input primitives.

  • Cross-Platform Mobile and Desktop:
    Integrates via the Flutter GenUI SDK, compiling A2UI messages directly into native Flutter widget trees on iOS, Android, macOS, and Windows.

10. Teardown and Cleanup Procedures

To maintain infrastructure hygiene and eliminate resource costs following codelab completion, execute cleanup routines:

Stop Local Runtime Daemons

Terminate the ADK web development server by sending a SIGINT signal (Ctrl + C) within the running Cloud Shell terminal session.

Delete Cloud Project Infrastructure

If the Google Cloud project was created solely for this codelab, delete the project and all associated Vertex AI metadata:

gcloud projects delete test20260902001 --quiet


11. Conclusion and Architectural Insights

Declarative agent interfaces represent an evolution in human-agent collaboration. By decoupling backend agent reasoning from frontend visual rendering, engineering organizations unlock key architectural advantages:

  1. Zero-Code UI Evolution: Agents compose novel, context-appropriate dashboards, inspection panels, and interactive forms on the fly without requiring frontend engineering cycles for every new capability.
  2. Unified Core Logic Across Clients: The same backend ADK Agent and tool definitions simultaneously drive React web applications, Flutter mobile applications, and command consoles.
  3. Optimized Token Efficiency: A2UI's flat component model and data-model separation avoid recursive payload duplication and minimize context window consumption.
  4. Enterprise Guardrails: By restricting output composition to 18 validated primitives, organizations guarantee visual consistency, accessibility compliance, and design-system integrity across all generative user experiences.

上一篇
OpenPassExam 建置開發並發布到 Google Cloud Run 全過程
下一篇
使用今日發表的全新 Gemini 3.8 Flash 再次對照需求和規格文件進行疊代
系列文
將考國際證照的應用程式變成開源64
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言