AI Agent驱动Android真机测试:ARTEMIS与MCP实战
发布时间:2026/9/28 16:07:34来源:尧图网络
1. 真机测试这件事为什么一直让人又爱又恨做过 Android 开发的人大概都有这种体会模拟器跑得好好的一上真机就翻车。要么是某个厂商 ROM 把后台服务杀了要么是权限弹窗的文案和位置跟原生系统完全不一样要么是某个机型的分辨率导致按钮点不到。模拟器能帮你验证逻辑但验证不了真实世界的混乱。这就是真机测试存在的意义也是它一直让人头疼的原因——设备多、系统版本杂、人工操作重复且容易漏。Google 开源的 ARTEMISAndroid Real-Time Multi-Device Intelligent Testing System这个名字是我根据项目定位理解的具体命名以官方仓库为准想解决的就是这个矛盾用 AI Agent 去驱动真机让机器自己看屏幕、自己决策、自己点击把原本需要人盯着手机一步步操作的过程自动化掉。它不是一个简单的 UI 自动化脚本框架而是一套感知—决策—执行的闭环系统核心关键词就是AI Agent、Android、MCP、真机测试。这篇文章适合三类人看一是手上有一堆真机、天天做兼容性回归的测试工程师二是想把自己从重复点击里解放出来的 Android 开发者三是对 AI Agent 落地场景感兴趣、想找一个真实项目练手的技术人。我会从整体设计思路讲到具体实操把踩过的坑和能直接抄的配置都摊开说尽量让你看完就能动手。先说清楚一个前提ARTEMIS 这类方案的本质是把人看屏幕做判断这件事交给多模态模型把点屏幕这件事交给 ADB 或设备端的自动化通道中间用 MCPModel 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 感知—决策—执行闭环拆解整个系统跑起来是一个循环我把它拆成四步感知Perception通过 ADB 截取当前屏幕拿到 PNG 图像同时通过dumpsys或 UiAutomator 拿到当前界面的控件树XML两者结合给模型提供视觉 结构双重信息。决策Reasoning把截图、控件树、当前任务目标、历史操作记录一起塞给多模态模型让模型输出下一步动作通常是 JSON 格式比如{action: tap, x: 540, y: 1200, reason: 点击登录按钮}。执行Action把模型输出的动作翻译成 ADB 命令或 UiAutomator 调用真正在设备上执行。校验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 这类框架通常会在配置里维护一个设备池给每台设备分配一个逻辑 IDAgent 决策时带上设备 ID执行层再路由到对应的 serial。这里有个容易被忽略的点不同厂商的 ADB 授权行为不一样。小米、OPPO 这类机器除了 USB 调试还要单独开USB 安装和USB 调试安全设置否则adb install会静默失败。华为部分机型需要登录账号才能开调试。这些坑在批量部署设备时特别致命建议提前把每台机器的授权状态用脚本检查一遍。# 检查设备是否已授权且可安装 adb -s serial shell getprop ro.product.model adb -s serial install -r test.apk3.2 屏幕感知截图与控件树怎么拿感知层要解决的是Agent 怎么知道现在屏幕上有什么。最直接的是截图adb -s serial exec-out screencap -p screen.pngexec-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 ServerAndroid 侧暴露设备能力可自研或用社区实现一台真机被测目标建议先固定一台跑通再扩Python 环境建议用虚拟环境隔离避免和系统包打架python -m venv artemis-env source artemis-env/bin/activate # Windows 用 artemis-env\Scripts\activate pip install mcp adb-shell pillow4.2 写一个最小的 Android MCP ServerMCP 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_outputTrue, textTrue ) app.list_tools() async def list_tools(): return [ Tool(namescreenshot, description截取当前屏幕, inputSchema{type: object, properties: {}}), Tool(nametap, 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(typetext, textfdata:image/png;base64,{img})] if name tap: adb([shell, input, tap, str(arguments[x]), str(arguments[y])]) return [TextContent(typetext, texttapped)] 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_steps40): 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, historymax_steps是保命参数。没有它模型可能陷入死循环一直点同一个地方。40 步对大多数单页面任务够用跨页面流程可以放宽到 80。4.4 一次真实跑通的记录我拿一个电商 App 的登录流程做了测试任务描述是用手机号 138xxxx 和密码 test1234 登录进入首页后停止。实际跑下来用了 11 步截图识别到启动页模型判断等待输出wait(2000)。进入登录页识别到手机号输入框点击。输入手机号。点击密码框。输入密码。点击登录按钮。出现图形验证码模型识别到并输出wait等验证码加载。识别验证码图片这一步纯视觉模型搞不定我加了人工兜底暂停等人工输入。继续点击登录。出现是否允许通知系统弹窗模型识别并点击允许。进入首页模型输出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 的成本可能比人工还高。工具是拿来省时间的别为了自动化而自动化。
网站建设高端定制企业官网