之前介绍过最好用的 Flutter AI 测试框架,然后有很多人在问有没有原生平台的,刚好就看到了一个原生平台的开源测试框架 agent-device ,甚至它还支持鸿蒙:

agent-device 是专门为 AI Agent 设计的设备自动化 CLI 工具 ,可以类比 Vercel 的 agent-browser 是给 AI 用的浏览器自动化工具那样,agent-device 是给 AI 用的 App 准备的标准化通道,例如:
sql
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
agent-device snapshot -i
# @e1 [heading] "Settings"
# @e2 [button] "Sign In"
# @e3 [text-field] "Email"
这样 AI 就可以直接用 @e3 操作 「[text-field] "Email"」,不需要自己去读坐标,也不需要每次截图分析,同时功能上支持:
- 截图:用于 debug/回放
- 视频录制(record):录制操作流程
- 日志(logs):app 日志流
- 网络流量(network):HTTP 请求抓包
- 性能采样(perf):CPU/内存
- 崩溃上下文:crash 时的现场信息
甚至它还支持工作流回放,将 AI 探索过的交互录制成 .ad 脚本,然后可以在 CI、本地、重复场景中回放执行,等效于 e2e 测试用例。
agent-device的场景其实就是:Agent 写代码,之后安装到对应设备,然后 agent-device 打开 App ,通过 snapshot 读取当前 UI 结构,之后 Agent 根据@e1/@e2/selector操作 UI ,然后得到 「截图 / 录屏 / 日志 / 网络 / 性能」等证据存档,最后再让 Agent 根据测试结果修复问题。

当然,最最最最重要的是它是多平台支持:
- iOS 真机/Simulator(XCTest 驱动)
- Android Emulator / 真机(ADB 驱动)
- tvOS / Android TV
- macOS App
- Linux (AT-SPI)
- HarmonyOS (HDC 驱动)
整个流程大概如下图所示:

如果拆开来看,整个架构大概如下:
- Session 模型 :先
open一个设备会话,后续所有命令在这个 session 上下文内执行(锁住设备资源、保持状态) - Daemon 架构 :后台运行一个长驻进程(daemon),CLI 命令通过 IPC 与 daemon 通信
- 提升效率 :snapshot 使用无障碍树而不是视觉截图,让 AI 用最少 token 获取 UI 信息;
-i(interactive only)进一步只返回可交互元素,而且截图测试的方式是真的死慢 - 语义引用系统 (Refs):
@e1~@eN是每次 snapshot 后分配的临时引用,命令可直接用 ref 而不是坐标,降低 AI 犯错概率,比如坐标在滚动后就失效,但 ref 是语义感知的 - Interactor 模式 :每个平台实现一个统一的
Interactor接口(tap,swipe,snapshot,screenshot...),核心调度层面对接口编程,平台差异被隔离在各自的platforms/目录 - 可控范围 :
src/core/capabilities.ts给每条命令定义哪些平台/设备类型支持,防止 AI 调用不支持的命令,会在 admission 阶段直接报错而不是无意义地发送到设备
当然,也存在一些平台限制,例如:
| 局限 | 具体表现 |
|---|---|
| pinch/rotate 手势 | 仅支持 iOS Simulator + Android,HarmonyOS 不支持 pinch/rotate |
read(文本识别) |
只支持 iOS/Android |
logs/network/perf |
HarmonyOS 均不支持 |
clipboard |
HarmonyOS 需要 app-level 授权,读写均可能失败 |
| 权限管理 | HarmonyOS 仅支持 grant,deny/reset 未实现 |
然后在平台链路上,每个平台都有自己一套实现,从代码看,你会看到一套非常复杂的工作流程。
iOS 体系
首先是 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 状态机:
-
每个命令发出前打上唯一
commandId -
如果传输中断,发送
status命令查询该commandId的状态 -
根据
lifecycleState决策:completed(已完成):从lifecycleResponseJson取结果,session 保留failed(已失败):抛出 Runner 报告的错误,session 保留accepted/started(仍在飞行中): 抛 in-flight 错误,session 保留- 未知状态 : 保守路径:invalidate session,重新启动
比如 CLI 发出了一个
tap命令,TCP 连接中途断了,CLI 根本不知道 Runner 是否已经执行了这个 tap,如果重试可能会 double-tap ,但是如果不重试,可能 tap 根本没发生,这时候就需要通过 lifecycle 判断。

另外 macOS 和 iOS 共用了一套后端,所以逻辑基本可以复用,另外截图功能上也是做了很多细节处理,比如:
状态栏内容覆盖,将时间/电量伪造为固定值,让截图更容易区分。
Android
Android 平台就是 ADB + 自定义 Snapshot Helper APK ,类似 iOS 的 Runner transport,只是 Android 的走的是 adb 命令(adb shell、adb exec-out、adb install 等):
- 本地
adb -s <serial>直连 - Provider 注入模式(可以支持云端/远程 Android 设备)
- 文本注入 provider 覆盖(用于非 ASCII 字符)
Android 平台最有意思的就是双引擎架构快照实现 :

为什么需要自定义 Helper APK?因为 Android 原生的 uiautomator dump 有严重缺陷:
- 经常超时(UI 繁忙时 hang 住)
- 返回的节点数有限,无法获取完整
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
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
if (type.includes('dialog') || type.includes('alert') ||
label.includes('暂无可用打开方式') || label.includes('权限') ...) {
// 识别为弹窗,提取 buttons
}
最后
对比 Appium、Detox、Maestro 这些传统 mobile automation framework, agent-device 是为 AI Agent 优化的,更适合 AI 自动化测试,当然它对人写的场景就不是很友好了,如果真要对比,我理解是:
- Appium / Detox / Maestro: 支持人写,重点是稳定、可维护、CI 回归
- agent-device:面向 AI Agent 现场探索、验证、debug、采证、沉淀 replay
目前看起来它还不是特别适合大规模的稳定回归测试,一些复杂 UI 的测试也还不够稳定,所以它更适合作用 Agent 开发过程中的实时反馈和修复支持,让 Agent 改完代码后自己跑起来看、自己点、自己截图、自己抓日志,然后基于真实结果继续修,而且支持原生跨平台,最重要是,开源,免费,MIT。