当通用 Agent 框架下沉到测试场景落地难度直接翻倍。这篇文章聊聊我在实际项目中把一套偏向通用任务的 LLM Agent 框架改造为移动端自动化测试引擎时踩过的坑和沉淀下来的方案。核心观点就一句Agent 本身不强强的是围绕它建的那套约束系统。1. 为什么当时会选这条费力不讨好的路动机与选型复盘1.1 测试团队面临的真实痛点用例维护成本远超编写成本先说背景。我们团队维护着一款中等体量的移动端 App业务迭代节奏很快几乎每两周就要发一版。早期用主流的自动化测试框架做 UI 回归用例脚本数量到一千条左右时日子就开始难过了。最明显的现象是写一条新用例可能要半天但修一条因为页面改版而挂掉的旧用例可能要一天。定位问题、确认新的 xpath 或 accessibility id、更新断言、本地验证、再提交全流程下来非常磨人。更麻烦的是很多用例对元素定位的依赖极深。开发把某个按钮的 resource-id 从btn_submit_old改成btn_submit_new对应的一组用例全部 red。如果只是 UI 文案变化比如把确认支付改成确认付款用文本匹配的用例又会踩坑。此类琐碎的维护工作逐渐吞掉了团队的创新精力大家每天都在跟元素定位器搏斗而不是在思考业务流程的覆盖是否合理。痛点积累到一定程度我们开始反思两个底层问题。第一为什么测试用例不能像人一样按照业务意图去操作而不是死盯着某个控件的标识人看到一个按钮即使文案变化也能根据位置和语义判断出来。第二为什么用例维护不能自适应页面改了测试逻辑能不能自己跟着调整而不是由人来逐条修改这两个问题指向同一个方向让测试用例具备语义理解能力。而当时 LLM 的爆发正好给了我们一个新的解题思路。1.2 三种技术方案的比较脚本框架、视觉回归与 Agent 框架团队内部做了三轮技术预研对比了三条路线。第一轮是继续走传统脚本框架路线在现有框架基础上做封装提升定位器的健壮性引入页面对象模型维护一份元素映射表。好处是稳妥、可控、团队上手快坏处是治标不治本元素结构一变映射表照样要人工维护只是把分散的问题集中到一张表上。而且对于动态渲染的页面尤其是大量依赖服务端返回数据的页面元素映射表的意义会大打折扣。第二轮是视觉回归路线通过截图对比发现 UI 异常典型工具是各类像素级对比库。这个方案对布局还原度要求高的场景很有效但它有一个致命短板只能发现看起来不一样无法验证业务流程是否正确。支付成功了页面展示的订单状态是否正确这类逻辑层问题视觉对比完全无能为力它更像是一个辅助手段而不是测试主引擎。第三轮就是我们最终选择的路线基于 LLM 的 Agent 框架。思路是让大模型理解当前页面状态自主决定下一步操作就像一个人在真实使用 App。这条路想象空间最大但风险也最高模型有幻觉、有随机性、响应延迟不稳定、 token 成本不可控。团队内部争论很激烈有人觉得这是拿生产稳定性开玩笑有人觉得既然要做语义化测试Agent 是绕不开的方向。最终说服团队的不是技术参数而是一组成本测算。按当时的 API 价格跑一条中等复杂度的端到端用例大模型推理成本大约几毛钱人民币而人工维护一条用例的工时成本折算下来是几十块钱。即使 Agent 方案的稳定性有折扣只要它能显著降低维护成本从投入产出比上看就有尝试价值。1.3 Agent 框架选型标准为什么没选纯微调路线确定方向后接下来是框架选型。我们调研了当时主流的几种开源 Agent 框架和自研方案。选型标准主要盯住四点一是对工具调用的原生支持是否完善二是状态管理与多轮推理的机制是否成熟三是是否方便接入自定义工具集四是社区活跃度和迭代速度。有一类方案是选一个开源大模型自己微调成测试专用模型让它直接输出操作指令。这个想法当时也在会上被提出来但我持强烈反对意见。理由有几个微调需要高质量的训练数据而我们当时连一套标注好的页面状态-操作决策数据集都没有模型迭代速度快微调出来的模型半年后可能就落后于新版本更关键的是微调的收益边界不清晰UI 操作决策是高度依赖上下文的任务不是靠背诵规律就能覆盖的。最终我们选择了一套符合 Agent 范式的开源框架作为底座它自带 ReAct 推理循环、工具注册机制和多轮对话管理能力。同时我们的改造重点不在模型而在框架与测试场景之间的适配层。后面的实践也证明这个决策是对的——把精力花在约束和工具设计上远比花在调模型上更划算。2. 通用 Agent 在移动端测试场景的真实能力落差扒开表象看细节2.1 能力短板一测试场景要求的可观测性是性命的而通用 Agent 并没有把通用 Agent 框架接入移动端才发现它原本是为开放任务设计的比如让用户描述一个需求Agent 自己去翻网页、调工具、整理答案。这种场景下过程是黑盒还是白盒不重要只要结果对了就行。但测试场景的逻辑恰好相反。测试用例执行失败了我们必须知道失败发生在哪一步、当时的页面状态是什么、Agent 基于什么信息做出了错误决策。没有这些排查问题如同大海捞针。通用 Agent 框架的多轮推理记录虽然存在但格式宽泛主要记录模型输入的 prompt、工具调用的参数和返回值。当我们想在页面截图、UI 层级树、Agent 的思考过程、工具返回的断言结果之间建立时间线关联时发现框架原生日志根本接不上只能拿到一段段孤立的信息无法还原现场。比如有一次用例跑到第三步Agent 点击了下一步按钮后页面没有如预期跳转。我们要判断是点击没生效还是跳转后的页面加载太慢还是 Agent 点错了位置。结果翻日志发现Agent 的思考过程只写了用户想要进入下一步所以点击下一步按钮完全没有记录它看到的 UI 结构里这个按钮的具体坐标、它基于哪一块截图做出的判断。这种信息密度根本没法定位问题。为了补足这类测试现场信息我们后来不得不自建一套会话记录器把每个推理步骤的输入、输出、操作结果强制绑定时间戳并和手机端的截图、UI 层级树快照做关联归档。2.2 能力短板二模型的动作泛化幻觉是测试场景的致命伤通用 Agent 框架的最底层依赖模型的语义理解能力。但大模型在 UI 操作任务上有一个非常隐蔽的坏习惯它会脑补页面上不存在的元素或者把语义上相似但物理位置上完全不同的元素当成同一个目标。举一个实际例子。某个设置页里有两个都带安全字样的区块一个是账号安全入口一个是安全提醒开关。Agent 收到指令打开安全设置模型在推理时把安全相关的元素全部纳入考虑范围然后选了一个语义上最接近的候选结果点到了安全提醒开关测试直接走偏。人不会犯这种错误因为人有视觉空间判断力但模型只看到一堆语义标签和坐标数据时很容易做表面关联。这类问题在传统自动化测试中完全不存在因为传统脚本用的是绝对定位器定位器指向谁就操作谁。而 Agent 方案引入了语义决策层反而把不确定性带了进来。应对这个问题的思路是给 Agent 的操作目标加多层校验先基于 LLM 语义分析筛出候选元素再结合视觉模型对截图中的目标区域进行二次确认最后还要做坐标级别的比对。只有三层信息一致操作才被放行否则要求 Agent 重新推理。2.3 能力短板三动作序列的正确性无法被过程保证只能靠结果验证通用 Agent 框架在开放任务中通常是一路推理一路执行走一步看一步。这在信息检索类任务中问题不大顶多是多绕点弯路。但测试执行是一连串有严格前置关系的动作序列如果前面一步就错了后面所有步骤的结果都会失真最终测试报告会给人完全错误的结论——测出通过的用例实际上后面根本没执行到正确页面。这里有一个深刻的教训。我们早期在设计 Agent 测试框架时每个步骤之间只校验动作是否执行成功比如点击动作有没有被系统接受。结果有一次跑通了一条价格计算的用例报告显示通过但复核时人工回放才发现Agent 在流程中其实点错了商品后续所有价格断言都是在错误商品页面上执行的。因为每一步操作系统层面都成功了并没有报错而价格计算的数值恰好在不同商品间有规律最终断言误通过了。这件事之后我们把过程校验强度提高了一个级别每个步骤不仅校验操作是否执行还要额外校验操作后页面状态是否符合预期比如断言当前页面标题、关键控件是否存在。一旦状态校验不过立即停止并标记失败绝对不带着错误状态继续往下跑。3. Agent 下沉测试场景的技术改造四个关键工程手段3.1 改造一给 Agent 造一套测试专用工具链而不是让它随便调用系统 API通用 Agent 框架通常自带一套通用工具库比如打开网页、搜索、运行脚本等。这些工具放在办公场景没问题但放到测试场景中就变成隐患。举个例子如果 Agent 在测试过程中能直接执行 shell 命令理论上它可以绕开 UI 操作去改 App 的本地存储直接让测试通过。这在真实测试中是绝对不可接受的——它测的根本不是用户路径而是伪造了结果。我们的做法是彻底移除通用工具库为测试场景定制一套专用操作原语。这套原语严格模仿用户行为只保留以下几类页面操作原语点击、长按、滑动、输入文本、返回、等待元素出现。状态读取原语读取当前页面截图、获取 UI 层级树、读取特定控件属性。断言原语判断当前页面是否包含某文本、某元素是否存在、某控件的 enable 状态是否为真。数据准备原语重置 App 到初始状态、注入特定的测试账号和测试数据。每一条原语在注册进 Agent 工具集之前都要经过严格的入参校验和返回格式标准化。比如点击操作Agent 传给工具的参数不能是自然语言点一下那个蓝色的按钮而是必须是结构化的坐标或元素标识。如果模型输出的参数无法通过校验工具调用会被拒绝并把这个错误反馈给模型作为新的上下文逼它重新思考。这样的设计会让 Agent 看起来笨了很多但测试场景要的恰恰是笨但可靠不需要聪明到能跨系统操作只需要规规矩矩地把 UI 操作执行到位。3.2 改造二双层状态感知机制——让 Agent 同时读懂布局和视觉移动端测试和 Web 测试最大的不同在于移动端的 UI 信息有两种存在形式一是框架层可以直接拿到的 UI 层级树包含控件的类型、文本、坐标、属性二是渲染出来的真实屏幕截图。这两种信息各有缺陷层级树可能拿不到自定义控件的完整描述而截图里的视觉信息则缺失结构化的语义标签。通用 Agent 框架在理解 UI 时通常只取其一。有些框架主打屏幕截图理解让视觉模型直接看截图决策有些主打层级树解析把节点数据转成 JSON 喂给模型。我们实测下来单取任何一种都不靠谱。纯截图方案的失败点在于小尺寸或低对比度文字关键信息识别率低而且无法精确到控件层面去定位纯层级树方案的失败点在于自定义绘制的元素比如炫酷的图表、画布类控件在层级树里根本没有语义描述只有一个空空如也的容器节点。最终我们设计了双层状态感知机制。每个决策周期框架同时采集当前屏幕的截图和 UI 层级树分别处理后合并成一个结构化的状态对象。具体流程如下抓取层级树把每个节点的 bounds、文本、resource-id、content-desc 提取出来对每个可点击节点计算它在屏幕上的中心坐标。对截图做裁剪把每个节点的坐标区域单独切出来送入一个小型视觉模型做二次描述补充层级树缺失的信息。将两路信息汇总生成一张包含控件二维表的状态 Summary每行对应一个可操作元素字段包括索引、控件类型、文本描述、中心坐标、操作建议。Agent 在决策时看到的就是这张二维表而不是一堆原始 JSON。这样模型不需要自己从混乱的数据里猜结构决策准确率有了质的提升。实测下来在常见原生页面和混合页面中双层机制相比纯层级树方案操作定位错误率下降了约四成。3.3 改造三用技能分解 确定性校验取代自由发挥通用 Agent 最迷人的地方是自由发挥但在测试世界里自由发挥等于灾难。一份测试用例要保证可重复性同一份代码在不同时间跑要得出同样的结论否则测试结果没有参考价值。这个要求跟通用 Agent 的随机性天然冲突。为了调和这对矛盾我们引入了一套技能分解机制把测试任务分层管控。整个执行过程分为两层第一层是任务编排层由测试人员在用例设计阶段预先定义好流程。就像传统的 Page Object 模式把一条端到端用例拆成若干步骤每个步骤定义清楚目标状态和验证点。只不过在这个框架里步骤不再是脚本代码而是一个个技能节点——技能是一段封装好的 Agent 执行单元内部可能包含多次推理和操作但对外只暴露清晰的输入输出接口。第二层是技能执行层由 Agent 在技能内部做自由的推理决策。举个例子登录流程是一个技能它的输入是用户名和密码输出是是否进入主页。在执行这个技能时Agent 会根据当前页面状态自主选择输入框、点击登录按钮、处理可能的异常弹窗。它在这个边界内是自由的但一旦操作结束框架会用硬性的校验逻辑检查输出是否符合预期。这个设计相当于给 Agent 加了一副无形的牵引绳它在技能内部可以灵活应变但不能冲破任务编排设定的框架。我们在实践中发现这套分层模式把用例的设计稳定性和执行灵活性同时兼顾了测试失败时也能很快定位到是编排层的逻辑问题还是技能内部的决策问题。3.4 改造四会话流程的状态持久化与故障自愈通用 Agent 框架在长任务执行中有一个老大难问题上下文长度。一个复杂的端到端测试流程可能涉及十几步操作每一步光状态 Summary 就有几千个 token。如果用多轮对话的方式把每一步的完整上下文全量塞给模型很快就把上下文窗口撑爆。就算窗口够大模型在超长上下文里的注意力分布也会越来越分散早期信息被遗忘的概率上升。我们借鉴了软件工程里的状态持久化思路。框架不再把每一步的完整历史都传给模型而是在每个技能执行完成后将关键状态抽象成一个结构化的摘要对象写入独立的存储空间。大模型在启动新技能时只需要读取当前状态摘要和本技能的目标指令不必背着全部历史包袱。这一改动带来了两个直接收益。一是 token 消耗大幅下降整体成本降低约六七成。二是故障自愈能力显著增强。说到故障自愈这其实是我们在改造过程中加的一个彩蛋功能。移动端测试天然有各种环境波动弹窗飘过、网络延迟、后台推送打扰分分钟让用例前功尽弃。传统脚本框架遇到这类问题大概率直接失败然后等人来处理。而 Agent 框架因为具备语义理解能力有能力识别这类异常是否值得处理。我们实现了三层自愈策略第一层是重试策略操作失败后根据错误类型做有限次数的重试第二层是跳转恢复策略遇到弹窗等干扰项时Agent 主动关闭弹窗并回到预期状态第三层是导航回退策略如果流程走偏Agent 依据状态摘要导航回上一个正确的页面节点。三层策略全部失败才会终止用例并标记失败。实战跑下来整个执行过程的环境类意外中断率下降了非常多不需要人工介入就能跑通的用例比例明显提高。4. 实测结果的相对结论稳定性、覆盖率与真实产出比4.1 稳定性真实执行环境的重复性到底如何技术改造完成后我们选取了一条核心业务链路——从登录到下单支付全流程进行了连续多天的稳定性测试。这里必须先坦白一个现实基于 LLM 的方案稳定性永远不可能达到传统脚本 100% 复现的级别。传统脚本的稳定性目标是 99.9%而 Agent 方案只要能稳定在 90% 以上就已经是巨大的成功了。连续执行的统计结果显示全流程成功率达到 91.7%。这个数字看起来没有传统自动化那么漂亮但结合场景看完全能接受——因为它不是靠死记硬背元素定位器跑出来的而是基于语义理解实时决策的。数据库里 80% 的用例在页面发生轻微改版后不需要任何代码修改就能继续运行这才是核心价值。为了更客观评估稳定性我们做了多组对照实验。同一用例跑 20 次统计每次的决策路径是否一致。结果发现核心路径上有大约七成的情况 Agent 会采用相同或高度相似的操作序列三成的情况会换条路走比如先点某个区域再点目标按钮而最终结果一致。这个现象说明Agent 是有自己的行为偏好的但并非完全确定。在做断言设计时你必须容忍这种多样性不能把断言写得太死否则会误报失败。4.2 问题发现能力预算内值不值讨论 Agent 测试框架能不能用绕不开一个问题它真的能发现 bug 吗还是只是把已有的问题重新验证一遍为了测出它的找 bug能力我们在测试环境故意注入了几类缺陷文案错误、元素错位、按钮点击无响应、数据计算逻辑错误、页面跳转异常。结果显示文案类错误和元素错位类错误Agent 的发现率很高因为它的语义理解能力可以自动识别这里显示的内容不符合上下文预期。点击无响应类错误Agent 能持续重试并感知到操作后页面状态未更新从而捕获异常并上报。跳转异常类错误双层状态感知机制能有效识别点了按钮但页面标题不对发现率较高。计算逻辑错误反而是最难的。如果页面渲染出来的结果没有明显异常Agent 很难凭语义判断数值对错。这块必须依赖传统断言比如在执行完成后核对最终价格靠 Agent 自己发现是不现实的。综合来看Agent 框架更适合做业务主流程的回归验证和探索式测试不适合替代精确的逻辑断言。两类工具配合使用才是完整的测试体系。4.3 真实的 ROI成本账与人力账最后算一笔账。我们统计了改造前后的数据。改造前核心用例集大约 300 条每两周迭代后的平均维护工作量是每人每天大约 5 小时。改造后因为大部分用例对 UI 细节不再敏感维护工作量降到了每人每天不到 2 小时。虽然前期搭建框架投入了约 6 周的人力但这条成本曲线在三个迭代周期后就拉平了。运行成本方面每条用例的平均推理 token 消耗一开始比较高经过技能分解和状态摘要机制优化后稳定在单条中等复杂度用例约 15 万 token。按当时的模型价格折算大约几毛钱人民币。跑一轮 300 条用例模型成本不到两百块。相比一个测试工程师半天的工时这个成本完全可以被接受。执行时间也是需要关注的一个指标。Agent 方案的特点是每一步决策都要花时间做推理所以单条用例的执行速度比传统脚本慢了约 60%。我们用并发执行来对冲在测试设备池里同时跑 10 台设备整体回归时间勉强控制在可接受范围内。如果预算足够扩容设备池时间问题基本可以忽略。5. 绕不开的边界那些必然失败的场景与应对策略5.1 场景一页面元素完全自定义绘制且无层级信息移动端有一些页面尤其游戏界面或复杂的可视化图表页整个界面是一块 Canvas 或者自绘容器UI 层级树里只有一个巨大的 view没有任何可用的控件信息。遇到这种页面Agent 的理解能力就断崖式下降。它看到了截图但不知道图上每个区域到底对应什么功能。我们的应对策略是把这类页面排除出 Agent 自动化的范围或者降级为半自动模式——由测试人员提前在页面上标注关键区域的热点坐标Agent 在热点内做操作。如果你计划用 Agent 框架覆盖全 App建议先做一轮页面清单评估把层级树丰富度极低的页面单独挑出来不要硬上。5.2 场景二强依赖真实后端数据且数据动态变化如果被测 App 的核心流程依赖复杂的数据状态比如订单状态、好友关系链、推荐算法结果Agent 在执行过程中会频繁遇到页面内容不稳定的挑战。今天跑这条用例首页推荐的是 A 商品明天跑就是 B 商品如果用例断言里写死了必须看到 A 商品Agent 再怎么聪明也救不了你。这类问题本质上不是 Agent 能解决的而是测试数据设计的问题。我们摸索出的有效手段是对涉及动态数据的用例强制走 mock 网关让后端返回固定数据。如果业务架构不允许 mock那么用例设计上就要把断言目标收敛到流程完成性而不是具体值。不能指望 Agent 能处理你测试数据设计上的缺陷。5.3 场景三强时效性交互比如实时音视频通信还有一个 Agent 框架头大的场景是强时效性交互比如实时音视频通话、直播连麦、或者是那种点了按钮之后只有几百毫秒反应时间才能看到状态的界面。Agent 的决策周期天然带有延迟从截图到推理到执行最快也要一两秒。如果页面的状态变化窗口比这个更快Agent 的每一步都像是在追一个跑掉的兔子永远慢半拍。这类测试不适合用 Agent 方案老老实实回到脚本层面用坐标直接操作甚至用更底层的协议来校验数据流。项目里如果有人问为什么这个模块没有接入 Agent 自动化答案就在这里——不适合的东西不该硬贴。5.4 场景四断言的分寸感刚才提到的断言如何设计是 Agent 测试框架独有的难题值得单独拿出来说。传统脚本的断言可以精确到一个控件的文本等于某个值因为脚本知道元素的精确位置。Agent 方案因为决策逻辑不固定断言如果太严会因为 Agent 换了路径而误报失败断言如果太松又可能放过真正的 bug。我们最终定了一条原则流程断言放在框架层业务断言放在校验函数层。框架层断言只验证是否到达了预期的页面节点比如页面标题、关键容器是否出现业务层校验函数则执行传统的数据校验。两层分工既保证了流程容错又保留了业务正确性的硬约束。这个分寸只有在大量真实执行数据中反复调整才能拿捏好。6. 最后的几条实操建议回顾整套改造过程如果现在有人要在一个新项目里复现这套方案我会给出这几条最核心的建议都是我踩过坑之后才想明白的。第一不要直接把通用 Agent 框架接到测试环境上。先花两周时间把工具链裁剪干净把所有可能的危险操作都禁用掉。测试框架的信任边界必须是最小的Agent 只能在极小范围内自主行动。第二状态读取和展示的设计优先级应该高于 Agent 的推理设计。测试场景里数据真实性和过程可观测性才是地板模型聪明程度只是天花板。把情感从让模型更聪明转向让现场更透明整个系统的稳定性和可排查性会同时上升。第三从第一天就记录完整的执行回放包括每一步的截图、层级树、模型推理摘要、工具调用参数和返回值。这些数据不仅是排查问题的凭据更是后续优化提示词、调整技能边界的重要素材。没有过程数据的 Agent 测试出问题都是玄学。第四对稳定性的目标设定要务实。不要追求 100% 的重复通过率而是追求页面变化后用例的自适应存活率。这两个目标背后的技术路径完全不同想明白你要哪个再决定怎么建设。通用 Agent 框架下沉到测试场景注定不会是一条平滑的路。它把很多以前靠人力和规则维护的隐性知识变成了模型需要动态推理的内容这个转变天然会带来阵痛。但从长期看只要你把约束系统建得足够扎实让 Agent 在受控边界内发挥它的灵活性这套体系带来的维护成本下降和用例自适应能力提升传统框架确实很难企及。
阅读完成 · 觉得有帮助?