资讯中心

谷歌ARTEMIS:多模态大模型驱动移动端AI自动化实战

📅 2026/9/28 16:09:20
谷歌ARTEMIS:多模态大模型驱动移动端AI自动化实战
1. 移动端 AI 自动化的破局点ARTEMIS 到底想解决什么问题移动端自动化测试和操作一直是个让人又爱又恨的领域。爱的是它确实能省下大量重复劳动恨的是传统方案要么依赖控件树、要么依赖固定坐标App 一改版就集体失效。谷歌开源的 ARTEMIS全称 Android Real-Time Environment Manipulation and Interaction System社区里更习惯直接叫它 ARTEMIS就是冲着这个痛点来的——它让 AI 助手像人一样看屏幕、理解界面、然后动手操作手机而不是死板地去找某个resource-id。我第一次接触这个项目的时候第一反应是这不就是把多模态大模型接到 Android 的 AccessibilityService 上吗。但真正读进去之后发现ARTEMIS 的价值不在于接了个模型而在于它把感知、推理、执行这三层拆得很干净并且给出了一个可复现的工程化路径。它解决的核心问题是当界面元素没有稳定标识、当布局动态变化、当操作需要跨应用跳转时如何让一个 AI Agent 稳定地把事情做完。这个项目适合谁如果你在做移动端自动化测试、RPA机器人流程自动化、无障碍辅助工具或者单纯想研究AI Agent 怎么操作真实设备ARTEMIS 都值得花时间啃一遍。它不需要你是 Android 系统专家但你需要对 Android 的基本组件Activity、AccessibilityService、ADB有概念否则读代码会很吃力。下面我会从设计思路、核心细节、实操落地、踩坑排查四个维度把这个项目拆开讲透。2. 整体架构与设计思路拆解2.1 为什么不用传统的控件定位方案传统移动端自动化主流就两条路一是基于 UiAutomator / Espresso 的控件树定位二是基于图像模板匹配的坐标点击。前者的问题是强依赖开发者在代码里埋的resource-id或content-desc很多第三方 App 根本不规范甚至故意混淆后者的问题是分辨率、主题、字体一变模板就废了。ARTEMIS 的思路是让模型看截图。它把当前屏幕的截图 界面层级信息一起喂给多模态模型让模型输出下一步该点哪里、该输入什么。这个思路的关键优势是鲁棒性只要人眼能看懂的界面模型大概率也能看懂不依赖开发者埋点。代价是推理延迟和成本所以 ARTEMIS 在工程上做了不少优化来平衡。提示这里说的看截图不是纯视觉ARTEMIS 会同时利用 AccessibilityService 拿到的节点树做辅助相当于视觉 结构双通道比纯视觉方案稳得多。2.2 三层架构感知层、决策层、执行层ARTEMIS 的代码结构基本对应这三个层次理解了这个分层后面看代码就不会迷路。感知层Perception负责采集当前设备状态。它通过 Android 的AccessibilityService拿到当前窗口的节点树同时用MediaProjection或截图接口拿到屏幕图像。节点树提供精确的控件边界和文本截图提供视觉上下文两者结合后序列化成模型能吃的格式。决策层Reasoning是核心。它把感知层的数据、当前任务目标、历史操作记录一起构造成 prompt发给多模态大模型模型返回一个结构化的动作指令比如{action: tap, target: 登录按钮}或者{action: input, text: 13800138000}。这一层最关键的设计是动作空间的定义——动作类型不能太多否则模型容易输出非法指令也不能太少否则表达能力不够。执行层Execution把决策层的抽象动作翻译成具体的 Android 操作。tap会转成AccessibilityService的dispatchGestureinput会转成ACTION_SET_TEXTswipe会转成带路径的手势。执行完后再回到感知层形成闭环。2.3 动作空间设计少即是多我特别想强调动作空间这一点因为这是很多自研 Agent 容易翻车的地方。ARTEMIS 定义的动作类型大致包括点击、长按、输入文本、滑动、返回、等待、任务完成。就这么几种没有花里胡哨的。为什么这么设计因为动作类型越多模型输出非法组合的概率越高后端的校验和容错成本也越高。把动作收敛到最小集合用参数来表达差异比如点击的目标用自然语言描述反而让整个系统更稳。这个取舍思路我觉得比具体代码更值得学。动作类型参数底层实现典型场景taptarget自然语言描述dispatchGesture点击按钮、图标long_presstargetdispatchGesture长按长按菜单、拖拽起点inputtarget, textACTION_SET_TEXT填写表单swipedirection, distancedispatchGesture路径翻页、滚动列表back无performGlobalAction返回上一级waitduration延时等待加载finish无回调任务结束2.4 与同类方案的横向对比市面上做移动端 AI 自动化的不止 ARTEMIS 一家我把它和几个常见思路做个对比方便你判断是否适合自己。方案类型代表优势劣势控件树定位UiAutomator精确、快依赖埋点、易失效图像模板匹配Airtest直观分辨率敏感、维护成本高纯视觉大模型各类 GPT-4V 方案通用性强延迟高、成本高、坐标不准ARTEMIS 混合方案ARTEMIS鲁棒 相对精确需要设备权限、有推理延迟ARTEMIS 的定位很清晰它不追求极致的速度而是追求在真实、混乱的 App 环境里把事做完。这个定位决定了它的技术选型也决定了它更适合做复杂流程的自动化而不是高频的批量点击。3. 核心细节解析与实操要点3.1 环境准备别急着跑先把地基打牢ARTEMIS 跑起来需要几个前提条件缺一个都动不了。我按重要性排个序。第一是Android 设备或模拟器。真机优先因为模拟器的截图和手势在某些场景下和真机有差异。系统版本建议 Android 9 以上因为dispatchGesture这个 API 是 API 24 引入的低版本用不了。如果你用模拟器推荐 Android 11 或 12 的镜像兼容性最好。第二是AccessibilityService 权限。这是 ARTEMIS 的命脉没有它就拿不到节点树、也执行不了手势。安装后需要手动到设置 - 无障碍里开启。这里有个坑很多国产 ROM 会限制无障碍服务的后台存活需要额外加白名单否则跑一会儿就被杀了。第三是截图权限。如果用MediaProjection方案首次运行会弹一个系统授权框必须点允许。这个授权在部分设备上重启后会失效需要重新授权做长期运行的话要考虑这个因素。第四是模型接口。ARTEMIS 需要一个多模态模型的 API可以是云端服务也可以是本地部署的模型。云端的话延迟低但依赖网络本地的话隐私好但对设备算力有要求。我实测下来如果只是做流程验证云端接口上手最快。# 检查设备连接 adb devices # 查看设备 Android 版本 adb shell getprop ro.build.version.release # 查看设备分辨率影响坐标换算 adb shell wm size注意adb shell wm size拿到的分辨率是物理分辨率但 ARTEMIS 截图后可能会缩放坐标换算时一定要用同一套基准否则点击会偏。这个坑我踩过点了半天点不中最后发现是缩放比例没对齐。3.2 感知层的数据构造截图和节点树怎么融合感知层的核心任务是把当前屏幕变成模型能理解的输入。ARTEMIS 的做法是双通道截图负责视觉节点树负责结构。节点树的处理有个细节值得说。原始节点树非常冗长一个复杂页面可能有几百个节点直接塞给模型会爆 token。ARTEMIS 会做节点过滤和精简只保留可交互的节点可点击、可输入、可滚动去掉纯装饰性的容器节点然后把节点的文本、类型、边界框提取出来按阅读顺序排列。截图这边一般会做缩放。原始截图可能是 1080x2400直接传给模型既慢又贵通常会缩到 720p 甚至更低。但缩放后坐标要能映射回原始分辨率这个映射关系必须维护好。# 感知层数据构造的伪代码示意 def build_perception_input(screenshot, node_tree): # 1. 截图缩放 scaled_img, scale_ratio resize_image(screenshot, target_width720) # 2. 节点树精简 interactive_nodes [] for node in node_tree: if node.is_clickable or node.is_editable or node.is_scrollable: interactive_nodes.append({ text: node.text, type: node.class_name, bounds: node.bounds, clickable: node.is_clickable }) # 3. 组装 return { image: scaled_img, scale_ratio: scale_ratio, nodes: interactive_nodes }这个构造过程看起来简单但实际调优空间很大。比如节点按什么顺序排、文本要不要截断、边界框用绝对坐标还是相对坐标都会影响模型的理解准确率。我的经验是节点按从上到下、从左到右的阅读顺序排模型理解起来最顺。3.3 决策层的 prompt 工程怎么让模型稳定输出决策层是整个系统最玄学的部分因为它依赖 prompt 的质量。ARTEMIS 的 prompt 设计有几个要点我拆开讲。首先是角色设定。要让模型明确自己是一个操作 Android 手机的助手而不是通用聊天机器人。角色设定越具体模型越不容易跑偏。其次是动作格式约束。必须用严格的 JSON schema 约束输出并且给出几个 few-shot 示例。我试过不给示例模型经常输出自然语言描述而不是结构化指令后端解析直接崩。第三是历史上下文。把前面几步的操作和结果带上模型才能知道我上一步点了什么、现在到哪了。但历史不能无限带一般保留最近 5 到 10 步就够了太多反而干扰。{ role: android_operator, task: 登录某应用, history: [ {step: 1, action: tap, target: 登录入口, result: success}, {step: 2, action: input, target: 手机号输入框, text: 138****8000, result: success} ], current_screen: { nodes: [...], image: base64 }, instruction: 根据当前屏幕输出下一步动作严格使用 JSON 格式 }提示prompt 里的 few-shot 示例要覆盖所有动作类型尤其是容易混淆的比如 tap 和 long_press。示例质量直接决定模型输出的稳定性这块值得反复打磨。3.4 执行层的坐标换算与手势模拟执行层最容易出 bug 的地方是坐标换算。模型输出的坐标是基于缩放后截图的但dispatchGesture需要的是屏幕物理坐标。中间要经过缩放坐标 → 原始截图坐标 → 屏幕物理坐标每一步都可能引入误差。ARTEMIS 的做法是尽量让模型输出目标描述而不是精确坐标比如点击登录按钮然后由执行层根据节点树的边界框算出中心点。这样即使模型对坐标的判断有偏差只要它认对了目标点击就是准的。这个设计很聪明把认目标和算坐标解耦了。// 执行层点击的伪代码 public void performTap(String targetDescription) { // 1. 从节点树里找到匹配的节点 AccessibilityNodeInfo targetNode findNodeByDescription(targetDescription); if (targetNode ! null) { // 2. 用节点边界框算中心点 Rect bounds new Rect(); targetNode.getBoundsInScreen(bounds); float centerX bounds.centerX(); float centerY bounds.centerY(); // 3. 构造手势 Path path new Path(); path.moveTo(centerX, centerY); GestureDescription gesture new GestureDescription.Builder() .addStroke(new StrokeDescription(path, 0, 100)) .build(); // 4. 执行 dispatchGesture(gesture, null, null); } else { // 5. 找不到节点回退到模型给的坐标 fallbackToCoordinateTap(targetDescription); } }这个节点优先、坐标兜底的策略是我认为 ARTEMIS 工程化做得最扎实的地方。纯靠模型坐标误差能到几十像素靠节点边界框误差基本在个位数。4. 实操过程与核心环节实现4.1 从零跑通一个登录流程光讲原理没意思我带你走一遍完整的实操。目标让 ARTEMIS 自动完成一个 App 的登录流程。第一步部署 ARTEMIS 到设备。把编译好的 APK 装到手机上开启无障碍服务。这一步如果卡住八成是 ROM 的限制去设置里把后台弹出界面自启动之类的权限都打开。第二步配置模型接口。在 ARTEMIS 的设置里填入模型 API 地址和密钥。如果是本地模型填本地服务的地址。填完先点测试连接确认能通再往下走。第三步定义任务。ARTEMIS 一般支持用自然语言描述任务比如打开某应用输入手机号和密码点击登录。任务描述要具体别写帮我登录一下模型会懵。第四步启动并观察。启动后 ARTEMIS 会开始循环截图 → 推理 → 执行 → 再截图。你要盯着看它每一步做了什么尤其是第一次跑很容易在某个界面卡住。第五步记录和复盘。跑完后看日志哪一步耗时最长、哪一步失败重试了这些都是优化的切入点。# 实时查看 ARTEMIS 日志 adb logcat | grep -i artemis # 如果日志太多按 tag 过滤 adb logcat -s ARTEMIS:D4.2 关键参数调优延迟和准确率的平衡ARTEMIS 跑起来后你会发现两个指标在打架响应速度和操作准确率。调优的本质就是在这两者之间找平衡点。截图质量截图分辨率越高模型看得越清楚但传输和推理越慢。我实测 720p 是个不错的平衡点再低模型就开始认不清小字了。推理频率每步都推理最准但慢。有些场景可以合并步骤比如输入手机号和输入密码如果在一个页面可以让模型一次输出两个动作。ARTEMIS 支持批量动作但要小心批量动作里有一个失败后面的可能全乱。重试策略某一步失败后是重试还是重新感知我的经验是点击类失败重试一次输入类失败直接重新感知。因为点击失败往往是坐标偏了重试可能碰巧中输入失败往往是焦点没对上重试没用。参数保守值激进值建议截图宽度1080540720单步超时30s10s15s失败重试次数312历史步数1035推理温度0.10.70.2注意推理温度这个参数很多人忽略。做自动化任务温度一定要低0.1 到 0.3否则模型每次输出都不一样流程没法复现。我见过有人用默认温度 0.7结果同一个任务跑十次十种结果排查了半天才发现是温度的问题。4.3 多应用跳转的处理真实任务经常要跨应用比如从微信复制一段文字粘贴到备忘录。ARTEMIS 处理跨应用跳转时感知层会重新采集新应用的界面决策层需要知道现在换应用了。这里有个细节跨应用时前一个应用的上下文要清掉否则模型会拿着旧界面的信息去操作新界面。ARTEMIS 的做法是检测包名变化一旦包名变了就重置历史上下文只保留任务目标。# 跨应用检测的伪代码 def check_app_switch(current_package, last_package): if current_package ! last_package: # 应用切换了重置上下文 reset_context(keep_taskTrue, keep_historyFalse) return True return False这个逻辑看着简单但不做的话跨应用任务基本必挂。我一开始自己写 Agent 的时候就栽在这模型拿着微信的界面描述去操作备忘录输出全是无效动作。4.4 任务完成的判定怎么知道任务做完了这是自动化里一个容易被低估的问题。ARTEMIS 支持几种判定方式模型主动输出finish动作、检测到特定界面元素、或者超时。最稳的是组合判定模型说完成了同时检测到预期界面比如登录后的首页双重确认才结束。只靠模型判断它有时候会幻觉说完成了只靠界面检测又不够灵活。def is_task_complete(model_output, current_screen, expected_elements): model_says_done model_output.get(action) finish screen_matches all( element in current_screen for element in expected_elements ) # 双重确认 return model_says_done and screen_matches5. 常见问题与排查技巧实录5.1 无障碍服务被系统杀掉怎么办这是最高频的问题没有之一。表现是跑着跑着突然不动了日志里也没有新输出。原因通常是国产 ROM 的后台管理策略。解决办法分三层第一层在设置里给 ARTEMIS 加自启动白名单、关闭电池优化第二层在最近任务里给 ARTEMIS 加锁防止被一键清理第三层如果还不行用adb命令定期检查服务状态掉了就重新拉起。# 检查无障碍服务是否在运行 adb shell settings get secure enabled_accessibility_services # 如果输出里没有 artemis说明被关了提示有些 ROM 的无障碍服务在锁屏后会断做长时间任务时要么保持屏幕常亮要么接受这个限制把任务拆成短流程。5.2 模型输出格式错误怎么兜底模型偶尔会输出不符合 JSON schema 的内容比如多了个逗号、少了个引号或者干脆输出一段自然语言。ARTEMIS 的兜底策略是先尝试解析失败就重试一次再失败就跳过这一步重新感知。我自己的经验是在 prompt 里加一句只输出 JSON不要任何解释能大幅降低格式错误率。另外解析时用宽容一点的解析器比如允许尾随逗号也能救回不少。错误类型表现解决JSON 语法错解析异常宽容解析 重试动作类型非法未知 action校验白名单非法则重试目标不存在找不到节点回退坐标点击坐标越界点击无效边界裁剪 重新感知5.3 点击不生效的排查思路点击不生效原因可能有很多层。我总结了一个排查顺序从外到内。先看坐标对不对。把模型输出的坐标画到截图上看是不是落在目标控件上。如果偏了检查缩放比例。再看控件是否可点击。有些控件看着能点实际clickable是 false点击事件被父容器拦截了。这种情况要往上找可点击的父节点。最后看是否有遮挡。弹窗、悬浮窗、输入法都可能挡住目标。ARTEMIS 的节点树里能看到遮挡层但模型不一定能理解需要在 prompt 里提醒它注意是否有弹窗。# 排查点击问题的辅助函数 def debug_tap_failure(target, screenshot, node_tree): # 1. 画出目标位置 draw_marker(screenshot, target.coords) # 2. 检查该位置是否有可点击节点 node_at_point find_node_at(node_tree, target.coords) print(f该位置节点: {node_at_point}) print(f是否可点击: {node_at_point.is_clickable if node_at_point else None}) # 3. 检查是否有遮挡 overlays find_overlays(node_tree) print(f遮挡层: {overlays})5.4 输入文本失败的几种情况输入失败比点击失败更隐蔽因为有时候文本看起来输进去了实际没生效。常见情况有三种。一是焦点没对上。点击输入框后焦点可能没落到预期的输入框上尤其是页面上有多个输入框时。解决办法是点击后先确认焦点再输入。二是输入法干扰。有些 App 的输入框会唤起自定义输入法ACTION_SET_TEXT可能不生效。这种情况要用ACTION_PASTE或者模拟按键。三是文本被截断。长文本输入时有些输入框有长度限制超出部分被截断。ARTEMIS 不会自动检测这个需要任务定义时注意。// 更稳的输入方式先聚焦再设置文本 public void performInput(AccessibilityNodeInfo node, String text) { // 1. 聚焦 node.performAction(AccessibilityNodeInfo.ACTION_FOCUS); // 2. 清空已有内容 Bundle clearBundle new Bundle(); clearBundle.putCharSequence( AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, ); node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, clearBundle); // 3. 设置新文本 Bundle bundle new Bundle(); bundle.putCharSequence( AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text); node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, bundle); }5.5 性能优化的几个实操技巧跑通之后下一步就是让它跑得快、跑得稳。分享几个我实测有效的技巧。减少截图频率。不是每一步都需要重新截图如果上一步是输入文本界面结构没变可以复用上一次的感知结果。ARTEMIS 支持这种缓存但要小心界面可能在你没感知的时候变了。并行化感知和推理。截图和节点树采集可以并行模型推理也可以和下一步的截图准备并行。这块需要改代码但收益明显。用更小的模型做简单决策。不是所有步骤都需要大模型比如点击返回这种用小模型甚至规则就能判断。ARTEMIS 支持模型路由简单步骤走小模型复杂步骤走大模型能省不少成本。优化手段收益代价感知结果缓存减少 30% 截图可能读到旧界面并行感知推理降低 20% 延迟代码复杂度上升模型路由降低 50% 成本需要维护路由规则批量动作减少推理次数容错性下降6. 我对 ARTEMIS 这类方案的判断ARTEMIS 最值得学的地方不是它用了多先进的模型而是它把AI 操作手机这件事拆成了可工程化的三层并且在每一层都做了务实的取舍。感知层用双通道保证鲁棒决策层用严格 schema 保证可控执行层用节点优先保证精确。这套思路你换成别的模型、别的平台照样能用。我自己在实际操作中的体会是这类方案的上限取决于感知层的质量而不是模型有多强。截图糊了、节点树缺了再强的模型也白搭。所以如果你要基于 ARTEMIS 做二次开发我建议先把感知层打磨好把截图分辨率、节点过滤规则、坐标映射这些基础打牢再考虑换模型、调 prompt。最后再分享一个小技巧调试阶段把每一步的截图、节点树、模型输入输出都存下来按时间戳命名。出问题的时候回放这些记录比看日志快十倍。这个习惯我从做自动化第一天就养成了到现在还在用。

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案