1. 项目概述当大模型“看见”并“操控”你的手机最近在捣鼓一个挺有意思的东西叫MobileAgent。简单来说它试图解决一个我们日常开发或测试中很头疼的问题如何让AI像真人一样看懂手机屏幕上的内容并自动执行点击、滑动、输入等操作。这听起来像是科幻电影里的场景但结合当下火热的多模态大模型这事儿正变得触手可及。传统的UI自动化框架比如Appium、Airtest本质上是一套基于坐标或元素定位的脚本。你得告诉它“去点击ID为‘login_button’的按钮”或者“在坐标200 300的位置滑动”。这种方式严重依赖于应用的结构化信息如UI层级树一旦应用界面改版、元素ID变化或者遇到动态加载的内容脚本就很容易“瞎掉”维护成本极高。而MobileAgent的思路则完全不同它让AI模型直接“看”手机屏幕截图理解屏幕上有什么文本、图标、布局然后自主决策下一步该做什么最后再通过模拟点击等方式执行。这就像给电脑装上了一双“眼睛”和一个“大脑”让它能像人一样与手机交互。这个项目特别适合几类朋友一是对多模态大模型应用落地方向感兴趣的开发者或研究者想看看大模型在具身智能Embodied AI或智能体Agent领域的实际玩法二是被繁琐的UI自动化测试或重复性手机操作折磨的测试工程师和业务人员寻求更智能、更健壮的自动化方案三是任何想探索下一代人机交互可能性的技术爱好者。接下来我会结合自己的实践拆解MobileAgent的核心思路、技术实现细节并分享一路踩坑填坑的经验。2. 核心架构与多模态大模型选型要让一个Agent能“看懂”并“操作”手机其核心架构通常包含几个关键模块环境感知Perception、任务理解与规划Planning、动作执行Execution以及一个协调各模块的“大脑”——智能体核心Agent Core。MobileAgent正是围绕这个逻辑构建的。2.1 系统工作流拆解一个完整的MobileAgent工作流可以概括为“观察-思考-行动”的循环观察ObservationAgent通过ADBAndroid Debug Bridge或其他屏幕捕获工具获取当前手机屏幕的截图。这是整个系统的“眼睛”输入是纯粹的像素信息。理解与规划Understanding Planning这是最核心的一步。截图被送入一个多模态大模型通常是视觉语言模型VLM。模型需要完成两项关键任务屏幕内容解析识别截图中的所有UI元素包括按钮、输入框、图标、文本内容及其相对位置。这不仅仅是OCR光学字符识别还需要理解元素的类型和功能比如这是一个“搜索框”还是一个“消息通知”。任务分解与下一步动作生成根据用户给定的高层指令如“在微信中给张三发一条‘晚上开会’的消息”模型需要理解当前屏幕状态并规划出达成目标所需的一系列原子操作Atomic Action。例如先“点击通讯录”再“点击搜索框”输入“张三”再“点击联系人”最后“点击输入框”并输入文本。在每一步模型只生成当前屏幕状态下最应该执行的一个动作。行动Action模型生成的原子操作如tap(x, y),swipe(start_x, start_y, end_x, end_y),text(“输入内容”)被转换成具体的ADB命令或模拟器指令发送给手机执行。循环与状态更新执行动作后手机会进入新状态。Agent再次捕获屏幕截图开始新一轮的“观察-思考-行动”直到任务完成或无法继续。这个循环的健壮性极度依赖于第二步中多模态大模型的能力。2.2 多模态大模型的核心作用与选型考量为什么必须是多模态大模型而不是传统的CV模型规则引擎因为手机屏幕的理解是一个典型的视觉-语言联合任务。模型需要将视觉信息图标形状、布局、颜色与语义信息按钮上的文字、应用上下文深度融合才能做出准确判断。一个“返回”箭头图标在不同应用、不同位置可能代表不同含义需要结合周围文本综合理解。目前可用于此类任务的多模态大模型主要有几类通用视觉语言模型General VLM如GPT-4V、Gemini Pro Vision、Claude 3。它们能力强大无需专门训练通过设计好的提示词Prompt就能完成屏幕理解和指令生成。优点是开箱即用泛化能力强缺点是API调用有成本和延迟且对于非常具体的UI元素如某个特定App的独特组件识别可能不够精确。专用屏幕理解模型例如Google的Pix2Struct、微软的ScreenAI以及一些在移动端UI数据集如RICO上微调过的模型。这类模型针对屏幕截图、网页、文档等结构化图像进行过优化在元素检测和功能分类上可能更准。但通常需要自行部署且指令跟随和任务规划能力可能不如通用大模型。开源多模态模型如LLaVA、Qwen-VL、CogVLM等。这类模型可以私有化部署数据隐私性好且经过特定微调后可以在专用任务上达到接近通用大模型的水平。这是目前很多研究项目和追求可控性的实践者的首选。实操心得模型选型的权衡在项目初期我强烈建议从通用大模型API如GPT-4V开始。虽然每次调用花点钱但它能帮你快速验证整个流程的可行性并积累高质量的指令-动作配对数据。当你摸清了任务范式和Prompt技巧后再考虑用这些数据去微调一个较小的开源模型如LLaVA以实现成本控制和离线部署。直接上手训练专用模型很容易在数据质量和任务定义上踩坑。2.3 动作空间与执行层设计模型生成的指令需要被翻译成手机能执行的动作。常见的原子动作空间包括tap(x, y): 在屏幕坐标(x, y)处点击。swipe(x1, y1, x2, y2): 从(x1, y1)滑动到(x2, y2)。text(“string”): 输入文本字符串。keyevent(“KEYCODE_HOME”): 发送系统按键事件如Home、Back。long_press(x, y): 长按。这里的一个关键设计点是坐标是绝对坐标还是相对坐标绝对坐标依赖于固定的屏幕分辨率换台设备就可能失效。更鲁棒的做法是让模型输出基于屏幕元素的相对描述比如“点击‘登录’按钮”再由一个额外的定位模块将描述映射为坐标。但在MobileAgent的纯视觉范式中更常见的做法是让VLM直接预测坐标或者预测一个可交互元素的边界框然后取其中点作为点击位置。这要求模型对空间位置有较好的理解。执行层则相对成熟利用adb shell input命令或uiautomator等工具即可实现上述动作。3. 实现细节从截图到动作的关键步骤理解了架构我们深入到实现层面。搭建一个可用的MobileAgent以下几个环节需要精心设计。3.1 环境搭建与屏幕信息获取首先需要一个可被控制的Android设备或模拟器。我推荐使用Android模拟器如Android Studio自带的或Genymotion便于调试和批量测试。确保ADB已正确连接。获取屏幕截图很简单adb exec-out screencap -p screen.png但高质量的输入是成功的一半。原始截图可能尺寸过大直接送给大模型既昂贵API按token收费又低效。通常需要做预处理缩放将截图缩放到模型支持的输入尺寸如336x336, 448x448, 672x672。保持宽高比很重要避免UI元素变形。图像编码将图像转换为Base64字符串用于API调用或模型所需的张量格式。可选的信息增强有时纯视觉信息不足。可以考虑将当前活动Activity名称、包名通过adb shell dumpsys window windows获取后以文本形式补充给模型作为上下文线索。3.2 设计与大模型对话的“提示词工程”这是连接人类意图和AI行动的桥梁也是整个项目的“玄学”所在。一个糟糕的Prompt会导致模型胡言乱语而一个好的Prompt能让它像资深测试工程师一样工作。我们的Prompt需要清晰定义几个部分系统角色System Role告诉模型它是什么。例如“你是一个手机UI自动化助手能够理解屏幕截图并生成操作指令。”任务描述Task Description明确输入输出格式。例如“我将给你一张手机屏幕截图和一条用户指令。你需要分析屏幕然后只生成一个下一步的原子操作。操作格式必须是tap(x, y)、swipe(x1, y1, x2, y2)、text(“content”)、keyevent(“key”)中的一种。坐标是相对于截图分辨率如720x1280的整数。”思维链Chain-of-Thought要求鼓励模型先“思考”再“行动”。例如“请先简要描述屏幕上的关键可交互元素和当前状态然后根据用户指令推理出下一步最合适的操作。”输出限制Output Constraints严格限制输出格式便于程序解析。例如“你的回答必须严格遵循以下JSON格式{“description”: “对屏幕和推理的简短描述”, “action”: “操作命令字符串”}。不要输出任何其他内容。”一个综合的Prompt示例你是一个专业的手机UI自动化智能体。你的任务是观察手机屏幕截图并执行用户指令。 当前屏幕分辨率720x1280。 用户指令[[USER_GOAL_HERE]]。 请遵循以下步骤 1. 描述屏幕上的主要元素如按钮、文本框、图标及其上的文字。 2. 结合用户指令分析当前状态和下一步目标。 3. 生成且仅生成一个下一步原子操作。操作必须是以下格式之一 - tap(x, y) - swipe(x1, y1, x2, y2) - text(输入文本) - keyevent(KEYCODE_BACK) 等 4. 将结果以JSON格式输出{thought: 你的推理过程, action: 操作命令} 截图如下[[IMAGE_DATA_BASE64]]注意事项Prompt的迭代与评估不要指望一次写出完美的Prompt。你需要准备一个涵盖各种场景主屏、列表页、弹窗、输入状态的测试截图集用不同的Prompt去试验评估模型输出的准确率和可靠性。关键评估点包括操作类型是否正确、坐标是否落在目标元素上、推理逻辑是否合理。这个过程可能很耗时但至关重要。3.3 动作解析与安全执行拿到模型输出的JSON后需要解析出action字段。这里要特别注意异常处理格式校验正则表达式匹配操作类型和参数确保格式正确。坐标边界检查确保x, y坐标在屏幕分辨率范围内防止模型“乱点”到系统禁区。操作延迟与状态同步执行一个点击后系统需要时间响应如加载新页面。必须在操作后添加适当的等待如1-3秒然后再截取下一张图。盲目连续操作会导致Agent在页面加载完成前就误判状态。失败处理与重试如果执行动作后屏幕状态未发生预期变化可通过图像哈希或关键元素检测判断应触发重试机制如用不同描述重新生成动作或失败处理逻辑如记录日志并停止。import re import json import subprocess def parse_and_execute(model_output, screen_width720, screen_height1280): try: result json.loads(model_output) action_str result.get(action, ).strip() # 解析tap操作 tap_match re.match(rtap\((\d),\s*(\d)\), action_str) if tap_match: x, y int(tap_match.group(1)), int(tap_match.group(2)) if 0 x screen_width and 0 y screen_height: subprocess.run(fadb shell input tap {x} {y}, shellTrue) print(f执行点击: ({x}, {y})) return True else: print(f坐标越界: ({x}, {y})) return False # 解析text操作 text_match re.match(rtext\((.)\), action_str) if text_match: text text_match.group(1) # 注意adb输入文本需要对空格和特殊字符进行转义 subprocess.run(fadb shell input text {text}, shellTrue) print(f输入文本: {text}) return True # ... 解析其他操作 except json.JSONDecodeError: print(模型输出非JSON格式) except Exception as e: print(f执行出错: {e}) return False4. 实战演练构建一个简易的MobileAgent我们用一个具体例子串联全过程让Agent在手机桌面找到并打开“设置”应用。4.1 步骤一初始化与首次观察首先确保手机处于主屏幕。我们通过ADB截图并缩放至672x672假设我们的VLM支持此尺寸。import subprocess import base64 from PIL import Image import io def take_screenshot(output_pathscreen.png, resize_to(672, 672)): # 获取截图 subprocess.run(adb exec-out screencap -p raw_screen.png, shellTrue) # 打开并缩放 img Image.open(raw_screen.png) img.thumbnail(resize_to, Image.Resampling.LANCZOS) img.save(output_path) # 转换为base64 buffered io.BytesIO() img.save(buffered, formatPNG) img_str base64.b64encode(buffered.getvalue()).decode() return img_str, img.size # 返回base64和缩放后的尺寸 b64_image, (resized_w, resized_h) take_screenshot()此时我们得到了一个Base64编码的缩放后图像以及缩放后的尺寸672, 672。但注意模型预测的坐标是基于这个缩放后图像的。我们需要一个映射关系将预测坐标转换回原始屏幕坐标才能执行。4.2 步骤二构造Prompt并调用大模型我们使用GPT-4V的API作为示例需准备API Key。import openai import os openai.api_key os.getenv(OPENAI_API_KEY) def ask_gpt4v(image_base64, user_goal, original_resolution(1080, 2340)): prompt f你是一个手机UI自动化智能体。当前屏幕原始分辨率是{original_resolution[0]}x{original_resolution[1]}但给你的截图已缩放。 用户指令{user_goal}。 请分析截图生成一个下一步操作。操作格式必须为tap(x, y), swipe(x1, y1, x2, y2), text(...), keyevent(...)。 你预测的坐标应基于我给你的这张缩放截图尺寸为672x672。请直接输出操作命令不要有其他文字。 response openai.ChatCompletion.create( modelgpt-4-vision-preview, messages[ { role: user, content: [ {type: text, text: prompt}, { type: image_url, image_url: { url: fdata:image/png;base64,{image_base64} }, }, ], } ], max_tokens300, ) return response.choices[0].message.content在这个Prompt中我明确告诉了模型截图的缩放信息并要求它基于缩放图预测坐标。这是一个关键点。4.3 步骤三坐标映射与动作执行假设模型返回了tap(320, 150)。这个坐标是相对于672x672图片的。我们需要将其映射回原始屏幕坐标例如1080x2340。def scale_coordinates(x_pred, y_pred, pred_img_size, original_screen_size): 将模型预测坐标映射回原始屏幕坐标。 x_pred, y_pred: 模型预测的坐标基于缩放图。 pred_img_size: 缩放图的尺寸如 (672, 672)。 original_screen_size: 手机屏幕原始分辨率如 (1080, 2340)。 scale_x original_screen_size[0] / pred_img_size[0] scale_y original_screen_size[1] / pred_img_size[1] # 通常缩放是等比例的但如果不是这里需要更复杂的处理如保持宽高比缩放后的黑边 x_original int(x_pred * scale_x) y_original int(y_pred * scale_y) # 确保坐标在屏幕内 x_original max(0, min(x_original, original_screen_size[0] - 1)) y_original max(0, min(y_original, original_screen_size[1] - 1)) return x_original, y_original # 假设模型输出是 tap(320, 150) action_str ask_gpt4v(b64_image, 打开设置应用) print(f模型输出: {action_str}) # 可能输出: tap(320, 150) # 解析坐标 match re.search(rtap\((\d),\s*(\d)\), action_str) if match: x_pred, y_pred int(match.group(1)), int(match.group(2)) x_ori, y_ori scale_coordinates(x_pred, y_pred, (672, 672), (1080, 2340)) # 执行点击 subprocess.run(fadb shell input tap {x_ori} {y_ori}, shellTrue) print(f映射后点击原始坐标: ({x_ori}, {y_ori}))点击后等待2秒然后再次截图观察是否成功进入了设置应用。如果屏幕变化符合预期则任务完成否则可能需要根据新屏幕重新规划。4.4 一个更复杂的任务链示例发微信消息任务“给联系人‘张三’发送消息‘你好我是MobileAgent’”。 这个任务需要多步完成Agent需要自主规划在主屏找到并点击微信图标。在微信主界面点击“通讯录”或“搜索框”。输入“张三”并点击搜索结果。进入聊天窗口后点击输入框。输入文本“你好我是MobileAgent”。点击发送按钮。每一步都遵循上述的“截图-Prompt-解析-执行-等待”循环。关键在于模型需要根据每一步的新屏幕状态决定当前步骤该做什么。这完全依赖于模型对任务上下文的理解和记忆能力或通过Prompt注入历史信息。5. 性能优化与工程化挑战将原型转化为稳定可用的系统会遇到诸多挑战。5.1 延迟与成本问题使用云端大模型API如GPT-4V的延迟和成本是不可忽视的。一次“截图-调用-执行”循环可能需要数秒甚至十几秒这对于需要快速交互的任务来说是难以接受的。成本方面高频率调用也是一笔开销。优化策略缓存与预测对于常见的、静态的界面如应用主屏可以缓存模型的分析结果下次遇到相同界面通过图像哈希判断时直接使用缓存的动作映射表无需再次调用模型。分层模型策略使用轻量级本地模型处理简单、重复性高的决策如“滑动列表”仅将复杂、不确定的屏幕交给大型VLM处理。转向开源模型最终方案往往是微调一个像LLaVA或Qwen-VL这样的中型模型部署在本地或内网GPU服务器上。虽然单次推理精度可能略低于GPT-4V但延迟和成本可控且数据隐私有保障。5.2 鲁棒性提升让Agent更“可靠”模型会“犯傻”比如点击位置有偏差、误判元素类型、在加载中转圈时盲目操作。提升措施动作后验证执行动作后不仅等待固定时间还主动验证状态是否改变。例如比较动作前后截图的感知哈希pHash如果变化超过阈值则认为页面已刷新或者用一个轻量级分类器检测特定元素如“加载中”图标是否消失。多模态确认结合ADB获取的当前Activity名称、窗口层级信息adb shell dumpsys activity作为辅助判断与视觉信息交叉验证。设计重试与回退机制当连续N步屏幕状态无显著变化或模型连续生成无效操作时触发回退策略。例如执行keyevent(“KEYCODE_BACK”)返回上一步或用更简化的Prompt重新分析当前屏幕。细化动作空间除了基础点击可以引入更精细的操作如tap_center_of(“搜索框”)让模型输出语义描述再由一个本地的OCR元素检测模块来精确定位降低模型直接回归坐标的难度。5.3 针对复杂场景的Prompt增强一些复杂场景需要特别设计的Prompt来应对列表滑动当目标元素不在当前视图内时模型需要生成滑动操作。Prompt中可以明确指示“如果目标不在当前屏幕请生成一个向上的滑动操作来滚动列表。”弹窗处理突然出现的权限弹窗、广告弹窗会打断任务流。Prompt中需要加入“优先处理或关闭任何弹窗如‘允许’、‘拒绝’、‘跳过’按钮再继续主任务。”状态记忆对于多步任务需要在Prompt中注入简短的历史操作序列帮助模型理解当前处于任务流的哪个阶段。例如“你刚刚打开了微信并点击了通讯录。现在屏幕显示的是通讯录列表。用户指令是找到‘张三’。”6. 常见问题与实战避坑指南在实际搭建和测试MobileAgent的过程中我遇到了不少坑这里总结一下。6.1 模型输出格式不稳定这是最常见的问题。即使Prompt里要求输出JSON模型有时还是会加上解释性文字。解决方案后处理解析要健壮不要依赖完美的JSON解析。使用正则表达式从返回文本中“抠”出操作命令。利用API的响应格式控制像OpenAI的API支持response_format{ “type”: “json_object” }参数可以强制模型输出JSON但需在系统消息中说明。这能极大提高格式稳定性。输出归一化层设计一个专门的模块来处理模型输出尝试多种解析方式并有一个默认的降级策略例如如果解析失败则执行一个安全操作如“返回桌面”。6.2 坐标预测不准模型预测的点击坐标经常偏离目标元素中心导致操作失败。解决方案Prompt明确要求在Prompt中强调“请点击目标元素的中心区域”。后处理平滑不要完全信任单次预测。可以连续采样几次模型的输出如果模型支持生成多个候选或者对同一屏幕多次调用模型然后对预测的坐标点集取中位数或均值这能过滤掉明显的异常点。结合元素检测使用一个专门的UI元素检测模型如基于YOLO微调的先框出所有可能元素再让大模型从这些候选框中选择并微调坐标。这相当于把“定位”和“识别”任务解耦提高了鲁棒性。6.3 任务陷入死循环Agent有时会在两个状态间来回切换或者重复无效操作。解决方案维护操作历史记录最近N步的屏幕哈希和操作。如果检测到循环相同的屏幕状态再次出现则强制跳出尝试替代操作或上报失败。设置最大步数为任何任务设置一个步数上限如50步超过则判定为失败防止无限循环。引入人工干预点对于关键任务步骤可以设置检查点由人工确认或提供简单规则如“如果看到这个界面就执行这个固定操作”来引导Agent。6.4 对动态内容和复杂交互束手无策对于视频播放界面、游戏界面或高度动态的图表纯视觉模型很难理解。解决方案混合方法在这些特定领域回归或结合传统的自动化方法。例如对于视频播放器可以通过ADB获取媒体会话信息来控制播放/暂停而非依赖视觉识别播放按钮。领域微调如果应用场景固定可以收集大量该应用下的截图-动作数据对VLM进行领域适应性微调提升其在特定动态内容上的理解能力。搭建MobileAgent的过程是一个在不断试错中平衡“智能”与“可控”的过程。大模型带来了前所未有的理解和规划能力但其不确定性也引入了新的复杂性。我的体会是不要追求一个全知全能、完全无需人工干预的Agent这在现阶段既不经济也不现实。更务实的路径是针对特定领域如特定几个App的自动化测试、数据录入打造一个在有限环境下高度可靠、并能与简单规则引擎协同工作的智能辅助系统。它可能无法处理所有边界情况但能覆盖80%的常规场景已经能带来巨大的效率提升。剩下的20%或许正是留给人类监督和规则补丁的空间。