之前介紹過最好用的 [Flutter AI 測試框架](https://juejin.cn/post/7646696467931807807),然後有很多人在問有沒有原生平台的,剛好就看到了一个原生平台的開源測試框架 `agent-device`,甚至它還支援鴻蒙:
agent-device 是專門為 AI Agent 設計的裝置自動化 CLI 工具,可以類比 Vercel 的 agent-browser 是給 AI 用的瀏覽器自動化工具那樣,agent-device 是給 AI 用的 App 準備的標準化通道,例如:
sql 体验AI代码助手 代码解读复制代码agent-device apps --platform ios
agent-device open <app> --platform ios
agent-device snapshot -i
agent-device press @e12
agent-device diff snapshot -i
agent-device close
比如這裡的
snapshot -i,它會把目前 UI 轉成 Agent 更容易理解的結構化樹,同時給可互動元素生成@e1、@e2這種引用。
它最核心的能力是不需要透過截圖去辨識控制項,agent-device 是透過檢查真實 App UI,輸出無障礙存取樹(accessibility snapshot),然後將 UI 中的可互動元素映射為短引用(@e1, @e2, @e3),讓 AI 知道畫面上有什麼:
java 体验AI代码助手 代码解读复制代码agent-device snapshot -i
# @e1 [heading] "Settings"
# @e2 [button] "Sign In"
# @e3 [text-field] "Email"
這樣 AI 就可以直接用 @e3 操作「[text-field] "Email"」,不需要自己去讀座標,也不需要每次截圖分析,同時功能上支援:
甚至它還支援工作流回放,將 AI 探索過的互動錄製成 .ad 腳本,然後可以在 CI、本地、重複場景中回放執行,等效於 e2e 測試案例。
agent-device的場景其實就是:Agent 寫程式,之後安裝到對應裝置,然後 agent-device 打開 App,透過 snapshot 讀取目前 UI 結構,之後 Agent 根據@e1/@e2/selector操作 UI,然後得到「截圖/錄影/日誌/網路/效能」等證據存檔,最後再讓 Agent 根據測試結果修復問題。

當然,最最最最重要的是它是多平台支援:
整個流程大概如下圖所示:

如果拆開來看,整個架構大概如下:
open 一個裝置工作階段,後續所有命令在這個 session 上下文內執行(鎖住裝置資源、保持狀態)-i(interactive only)進一步只回傳可互動元素,而且用截圖做測試的方式真的超慢@e1~@eN 是每次 snapshot 後分配的臨時引用,命令可直接用 ref 而不是座標,降低 AI 犯錯機率,比如座標在捲動後就失效,但 ref 是具語義感知的Interactor 介面(tap, swipe, snapshot, screenshot...),核心調度層對介面編程,平台差異被隔離在各自的 platforms/ 目錄src/core/capabilities.ts 為每條命令定義哪些平台/裝置類型支援,防止 AI 呼叫不支援的命令,會在 admission 階段直接報錯而不是無意義地送到裝置當然,也存在一些平台限制,例如:
局限具體表現pinch/rotate 手勢僅支援 iOS Simulator + Android,HarmonyOS 不支援 pinch/rotateread(文字識別)只支援 iOS/Androidlogs/network/perfHarmonyOS 均不支援clipboardHarmonyOS 需要 app-level 授權,讀寫均可能失敗權限管理HarmonyOS 僅支援 grant,deny/reset 未實作然後在平台鏈路上,每個平台都有自己一套實作,從程式碼看,你會看到一套非常複雜的工作流程。
首先是 iOS,iOS 核心用的還是我們熟悉的 XCTest Runner,iOS 本身沒有對外開放的 UI 自動化 API 給外部程序使用,這裡 agent-device 的做法是:
在目標裝置上動態建構並執行一個 XCTest bundle(
.xctestrun),透過 XCTest 框架的XCUITest能力操控 UI,然後在這個 Runner 程序和 CLI 之間建立 TCP 通訊通道。

更具體的結構是:
runner-xctestrun.ts:動態生成 .xctestrun 設定檔(相當於 Xcode 的測試 scheme)、建構參數、signing 設定runner-session.ts:管理 Runner 程序的生命週期,啟動 xcodebuild test-without-building、等待 ready、保持 session 熱啟動runner-transport.ts:TCP 通訊層,發送 JSON 命令到 Runner,接收回應,逾時/重連處理runner-client.ts:命令執行的上層入口,完整的故障復原邏輯runner-contract.ts:Runner 通訊協議定義,命令類型列舉interactions.ts:將 tap/swipe/type 等抽象命令翻譯為 Runner 命令xml.ts:解析 XCTest 回傳的 Accessibility XML 快照simctl.ts:呼叫 xcrun simctl 操作模擬器(boot/shutdown/install)devicectl.ts:呼叫 Xcode 15+ 的 xcrun devicectl 操作真機perf.ts:透過 xcodebuild + Instruments XML 擷取效能資料(CPU/記憶體/幀率)這裡最有意思的就是 Runner 故障復原機制,由於 XCTest Runner 是一個獨立程序,TCP 通訊可能在命令執行過程中中斷(網路抖動、Runner 當掉),所以專案設計了一套 lifecycle 狀態機:
commandIdstatus 命令查詢該 commandId 的狀態根據 lifecycleState 決策:
completed(已完成):從 lifecycleResponseJson 取結果,session 保留failed(已失敗):丟出 Runner 回報的錯誤,session 保留accepted/started(仍在飛行中):丟出 in-flight 錯誤,session 保留比如 CLI 發出了一個
tap命令,TCP 連線中途斷了,CLI 根本不知道 Runner 是否已經執行了這個 tap,如果重試可能會 double-tap,但如果不重試,可能 tap 根本沒發生,這時候就需要透過 lifecycle 判斷。

另外 macOS 和 iOS 共用了一套後端,所以邏輯基本可以複用,另外截圖功能上也是做了很多細節處理,比如:
狀態列內容覆蓋,將時間/電量偽造為固定值,讓截圖更容易區分。
Android 平台就是 ADB + 自訂 Snapshot Helper APK,類似 iOS 的 Runner transport,只是 Android 走的是 adb 命令(adb shell、adb exec-out、adb install 等):
adb -s <serial> 直連Android 平台最有意思的就是雙引擎架構快照實作:

為什麼需要自訂 Helper APK?因為 Android 原生的 uiautomator dump 有嚴重缺陷:
can-scroll-forward/backward 屬性所以 agent-device 開發了一個 android-snapshot-helper APK(專案根目錄有獨立子包),透過 Android Instrumentation 框架執行,能回傳更完整和穩定的 UI 樹 XML,同時它還維護了一個 persistent session(常駐背景程序),避免每次快照都 cold start,例如:
snapshot-helper-install.ts:檢測版本/安裝 APKsnapshot-helper-session.ts:常駐 session 管理snapshot-helper-capture.ts:透過 instrumentation 呼叫取得 XML另外一個就是捲動內容提示,這個也挺有意思,Android 快照時,agent-device 會額外執行 adb shell dumpsys activity top,從 Activity 狀態中推斷每個 ScrollView/RecyclerView 是否還有隱藏內容(上方/下方),然後標註到節點上,讓 AI 知道「這個列表下面還有更多內容」。
Linux 平台用的就是 AT-SPI2(Assistive Technology Service Provider Interface)+ Python 橋接,之前 agent-device 也嘗試過 node-gtk(Node.js 原生綁定),但 ABI 不相容、CI 編譯問題太多,所以現在改用 Python3 + python3-gi(PyGObject)呼叫 AT-SPI2,Node.js 負責啟動 Python 子程序、讀取 JSON 輸出。
AT-SPI2 是 Linux 桌面輔助功能標準(GNOME/KDE 等都支援),類似 macOS 的 Accessibility API,每個 GTK/Qt 視窗會把自己的 UI 樹註冊到 D-Bus 上,透過 AT-SPI2 可以遍歷所有應用程式的 UI 節點。
最後鴻蒙平台主要透過 HDC 鏈路來適配,可以類比 Android 的 ADB,HarmonyOS 使用的是 hdc 命令列工具,核心是 runHarmonyHdc(device, args) 會自動帶上 -t <serial> 裝置序號,與 ADB 的用法完全鏡像。
UI 快照層 ArkUI Hierarchy(等價於 iOS 的 XCTest / Android 的 ViewHierarchy),比如 hdc shell uitest dumpLayout -p /data/local/tmp/xxx.json,最終得到 @e1, @e2... 等節點,最重要的是它還處理了鴻蒙本身的一個問題相容:
uitest uiRecord 卡死時 dumpLayout 會 hang,所以專案加了預檢,發現 stuck 程序時直接報錯提示 reboot。
所以鴻蒙的 snapshot 是透過 uitest dumpLayout 生成 ArkUI component tree,然後支援 interactiveOnly、compact、depth、scope、raw 模式,同時透過 snapshot_display 進行截圖。
還有一些有趣的,比如 Alert 辨識(純 ArkUI 節點分析),不像 iOS 有原生的 alert API,HarmonyOS 的彈窗是普通 ArkUI 元件,所以 agent-device 是透過關鍵字匹配辨識彈窗並擷取按鈕座標,同時支援中英文按鈕文字(確定/取消/允許/拒絕/知道了...):
less 体验AI代码助手 代码解读复制代码if (type.includes('dialog') || type.includes('alert') ||
label.includes('暂无可用打开方式') || label.includes('权限') ...) {
// 识别为弹窗,提取 buttons
}
對比 Appium、Detox、Maestro 這些傳統 mobile automation framework,agent-device 是為 AI Agent 優化的,更適合 AI 自動化測試,當然它對人寫的場景就不是很友好了,如果真要對比,我理解是:
目前看起來它還不是特別適合大規模的穩定回歸測試,一些複雜 UI 的測試也還不夠穩定,所以它更適合作為 Agent 開發過程中的即時回饋和修復支援,讓 Agent 改完程式後自己跑起來看、自己點、自己截圖、自己抓日誌,然後基於真實結果繼續修,而且支援原生跨平台,最重要的是,開源、免費、MIT。