Jev视觉模型如何提升移动端UI自动化稳定性
发布时间:2026/10/3 18:48:13 作者:尧图编辑部 阅读量:1,286

做移动端自动化时间长了会有一种很深的体感脚本跑不过去大多数时候不是功能真的挂了而是“判断”写得不够聪明。之前我维护一套 App 回归用例最怕的不是流程复杂而是页面改个文案、换个图标、插一个弹层xpath 就全断、sleep 就失效、断言就扑空。后来我把本地部署的 Jev 模型接进了这套流程让它承接每一步的界面判断——从“界面到没到”到“目标能不能点”“点了之后有没有变化”全部交给模型给出结论而不是靠一屏写死的等待逻辑。实测下来原先最脆的几个用例从“每次跑都要盯”变成了“跑完看报告就行”。这篇文章会把整套做法拆开讲包括它的设计思路、接入方式、实际踩过的坑和最后的提速点。适合被 UI 自动化稳定性折磨过的人看也适合正在做 App 端 AI Agent、需要让模型理解界面状态的应用开发者参考。1. 移动端自动化的瓶颈界面判断这件事代码只能硬扛1.1 传统做法为什么越维护越难受早期做移动端自动化大家默认的方案都是老三样控件定位、显式等待、断言校验。控件定位用 resource-id、xpath、accessibility id找不到就刷新重试等待就用 time.sleep 或者直到元素可见断言就是检查某个 text、某个 view 是否存在。这套组合拳在页面结构稳定的时候挺好用但一旦进入真实项目的迭代节奏问题就全冒出来了。页面结构一变xpath 就断。安卓端还好iOS 端很多元素连稳定的 accessibility id 都没有开发稍微改个嵌套层级定位表达式就变成一纸空文。动态加载更是重灾区列表滑到底部才开始加载下一页广告位偶尔出现偶尔不出现网络慢的时候 loading 转圈的时间完全没规律。sleep 短了会偶发报错sleep 长了整套用例的时长肉眼可见地膨胀。断言部分也好不到哪去你只能判断“某个元素在不在”但很难判断“当前这个状态是不是真的符合业务预期”。我见过最典型的场景是注册流程里有一个协议勾选框开发把原来的 CheckBox 改成了自定义 Viewresource-id 没变但截图视觉样式完全不一样了传统定位照样能找到可用户看到的却是另一套交互。这种“结构上没坏、语义上已经坏了”的问题代码几乎无能为力。1.2 为什么 Jev 这类模型适合做界面判断后来我换了个思路与其继续在控件树里跟开发捉迷藏不如让模型直接“看界面”。Jev 是一个可以本地部署的视觉理解模型输入是截图和任务描述输出是结构化的判断结果。它跟 OCR 不一样OCR 只能告诉你图上有哪些文字Jev 能综合颜色、布局、文字、控件状态得出“这个页面现在处于什么阶段”“目标元素在什么位置”“当前状态是否符合预期”的结论。这个转变的本质是把“判断”从代码逻辑里剥出来交给更擅长语义理解的部分去完成。打个比方以前的自动化脚本靠的是“刷工牌识别身份”必须对方配合、证件齐全现在 Jev 这种方案靠的是“看脸认人”你不需要知道对方内部怎么组织只要界面长什么样就能判断。移动端界面恰恰是高度视觉化的页面是否加载完成、按钮是否可点、弹窗是否出现这些东西本质上都是视觉信号。用 Jev 承接每一步的界面判断还有一个额外收益跨端兼容变得简单。同一套业务逻辑iOS 和安卓的控件树完全不同传统脚本要维护两套定位器但如果判断逻辑是基于截图的两端的差异就被模型消化掉了。这也是我在标题里强调“每一步”的原因——不是只在关键节点用模型而是从启动 App 到最终断言每一步的界面状态都由 Jev 给结论脚本本身只负责执行动作。2. 让 Jev 承接每一步判断点怎么拆结果怎么用2.1 每个操作步骤里藏着五个判断点很多人以为“界面判断”就是检查目标元素在不在真正接进去才发现一个操作步骤里至少藏着五个判断点。操作前要判断目标是否出现、目标当前是否可点、如果页面有滚动还需要判断目标是否在可视区域内操作后要判断界面是否发生了预期变化、是否出现了异常弹窗比如升级提示、权限请求、崩溃弹窗。把这五个判断点列出来之后我对现有用例做了个改造不再写“等待 xx 元素出现”而是定义成“等待主界面完全就绪”由 Jev 去判断底部 Tab 是否高亮、首页内容是否渲染、loading 是否消失。点击操作也不再是“找到按钮就点”而是先让 Jev 确认按钮处于可点击状态再动作动作之后再截一张图让 Jev 判断页面跳转成功。这套做法的核心价值在于把“难以穷举的状态”变成了“可以用视觉规则描述的状态”。传统代码需要用大量条件和分支去覆盖各种边界情况而 Jev 只需要一张截图和一个问题就能返回当前状态属于哪种情况。我在内部项目里的经验是脚本里 70% 的 if-else 和重试逻辑都可以删掉换成 Jev 的判断结果。2.2 判断结果必须结构化不能只给一句人话一开始我让 Jev 直接返回自然语言比如“页面加载完成确认按钮在右下角”结果下游代码处理起来极其痛苦要自己解析定位词、要容错不同说法、要处理含糊表达。后来我把输出格式收敛成固定 JSON问题就清爽了{ status: ready, target: confirm_button, position: [638, 852], confidence: 0.94, reason: 标题已出现按钮变为高亮可点状态 }代码只需要判断 status 字段和 position 字段reason 只作为可读日志保存。建议你在接 Jev 的时候也对输出格式做一次强约束状态枚举、坐标数组、置信度、简要原因四样缺一不可。置信度这个字段非常重要它决定了自动化流程能不能做“低置信度重试”后面在排查章节我会详细讲。结构化返回还有一个好处方便接入 Codex 这类 Agent 工具。我在项目里把 Jev 封装成了一个工具函数Codex 在编排步骤的时候可以随时发起一次界面判断然后根据 Jev 的返回结果决定下一步动作。这样模型做决策、Jev 做感知分工比让 Codex 直接看图更稳定。2.3 部署方式与模型选型的实际考量Jev 的部署路径和一般本地视觉模型没有本质区别。先确认官方渠道的模型许可与权重获取方式拿到模型文件之后再做量化。显存够首选 16bit 精度跑服务只求速度和低资源占用就上量化版本。我实际是在一台 24G 显存的机器上用类 vLLM 的推理服务拉起的单卡同时处理 4 个并发请求没什么压力单次判断延迟大约在 400 毫秒到 800 毫秒之间这个区间对交互式自动化完全够用。接口设计上建议一个统一入口解决所有判断需求不要一个场景一个接口。我的方案是 POST 一个 JSON 请求到本地服务的/judge路径请求体包含截图路径、任务描述、历史上下文。截图直接传文件路径比传 base64 更快服务端加载图片也方便。任务描述一定要带明确的判断维度比如“判断是否出现权限弹窗”和“描述当前界面状态”后者会让模型输出一堆无关信息前者才能拿到可用的结论。注意本地部署模型最怕的就是混用推理框架导致输出格式不稳定。我踩过一次坑同一个模型文件不同框架加载后JSON 输出的格式竟然有细微差异。建议部署完立刻固定一套推理服务不要轻易换。3. 实操接入把 Jev 接进每一步操作3.1 一次完整判断的 Prompt 与请求示例接入 Jev 的关键环节是 Prompt 设计。界面判断的 Prompt 不能太开放必须把角色、任务、输出格式一次性说清楚。我整理了一份现在还在用的模板你是移动端界面判断引擎。根据给定的截图判断以下条件是否满足 条件确认按钮已变为可点击状态且当前页面处于注册流程的第二步。 输出要求 1. 返回 JSON不要有多余文字。 2. status 是 ready 或 not_ready。 3. target 填写确认按钮。 4. position 填写按钮中心点的 x,y 坐标基于截图原始像素。 5. confidence 是 0 到 1 的浮点数。 6. reason 用一句话解释判断依据。这段 Prompt 有一点很重要给出了“基于截图原始像素”这个约束否则模型会依赖图片的原始尺寸瞎猜坐标。移动端自动化里坐标精度直接决定点击准不准这个字段必须显式声明。请求示范如下import base64 import requests def judge(screenshot_path: str, condition: str, history: list None): with open(screenshot_path, rb) as f: image_b64 base64.b64encode(f.read()).decode(utf-8) payload { image: image_b64, prompt: f你是移动端界面判断引擎。{condition}, history: history or [], max_tokens: 300 } resp requests.post(http://127.0.0.1:9001/judge, jsonpayload, timeout10) resp.raise_for_status() return resp.json()3.2 场景一等待首页加载完成这个场景最有代表性。以前我会写成“等待首页商品卡片 xpath 可见”结果 App 做了首页改版卡片资源 id 变了定位失败。换成 Jev 之后等待逻辑变成了启动 App截图。调用 Jev 判断“首页是否已经完全加载判断依据底部导航栏是否出现、首页内容是否不再是 loading 占位图、是否出现任意一个商品卡片。”如果 Jev 返回 ready就继续下一步如果返回 not_ready等待 1 秒后重新截图最多重试 5 次。超过重试次数直接判定为失败保存最后一次截图作为失败证据。这里没有写死任何一个控件 id页面改成什么样都不影响判断逻辑。我实测过第一次启动 App 遇到低网速时传统 sleep 等待容易不够Jev 方案反而稳定得多因为它能真正看到“页面是否就绪”而不是猜时间。3.3 场景二点击目标并校验操作结果点击是比等待更需要界面的环节。目标是否可点在代码里要看 enabled 属性但很多自定义控件这个属性并不可信。Jev 的做法是直接看视觉状态按钮是否高亮、是否置灰、是否有遮挡。我的流程是这样的先让 Jev 判断目标按钮的位置和状态拿到坐标后执行点击。点击完立刻截图再让 Jev 判断“当前是否已经离开注册页进入主界面”。这一次判断不是可选的它是整个校验链的关键。两道判断都通过步骤才算成功。这里分享一个提速细节点击后的判断截图可以和操作前截图做区域裁剪对比。比如我只关注页面顶部的标题区域就把截图裁剪成 200x80 的局部图再送给 Jev模型推理速度会快很多。区域裁剪的概念在后面会展开讲它能有效缓解“每一步都判断”带来的性能压力。3.4 提速的核心不是每一步都做全图推理“每一步都让 Jev 看全图”是很自然的写法但性能不可接受。我跑了一轮完整回归120 个步骤全用全图推理总耗时比原来多了将近 5 倍。后来我做了三个优化实测提速非常明显第一区域裁剪。把截图按业务区域切块比如顶部标题栏、中间内容区、底部操作栏判断时只把相关区域送给模型。第二状态缓存。如果同一个页面连续操作前一步已经判断过“页面未变化”下一步就可以复用上一次的结论不必重新推理。第三异步预判。在执行较长动画的时候先在后台启动下一次判断动画一结束立刻拿到结果消除等待时间。这三个优化做完平均单步判断耗时从 600 毫秒降到了 150 到 250 毫秒。对于大多数移动端自动化场景这个延迟已经完全无感了。总结起来就是让 Jev 干它擅长的事但别让它干重复的事。4. 常见问题与排查技巧实录4.1 高置信度失败模型说看到了实际点不到这是接 Jev 之后遇到最多的问题。Jev 返回 ready置信度 0.9但脚本点击后页面没有任何反应。排查下来发现两个原因一是目标元素被系统弹窗或半透明遮罩盖住视觉上能看到但交互上点不进去二是坐标换算问题Jev 用的是截图原始像素而自动化框架执行点击用的是屏幕物理坐标分辨率不同会出现偏差。解决第一个问题需要在 Prompt 里增加一条约束“如果存在遮挡或半透明遮罩状态视为不可用”。解决第二个问题更简单在截图前先把设备分辨率设成固定值并让截图尺寸和物理坐标保持一致。如果实在不能保持一致就加一层坐标映射函数把模型输出的像素坐标换算到屏幕坐标。4.2 低置信度场景该怎么处理模型不是每次都给的结论都很肯定。网络慢的时候页面只加载了一半Jev 的置信度会降到 0.5 左右。如果代码只看 status不看置信度就会误判。我的处理策略是加一个阈值判断置信度低于 0.7 时认为是“状态不够明确”重试一次重试后仍然低于 0.7 就额外截一张局部放大图再让 Jev 判断。多数情况下局部放大图能显著提升置信度。置信度问题不能靠调 Prompt 硬解因为低置信度本身往往反映的是真实界面的模糊状态脚本必须接受这种不确定性。4.3 模型返回格式偶尔跑偏本地模型偶尔会在 JSON 前后附上一些解释性文字比如“根据截图判断结果如下json...”之类。这在脚本化调用里非常致命。我的做法不是去改 Prompt 让它别啰嗦而是在代码层做一次清洗把返回内容里的 markdown 代码块标记去掉再用正则取出第一个完整 JSON。这属于典型的“模型不稳代码来兜”思路。另外我还加了输出 schema 校验解析失败就自动重试一次仍然失败才标记用例失败。自动化和纯模型应用的不同就在这模型可以犯小错但自动化框架必须有容错机制。4.4 上下文太长导致判断变慢Jev 模型在连续步骤里会积累历史截图和判断结果上下文很快就满了。上下文一长推理速度明显下降有时候还会产生幻觉把旧页面的状态当成当前页面的。我现在的做法是只保留最近三步的上下文更早的步骤直接裁剪掉。如果需要追溯就把历史判断结果放到日志里而不是全塞给模型。上下文裁剪对视觉模型尤其重要因为截图占用的 token 比文字大得多通常二十步左右就会把上下文撑爆。4.5 问题排查速查表现象可能原因解决方案高置信度但点击无效被遮挡 / 坐标换算错误Prompt 加遮挡约束 / 统一坐标映射频繁低置信度页面加载不完整局部放大重试 / 延长轮询间隔JSON 解析报错输出包含多余文字代码层清洗 / schema 校验 / 重试推理延迟持续走高上下文太长只保留最近三步历史不同设备表现不一致截图分辨率不同固定设备分辨率 / 设设备无关坐标5. 最后分享一个很实用的小技巧接 Jev 承接界面判断之后调试的难度反而上来了模型判断错了你很难知道它是怎么想的。我的做法是在自动化报告里把每一次 Jev 判断的截图、Prompt、返回 JSON、置信度全部记录下来按步骤编号归档。出问题的时候直接看那个时间点的记录一眼就能找到是模型看走眼了还是流程设计错了。这个小习惯对排查提升特别大。有一次跑电商购物流程脚本在支付页卡住我打开 Jev 记录发现模型一直把“忘记密码”链接误认为“确认支付”按钮。原因是页面底部的按钮区域布局太接近裁剪区域把两个元素混在了一起。我把裁剪区域往左移了 50 像素问题就彻底消失了。这种问题如果不看记录可能要调试一整天。再给一个更保守的建议如果把 Jev 用在支付、删除这类高风险操作上不要把模型判断作为唯一依据保留传统的二次确认。比如支付前除了让 Jev 看页面还要校验当前页面的 URL 或者关键布局标识。模型负责看清代码负责守底线两者结合才是移动端自动化提速又不失稳的最佳状态。