news 2026/9/28 16:07:34

AI Agent驱动Android真机测试:ARTEMIS与MCP实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI Agent驱动Android真机测试:ARTEMIS与MCP实战

1. 真机测试这件事,为什么一直让人又爱又恨

做过 Android 开发的人大概都有这种体会:模拟器跑得好好的,一上真机就翻车。要么是某个厂商 ROM 把后台服务杀了,要么是权限弹窗的文案和位置跟原生系统完全不一样,要么是某个机型的分辨率导致按钮点不到。模拟器能帮你验证逻辑,但验证不了"真实世界的混乱"。这就是真机测试存在的意义,也是它一直让人头疼的原因——设备多、系统版本杂、人工操作重复且容易漏。

Google 开源的 ARTEMIS(Android Real-Time Multi-Device Intelligent Testing System,这个名字是我根据项目定位理解的,具体命名以官方仓库为准)想解决的就是这个矛盾:用 AI Agent 去驱动真机,让机器自己看屏幕、自己决策、自己点击,把原本需要人盯着手机一步步操作的过程自动化掉。它不是一个简单的 UI 自动化脚本框架,而是一套"感知—决策—执行"的闭环系统,核心关键词就是AI Agent、Android、MCP、真机测试。

这篇文章适合三类人看:一是手上有一堆真机、天天做兼容性回归的测试工程师;二是想把自己从重复点击里解放出来的 Android 开发者;三是对 AI Agent 落地场景感兴趣、想找一个真实项目练手的技术人。我会从整体设计思路讲到具体实操,把踩过的坑和能直接抄的配置都摊开说,尽量让你看完就能动手。

先说清楚一个前提:ARTEMIS 这类方案的本质,是把"人看屏幕做判断"这件事交给多模态模型,把"点屏幕"这件事交给 ADB 或设备端的自动化通道,中间用 MCP(Model Context Protocol)把模型和设备能力对接起来。理解了这条主线,后面所有细节都好串。

2. 整体设计思路:为什么是 Agent + MCP + 真机

2.1 传统 UI 自动化的死穴在哪

在聊 ARTEMIS 之前,得先搞清楚老办法为什么不够用。传统的 Android UI 自动化,主流是 UiAutomator、Espresso、Appium 这几套。它们的共同点是:你必须提前告诉它每一步做什么。比如"找到 id 为 btn_login 的控件,点击它,然后等待 3 秒,再找到输入框输入用户名"。这套逻辑在稳定环境下没问题,但真机测试的环境恰恰不稳定。

问题集中在三个地方。第一,控件定位脆弱。开发改个 id、换个布局层级,脚本就挂了。第二,无法处理意外弹窗。真机上随时可能冒出系统更新提示、权限申请、广告弹窗,脚本没有"应变"能力,只能卡死或报错。第三,维护成本随用例数量线性增长。一百条用例就是一百份维护负担,改一个流程要动几十个脚本。

AI Agent 的思路完全不同。它不预设每一步,而是给一个目标(比如"完成登录并进入首页"),让模型看着当前屏幕截图,自己判断下一步该点哪里。屏幕变了、弹窗来了,模型能重新决策。这就把"写死流程"变成了"给目标 + 动态决策",维护成本大幅下降。

2.2 MCP 在中间扮演什么角色

MCP 是 Anthropic 提出的一套协议,全称 Model Context Protocol。你可以把它理解成"模型和外部工具之间的标准插头"。以前要让模型操作设备,你得自己写一堆胶水代码,把 ADB 命令包装成模型能调用的函数。MCP 把这层标准化了:设备侧提供一个 MCP Server,暴露"截图""点击""滑动""输入文本""获取当前 Activity"这些能力;模型侧作为 MCP Client,按协议去调用。

这样做的好处很实在。一是解耦,模型换一个、设备换一批,只要都遵守 MCP,接口不用重写。二是可组合,你可以在同一个 Agent 里挂多个 MCP Server,比如一个管 Android 设备,一个管浏览器(Playwright MCP),一个管接口调试(BurpSuite MCP),让 Agent 同时操作 App 和后台。三是生态复用,社区里已经有大量现成的 MCP Server,不用从零造轮子。

ARTEMIS 选择 MCP 作为设备对接层,我认为是这套方案里最关键的一个设计决策。它让"AI 驱动真机"从一次性 demo 变成了可扩展的工程方案。

2.3 感知—决策—执行闭环拆解

整个系统跑起来是一个循环,我把它拆成四步:

  1. 感知(Perception):通过 ADB 截取当前屏幕,拿到 PNG 图像;同时通过dumpsys或 UiAutomator 拿到当前界面的控件树(XML),两者结合给模型提供"视觉 + 结构"双重信息。
  2. 决策(Reasoning):把截图、控件树、当前任务目标、历史操作记录一起塞给多模态模型,让模型输出下一步动作,通常是 JSON 格式,比如{"action": "tap", "x": 540, "y": 1200, "reason": "点击登录按钮"}。
  3. 执行(Action):把模型输出的动作翻译成 ADB 命令或 UiAutomator 调用,真正在设备上执行。
  4. 校验(Verification):执行后重新截图,判断目标是否达成。没达成继续循环,达成则进入下一个任务节点。

这个闭环里,决策质量取决于模型能力,执行稳定性取决于设备通道,校验准确性取决于断言设计。三者缺一不可,后面实操部分我会分别展开。

提示:不要指望模型一次决策就对。实际跑下来,一个稍微复杂的流程(比如跨三个页面的下单)往往需要 15 到 40 轮循环。所以循环的健壮性和超时控制比单次决策准确率更重要。

3. 核心细节解析:从设备连接到 Agent 决策

3.1 真机连接与设备管理

真机测试第一步永远是让电脑认到设备。基础操作是开 USB 调试,然后adb devices确认。但真机测试的麻烦在于多设备。你可能有五台不同厂商、不同 Android 版本的机器同时挂着,Agent 得知道每一步操作发给谁。

# 查看所有已连接设备 adb devices -l # 输出示例 # R3CT90XXXXX device product:beyond1qlteue model:SM_G973U device:beyond1 # emulator-5554 device product:sdk_gphone_x86 model:sdk_gphone_x86 device:generic_x86

多设备场景下,每条 ADB 命令都要带-s <serial>指定目标。ARTEMIS 这类框架通常会在配置里维护一个设备池,给每台设备分配一个逻辑 ID,Agent 决策时带上设备 ID,执行层再路由到对应的 serial。

这里有个容易被忽略的点:不同厂商的 ADB 授权行为不一样。小米、OPPO 这类机器,除了 USB 调试,还要单独开"USB 安装"和"USB 调试(安全设置)",否则adb install会静默失败。华为部分机型需要登录账号才能开调试。这些坑在批量部署设备时特别致命,建议提前把每台机器的授权状态用脚本检查一遍。

# 检查设备是否已授权且可安装 adb -s <serial> shell getprop ro.product.model adb -s <serial> install -r test.apk

3.2 屏幕感知:截图与控件树怎么拿

感知层要解决的是"Agent 怎么知道现在屏幕上有什么"。最直接的是截图:

adb -s <serial> exec-out screencap -p > screen.png

exec-out比shell更靠谱,因为它不会因为换行符转换把 PNG 数据搞坏。截图拿到后,直接喂给多模态模型。

但光有图不够。模型看截图能知道"这里有个按钮",但不知道这个按钮的 id、能不能点、是不是可滚动容器。所以还要拿控件树:

adb -s <serial> shell uiautomator dump /sdcard/window_dump.xml adb -s <serial> pull /sdcard/window_dump.xml

控件树是 XML,里面有每个节点的bounds、resource-id、text、clickable等属性。把截图和控件树一起给模型,模型就能做更精准的决策——比如它可以说"点击 resource-id 为 com.example:id/login 的控件",而不是"点击坐标 (540, 1200)"。前者在分辨率变化时更稳。

注意:uiautomator dump在部分机型上会失败,尤其是页面有持续动画或 WebView 内容时。稳妥做法是加超时和重试,失败时降级为纯截图模式,让模型靠视觉定位。

3.3 决策提示词的设计要点

Agent 的决策质量,八成取决于提示词。我见过太多人把提示词写成"请帮我操作手机",然后抱怨模型不听话。好的提示词要包含四块内容:

  • 角色与目标:明确告诉模型它是 Android 测试 Agent,当前任务是"完成登录"。
  • 当前状态:截图 + 控件树 + 当前 Activity 名。
  • 可用动作:列出它能调用的 MCP 工具,比如tap、swipe、input_text、press_back、wait。
  • 输出格式:强制 JSON,并给出字段说明和示例。

一个简化版的提示词骨架长这样:

你是一个 Android 真机测试 Agent。当前任务:{task} 当前界面 Activity:{activity} 可用动作:tap(x,y) / tap_by_id(resource_id) / input_text(text) / swipe(x1,y1,x2,y2) / press_back() / wait(ms) 请根据截图和控件树,输出下一步动作,格式为 JSON: {"action": "...", "params": {...}, "reason": "..."} 如果任务已完成,输出 {"action": "done", "reason": "..."}

reason字段特别重要。它逼着模型把决策理由说出来,一方面方便你调试(看它为什么点错),另一方面能显著提升决策质量——这跟人写解题步骤是一个道理。

3.4 执行层的稳定性处理

模型输出动作后,执行层要把它变成真实操作。点击用adb shell input tap x y,输入文本用adb shell input text,滑动用adb shell input swipe。这些命令本身简单,但真机上有一堆坑。

输入中文是经典难题。adb shell input text不支持中文和特殊字符。常见解法是装一个 ADBKeyboard 输入法,通过广播发送文本:

adb shell am broadcast -a ADB_INPUT_TEXT --es msg "测试文本"

点击偏移也常见。input tap用的是物理坐标,但截图可能是缩放过的。如果截图被压缩到 1080 宽而设备是 1440 宽,坐标就得按比例换算。这个换算必须在执行层统一处理,否则模型给的坐标永远点不准。

执行后的等待不能省。点完按钮界面不会瞬间刷新,立刻截图会拿到旧画面,导致模型误判。稳妥做法是执行后固定等 500ms 到 1s,再用dumpsys window检查 Activity 是否变化,变了再截图。

4. 实操过程:从零搭一个能跑的最小闭环

4.1 环境准备清单

动手之前,把这几样东西备齐:

组件作用备注
Android SDK Platform-Tools提供 adb版本建议 34 以上
Python 3.10+写 Agent 主逻辑3.10 是多数 MCP SDK 的底线
多模态模型 API做视觉决策需支持图像输入
MCP Server(Android 侧)暴露设备能力可自研或用社区实现
一台真机被测目标建议先固定一台,跑通再扩

Python 环境建议用虚拟环境隔离,避免和系统包打架:

python -m venv artemis-env source artemis-env/bin/activate # Windows 用 artemis-env\Scripts\activate pip install mcp adb-shell pillow

4.2 写一个最小的 Android MCP Server

MCP Server 的核心是注册工具。下面是一个精简示例,暴露截图和点击两个能力:

from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent import subprocess, base64 app = Server("android-mcp") SERIAL = "R3CT90XXXXX" def adb(args): return subprocess.run( ["adb", "-s", SERIAL] + args, capture_output=True, text=True ) @app.list_tools() async def list_tools(): return [ Tool(name="screenshot", description="截取当前屏幕", inputSchema={"type": "object", "properties": {}}), Tool(name="tap", description="点击坐标", inputSchema={"type": "object", "properties": {"x": {"type": "integer"}, "y": {"type": "integer"}}, "required": ["x", "y"]}), ] @app.call_tool() async def call_tool(name, arguments): if name == "screenshot": result = adb(["exec-out", "screencap", "-p"]) img = base64.b64encode(result.stdout.encode("latin1")).decode() return [TextContent(type="text", text=f"data:image/png;base64,{img}")] if name == "tap": adb(["shell", "input", "tap", str(arguments["x"]), str(arguments["y"])]) return [TextContent(type="text", text="tapped")] async def main(): async with stdio_server() as (r, w): await app.run(r, w, app.create_initialization_options()) if __name__ == "__main__": import asyncio asyncio.run(main())

这段代码跑起来后,任何支持 MCP 的客户端都能通过标准输入输出调用它。注意screencap的输出是二进制,用latin1编码再 base64 是为了不破坏字节。

4.3 Agent 主循环怎么写

Agent 主循环负责把"截图—决策—执行"串起来。核心逻辑如下:

import json, time from mcp_client import MCPClient # 假设已封装好 MCP 客户端 from llm import call_vision_model # 假设已封装好多模态调用 async def run_task(task, max_steps=40): client = MCPClient("android-mcp") history = [] for step in range(max_steps): # 1. 感知 shot = await client.call("screenshot") tree = await client.call("dump_ui") # 2. 决策 prompt = build_prompt(task, shot, tree, history) resp = call_vision_model(prompt) action = json.loads(resp) # 3. 执行 if action["action"] == "done": return True, history await client.call(action["action"], action.get("params", {})) history.append(action) # 4. 等待界面稳定 time.sleep(0.8) return False, history

max_steps是保命参数。没有它,模型可能陷入死循环,一直点同一个地方。40 步对大多数单页面任务够用,跨页面流程可以放宽到 80。

4.4 一次真实跑通的记录

我拿一个电商 App 的登录流程做了测试,任务描述是"用手机号 138xxxx 和密码 test1234 登录,进入首页后停止"。实际跑下来用了 11 步:

  1. 截图识别到启动页,模型判断等待,输出wait(2000)。
  2. 进入登录页,识别到"手机号"输入框,点击。
  3. 输入手机号。
  4. 点击密码框。
  5. 输入密码。
  6. 点击"登录"按钮。
  7. 出现图形验证码,模型识别到并输出wait,等验证码加载。
  8. 识别验证码图片,这一步纯视觉模型搞不定,我加了人工兜底,暂停等人工输入。
  9. 继续,点击登录。
  10. 出现"是否允许通知"系统弹窗,模型识别并点击"允许"。
  11. 进入首页,模型输出done。

第 8 步暴露了一个现实问题:图形验证码、滑块验证这类反自动化机制,纯 Agent 方案搞不定。这不是 ARTEMIS 的缺陷,而是所有自动化方案的共同边界。实操中要么接打码服务,要么在关键节点留人工介入口。

5. 常见问题与排查技巧实录

5.1 模型点不准、点错位置怎么办

这是最高频的问题。排查顺序建议这样走:

  • 先确认坐标换算。截图分辨率和设备物理分辨率是否一致?不一致就要按比例缩放。我踩过一次坑,截图是 720 宽,设备是 1080 宽,模型给的坐标全部偏左三分之一,查了半天才发现是缩放问题。
  • 再确认控件树是否新鲜。uiautomator dump拿到的可能是上一次的缓存,尤其是页面刚切换时。加一个"dump 前先等 Activity 变化"的判断。
  • 最后看提示词。如果提示词里没告诉模型"坐标基于截图左上角原点",它可能按别的坐标系理解。

5.2 设备掉线、ADB 断连怎么处理

真机长时间跑测试,掉线是常态。USB 线松动、设备休眠、ADB 服务崩溃都会导致。稳妥做法是加一个守护线程,每隔 30 秒adb devices检查一次,发现设备消失就重连:

adb kill-server adb start-server adb devices

如果设备频繁掉线,优先怀疑线材和 USB 口供电。我实测下来,用带独立供电的 USB Hub 比直插主板稳定得多,尤其是同时挂多台设备时。

5.3 常见问题速查表

现象可能原因解决方向
截图全黑设备锁屏或 DRM 保护唤醒屏幕,关闭安全窗口
点击无反应坐标换算错误核对截图与物理分辨率
输入中文乱码input text 不支持改用 ADBKeyboard 广播
模型反复点同一处缺少历史上下文把历史动作加入提示词
循环不终止无 max_steps强制设置步数上限
控件树为空页面是 WebView降级为纯视觉模式
多设备串扰未指定 serial每条命令带 -s 参数

5.4 几个能省大量时间的实操心得

第一,先固定一台设备跑通,再扩多台。多设备并行会引入大量并发问题,一开始就上多台,你会分不清是 Agent 逻辑问题还是设备管理问题。

第二,把每次循环的截图和决策都存下来。出问题时回放这些记录,比盯着日志猜快十倍。我习惯按任务名/时间戳/step_序号.png存,配合 JSON 决策记录,复盘效率极高。

第三,给模型的动作加白名单校验。模型偶尔会输出不存在的动作名或越界坐标。执行前做一次校验,非法动作直接丢弃并让模型重试,能避免很多莫名其妙的崩溃。

第四,超时和重试要分层。ADB 命令级别超时设 10 秒,单步决策超时设 30 秒,整个任务超时设 10 分钟。三层都设上,任何一层卡住都不会拖垮全局。

6. 这套方案能扩展到哪里

跑通最小闭环之后,你会发现 ARTEMIS 这类方案的想象空间比想象中大。最直接的扩展是多设备并行回归:把同一套任务分发到十台不同机型上,Agent 各自跑,最后汇总每台的通过率和截图证据。这基本就是兼容性测试的自动化形态。

再往上一层,可以接CI/CD。把 Agent 任务包装成一个 Jenkins Job 或 GitHub Action,每次发版自动触发一轮真机冒烟。配合 MCP 的多 Server 能力,还能让 Agent 同时操作 App 和后台接口——比如先在后台造一条测试数据,再在 App 里验证这条数据能正确展示,端到端全自动。

还有一个我觉得很有意思的方向:用 Agent 做探索性测试。传统自动化只能验证"预期行为",而 Agent 可以给一个模糊目标(比如"尽可能多地触发这个页面的不同状态"),让它自由探索,把发现的崩溃和异常截图记录下来。这相当于给测试团队配了一个不知疲倦的探索者。

不过话说回来,这套方案目前还不是银弹。模型决策有成本(每次调用都要钱和时间),复杂流程的稳定性还需要打磨,反自动化机制依然是硬边界。我的建议是:先从高频、稳定、重复度高的回归用例切入,比如登录、下单、支付这些每天都要跑的主流程,用 Agent 替代人工点击,收益最明显。等这套跑顺了,再往探索性测试和兼容性矩阵扩展。

最后分享一个我自己的判断标准:如果一个测试用例,人工跑一次要 3 分钟以上,且每周至少跑 5 次,那它就值得用 Agent 自动化。低于这个频率的,维护 Agent 的成本可能比人工还高。工具是拿来省时间的,别为了自动化而自动化。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 16:07:30

目标检测数据集制作:VOC/COCO/YOLO格式深度解析与零误差SOP

1. 为什么“数据集制作”才是目标检测项目里最耗时、最容易翻车的环节你花三天调通了YOLOv8的训练脚本&#xff0c;改好了学习率和anchor&#xff0c;信心满满地跑完第一个epoch——结果mAP只有0.02。你反复检查代码&#xff0c;重装CUDA&#xff0c;甚至怀疑显卡温度过高影响了…

作者头像 李华
网站建设 2026/9/28 16:07:26

基于JSP的车险模拟系统:Tomcat部署+保费计算+PDF保单生成

简介&#xff1a;本资源是一套面向计算机专业本科生的毕业设计级JSP Web应用项目&#xff0c;聚焦车险业务全流程模拟&#xff0c;涵盖保费计算、保单管理、理赔申请等核心功能&#xff0c;适用于Java Web课程设计、毕设参考及JSP技术实战学习。压缩包共950个文件&#xff0c;2…

作者头像 李华
网站建设 2026/9/28 16:04:57

Keil5 RTE快速搭建STM32工程:自动配置外设驱动与启动文件

STM32的入门门槛&#xff0c;说实话&#xff0c;一半卡在硬件接线&#xff0c;另一半就卡在开发环境上。我见过太多人Keil5装好了、芯片包也打了&#xff0c;结果新建工程之后对着空荡荡的左侧目录发呆——外设库文件呢&#xff1f;启动文件呢&#xff1f;难道要一个个手动往工…

作者头像 李华
网站建设 2026/9/28 16:04:25

MCP协议实战:从多客户端适配崩溃到统一工具调用标准

1. 从一次接口对接的崩溃说起&#xff1a;MCP 到底想解决什么问题如果你最近半年在折腾 LLM 应用&#xff0c;大概率经历过这种场景&#xff1a;为了让模型能读到一个本地文件、查一次数据库、调一次内部接口&#xff0c;你得给每个模型客户端单独写一套适配代码。Claude Deskt…

作者头像 李华
网站建设 2026/9/28 16:03:55

Multisim 14.0单相桥式全控整流电路仿真:参数优化与波形调试指南

1. 从一次“波形对不上”的调试说起如果你正在做电力电子课程的仿真作业&#xff0c;或者刚接手一个整流电路的参数验证任务&#xff0c;大概率会遇到这样一个场景&#xff1a;照着教材上的单相桥式全控整流电路在Multisim里搭好了模型&#xff0c;触发脉冲也给了&#xff0c;示…

作者头像 李华
网站建设 2026/9/28 16:03:38

垂直领域问答助手落地指南:RAG架构、技术选型与踩坑实战

做垂直领域问答助手之前&#xff0c;我建议你先别急着写代码。这个题目听起来很直接&#xff0c;无非是“喂一批文档进去&#xff0c;让大模型回答这个领域的问题”&#xff0c;但真正落地之后你会发现&#xff0c;方案选型、知识库处理、检索质量、Agent编排、效果评估&#x…

作者头像 李华