A2UI-Android:讓 AI 透過 JSON 動態產生 Jetpack Compose 原生介面
過去我們在 Android App 中整合大型語言模型時,通常都是讓 AI 回傳一段文字。
A2UI-Android:讓 AI 透過 JSON 動態產生 Jetpack Compose 原生介面
過去我們在 Android App 中整合大型語言模型時,通常都是讓 AI 回傳一段文字。
例如使用者詢問:
幫我規劃一趟三天兩夜的台南旅行。
AI 可能會回覆:
- 第一天安排安平與國華街
- 第二天前往奇美博物館
- 第三天安排市區咖啡廳與伴手禮
這樣的結果沒有問題,但從 App 的使用體驗來看,它仍然只是一段文字。
更理想的方式,應該是直接顯示:
- 每日行程卡片
- 景點圖片
- 預估停留時間
- 行程完成狀態
- 預算輸入欄位
- 飲食偏好選項
- 「加入行程」按鈕
- 「重新產生」按鈕
問題在於,這個畫面不一定能事先預測。
不同使用者、不同需求,可能需要完全不同的介面。開發者也不可能為 AI 的每一種回答,事先建立一個固定畫面。
這正是 A2UI 想解決的問題。
什麼是 A2UI?
A2UI 的全名是:
Agent-to-User Interface
它是一套讓 AI Agent 使用宣告式格式描述使用者介面的協定。
簡單來說,Agent 不再只能回傳文字,也可以回傳一份描述 UI 的結構化資料。
客戶端收到資料後,再使用自己的原生 UI Framework 進行渲染。
在 Android 上,可以將它理解成:
AI Agent
↓
A2UI JSON
↓
Android Renderer
↓
Jetpack Compose Native UI
因此,A2UI 的核心概念很像:
JSON to Compose UI
AI 或後端負責描述畫面結構,Android App 則負責把這份描述轉換成真正的 Jetpack Compose 元件。
A2UI-Android 是什麼?
A2UI 本身是一套跨平台的 UI 描述協定,但每個平台仍然需要自己的 Renderer。
A2UI-Android 就是一套以 Jetpack Compose 實作的 Android Renderer。
它負責處理以下流程:
接收 A2UI JSON
↓
解析 Component 類型
↓
找到對應的 Compose 元件
↓
建立元件階層
↓
綁定資料與 Action
↓
顯示原生 Android UI
假設 Agent 傳來一份概念上類似以下內容的 JSON:
{
"component": "Button",
"text": "加入行程",
"action": {
"name": "add_to_plan"
}
}
Android Renderer 收到後,可以將它對應到概念上類似以下的 Compose 程式:
Button(
onClick = {
dispatchAction("add_to_plan")
}
) {
Text("加入行程")
}
使用者最後看到的不是 HTML,也不是 WebView,而是真正由 Jetpack Compose 建立的原生按鈕。
它並不是讓 AI 產生 Kotlin 程式碼
這是理解 A2UI 時非常重要的一點。
A2UI 並不是讓 AI 產生一段 Kotlin 程式碼,再交給手機動態編譯與執行。
它也不是讓 AI 直接控制 App 裡的任意程式邏輯。
AI 產生的是一份受限制的 UI 描述:
使用 Column
加入一段 Text
加入一張 Card
加入兩個 Button
將 Button 綁定到指定 Action
App 必須先準備好一組允許使用的 Component Catalog,例如:
Text
Button
Card
Row
Column
TextField
Switch
Slider
Image
List
Tabs
Agent 只能從這些已註冊的元件中選擇與組合。
因此,更精準的說法不是:
AI 動態創造一個全新的 Compose 元件。
而是:
AI 動態組合 App 已經提供並信任的 Compose 元件。
這有點像將 Compose Component Registry、Server-Driven UI 與 AI Agent 結合在一起。
A2UI 與 Server-Driven UI 有什麼關係?
熟悉大型 App 架構的 Android 工程師,對 Server-Driven UI 應該不陌生。
傳統 Server-Driven UI 的流程通常是:
Server
↓
UI JSON
↓
Android Renderer
↓
Native UI
後端決定畫面上要顯示哪些元件,App 則根據 JSON 建立對應的 View 或 Compose UI。
A2UI 的概念非常接近 Server-Driven UI。
主要差異在於,傳統 Server-Driven UI 的 JSON 通常由後端工程師預先定義;A2UI 的 UI 描述則可能由 AI Agent 根據當下的對話內容即時產生。
傳統流程:
工程師定義 UI Schema
→ Server 回傳固定 JSON
→ App 顯示畫面
A2UI 流程:
使用者提出需求
→ Agent 理解需求
→ Agent 產生 UI 描述
→ App 顯示對應畫面
例如使用者說:
幫我建立一份出國行李檢查表。
Agent 可以產生包含以下元件的 UI:
- 護照 Checkbox
- 行動電源 Checkbox
- 轉接頭 Checkbox
- 行李重量 Slider
- 出發日期 Date Picker
- 完成進度
- 儲存按鈕
這個畫面不需要事先存在於 App 中。
只要所有需要的基礎元件都已經存在於 Component Catalog,Agent 就能動態組合出適合目前任務的介面。
為什麼不直接讓 AI 產生 HTML?
既然 AI 很擅長產生 HTML、CSS 與 JavaScript,為什麼不直接將產生的網頁放進 WebView?
技術上當然可以,但這會帶來一些問題。
1. 安全性
直接執行 AI 產生的 JavaScript,代表 AI 可能產生具有不可預期行為的程式碼。
A2UI 傳遞的是資料,而不是可執行程式碼。
App 只會渲染自己允許的元件,並處理自己註冊的 Action。
2. 原生體驗
WebView 裡的按鈕,終究是網頁元件。
透過 Compose Renderer 建立的按鈕,則可以直接使用 App 現有的:
- Material Theme
- Typography
- Shape
- Color Scheme
- 無障礙設定
- 深色模式
- Android 互動行為
因此,動態產生的 UI 仍能維持與 App 一致的設計語言。
3. App 能掌握控制權
Agent 可以要求顯示:
save_travel_plan
但真正如何儲存行程,仍由 Android App 決定。
例如:
when (action.name) {
"save_travel_plan" -> {
travelPlanRepository.save()
}
}
Agent 只能提出 Action,不能直接操作 Android 系統或 App 內部資料。
UI 與業務邏輯如何分工?
A2UI 比較適合負責「介面描述」,而不是完整的 App 業務邏輯。
可以將責任分成兩部分。
Agent 負責
- 決定畫面需要哪些元件
- 組合元件階層
- 填入文字與資料
- 描述輸入欄位
- 指定 Action 名稱
- 根據對話更新畫面
Android App 負責
- 網路請求
- 權限檢查
- 資料庫存取
- 檔案處理
- 系統 API
- 身分驗證
- 支付流程
- Action 白名單
- 輸入資料驗證
- 最終安全檢查
例如 Agent 產生:
{
"component": "Button",
"text": "刪除這份行程",
"action": {
"name": "delete_plan",
"planId": "trip_001"
}
}
App 收到 Action 後,不應直接刪除資料,而是先確認:
- Action 是否在允許清單中
planId是否有效- 使用者是否確認
- 該筆資料是否屬於目前使用者
- 是否需要提供復原機制
A2UI 解決的是 UI 動態性,不會自動解決應用程式安全問題。
對 Android App 有哪些應用場景?
AI 旅遊規劃
使用者可以直接輸入:
幫我規劃一趟預算兩萬元、偏美食與攝影的三天兩夜旅行。
Agent 可以根據需求產生:
- 每日行程卡片
- 預算分配
- 景點圖片
- 用餐選項
- 攝影建議
- 行程完成狀態
- 偏好調整選項
- 儲存計畫按鈕
不同使用者的需求不同,Agent 也可以產生不同結構的 UI,而不只是固定格式的文字回答。
動態表單
使用者可以告訴 AI:
幫我建立一份 Android Bug 回報表。
Agent 可以組合出:
- 裝置型號
- Android 版本
- App 版本
- 問題描述
- 重現步驟
- 嚴重程度
- Log 上傳
- 提交按鈕
表單內容也可以依照問題類型自動調整。
App 內部 Debug 工具
對開發團隊而言,A2UI 也很適合用來建立內部工具。
例如讓 Agent 根據目前系統狀態產生:
- API 狀態面板
- Worker 執行狀態
- Feature Flag
- 記憶體使用量
- Log 篩選器
- Cache 清除按鈕
- 測試環境切換介面
這類工具變化速度很快,而且通常不需要提供給一般使用者,適合先作為 A2UI 的實驗場景。
個人化學習介面
假設使用者對 AI 說:
幫我設計一個每天練習英文口說的計畫。
Agent 可以建立:
- 今日練習主題
- 單字列表
- 句型卡片
- 練習進度
- 難度 Slider
- 完成 Checkbox
- 錄音按鈕
- 下一題按鈕
如果使用者回答得不錯,Agent 可以調高難度;如果答題困難,也可以重新生成較簡單的介面。
自訂 Compose 元件才是最有價值的部分
A2UI 不一定只能使用通用的 Button、Text 或 Card。
Android App 也可以建立自己的領域元件,例如:
TravelPlanCard
HotelOptionCard
RestaurantCard
WeatherWarningCard
BudgetSummaryCard
StudyProgressCard
BugReportCard
接著將這些元件註冊到 Catalog。
以旅遊規劃 App 為例,可以建立:
@Composable
fun TravelPlanCard(
title: String,
description: String,
duration: String,
estimatedCost: String,
onAddToPlan: () -> Unit
)
Agent 不需要知道這個元件內部如何實作。
它只需要產生概念上類似:
{
"component": "TravelPlanCard",
"title": "安平老街半日行程",
"description": "適合街拍、美食與歷史建築",
"duration": "4 小時",
"estimatedCost": "NT$800",
"action": {
"name": "add_to_plan",
"itemId": "plan_item_1"
}
}
Renderer 再將它對應到 App 內真正的 TravelPlanCard。
這樣不但能保留品牌設計與原生體驗,也能限制 Agent 只能使用經過設計與測試的領域元件。
A2UI 不代表所有畫面都應該動態產生
A2UI 很有想像空間,但不代表整個 App 都應該改成由 AI 產生。
以下畫面通常仍適合使用固定 UI:
- 登入
- 註冊
- 付款確認
- 權限說明
- 隱私設定
- 帳號管理
- 法律同意流程
- 高度品牌化的首頁
- 需要嚴格測試的操作流程
原因很簡單:固定 UI 比較容易進行完整的測試、無障礙驗證與產品體驗控制。
A2UI 更適合用在:
- AI 回答結果
- 動態表單
- 輔助操作
- 個人化 Dashboard
- 搜尋結果
- Agent 工具介面
- 低風險的任務型畫面
- 公司內部工具
因此,比較合理的架構不是「AI 取代整個 App UI」,而是:
固定 App Shell
+ 固定核心流程
+ 可控的 Agent-Generated UI 區域
例如 App 的頂部導覽列、底部 Navigation Bar 與主要頁面仍由開發團隊控制;頁面中間的一塊內容區域,才交給 A2UI Renderer 動態產生。
導入時應注意哪些風險?
UI 結果不一定穩定
LLM 即使收到相同需求,也可能產生不同的元件排列方式。
正式產品需要加入:
- JSON Schema 驗證
- Component 白名單
- 最大元件數量
- 最大階層深度
- 字數限制
- 不支援元件的 fallback
- 解析錯誤處理
- Loading 與 Retry 狀態
Action 必須受到限制
不能因為 Agent 回傳:
delete_account
App 就直接刪除帳號。
所有 Action 都應經過明確註冊:
val allowedActions = setOf(
"open_detail",
"add_to_plan",
"save_form",
"regenerate_content"
)
高風險操作還需要額外確認。
設計一致性
如果 Agent 可以自由指定每個元件的顏色、大小與間距,很容易產生不一致的畫面。
比較合理的方式是讓 Agent 描述語意:
primary
secondary
warning
error
compact
comfortable
再由 App Theme 決定實際顏色與尺寸。
不要讓 Agent 直接指定任意 Hex Color 或 dp 數值。
效能
動態 UI 必須避免:
- 每次資料更新都重建整棵 UI Tree
- 過深的 Component 階層
- 大量無 Key 的 List Item
- 圖片無限制載入
- 頻繁更新造成 Recomposition
- 未受到限制的動畫
- 過大的 JSON Payload
即使是 AI 產生的 UI,仍然需要遵守 Compose 的狀態管理與效能原則。
A2UI 與 Function Calling 的關係
Function Calling 解決的是:
AI 要求 App 或 Server 執行什麼功能。
A2UI 解決的是:
AI 要求 App 顯示什麼介面。
兩者可以搭配使用。
使用者提出需求
↓
Agent 呼叫 Function
↓
Server 查詢資料
↓
Agent 整理結果
↓
Agent 產生 A2UI JSON
↓
Android 顯示 Compose UI
↓
使用者點擊 Action
↓
App 執行對應功能
例如使用者想規劃旅遊行程:
- Agent 使用 Function Calling 查詢景點、天氣與住宿資料。
- Server 回傳整理後的結果。
- Agent 產生每日行程卡片與預算摘要。
- Android 透過 A2UI Renderer 顯示畫面。
- 使用者點擊「加入行程」。
- Android App 將內容儲存到本機或雲端。
Function Calling 與 A2UI 分別處理「能力」與「呈現」,兩者結合後,才會形成較完整的 Agent App 體驗。
我的看法
A2UI-Android 可以被理解成一個:
為 AI Agent 設計的 Server-Driven Compose UI Renderer。
它真正有價值的地方,不只是「用 JSON 產生 UI」。
傳統 Server-Driven UI 早就能做到這件事。
更重要的是,它嘗試建立一套 Agent 與 Client 之間的共通 UI 語言,讓 AI 不再只能輸出文字,而是能根據目前任務選擇更適合的互動方式。
當問題適合閱讀時,Agent 可以回傳文字。
當問題適合比較時,Agent 可以產生卡片或表格。
當任務需要輸入時,Agent 可以產生表單。
當使用者需要做決策時,Agent 可以顯示選項與操作按鈕。
這會讓 AI 從單純的 Chatbot,逐漸變成真正能夠組織使用者介面的 Agent。
不過,A2UI 仍然不應被理解成「讓 AI 完全接管 App」。
比較安全且實際的做法,是將它限制在一個受控區域中:
- 只能使用允許的元件
- 只能觸發允許的 Action
- 不能執行任意程式碼
- 高風險操作必須經過確認
- App 保留最終控制權
在這個前提下,A2UI-Android 是一個相當值得 Android 開發者研究的方向。
它所代表的,可能不只是另一套 JSON UI 框架,而是未來 AI Agent 與 Native App 互動方式的一種雛形。
結論
用一句 Android 工程師容易理解的方式總結:
A2UI-Android 是一套將 Agent 產生的宣告式 JSON,轉換成 Jetpack Compose 原生 UI 的 Renderer。
它不是 WebView,也不是動態執行 AI 產生的 Kotlin 程式碼。
它是在 App 預先提供的安全元件範圍內,讓 AI 根據任務動態組合介面。
JSON 描述 UI
+ Compose Component Catalog
+ Action Dispatcher
+ Android 業務邏輯
= Agent-Driven Native UI
對於正在研究 Gemini、Function Calling、AI Agent 與 Jetpack Compose 的 Android 開發者而言,A2UI-Android 是一個很適合拿來製作 PoC 的開源專案。
第一個實驗不需要太複雜。
可以先從一個簡單需求開始:
讓 Gemini 根據使用者輸入,動態產生一份旅遊規劃表,包含文字、日期、預算、偏好選項與儲存按鈕。
當 AI 不再只是回答問題,而是能根據問題建立最適合的操作介面時,Android App 與 AI 的整合方式,也會開始出現新的可能性。
메타데이터
- post_id
- 494d69e1fd86
- slug
- a2ui-android-讓-ai-透過-json-動態產生-jetpack-compose-原生介面-494d69e1fd86
- url
- https://medium.com/@jefflin1982/a2ui-android-%E8%AE%93-ai-%E9%80%8F%E9%81%8E-json-%E5%8B%95%E6%85%8B%E7%94%A2%E7%94%9F-jetpack-compose-%E5%8E%9F%E7%94%9F%E4%BB%8B%E9%9D%A2-494d69e1fd86
- canonical_url
- https://medium.com/@jefflin1982/a2ui-android-%E8%AE%93-ai-%E9%80%8F%E9%81%8E-json-%E5%8B%95%E6%85%8B%E7%94%A2%E7%94%9F-jetpack-compose-%E5%8E%9F%E7%94%9F%E4%BB%8B%E9%9D%A2-494d69e1fd86
- author_url
- https://medium.com/@jefflin1982
- status
- ok
- fetched_at
- 2026-06-17 18:55:25