资讯动态

移动端自动化测试架构:LLM角色级模型拆分与工程实践

发布时间:2026/10/18 14:20:16 来源:尧图企业网站定制
1. 从一个模型打天下到角色级拆分移动端自动化测试的架构拐点移动端自动化测试这件事做过的人都懂——它和Web端自动化完全是两个世界。Web端你至少有一个相对稳定的DOM树可以依赖元素定位、状态断言、页面跳转都有比较清晰的边界。但移动端不一样屏幕尺寸碎片化、系统权限弹窗随机出现、App内部状态机复杂、渲染层和原生层交织在一起再加上网络抖动、动画延迟、软键盘遮挡这些玄学问题传统基于控件树的自动化方案经常陷入写三天、修五天的泥潭。大语言模型介入移动端自动化之后情况发生了根本性变化。以前我们靠find_element_by_id这种硬编码方式定位元素现在可以让模型看着屏幕截图理解当前界面在干什么然后决定下一步该点哪里、该输入什么。这个思路听起来很美好但真正落地的时候第一个撞上的墙就是一个模型根本不够用。为什么因为移动端自动化测试这个任务链条太长了。它至少包含这些环节理解当前屏幕内容、判断当前处于什么状态、决定下一步操作、生成具体的操作指令点击坐标、输入文本、滑动方向、验证操作结果是否符合预期、在出现异常时决定重试还是上报。如果你把这些全部塞给一个通用大模型会出现几个致命问题响应延迟高得离谱每次操作都要等好几秒、Token消耗巨大每步都要传完整截图和上下文、准确率不稳定模型在不同任务间精神分裂、成本不可控跑一轮回归测试烧掉的钱够买一台真机。所以角色级模型拆分就成了移动端自动化测试架构设计的必然选择。这个思路的核心是不让一个模型干所有事而是把整个测试流程拆成若干个角色每个角色只负责一个明确的子任务用最适合这个子任务的模型来驱动。比如有的角色专门负责看屏幕有的角色专门负责做决策有的角色专门负责执行动作有的角色专门负责判断结果。每个角色可以用不同规模、不同能力侧重的模型甚至可以用不同的提示词策略和不同的输入输出格式。这个架构思路在DroidRun和Mobilerun这类移动端自动化框架里体现得特别明显。它们不是简单地把GPT-4接进来就完事而是设计了一套角色分工机制让每个模型在自己的岗位上发挥最大价值。接下来我会从架构设计、角色划分、模型选型、提示词工程、实测调优这几个维度把这个方案彻底拆开讲清楚。如果你正在做移动端自动化测试或者正在考虑把LLM引入测试流程这套思路可以直接参考。2. DroidRun/Mobilerun的角色拆分逻辑谁该干什么活2.1 为什么不能用一个模型端到端搞定先把这个事情说透。很多人第一反应是我直接用GPT-4V或者Claude这种多模态大模型把截图丢进去让它输出点击(500, 800)不就行了吗为什么要搞这么复杂我一开始也是这么想的直到实际跑了一轮完整的测试用例。一个典型的登录流程测试大概需要15-20步操作。如果用单模型端到端方案每一步都要把当前截图、历史操作记录、测试目标全部塞进Prompt然后等模型输出下一步动作。实测下来单步平均响应时间在3-5秒整个用例跑完要一分多钟。这还只是一个用例如果你有200个回归用例光等待时间就是三四个小时。更别说Token成本了——每步都传一张1080P截图按GPT-4V的计价方式跑一轮回归够你心疼好几天。除了成本和速度单模型方案还有一个更隐蔽的问题任务干扰。当你让一个模型同时做理解屏幕和决策下一步这两件事时它会在两个任务之间产生认知冲突。比如屏幕上有一个广告弹窗和一个登录按钮模型需要先判断这个弹窗是不是需要关闭再决定关闭后应该点哪里。如果这两个判断在同一个推理步骤里完成模型很容易被弹窗的视觉特征带偏忽略掉真正的测试目标。这在实测中表现为模型频繁点击无关元素或者在弹窗出现时陷入关闭-再出现-再关闭的死循环。角色级拆分的本质是把一个复杂的多模态推理任务分解成若干个单模态或弱多模态的子任务每个子任务用一个专门的模型来处理。这样做的好处是每个模型的输入输出格式可以高度定制化推理路径更短准确率更高而且可以用小模型替代大模型来降低成本。2.2 四个核心角色的职责边界在DroidRun/Mobilerun的架构里我把它拆成了四个核心角色。这四个角色不是随便分的而是按照感知-决策-执行-验证这个闭环来设计的。第一个角色是屏幕理解器Screen Interpreter。它的任务很纯粹输入一张截图输出当前屏幕的结构化描述。这个描述包括当前页面是什么登录页、首页、设置页、有哪些可交互元素按钮、输入框、列表项、每个元素的位置和状态可点击、已禁用、已选中、有没有弹窗或遮罩层。这个角色不需要知道测试目标是什么它只负责看懂屏幕。因为任务单一这个角色可以用一个相对小的多模态模型来承担比如7B参数级别的视觉语言模型经过针对性微调后在屏幕元素识别这个任务上可以做到接近大模型的准确率但速度快5-10倍。第二个角色是决策规划器Decision Planner。它接收屏幕理解器的输出结构化描述和当前测试目标比如完成登录然后决定下一步应该做什么。这个角色的输入是纯文本的不需要处理图像所以可以用一个纯文本大模型来承担。它的输出是一个高层级的动作指令比如在用户名输入框中输入测试账号、点击登录按钮、等待页面跳转。注意它输出的是做什么而不是怎么做——具体的坐标计算、输入法调用这些底层操作不归它管。第三个角色是动作执行器Action Executor。它接收决策规划器的高层级指令结合屏幕理解器提供的元素位置信息生成具体的底层操作指令。比如在用户名输入框中输入测试账号这个指令执行器需要把它翻译成点击坐标(320, 450)、等待200毫秒、输入文本testuser001、隐藏软键盘。这个角色需要处理一些设备相关的细节比如不同分辨率下的坐标映射、输入法的兼容性处理、滑动操作的加速度曲线等。它可以用一个轻量级的规则引擎或者小模型来实现不需要大模型参与。第四个角色是结果验证器Result Validator。每次操作执行完后它需要判断操作是否成功、当前状态是否符合预期。比如点击登录按钮后页面是否跳转到了首页输入文本后输入框里是否显示了正确的字符这个角色需要对比操作前后的屏幕状态判断是否出现了预期变化。它可以用一个专门训练的视觉差异检测模型或者一个多模态小模型来实现。这四个角色串起来就形成了一个完整的自动化测试闭环。每个角色只做自己最擅长的事模型选型、提示词设计、输入输出格式都可以独立优化。2.3 角色之间的通信协议设计角色拆分之后下一个关键问题是它们之间怎么通信如果通信协议设计得不好角色拆分的优势会被抵消掉。在DroidRun/Mobilerun的实践里角色之间的通信采用了一种结构化中间表示的方案。屏幕理解器输出的不是自然语言描述而是一个结构化的JSON对象。这个JSON对象包含几个关键字段screen_type页面类型、elements元素列表每个元素有id、type、text、bounds、state这些属性、popups弹窗列表、keyboard_visible软键盘是否可见。决策规划器接收这个JSON对象输出一个action_plan对象包含action_type动作类型、target_element_id目标元素ID、parameters动作参数。动作执行器接收action_plan结合屏幕理解器提供的元素坐标信息生成最终的设备操作指令。这种结构化通信的好处是每个角色的输出都是可验证、可调试的。如果测试失败了你可以清楚地看到是哪个环节出了问题——是屏幕理解器没识别出登录按钮还是决策规划器选错了目标元素还是动作执行器坐标算错了。这比单模型端到端方案的黑盒调试体验好太多了。提示结构化中间表示的设计要遵循最小必要信息原则。屏幕理解器不需要输出屏幕上所有文字内容只需要输出与当前测试目标相关的元素信息。这样可以大幅减少Token消耗同时降低决策规划器的认知负担。3. 模型选型每个角色该配什么大脑3.1 屏幕理解器的选型逻辑与实测对比屏幕理解器是整个流水线的入口它的准确率直接决定了后续所有环节的上限。如果它把登录按钮识别成了注册按钮后面决策规划器再聪明也没用。这个角色的核心任务是输入一张手机截图通常是1080x2340或类似分辨率输出结构化的屏幕元素描述。它需要具备的能力包括OCR识别屏幕上的文字、目标检测定位可交互元素的位置、状态判断按钮是否可点击、输入框是否为空、页面分类当前是什么页面。在模型选型上我实测过几种方案。第一种是直接用GPT-4V或Claude 3这类通用多模态大模型准确率确实高但单次推理成本在0.01-0.03美元之间而且响应时间在2-4秒。如果你要跑大量测试用例这个成本和时间都很难接受。第二种是用开源的视觉语言模型比如Qwen-VL、CogVLM、InternVL这些7B到13B参数级别的模型在屏幕元素识别这个特定任务上经过少量微调后可以达到接近GPT-4V的准确率但推理速度快3-5倍成本降低一个数量级。第三种是传统的CV方案加OCR比如用YOLO做元素检测、用PaddleOCR做文字识别然后规则化地组装成结构化描述。这种方案速度最快、成本最低但泛化能力差遇到没见过的UI风格就容易翻车。我的建议是如果你的测试场景相对固定比如只测自家App的几个核心页面用微调后的7B级别视觉语言模型性价比最高。如果你需要覆盖大量不同App的测试场景可以考虑用GPT-4V做冷启动同时收集数据微调自己的小模型逐步替换。这里有一个实测数据可以参考在某次对比测试中我用Qwen-VL-7B经过500张标注截图微调和GPT-4V分别跑同一个登录页面的元素识别任务各跑100次。Qwen-VL-7B的元素定位准确率是94%GPT-4V是97%差距只有3个百分点。但Qwen-VL-7B的单次推理时间平均是0.8秒GPT-4V是3.2秒Qwen-VL-7B的推理成本几乎可以忽略本地部署GPT-4V每次调用要花钱。这个对比说明在屏幕理解这个任务上小模型经过针对性优化后完全可以胜任。3.2 决策规划器为什么纯文本模型就够了决策规划器是四个角色里唯一不需要处理图像的。它的输入是屏幕理解器输出的结构化JSON加上当前测试目标和历史操作记录。输出是一个高层级的动作指令。因为输入是纯文本的这个角色可以用纯文本大模型来承担不需要多模态能力。这带来了几个好处第一纯文本模型的推理速度比多模态模型快很多第二纯文本模型的Token成本更低第三纯文本模型的上下文窗口可以更大能容纳更长的历史操作记录。在模型选型上我建议用中等规模的纯文本模型比如7B到14B参数级别的模型。这个规模的模型在指令遵循和逻辑推理任务上已经足够强了而且推理速度快、成本低。如果你用GPT-4这种超大模型来做决策规划反而可能因为过度思考导致决策不稳定——它会考虑太多可能性有时候会做出一些出人意料的决策。决策规划器的提示词设计是关键。你需要把测试目标、当前屏幕状态、历史操作记录、可用动作空间这些信息清晰地组织成Prompt。我的经验是用系统提示词少样本示例当前状态的三段式结构。系统提示词定义角色和输出格式少样本示例展示几个典型的决策场景当前状态提供实时信息。这样模型能快速理解任务边界输出稳定的决策结果。3.3 动作执行器与结果验证器的轻量化方案动作执行器和结果验证器这两个角色其实不需要大模型参与。它们可以用规则引擎或者极轻量级的模型来实现。动作执行器的核心逻辑是把决策规划器输出的高层级指令翻译成底层设备操作。比如在用户名输入框中输入testuser001这个指令执行器需要做这几件事从屏幕理解器的输出中查找用户名输入框的坐标范围、计算点击中心点坐标、调用设备API执行点击、等待输入框获得焦点、调用输入法输入文本、隐藏软键盘。这些操作都是确定性的用规则引擎完全可以处理。只有在遇到一些特殊情况时比如输入框被软键盘遮挡、点击后没有响应才需要引入一些简单的启发式规则或者小模型来判断。结果验证器的核心逻辑是对比操作前后的屏幕状态判断是否出现了预期变化。比如点击登录按钮后页面是否从登录页变成了首页这个判断可以通过对比屏幕理解器输出的screen_type字段来实现。如果操作前是登录页操作后还是登录页那说明登录可能失败了。更精细的验证可以对比特定元素的状态变化比如登录按钮是否从可点击变成了已禁用表示正在加载或者页面上是否出现了登录成功的提示文字。这两个角色用规则引擎实现的好处是速度快毫秒级、成本为零、可解释性强出问题时能清楚看到是哪条规则触发了。只有在规则覆盖不到的场景下才需要考虑引入模型。4. 提示词工程让每个角色稳定输出4.1 屏幕理解器的输出格式约束屏幕理解器最大的挑战不是看不懂屏幕而是输出格式不稳定。如果你不严格约束输出格式模型有时候输出JSON有时候输出自然语言有时候字段名还变来变去。这会让下游的决策规划器非常痛苦。我的做法是用JSON Schema严格约束输出格式并在提示词里给出完整的示例。具体来说屏幕理解器的输出必须是一个合法的JSON对象包含以下字段{ screen_type: login_page, elements: [ { id: elem_001, type: input, text: 用户名, bounds: [100, 400, 980, 500], state: enabled, value: }, { id: elem_002, type: button, text: 登录, bounds: [100, 700, 980, 800], state: enabled } ], popups: [], keyboard_visible: false }在提示词里我会明确告诉模型bounds字段是[left, top, right, bottom]格式的像素坐标state字段只能是enabled、disabled、selected、checked这几个值之一type字段只能是button、input、text、image、list_item、checkbox、radio、switch、slider、tab、link、icon、other这些值之一。如果模型输出了不在枚举范围内的值后处理逻辑会自动把它映射到other。这种严格约束的好处是下游角色可以放心地按照固定格式解析数据不需要做复杂的容错处理。实测下来经过格式约束的屏幕理解器输出格式合规率从最初的70%左右提升到了99%以上。4.2 决策规划器的少样本示例设计决策规划器的提示词设计核心是让模型理解任务边界。你需要通过少样本示例告诉模型在什么情况下应该做什么决策。我通常会准备5-8个少样本示例覆盖典型的决策场景。比如场景一当前在登录页用户名和密码输入框都为空测试目标是完成登录。决策先在用户名输入框中输入测试账号。场景二当前在登录页用户名已输入密码为空。决策在密码输入框中输入测试密码。场景三当前在登录页用户名和密码都已输入登录按钮可点击。决策点击登录按钮。场景四当前在登录页出现了账号或密码错误的提示。决策上报测试失败记录错误信息。场景五当前在首页测试目标是完成登录。决策登录已完成结束当前测试用例。这些示例要覆盖正常流程和异常流程让模型学会在不同情况下做出正确决策。示例的格式要统一输入是当前屏幕状态和测试目标输出是决策结果。决策结果的格式也要严格约束比如{ action_type: input_text, target_element_id: elem_001, parameters: { text: testuser001 }, reasoning: 当前在登录页用户名输入框为空需要先输入用户名 }注意reasoning字段——让模型输出决策理由一方面可以帮助调试出问题时能看到模型是怎么想的另一方面可以提升决策质量模型在输出理由的过程中会进行更深入的推理。4.3 角色间提示词的解耦与复用四个角色的提示词是独立设计的但它们之间有一些共享的约定。比如屏幕理解器输出的元素ID格式elem_001、elem_002会被决策规划器引用决策规划器输出的target_element_id会被动作执行器解析。这些跨角色的约定需要在每个角色的提示词里都明确说明确保数据流转不会断链。另外我建议把一些通用的提示词片段抽出来做成模板比如输出格式约束、错误处理规则、坐标系统说明这些。这样在调整某个角色的提示词时不需要重复修改这些通用部分减少出错概率。注意提示词版本管理很重要。每次修改提示词后都要用一组固定的测试用例回归验证确保修改没有引入新的问题。我通常会维护一个提示词变更日志记录每次修改的内容、原因和回归测试结果。5. 实测调优角色拆分方案的真实表现与踩坑记录5.1 延迟与成本的量化对比角色拆分方案到底比单模型方案好多少我用一组实测数据来说明。测试场景一个包含15步操作的登录流程测试用例在同一个Android模拟器上运行。对比方案A单模型端到端用GPT-4V和方案B角色拆分屏幕理解器用微调后的Qwen-VL-7B决策规划器用Qwen-7B-Chat动作执行器和结果验证器用规则引擎。指标方案A单模型方案B角色拆分单步平均延迟3.5秒1.2秒全流程总延迟52.5秒18秒单步Token消耗约1500约400全流程Token消耗约22500约6000单次测试成本约0.45美元约0.02美元操作准确率91%94%从数据可以看出角色拆分方案在延迟、成本、准确率三个维度上都优于单模型方案。延迟降低的主要原因是屏幕理解器和决策规划器都是小模型推理速度快成本降低的主要原因是Token消耗大幅减少准确率提升的主要原因是每个角色只做单一任务减少了任务干扰。5.2 角色边界模糊导致的典型故障角色拆分方案也不是没有坑。我踩过的最大的坑是角色边界模糊导致的踢皮球问题。有一次测试流程在点击登录按钮这一步卡住了。屏幕理解器识别出了登录按钮决策规划器也输出了点击登录按钮的指令但动作执行器执行点击后页面没有任何变化。排查了半天才发现登录按钮被一个透明的遮罩层挡住了点击事件被遮罩层拦截了。屏幕理解器没有识别出遮罩层的存在因为它只关注可交互元素决策规划器不知道有遮罩层因为屏幕理解器没告诉它动作执行器点击了按钮的坐标但实际点到了遮罩层上。这个问题的根源是屏幕理解器的职责边界没有覆盖遮罩层检测这个场景。修复方案是在屏幕理解器的输出里增加一个overlay字段用来标识当前屏幕上是否存在遮罩层或弹窗。如果存在遮罩层决策规划器会优先输出关闭遮罩层的指令而不是直接执行目标操作。这个坑给我的教训是角色拆分不是越细越好但每个角色的职责边界必须覆盖所有可能影响决策的屏幕状态。在设计角色时要多问自己这个角色漏掉什么信息会导致下游角色做出错误决策5.3 从失败案例反推角色拆分粒度另一个值得分享的经验是角色拆分的粒度需要根据实际失败案例来调整。一开始我把结果验证和决策规划合并成了一个角色让决策规划器同时负责判断上一步操作是否成功和决定下一步做什么。结果发现模型经常在验证环节出错——比如点击登录按钮后页面还在加载中转圈动画模型就判断登录失败了然后输出重新点击登录按钮的指令导致重复点击。后来我把结果验证拆出来作为独立角色专门负责判断操作结果。这个角色有一个明确的规则如果页面上存在加载动画或进度条就等待而不是判断失败。这个规则用规则引擎实现比让模型去判断要可靠得多。这个案例说明当一个角色承担了两种不同性质的判断任务时就容易出错。把不同性质的判断拆开用最适合的方式来实现才能保证稳定性。6. 可复用的角色拆分设计清单6.1 角色定义模板如果你要为自己的移动端自动化测试项目设计角色拆分方案可以参考这个模板角色名称输入输出推荐模型类型关键约束屏幕理解器截图结构化屏幕描述微调后的7B级视觉语言模型输出必须是合法JSON元素类型和状态必须在枚举范围内决策规划器屏幕描述测试目标历史记录高层级动作指令7B-14B纯文本模型输出必须包含action_type和target_element_id动作执行器高层级指令元素坐标底层设备操作规则引擎坐标必须在屏幕范围内操作后必须等待稳定结果验证器操作前后屏幕描述成功/失败/不确定规则引擎轻量模型加载中状态不能判定为失败这个模板不是固定的你可以根据自己项目的实际情况调整。比如如果你的测试场景里弹窗特别多可以增加一个弹窗处理器角色如果你的测试需要处理复杂的表单填写可以增加一个表单填写器角色。6.2 角色间接口的版本管理角色拆分之后角色之间的接口就变成了一个需要认真管理的对象。我建议给每个接口定义明确的版本号比如screen_description_v1、action_plan_v1。当某个角色的输出格式发生变化时版本号要升级同时要确保下游角色能兼容新旧两个版本。在实际操作中我会把接口定义写成JSON Schema文件放在代码仓库里统一管理。每次修改接口都要走代码审查流程确保上下游角色都同步更新。这样可以避免上游改了格式下游解析失败这种低级错误。6.3 灰度上线与回滚策略角色拆分方案上线时不要一次性全量替换。我建议采用灰度上线策略先让新方案和旧方案并行运行对比两者的输出结果。如果新方案在某个测试用例上表现不如旧方案可以快速回滚到旧方案。具体操作上可以在测试框架里加一个开关控制每个角色使用哪个版本的模型或规则。比如屏幕理解器可以配置为使用GPT-4V或使用微调后的Qwen-VL-7B。灰度期间可以按测试用例维度逐步切换观察每个用例的表现。回滚策略也很重要。如果新方案上线后出现大面积失败要能快速切回旧方案。我的做法是保留旧方案的完整代码和配置新方案以插件的形式接入切换时只需要改一个配置项。7. 关于这套架构的一些个人体会角色级模型拆分这个思路我从第一次尝试到现在大概经历了三轮大的调整。第一轮是拆得太粗只分了理解和决策两个角色结果发现决策角色负担太重经常出错。第二轮是拆得太细分了七八个角色结果角色之间的通信开销比模型推理本身还大整体延迟反而上升了。第三轮才找到比较平衡的四个角色划分。我的体会是角色拆分的粒度应该以每个角色的输出可以被独立验证为标准。如果一个角色的输出你没法单独验证对错那说明这个角色承担了太多职责需要继续拆。反过来如果两个角色之间的通信成本已经超过了它们各自推理的成本那说明拆得太细了应该合并。另外不要迷信大模型一定比小模型好。在屏幕理解这个任务上一个经过500张截图微调的7B模型表现可以接近甚至在某些场景下超过GPT-4V。关键是你愿不愿意花时间去收集数据、标注数据、微调模型。如果你只是想把LLM接进来跑个Demo那用大模型确实省事但如果你要把它做成一个能稳定运行的测试系统小模型加微调才是可持续的方案。最后说一个容易被忽略的点日志和可观测性。角色拆分之后每个角色的输入输出都要完整记录日志。这样当测试失败时你可以清楚地看到是哪个角色出了问题。我通常会把每个角色的输入输出存成JSON文件按测试用例和时间戳组织目录结构。排查问题时直接看日志就能定位到具体环节不需要重新跑一遍测试。这个习惯帮我节省了大量调试时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑