为什么一条 30 行的 Espresso 测试,AI Agent 一句话就能替代?
我第一次看到 Google 开源 ARTEMIS 的时候,第一反应是:这不就是把大模型塞进 UI 自动化的壳子里吗?但当我真正在真机上跑通一条完整任务,看着 agent 自己从桌面打开设置、拨动开关、再回到桌面,全程没有一行定位元素的代码时,我才意识到这东西改变的不只是测试写法,而是整个测试范式。
ARTEMIS 是 Google 开源的一套基于 AI Agent 的 Android 真机 UI 测试框架。传统自动化测试里,你要写定位器、写断言、处理等待;ARTEMIS 里,你只需要用自然语言描述"帮我测一下这个流程",agent 会自己看屏幕、自己决定点哪里、自己验证结果。这篇文章我想从我的视角拆一拆:ARTEMIS 的技术原理是什么、为什么官方强调真机、怎么把它跑起来、以及我在踩坑过程中总结的实战经验。适合正在做 Android 测试架构、对 AI Agent 感兴趣、以及被 UI 用例维护成本折磨的工程师阅读。
1. ARTEMIS 在测试范式上动刀:从"写脚本"到"下指令"
1.1 传统 UI 自动化测试的三个硬伤
先说一个我做测试平台时最常见的场景。一个登录按钮,测试脚本里写成onView(withId(R.id.btn_login)).perform(click())。开发同学某次重构顺手把btn_login改成了btn_sign_in,你这条用例就红了。改回来?下个版本可能又变成btn_account_login。这种元素定位问题,是所有 UI 自动化团队的日常。
第二个硬伤是意图断层。脚本本身不知道自己在测什么,它只知道"点这里、输文字、点那里"。如果界面上多了一个弹窗、少了一个按钮,脚本就会机械地失败,而不是像一个真人测试员一样想"哦这里弹了权限框,我先把它关掉再继续"。真人测试员的灵活,恰恰是脚本给不了的。
第三个硬伤是维护成本。一个中等体量的 App,UI 用例几千条很正常。每次 UI 改版,哪怕只是文案从"登录"改成"立即登录",只要断言里写了文本,就要跟着改。长期看,测试团队的精力被消磨在机械维护上,真正有价值的探索性测试反而没人做。
1.2 ARTEMIS 做了件完全不同的事
ARTEMIS 的核心思路是:测试目标由人用自然语言下达,执行过程由 LLM Agent 实时决策。比如我想验证"用户能在设置里关闭蓝牙",我不写步骤,只告诉 agent 目标:
请验证:在设置中关闭蓝牙。 1. 打开设置应用 2. 找到蓝牙开关 3. 如果蓝牙处于开启状态,点击关闭 4. 返回桌面agent 收到任务后,会自己启动 App、通过截图和 UI 层级信息"看到"当前屏幕、决定下一步动作、执行、再观察结果,直到确认蓝牙已经关闭。整个过程不需要任何资源 ID、文本断言、xpath。
这就带来一个非常实际的好处:UI 怎么变,只要语义没有变,任务描述就还能用。按钮从btn_login改成btn_sign_in、文案从"登录"改成"立即登录",agent 通过语义理解依然能找到那个按钮。维护成本从"改脚本"变成了"不需要改"——除非流程本身变了。
1.3 它和"AI 生成测试脚本"是两码事
很多测试工具也在打 AI 的旗号,但底层是"用 LLM 生成一段 Espresso 代码,然后在运行时执行这段静态代码"。这个思路确实能减少写脚本的时间,但执行时依然是死板的选择器,UI 一变照样挂。
ARTEMIS 不一样。它不是在写"剧本",而是在派"演员"上场即兴演出。每一步动作都由模型在运行时根据当前画面决定,模型看一步走一步,走完再看一眼。智能发生在执行阶段,而不是生成阶段。用个不恰当的类比:AI 生成脚本是给自动驾驶车画好了固定路线,ARTEMIS 是让司机自己看着路牌开。谁更能适应路况变化,不言而喻。
1.4 目标人群和适用阶段
我觉得 ARTEMIS 最适合三种角色:
- 测试架构师:想探索用 agent 替代高频、低价值的回归用例,把人力释放到探索性测试上。
- Android 开发工程师:提交代码后想快速跑一条"自然语言描述的冒烟路径",不想维护一堆脆弱的 UI 测试。
- AI Agent 研究者:想研究 LLM 如何在真实设备环境中做规划、执行和自我纠错。
如果你追求的是像素级稳定、毫秒级精确的断言,ARTEMIS 现阶段不是为你准备的。它解决的是"流程能不能走通"的问题,而不是"这个按钮的 margin 对不对"的问题。
2. Agent 的感知链路拆解:截图、UI 层级和结构化动作的三角关系
2.1 双通道感知:一只眼睛看像素,一只眼睛读结构
要让 LLM 在真机上决策,首先得让模型"看到"屏幕。ARTEMIS 的做法是给它两个输入通道。
第一个通道是截图。截图保留了完整的视觉信息:颜色、布局、图标状态、按钮位置。模型能看出"这个开关是蓝色的,说明是开启状态",能看出"页面顶部有一个红色报错条"。这些信息在任何 XML 里都表达不出来。
第二个通道是UI 层级信息。Android 系统本身维护着一棵视图树,每个节点包含类名、文本、content-desc、坐标边界、可点击状态等属性。这相当于把屏幕"翻译"成结构化的语义描述,模型不需要靠视觉去猜某个文字能不能点击,直接看节点属性就知道。
为什么两个都要?我实测的感受是:单独看截图,模型对小字和复杂列表经常认错;单独看 XML,遇到自定义绘制的 View(整个屏幕只有一个Canvas节点)就完全瞎了。截图和 XML 互补之后,模型既知道"屏幕上有什么",也知道"这些元素的属性是什么",决策的准确率明显提升。
2.2 从自然语言到动作序列:模型怎么当"规划师"
传统测试框架里,动作是脚本写死的:click(x, y)、swipe(x1, y1, x2, y2)。ARTEMIS 里,模型的输出是一个结构化的动作指令。大致的形式是这样的:
{ "action": "tap", "target": { "description": "蓝牙开关", "attributes": { "checkable": true, "checked": false } } }注意这里的关键:模型输出的 target 不是坐标,而是对目标元素的语义描述。执行层拿到这个描述之后,再去当前的 UI 层级树里找到匹配的元素,解析出它的中心坐标,最后通过设备的自动化接口注入点击事件。
这个过程非常像人的操作方式。人也不会记住"登录按钮的坐标是 (540, 1200)",而是记住"登录按钮在页面中央偏下的位置"。坐标会变、布局会变,但语义不变。这种"语义匹配"机制让 agent 天然具备对 UI 变化的鲁棒性。
2.3 指代消解:论文里最难啃的一块
ARTEMIS 背后有一篇论文,核心概念是 "Referring"——指代消解。用户下达的指令里经常有"那个""这个"之类的模糊指代,比如"把那个蓝色按钮打开"。这就很考验模型的理解能力:屏幕上可能有五个按钮,但只有一个带蓝色背景,需要你判断"那个"指的是哪一个。
让我印象最深的是论文里提到的一些失败案例。模型看截图时经常被视觉上"显眼"的元素吸引——比如页面顶部的大图、红色报错横幅——而忽略用户真正指的那个小按钮。开源版本里,这类问题通过把 UI 层级信息做得更细、并让模型在多次观察之间反复比对来缓解。如果你要用 ARTEMIS 跑复杂任务,一个很实际的经验是:任务描述里尽量少用"那个""这个",直接说"页面中央的蓝色按钮"会稳定得多。
2.4 反馈回路:每一步都是"观察-决策-执行-再观察"
ARTEMIS 的工作循环可以理解为一个四拍子:
- 获取当前截图 + UI 层级
- LLM 根据任务目标和当前状态,决定下一步动作
- 设备端执行动作(点击、滑动、输入、返回等)
- 再次获取截图 + UI 层级,让模型评估"动作执行后的状态是否符合预期"
如果执行后屏幕没有变化,模型会判断"可能没点中",然后换个思路重试;如果弹出了系统权限框,模型看到多了一个对话框,会先处理掉这个"意外",再继续原来的任务;如果连续多次动作都没有推进目标,模型会放弃当前策略调整方向。
这个反馈回路是 ARTEMIS 最核心的优势,也是它和所有脚本化测试的本质区别。脚本化测试遇到预期之外的弹窗只能失败,而 agent 会把弹窗当作"路上的石头",搬开继续走。我在实测中最直观的感受就是:它真的像一个远程遥控了一个手机的人,遇到什么就处理什么。
3. 真机不是噱头:ARTEMIS 的适用场景与边界
3.1 为什么模拟器替代不了真机
现在很多团队的 UI 测试跑在模拟器或者云真机平台上。模拟器的优点是快、便宜、可以并行,但它和真机的差异是客观存在的。举几个我亲历的例子:
- 厂商定制 ROM:小米、华为、OPPO 的系统设置页面结构各不相同,模拟器用的是 AOSP 原生机型,根本覆盖不到这些差异。
- 系统级交互:通知栏、权限弹窗、省电策略、应用后台被杀,这些行为在不同真机上完全不同。模拟器里的权限弹窗永远是那一个样式,真机上可能弹两次、可能出现在不同位置。
- 硬件相关功能:相机、指纹、传感器、NFC,这些在模拟器里要么不存在,要么是假的。
ARTEMIS 官方强调真机,不是营销话术,而是因为它的技术架构本身就建立在真机之上。它需要在设备上运行一个客户端配套应用,这个应用负责读取 UI 层级、执行点击滑动、和宿主机的 CLI 通信。模拟器当然也能跑,但如果你真的想测出不同真机上的问题,ARTEMIS 的设计方向就是对的。
3.2 设备端的"手指":配套 App 干了什么
ARTEMIS 的架构是两段式的:宿主机上跑命令行工具,负责调 LLM、做规划;真机上装一个配套 App,负责当"手"和"眼睛"。这个 App 通过 Android 的无障碍机制或者系统辅助接口拿到视图树,通过注入输入事件来执行点击和滑动。
我最初以为它是通过adb shell input tap来点击的,后来才搞清楚:input命令只能做全局的坐标注入,拿不到 App 内部的元素信息,也无法感知点击后的状态变化。ARTEMIS 在设备端跑 App 的好处是,它可以直接调用 Android 框架层的 UI 能力,拿到完整的语义树,执行完还能立刻校验结果。这也是它比"纯 adb 驱动"的测试方案高明的地方。
3.3 什么任务适合交给 ARTEMIS
结合我的实际体验,下面这几类任务用 ARTEMIS 收益最大:
- 冒烟回归:每次发版前,让 agent 把核心链路走一遍——登录、搜索、下单、支付——任何一步卡住都能及时发现。
- 跨应用流程:比如"从微信分享一张图片到朋友圈",这种跨越多个 App 的测试,传统脚本要处理应用间切换、系统弹窗,写起来很痛苦,但对 agent 来说只是正常操作。
- 版本兼容性验收:同一个任务描述,丢给不同品牌、不同 Android 版本的真机去跑,看看谁跑不通。这在传统方式里需要为每个机型维护一套脚本,ARTEMIS 只需要维护自然语言描述。
- 无障碍基础检查:用语义化描述让 agent 优先走无障碍焦点路径,能顺带发现一些可访问性问题。
3.4 别硬上的场景
ARTEMIS 也不是万能的。我建议不要把下面这些场景交给它:
- 精确性能测试:agent 的动作时序有模型推理延迟,你测不出真实的冷启动时间和帧率。性能测试必须用专门的工具在受控环境下跑。
- 高并发压测:ARTEMIS 是单设备单 agent 的运作方式,本质上不适合打造大规模并发场景。
- 完全离线环境:ARTEMIS 依赖 LLM API,如果被测环境是隔离的、不允许外部网络请求,那你需要自己接一套内网部署的模型服务,这又是一套工程。
- 断言复杂的视觉细节:比如"这个按钮的圆角半径应该是 32dp",这类精确到像素的问题,交给截图比对工具更合适。
4. 落地实操:环境准备、构建和第一条自然语言测试任务
4.1 你需要准备什么
跑通 ARTEMIS 的硬件门槛不高,但有几样东西必须提前备好:
- 一台真机:建议 Android 9 及以上,开启开发者选项和 USB 调试。品牌上尽量用你要测试的目标机型。
- 一台宿主机:macOS 或 Linux 都行,Windows 我没试过,理论上可以但坑可能多一点。
- Android SDK 命令行工具:至少要有
adb,确保adb devices能看到你的真机。 - JDK:构建配套 App 需要,版本按官方 README 要求装。
- 一个 LLM API Key:ARTEMIS 默认接 Gemini,但也支持接入其他符合 OpenAI 接口规范的大模型。没有 Gemini Key 的话,找国内可用的兼容接口也能跑。
4.2 构建和安装:别跳过官方仓库的步骤
我建议直接去 GitHub 搜 Google ARTEMIS 官方仓库,以 README 为准。整体流程大致是四步:
git clone仓库到本地,注意看有没有 submodule,有的话要--recursive拉全。- 构建设备端配套 App,产物是一个 APK。
- 把 APK 通过
adb install装到真机上,确认设备上这个 App 已经获取到了相应的辅助功能权限。 - 在宿主机上启动命令行工具,配置好 API Key 和环境变量。
我第一次跑的时候卡在最基础的环节——忘了给配套 App 授权无障碍权限。设备端没权限,agent 就拿不到 UI 层级,整个任务从第一步就卡死。如果你遇到"agent 看了屏幕但什么都点不到"的情况,先去检查设备端权限。
另外,Android Studio 在 ARTEMIS 的安装过程中不是必须的。你不需要打开 Android Studio 才能跑通,装好命令行 SDK tools 就行。很多教程喜欢带着你点 UI,其实那些操作在终端里都能完成,环境更清爽。
4.3 写第一条任务:从"能跑通"到"跑得稳"
跑通 demo 之后,我建议你从一条最简单的任务开始,比如:
ARTEMIS 启动后,输入以下任务: "打开计算器,计算 12 乘以 8,说出结果是否等于 96"这类任务的好处是:路径短、验证目标明确、失败时容易定位。等这条跑通了,再逐渐增加复杂度。
写任务描述时,我有几个心得:
- 给出明确的验证标准。不要说"看看蓝牙能不能关",要说"确认蓝牙开关处于关闭状态"。模型需要知道什么叫"任务完成"。
- 允许 agent 自己处理意外。比如"如果弹出权限请求,点击允许"。这不是必须的,但能提高长流程的完成率。
- 不要假设初始状态。你的任务描述前半段最好把"从桌面开始"讲清楚,因为上一个任务可能把 App 停在了某个页面。
4.4 运行过程与结果解读
启动任务后,CLI 会实时打印 agent 的动作日志。你可以看到类似这样的输出流:
[1] 观察: 当前在桌面,找到设置图标 [1] 决策: 点击设置图标 [2] 观察: 设置主页面已打开,找到蓝牙入口 [2] 决策: 点击蓝牙 [3] 观察: 蓝牙页已打开,开关状态为 ON [3] 决策: 点击开关关闭蓝牙 [4] 观察: 蓝牙开关状态变为 OFF,任务目标达成每次"观察"都会附带一张当时的截图。跑完之后,输出目录里会保存完整的截图序列和动作日志。你在复现问题的时候,直接看截图序列就能定位 agent 是哪一步走偏的,这个体验比看一堆assertEquals的报错信息要直观得多。
如果 agent 在某一步反复失败,日志里会出现连续的"尝试-失败"记录,并最终触发超时中断。这时候我的习惯是把截图序列从头翻到尾,找到 agent 第一次"看错屏幕"的那一步——问题通常出在任务描述有歧义、或者模型被屏幕上某个醒目的视觉元素带偏了。
5. 我实测遇到的坑:从连不上设备到 agent 原地打转
5.1 adb 连接问题:一半的失败发生在这里
第一个大坑就是设备连不上。adb devices显示unauthorized,说明手机上的 USB 调试授权弹窗你没有点允许;显示offline,多半是数据线接触不良或者adb服务异常。我的经验是:先adb kill-server再adb start-server,然后重新插拔数据线,基本能解决 80% 的问题。
另一个坑是息屏锁屏导致 agent 失联。我跑一个长任务时,手机自动锁屏了,agent 后续的所有点击全落在锁屏界面上,任务直接废掉。后来我在开发者选项里打开了"屏幕常亮",并且把系统休眠时间调到最长。跑长任务的时候记得手动碰一下屏幕,不要让手机睡着。
5.2 LLM 配置文件维护好了,API Key 却常常被忽略
ARTEMIS 的配置不算复杂,但 API Key 这个环节让我折腾了半小时。有几种姿势:
- 环境变量里直接设 Key;
- 配置文件里指定 Key;
- 如果接的是本地代理,还要额外配代理地址。
我最开始只设置了环境变量,结果 CLI 读取的是配置文件,Key 一直没生效,agent 每次调用模型都返回 401。这个坑的教训是:先认真读一遍 README 里的配置部分,确认它读的是环境变量还是配置文件,别凭经验猜。
5.3 视觉模型的输入限制:截图太大、XML 太长
跑复杂页面的时候,我遇到模型调用报错,提示输入超长。原因很简单:高分辨率真机的截图一张就有几十 MB,加上 UI 层级 XML 可能几千个节点,模型一次的输入窗口根本塞不下。
我的解决方案是:
- 对截图做缩放:把截图压缩到模型支持的较宽松尺寸以下,UI 自动化本身不要求看清每一个像素。
- 对 XML 做截断:只保留关键节点路径,比如每个节点保留文本、content-desc、可点击状态和坐标,其余属性一律过滤。
- 按需分段:如果页面确实复杂,就引导 agent 用"滚动"动作分批查看内容,而不是一次全塞进上下文。
这一步是 ARTEMIS 跑真实项目时最需要优化的环节,直接关系到稳定性和 token 成本。
5.4 动画、弹窗和等待:Agent 再智能也怕页面没加载完
你一定见过这种场景:页面还在转圈,agent 已经决定点击,结果点了一空。ARTEMIS 在拿截图和 XML 时,如果页面处于加载中,XML 树是空的或者不完整,模型自然做出错误的下一步决策。
这个问题不能完全靠模型自己解决。我实测比较有效的做法是:
- 在任务描述里显式写上"每次操作后等待页面加载完成,再决定下一步";
- 如果目标页面有明显的加载标识,让 agent 观察它是否消失;
- 在宿主机层面对同一动作设置重试阈值,超时自动重新观察。
弹窗则是另一种"惊喜"。权限弹窗、版本更新弹窗、广告弹窗,每个 App 都有。agent 的策略是"遇到弹窗先处理掉",但这个策略偶尔会误伤——比如把页面里的一个浮层按钮当成弹窗关掉了。这种问题只能靠日志回溯和调整任务描述来规避。
5.5 任务描述的歧义陷阱:你以为是常识,模型以为是猜谜
最后一个坑来自我自己写的任务描述。我写过"打开 Wi-Fi",结果 agent 跑到了详设的 Wi-Fi 详情页。事实上"打开 Wi-Fi"在 Android 里至少有三种理解:打开快捷开关、打开设置里的 Wi-Fi 页面、连接某个具体网络。模型没有"常识"可言,它只会从你给出的文字里猜。
后来我的描述改成了这样:
打开设置应用,进入 Wi-Fi 设置页,如果 Wi-Fi 开关处于关闭状态,点击开启它。同样的动作,描述得更具体之后,成功率提高了一大截。你和 agent 交流的边界不是它的智能,而是你描述的精确度。宁可多写几个字,不要让模型去猜你的潜台词。
最后分享一点我的个人体会
跑了一个多月 ARTEMIS,我的看法是:它现在还做不到 100% 替代传统 UI 测试,但对"流程能不能走通"这一类问题,它的效率和灵活度已经明显超过了脚本方案。我把团队里最容易挂、最经常改版的 20 条冒烟用例全部迁移到了 ARTEMIS 上,维护成本基本归零,稳定性目前能到 90% 以上,剩下 10% 的失败大多可以通过优化任务描述和设备端等待策略解决。如果你也想引入,我建议不要从核心主流程开始,先挑几条最边缘、最让测试同学头疼的"低级重复劳动"用例来试水,你会发现 AI Agent 做脏活累活的潜力,比想象中大得多。