移动端自动化测试:LLM角色级模型拆分与多模型协作实践

📅 发布时间:2026/10/16 10:55:50
移动端自动化测试:LLM角色级模型拆分与多模型协作实践
1. 移动端自动化测试的模型拆分困局做过移动端自动化的人都有一个共识脚本写起来不难难的是让它稳定跑下去。UI 元素定位、页面跳转时序、弹窗干扰、设备差异每一个环节都能让一条昨天还跑得好好的用例今天直接红掉。前几年大家靠 UiAutomator、Appium 这类框架硬扛后来开始引入图像识别和 OCR 做兜底再后来大模型来了很多人第一反应是让 LLM 直接看截图告诉我点哪里。这个思路方向没错但真正落地的时候问题就来了。你把截图丢给一个通用大模型让它输出点击登录按钮它确实能告诉你大概位置但接下来呢坐标怎么换算不同分辨率怎么办元素层级信息要不要一起给它如果页面有十几个可交互元素它怎么知道该点哪个更关键的是一个页面上往往同时存在理解当前界面是什么和决定下一步做什么两个完全不同的认知任务用一个模型一把梭效果和成本都很难看。DroidRun 和 Mobilerun 这两个项目在移动端自动化圈子里逐渐被讨论核心原因就是它们没有走一个大模型包打天下的路子而是做了角色级的模型拆分。所谓角色级拆分就是不再把 LLM 当成一个万能的黑盒而是按照自动化流程中不同环节的认知需求把任务分配给不同角色、不同规格的模型去处理。这个思路听起来简单但真正拆得好、拆得合理里面有不少门道。这篇文章适合两类人看一类是已经在做移动端自动化、想引入 LLM 能力但不知道怎么落地的测试开发同学另一类是对 Agent 架构感兴趣、想了解多模型协作在具体场景里怎么设计的技术人。我会从整体设计思路讲起把角色拆分的逻辑、每个角色的职责边界、模型选型的考量、实操中的配置方法、以及踩过的坑都摊开来说。读完你至少能搞清楚一件事为什么移动端自动化里的 LLM 不该只有一个角色以及怎么拆才不给自己挖坑。2. 角色级模型拆分的整体设计思路2.1 为什么不能用一个模型干所有事先想清楚移动端自动化到底涉及哪些认知任务。我把它拆成四类第一类是界面理解。给你一张截图你要能说出当前是什么页面、有哪些可交互元素、每个元素大概是什么功能。这是纯视觉理解任务需要模型有强的多模态能力。第二类是动作决策。基于当前界面状态和测试目标决定下一步该点哪里、输入什么、滑动还是等待。这是规划和推理任务需要模型有强的逻辑能力。第三类是结果校验。执行完一个动作后判断页面是否跳转正确、断言是否通过。这是对比和判断任务需要模型能理解预期和实际的差异。第四类是异常恢复。遇到弹窗、加载失败、权限请求等意外情况时决定怎么处理。这是策略选择任务需要模型有一定的常识和容错判断。这四类任务的输入输出形态、对模型能力的要求、以及对延迟和成本的敏感度都不一样。界面理解需要多模态、可以接受稍高的延迟动作决策需要强推理、但输入可以压缩成结构化描述结果校验相对简单、可以用小模型快速跑异常恢复需要常识推理、但触发频率低。如果你用一个通用大模型全包会发生什么要么模型太大、每步都调一次成本爆炸要么模型太小、复杂页面理解不了。而且不同任务的 prompt 混在一起模型很容易串味——本该做决策的时候它在描述界面本该校验的时候它在规划下一步。2.2 角色拆分的核心原则DroidRun 和 Mobilerun 的做法本质上是把上面四类任务映射到不同的模型角色上。拆分的时候有几条原则值得参考原则一按认知任务类型拆不按调用顺序拆。很多人第一反应是按流程拆——先理解、再决策、再执行、再校验。但这样拆的问题是同一个模型可能同时承担理解和决策职责还是混的。正确的做法是按这个任务需要什么能力来拆理解归理解、决策归决策。原则二每个角色有明确的输入输出契约。界面理解角色的输出必须是结构化的元素列表而不是一段自然语言描述。动作决策角色的输入必须是结构化的界面状态加目标输出必须是明确的动作指令。契约清晰了角色之间才能解耦才能单独替换和优化。原则三模型规格和任务难度匹配。不是所有角色都需要最强的模型。界面理解可以用中等规模的多模态模型动作决策可以用推理能力强但不需要多模态的模型结果校验甚至可以用小模型或者规则引擎兜底。这样整体成本和延迟都可控。原则四角色之间通过结构化中间态通信。不要让角色之间传自然语言那样信息损失大、还容易产生歧义。界面理解的输出应该是一份结构化的页面描述动作决策基于这份描述工作校验角色也基于这份描述做对比。2.3 拆分带来的实际收益我实测下来角色拆分最直接的收益有三个。成本下降明显。原来每步都调多模态大模型现在只有界面理解这一步需要多模态动作决策和校验用纯文本模型就行。多模态模型的调用成本通常是纯文本的好几倍拆完之后整体成本能降一半以上。稳定性提升。每个角色的 prompt 更聚焦模型不容易跑偏。界面理解角色只负责描述页面不会自作主张去决策动作决策角色只负责选动作不会去描述它看到了什么。职责单一之后输出格式的稳定性好了很多。可调试性变强。出问题的时候你能快速定位是哪个角色出了问题。是界面理解错了还是决策逻辑不对还是校验太严格每个角色的输入输出都有日志排查起来比一个大黑盒清晰得多。3. 各角色的职责边界与模型选型3.1 界面理解角色多模态能力是硬门槛界面理解角色的任务很明确输入一张截图输出当前页面的结构化描述。这个描述至少包含页面类型首页、列表页、详情页、弹窗等、可交互元素列表每个元素包含类型、文本、位置、是否可点击、以及当前页面的关键状态信息比如是否在加载、是否有错误提示。这个角色对模型的要求是多模态理解能力。它需要能看懂截图里的文字、图标、布局关系。纯文本模型做不了这个事所以这个角色必须用多模态模型。选型的时候有几个考量点。第一是元素定位精度模型能不能准确说出每个元素的位置。有些多模态模型能理解页面内容但给出的坐标很粗糙这种就不适合。第二是结构化输出能力模型能不能稳定输出 JSON 格式的元素列表而不是一段散文式的描述。第三是延迟界面理解是每步都要调用的延迟太高会拖慢整个流程。我的经验是这个角色不需要用最强的多模态模型中等规模的就够用。因为界面理解本质上是描述你看到的东西不需要太强的推理能力。反而是一些专门优化过 UI 理解的小模型在这个任务上表现更好、速度更快。注意界面理解角色的输出格式一定要用 schema 约束住。我试过让模型自由输出结果有时候给 JSON、有时候给 markdown 表格、有时候给自然语言下游解析起来非常痛苦。用 JSON schema 强制约束之后稳定性好了很多。3.2 动作决策角色推理能力优先不需要多模态动作决策角色拿到的是界面理解角色输出的结构化页面描述加上当前测试目标和历史动作记录输出下一步该执行的动作。动作的类型包括点击某个元素、在某个输入框输入文本、滑动、等待、返回等。这个角色的核心要求是推理和规划能力。它需要理解测试目标比如完成登录流程结合当前页面状态比如当前在登录页有用户名输入框、密码输入框、登录按钮决定下一步动作比如在用户名输入框输入测试账号。因为输入已经是结构化的文本描述这个角色不需要多模态能力用纯文本的推理模型就行。选型的时候重点看指令遵循能力和输出格式稳定性。这个角色最容易出的问题是自作主张——比如目标是在用户名框输入它却去点了登录按钮。所以 prompt 里要把动作空间约束死让它只能从预定义的动作类型里选。模型规格上这个角色值得用推理能力强一些的模型。因为决策质量直接决定测试能不能跑通这里省成本不划算。但也不需要最大的模型中等偏上的推理模型就够。3.3 结果校验角色轻量化能规则化就规则化结果校验角色的任务是执行完一个动作后判断页面是否达到了预期状态。比如点击登录按钮后预期是跳转到首页校验角色需要判断当前页面是不是首页。这个角色其实大部分场景可以用规则引擎搞定。比如判断页面标题是否包含某个关键词、判断某个元素是否存在、判断 URL 是否变化。这些用规则比用模型更快更准。只有那些规则难以描述的校验场景才需要模型介入。比如判断页面是否显示了错误提示错误提示的文案可能有很多种规则写不全这时候可以用一个小模型或者文本分类模型来判断。我的建议是校验角色优先用规则规则覆盖不了的再用小模型。不要一上来就用大模型做校验那样既慢又贵而且大模型在简单判断任务上不一定比规则准。3.4 异常恢复角色低频但关键异常恢复角色处理的是意外情况弹窗、权限请求、网络错误、页面加载失败等。这个角色的触发频率不高但一旦触发处理不好整个测试就卡住了。这个角色需要的是常识推理和策略选择能力。比如遇到权限请求弹窗它需要判断这个权限是不是测试必需的如果是就点允许如果不是就点拒绝。遇到网络错误提示它需要判断是重试还是跳过。因为触发频率低这个角色可以用强一些的模型成本影响不大。但要注意的是异常恢复的策略最好也有一部分规则兜底比如遇到已知的广告弹窗直接关闭这种用规则处理比用模型快得多。3.5 角色与模型选型对照把上面的分析整理成一张表方便对照角色核心能力要求是否需要多模态建议模型规格调用频率界面理解视觉理解、结构化输出是中等多模态模型每步动作决策推理规划、指令遵循否中等偏上推理模型每步结果校验对比判断否规则引擎优先小模型兜底每步异常恢复常识推理、策略选择否强推理模型低频这张表是我实际用下来觉得比较合理的配置。当然具体选哪个模型还要看你的预算、延迟要求和测试场景的复杂度。但拆分的逻辑是通用的多模态能力只给需要的角色推理能力给关键角色简单任务用规则兜底。4. 实操配置与核心环节实现4.1 环境准备与依赖安装先把基础环境搭起来。DroidRun 和 Mobilerun 这类框架通常依赖几个东西移动端设备连接真机或模拟器、ADB 工具、Python 运行环境、以及模型 API 的访问配置。设备连接这块我用的是 Android 模拟器加真机混合的方式。模拟器用于日常调试真机用于最终验证。ADB 要确保能正常识别设备adb devices能列出设备才行。如果是多设备并行需要给每个设备分配独立的端口。Python 环境建议用 3.10 以上因为很多 LLM 相关的库对版本有要求。依赖安装的时候注意多模态模型调用和纯文本模型调用可能依赖不同的 SDK要分别装好。模型 API 配置这块我建议把不同角色的模型配置分开管理。用一个配置文件把界面理解、动作决策、结果校验、异常恢复四个角色的模型名称、API 地址、超时时间、重试次数都配好。这样切换模型的时候不用改代码改配置就行。# model_config.yaml roles: ui_understanding: model: multimodal-model-a timeout: 30 max_retries: 2 action_decision: model: reasoning-model-b timeout: 20 max_retries: 3 result_validation: model: small-model-c timeout: 10 max_retries: 1 exception_recovery: model: reasoning-model-b timeout: 25 max_retries: 24.2 界面理解角色的实现细节界面理解角色的核心是把截图转成结构化描述。实现的时候有几个关键点。截图预处理。原始截图往往很大直接丢给模型既慢又贵。我的做法是先做一次压缩把分辨率降到模型能看清元素的程度就行。同时可以裁掉状态栏和导航栏这些无关区域减少干扰。Prompt 设计。界面理解的 prompt 要明确告诉模型你是一个 UI 描述器你的任务是列出当前页面上所有可交互元素输出 JSON 格式。不要做任何决策不要建议下一步动作。我试过在 prompt 里加一句只描述不决策模型跑偏的概率明显降低。输出 schema 约束。用 JSON schema 把输出格式固定住。每个元素包含元素类型按钮、输入框、文本、图标等、元素文本、位置坐标左上角和右下角、是否可点击。schema 约束之后下游解析就简单了。ui_schema { type: object, properties: { page_type: {type: string}, elements: { type: array, items: { type: object, properties: { type: {type: string}, text: {type: string}, bounds: {type: array, items: {type: number}}, clickable: {type: boolean} }, required: [type, text, bounds, clickable] } }, page_state: {type: string} }, required: [page_type, elements, page_state] }坐标换算。模型给出的坐标是基于压缩后的截图的要换算回原始屏幕坐标才能用于实际点击。换算比例就是原始分辨率除以压缩后分辨率。这个换算逻辑要封装好别每次手动算。4.3 动作决策角色的实现细节动作决策角色拿到界面描述后要输出下一步动作。实现的时候重点是动作空间的约束和历史上下文的处理。动作空间定义。预定义好所有可能的动作类型click、input、swipe、wait、back、assert。每个动作有对应的参数。click 需要元素索引或坐标input 需要元素索引和文本swipe 需要方向和距离。把动作空间用 schema 约束住模型只能从这些类型里选。历史上下文。决策角色需要知道之前做了什么避免重复动作或者陷入循环。我的做法是把最近 N 步的动作和结果压缩成简短记录放在 prompt 里。N 不用太大5 到 10 步就够太多反而干扰模型判断。目标描述。测试目标要写得具体。不要写测试登录功能要写在登录页输入用户名 testuser、密码 testpass点击登录按钮预期跳转到首页。目标越具体模型决策越准。action_schema { type: object, properties: { action_type: { type: string, enum: [click, input, swipe, wait, back, assert] }, target_index: {type: integer}, input_text: {type: string}, swipe_direction: {type: string, enum: [up, down, left, right]}, wait_seconds: {type: number}, reason: {type: string} }, required: [action_type, reason] }实操心得决策角色的 prompt 里加一个reason字段让模型解释为什么选这个动作。这个字段不参与实际执行但排查问题的时候非常有用。模型决策错了看 reason 就能知道它当时是怎么想的。4.4 结果校验角色的实现细节校验角色的实现我强烈建议规则优先。大部分校验场景都能用规则覆盖页面跳转校验判断当前页面类型是否等于预期类型元素存在校验判断某个文本的元素是否在元素列表里文本内容校验判断某个元素的文本是否包含预期关键词状态校验判断 page_state 是否符合预期这些用几行代码就能搞定比调模型快几个数量级。只有规则覆盖不了的场景比如判断页面是否显示了某种语义上的错误才需要模型介入。模型介入的时候用一个小模型做二分类就行输入是页面描述和预期输出是通过或不通过加原因。不需要用大模型大模型在这个任务上反而容易过度解读。4.5 异常恢复角色的实现细节异常恢复角色的触发条件是动作执行后页面状态不符合预期或者界面理解角色检测到了异常元素弹窗、错误提示等。实现的时候先过一遍已知异常规则库。比如检测到包含更新和取消的弹窗点击取消、检测到包含允许和拒绝的权限弹窗根据测试配置决定点哪个。这些规则能覆盖大部分常见异常。规则覆盖不了的再交给模型。模型的 prompt 要包含当前页面描述、触发的异常类型、测试目标、以及可选的恢复策略列表。让模型从策略列表里选一个而不是自由发挥。recovery_strategies [ dismiss_popup, # 关闭弹窗 grant_permission, # 授予权限 deny_permission, # 拒绝权限 retry_action, # 重试上一步 skip_step, # 跳过当前步骤 restart_app, # 重启应用 abort_test # 终止测试 ]4.6 角色间的编排与通信四个角色不是孤立的需要一个编排器把它们串起来。编排器的逻辑是截图调界面理解角色拿到页面描述调动作决策角色拿到下一步动作执行动作截图调结果校验角色判断是否达到预期如果没达到预期调异常恢复角色决定怎么处理循环直到测试目标完成或达到最大步数编排器要处理好超时和重试。每个角色的调用都要设超时超时后要有降级策略。比如界面理解超时了可以重试一次还不行就用上一次的页面描述凑合。动作决策超时了可以降级到基于规则的默认动作。角色之间的通信全部走结构化数据不传自然语言。界面理解的输出直接作为动作决策的输入动作决策的输出直接作为执行器的输入。这样每个角色的输入输出都可以单独测试和 mock调试起来方便很多。5. 常见问题与排查技巧实录5.1 界面理解角色常见问题问题一元素识别不全。模型漏掉了一些可交互元素导致决策角色找不到目标。排查思路先看截图质量压缩太狠会导致小元素看不清再看 prompt是不是没有明确要求列出所有可交互元素最后看模型能力有些多模态模型对小图标的识别确实弱。问题二坐标偏移。模型给出的坐标和实际元素位置对不上。排查思路检查压缩比例换算是否正确检查截图是否包含了状态栏而模型没算进去检查模型输出的坐标是相对于压缩图还是原图。问题三输出格式不稳定。有时候输出 JSON有时候输出别的。排查思路检查 schema 约束是否生效检查 prompt 里是否给了明确的格式示例检查模型是否支持结构化输出。5.2 动作决策角色常见问题问题一决策循环。模型反复执行同一个动作陷入死循环。排查思路检查历史上下文是否传入了检查 prompt 里是否有避免重复动作的约束检查目标描述是否太模糊导致模型不知道什么时候算完成。问题二动作越界。模型输出了预定义动作空间之外的动作。排查思路检查 schema 的 enum 约束是否生效检查 prompt 里是否明确列出了可选动作检查模型是否理解动作的含义。问题三决策质量差。模型选的动作明显不合理。排查思路检查界面描述是否准确检查目标描述是否具体检查模型规格是否够用可能需要换更强的推理模型。5.3 校验与恢复角色常见问题问题一校验过严。明明页面已经正确跳转校验却说不通过。排查思路检查校验规则是否太死板比如要求页面标题完全匹配但实际标题有细微差异检查页面描述是否准确。问题二校验过松。页面明显不对校验却通过了。排查思路检查校验规则是否覆盖了关键断言检查是否只校验了页面类型而没校验关键元素。问题三恢复策略不当。异常恢复角色选了错误的策略导致测试卡死。排查思路检查策略列表是否完整检查 prompt 里是否给了足够的上下文让模型判断检查是否有规则兜底。5.4 常见问题速查表问题现象可能原因排查方向解决建议元素识别不全截图压缩过度检查压缩比例降低压缩率或分区域截图坐标偏移换算逻辑错误检查分辨率换算封装统一的坐标换算函数输出格式不稳定schema 未生效检查 schema 配置强制 JSON schema 约束决策循环历史上下文缺失检查上下文传递传入最近 N 步动作记录动作越界动作空间未约束检查 enum 配置用 schema 限制动作类型校验过严规则太死板检查匹配逻辑改用包含匹配而非完全匹配恢复策略不当上下文不足检查 prompt 内容补充测试目标和策略说明5.5 独家避坑技巧技巧一给每个角色的输出加置信度。让模型在输出的时候附带一个置信度分数。置信度低的输出编排器可以选择重试或者降级处理。这个在界面理解角色上特别有用置信度低说明模型对页面理解不确定可以重新截图再试。技巧二用缓存减少重复调用。如果连续两步的截图完全一样界面理解的结果可以直接复用不用重新调模型。这个在等待加载的场景下能省不少调用。技巧三把常见页面的描述缓存起来。测试过程中会反复回到某些页面比如首页、登录页这些页面的描述可以缓存下次遇到直接查缓存不用调模型。缓存 key 可以用截图的感知哈希。技巧四决策角色的 prompt 里加负面示例。告诉模型不要做以下动作比只告诉它要做以下动作效果好。比如不要在未输入用户名的情况下点击登录按钮这种负面约束能减少很多低级错误。技巧五定期回放失败用例的日志。把失败用例的每个角色输入输出都存下来定期回放分析。你会发现很多问题是有规律的比如某个页面模型总是理解错某个动作模型总是决策错。针对性地优化这些高频问题比泛泛地调 prompt 有效得多。6. 拆分方案的扩展与演进角色级拆分不是终点而是一个可以持续演进的基础架构。我在实际用下来觉得有几个方向值得继续探索。方向一角色内部的模型动态切换。不是所有页面都需要同等规格的模型。简单页面可以用小模型快速处理复杂页面再切换到强模型。可以根据页面元素数量、页面类型等特征动态选择模型规格。这样能在保证效果的前提下进一步降成本。方向二引入记忆机制。让每个角色有自己的记忆。界面理解角色记住常见页面的描述动作决策角色记住成功和失败的动作序列异常恢复角色记住处理过的异常类型。记忆能减少重复推理也能提升决策质量。方向三角色间的反馈闭环。现在角色之间是单向传递可以引入反馈机制。比如校验角色发现决策角色的动作有问题可以把反馈传回决策角色让它重新决策。这样能提升单步的准确率减少错误累积。方向四多设备并行时的角色复用。多设备并行测试的时候界面理解角色可以复用——如果多个设备在同一页面截图可能不同但页面结构相似可以共享部分理解结果。这个需要设计好复用策略避免设备间状态串扰。方向五把角色拆分思路迁移到其他端。Web 自动化、桌面自动化、甚至 API 自动化都可以用类似的角色拆分思路。界面理解、动作决策、结果校验、异常恢复这四个角色在 Web 端几乎可以直接对应。核心的拆分逻辑是通用的只是具体实现要适配不同端的特性。我个人在实际操作中的体会是角色拆分最大的价值不是省了多少钱而是让整个系统变得可理解、可调试、可演进。一个大黑盒模型出问题了只能猜拆成四个角色之后每个角色的职责清晰出问题了能快速定位想优化也有明确的切入点。这个价值在长期维护中会越来越明显。最后分享一个小技巧刚开始做角色拆分的时候不要一上来就追求完美。先把四个角色搭起来用最简单的 prompt 和最小的模型跑通流程然后再逐个角色优化。我见过太多人一开始就想把每个角色的 prompt 调到最优结果卡在某个角色上迟迟跑不通全流程。先跑通、再优化这个顺序很重要。