news 2026/11/4 5:25:36

LLM自动化测试:视觉定位、DOM语义化与Computer Use统一底座

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LLM自动化测试:视觉定位、DOM语义化与Computer Use统一底座

1. 为什么“统一技术底座”是 LLM 自动化测试的分水岭

做过 UI 自动化的人都有一个共同的痛:脚本写起来容易,维护起来要命。传统方案里,定位一个按钮要么靠 DOM 选择器,要么靠图像模板匹配,两条路各管各的,互不兜底。一旦前端改了个 class 名,或者分辨率变了一下,脚本就集体飘红。到了 LLM 驱动的自动化测试阶段,这个问题被进一步放大——因为 LLM 要“看懂”界面才能决策,而它看界面的方式,直接决定了整个 Agent 的上限。

我最近在搭一套 LLM 自动化测试框架,踩了一圈坑之后得出一个结论:视觉定位、DOM 语义化、Computer Use 这三者不是三选一的关系,而是必须统一到一个技术底座上。这个底座要解决的核心问题是——让 LLM 在任意时刻都能拿到一份“它看得懂、且足够稳定”的界面表征,然后基于这份表征做定位、断言和操作。

为什么强调“统一”?因为实际项目里我见过太多拼接式的做法:视觉定位用一套坐标系统,DOM 解析用另一套节点树,Computer Use 又走截图加坐标点击。三套东西各说各话,LLM 拿到的上下文是割裂的,定位失败时你根本不知道是视觉模型没识别准,还是 DOM 选择器写错了,还是坐标换算偏了。排查成本高到离谱。

统一底座的价值就在于:同一份界面状态,同时输出结构化语义、视觉坐标和可操作句柄,三者互相校验、互相兜底。LLM 只需要面对一种数据格式,框架内部负责把三种信号融合成最可靠的定位结果。这篇文章就把我这套底座的拆解思路、关键实现和踩坑记录完整摊开,适合正在做 AI 自动化测试、或者准备把 LLM 接入现有测试体系的同学参考。不管你是用 Playwright、Appium 还是自研的 Agent 框架,这套思路都能直接迁移。

2. 三种定位范式的本质差异与融合逻辑

2.1 视觉定位:像人一样“看”,但别指望它精确到像素

视觉定位的底层逻辑是:截取当前界面图像,用视觉模型(可以是传统模板匹配,也可以是多模态 LLM)识别出目标元素的位置,返回一个边界框或中心坐标。它的最大优势是跨平台、跨技术栈——不管是 Web、原生 App 还是桌面软件,只要能截图就能定位,不依赖任何 DOM 或控件树。

但视觉定位的坑也很明显。第一,分辨率敏感。同一套坐标在 1080P 上能用,换到 2K 屏就偏了。第二,动态内容干扰。页面上有个轮播图或者加载动画,模板匹配直接失效。第三,语义缺失。视觉模型告诉你“这里有个按钮”,但它不知道这个按钮是“提交”还是“取消”,除非你额外做 OCR 或者用多模态模型做理解。

我在实际项目里的做法是:视觉定位只作为兜底和交叉验证手段,不作为主定位方式。具体来说,当 DOM 定位失败或者元素被遮挡时,才启用视觉定位,并且视觉定位的结果必须和 DOM 语义做一致性校验——比如视觉识别到的区域,在 DOM 树里对应的是不是一个可点击元素。如果对不上,就标记为“定位存疑”,交给人工复核或者让 LLM 重新决策。

注意:视觉定位的坐标一定要做归一化处理。我习惯把坐标统一转换成相对于视口宽高的百分比,这样换分辨率时只需要重新计算一次映射关系,不用改脚本。

2.2 DOM 语义化:结构最可靠,但别被“语义”两个字骗了

DOM 语义化的核心思路是:不直接用 CSS 选择器或 XPath 去“硬找”元素,而是把 DOM 树转换成一份带语义描述的节点列表,让 LLM 基于语义描述来定位。比如一个按钮,不再写成div.btn-primary > span,而是描述成“位于表单底部的提交按钮,文本为‘确认提交’,当前可点击”。

这样做的好处是抗前端改动能力强。前端改 class 名、调层级结构,只要按钮的语义没变,LLM 依然能定位到。而且语义化描述天然适合 LLM 理解,不需要额外做选择器生成。

但这里有个关键问题:语义化描述的质量取决于 DOM 解析的粒度。如果解析得太粗,一个 div 包了十个按钮,LLM 分不清;解析得太细,每个 span 都变成一个节点,上下文爆炸。我的经验是:以“可交互元素”为基本单位做语义化,也就是按钮、输入框、链接、下拉框这些,其他纯展示性的容器节点做聚合折叠。具体实现上,可以用 Playwright 的 accessibility tree 作为基础,再叠加自定义的语义标注。

另外,DOM 语义化在 Web 场景好用,到了原生 App 或者桌面软件就抓瞎了。这时候就需要 Computer Use 来补位。

2.3 Computer Use:最接近“真人操作”,但延迟和稳定性是硬伤

Computer Use 这个概念最近很火,本质上是让 LLM 直接控制鼠标键盘,像人一样操作界面。它的优势是通用性极强——不依赖任何 DOM、控件树或 API,只要能截图和模拟输入,就能操作任何界面。对于那种没有开放自动化接口的老系统、Flash 应用、远程桌面场景,Computer Use 几乎是唯一解。

但它的缺点同样致命。第一,延迟高。每一步操作都要截图、推理、执行,一轮下来几秒钟就过去了,跑一个上百步的用例,时间成本难以接受。第二,稳定性差。LLM 的视觉理解不是 100% 准确,偶尔会把“取消”看成“确认”,这种错误在测试场景里是灾难性的。第三,难以断言。操作完了之后,怎么判断结果对不对?还是得回到截图比对或者 DOM 检查。

所以我的策略是:Computer Use 作为最后手段,只在 DOM 和视觉都搞不定的场景下启用,并且启用时要加一层“操作确认”机制——LLM 给出操作意图后,框架先做一次干跑校验,确认目标元素和操作类型匹配,再真正执行。

2.4 融合逻辑:三路信号如何汇成一份定位结果

把三种范式统一到一个底座,核心是设计一个定位仲裁层。我的做法是:

  1. 优先走 DOM 语义化定位,拿到候选元素列表和置信度。
  2. 同时跑视觉定位,拿到视觉候选区域。
  3. 做交叉校验:如果 DOM 候选和视觉候选在空间上重叠,置信度加权提升;如果只有一路有结果,标记为“单路定位”,降级使用。
  4. 如果两路都失败,触发 Computer Use 兜底,但要求 LLM 给出操作理由,并记录到日志里供后续分析。

这套仲裁逻辑用表格表示更清楚:

场景DOM 语义化视觉定位仲裁结果后续动作
理想情况命中,置信度高命中,区域重叠直接使用 DOM 句柄正常执行
DOM 失效未命中命中使用视觉坐标,标记降级执行后校验结果
视觉干扰命中未命中或偏移使用 DOM 句柄,忽略视觉正常执行
双路失败未命中未命中触发 Computer Use记录日志,人工复核
冲突命中 A命中 B,不重叠暂停,请求 LLM 重新决策记录冲突样本

这张表是我在实际项目里反复调整后定下来的,核心原则是:DOM 优先、视觉校验、Computer Use 兜底、冲突必记录。冲突样本特别有价值,它们是后续优化定位策略的训练数据。

3. 统一底座的核心模块拆解与实操要点

3.1 界面表征层:把界面变成 LLM 能吃的“三合一”数据

界面表征层是整个底座的地基。它的任务是在每一次操作前,把当前界面转换成一份统一的数据结构,我把它叫做Unified UI State,包含三个部分:

  • 语义树:从 DOM 或控件树提取的可交互元素列表,每个元素带角色、文本、状态、层级路径。
  • 视觉快照:当前界面的截图,以及每个语义元素对应的边界框(如果能拿到的话)。
  • 操作句柄:每个语义元素对应的可执行句柄,Web 场景是 Playwright 的 Locator,App 场景是 Appium 的 WebElement,桌面场景是坐标加输入模拟。

这三部分用同一个元素 ID 关联起来。LLM 看到的是语义树加视觉快照的描述,它决定操作哪个元素后,框架通过元素 ID 找到对应的操作句柄去执行。

实操中有几个关键点:

第一,语义树的裁剪。一个复杂页面可能有上千个 DOM 节点,全丢给 LLM 上下文直接爆。我的做法是只保留“可见、可交互、有语义”的节点,并且做层级聚合。比如一个表格,不需要把每个单元格都列出来,只需要描述“这是一个 5 行 3 列的数据表格,表头为……”,LLM 需要具体单元格时再按需展开。

第二,视觉快照的标注。单纯给 LLM 一张截图,它很难把截图里的区域和语义树里的元素对应起来。我的做法是在截图上叠加编号标注,每个可交互元素画一个框并标上 ID,LLM 看到的是“带标注的截图加语义树”,对应关系一目了然。

第三,句柄的懒加载。操作句柄不需要一开始就全部解析出来,那样太慢。我的做法是语义树里只存定位描述,等 LLM 真正决定操作某个元素时,再实时解析出句柄。这样既保证了速度,又避免了句柄失效的问题。

提示:界面表征的生成频率要控制好。不是每一步操作前都重新生成,而是在界面发生明显变化时才刷新。我一般用 DOM 变更监听加截图差异检测来判断是否需要刷新。

3.2 定位仲裁层:三路信号怎么投票

定位仲裁层的输入是 LLM 给出的目标描述(比如“点击提交按钮”),输出是一个确定的元素句柄。它的工作流程分四步:

第一步,语义匹配。把目标描述和语义树里的元素做匹配,可以用关键词匹配加向量相似度。比如“提交按钮”会匹配到文本包含“提交”、角色为 button 的元素。匹配结果按置信度排序,取 Top 3 作为候选。

第二步,视觉验证。对每个候选元素,检查它的边界框在当前截图里是否可见、是否被遮挡。如果候选元素在视觉上不可见,置信度直接降为零。这一步能过滤掉很多“DOM 里有但用户看不到”的坑,比如隐藏的弹窗、折叠的菜单。

第三步,冲突检测。如果 Top 1 候选和 Top 2 候选的置信度差距小于阈值(我设的是 0.15),就判定为“定位歧义”,不直接执行,而是把候选列表返回给 LLM,让它重新选择或者补充描述。

第四步,句柄解析与预检。确定目标元素后,解析出操作句柄,并做一次预检——比如检查元素是否可点击、是否在视口内、是否有遮挡。预检不通过就触发滚动或等待,而不是直接报错。

这套流程听起来步骤多,但实际跑起来很快,因为大部分匹配和验证都是本地计算,不涉及 LLM 调用。只有出现歧义时才回退给 LLM,这样既保证了速度,又保证了准确率。

3.3 操作执行层:把 LLM 的意图翻译成可靠动作

操作执行层负责把 LLM 的决策变成实际的鼠标键盘或 API 调用。这里的关键是动作的原子化和可回滚。

我把所有操作拆成原子动作:点击、输入、滚动、拖拽、按键、等待。每个原子动作都有明确的参数和前置条件。比如“点击”这个动作,前置条件是元素可见且可点击,参数是元素句柄和点击方式(单击/双击/右键)。

执行时,每个原子动作都会记录执行前后的界面状态,方便出问题时回溯。如果某个动作执行失败,框架会尝试自动恢复——比如点击失败时先滚动到元素可见再重试,输入失败时先清空再重试。重试次数我一般设 2 次,超过就标记为失败并记录详细日志。

还有一个容易被忽略的点:操作后的等待策略。传统自动化测试里用固定 sleep 是最蠢的做法,但完全依赖智能等待也有风险。我的做法是混合策略:操作后先等 DOM 稳定(用 MutationObserver 监听),再等网络空闲(如果有网络请求),最后加一个短时间的视觉稳定检测(连续两帧截图差异小于阈值)。三层等待叠加,既快又稳。

3.4 断言与校验层:怎么判断“操作成功了”

LLM 自动化测试最难的部分其实是断言。传统测试里你可以写expect(element).toHaveText('成功'),但 LLM 场景下,界面状态是动态的,断言条件也需要动态生成。

我的做法是:让 LLM 在给出操作指令的同时,也给出预期结果描述。比如“点击提交按钮,预期出现‘提交成功’提示”。框架执行完操作后,把当前界面表征和预期结果描述一起丢给 LLM,让它判断是否满足预期。为了降低成本,简单的断言可以用规则匹配(比如文本包含、元素出现),复杂的才走 LLM 判断。

这里有个坑:LLM 判断断言时容易“过度宽容”。比如预期是“提交成功”,实际界面显示的是“提交失败”,但 LLM 可能因为两者都有“提交”两个字就判为通过。解决办法是给 LLM 的 prompt 里加明确的判断标准,要求它逐项核对关键词,并且给出判断理由。理由不充分的断言结果要标记为“存疑”,人工复核。

4. 完整实操流程:从零搭一个统一底座的 Demo

4.1 环境准备与依赖选型

我以 Web 场景为例,搭一个最小可用的统一底座 Demo。技术栈选型如下:

  • 浏览器自动化:Playwright。选它是因为它的 accessibility tree 和 Locator API 对语义化定位支持最好,而且跨浏览器一致性强。
  • 视觉处理:OpenCV 加 Pillow。做截图裁剪、标注绘制、模板匹配够用了。如果需要更强的视觉理解,可以接多模态模型,但 Demo 里先用传统方法。
  • LLM 接入:任意支持 function calling 的模型都行。Demo 里我用一个抽象的 LLM Client 接口,方便替换。
  • 测试框架:pytest。生态成熟,插件多,和 Playwright 集成方便。

安装依赖:

pip install playwright opencv-python pillow pytest pytest-asyncio playwright install chromium

目录结构我习惯这样组织:

unified-ui-base/ ├── core/ │ ├── ui_state.py # 界面表征数据结构 │ ├── semantic_tree.py # 语义树提取 │ ├── visual_snapshot.py # 视觉快照生成 │ └── locator_arbiter.py # 定位仲裁 ├── executor/ │ ├── actions.py # 原子动作 │ └── wait_strategy.py # 等待策略 ├── llm/ │ └── client.py # LLM 接口封装 └── tests/ └── test_demo.py # 示例用例

4.2 语义树提取的关键代码与参数说明

语义树提取的核心是从 Playwright 的页面对象里抽取出可交互元素。我不用完整的 accessibility tree,因为那个太啰嗦,而是自己写了一个提取器:

async def extract_semantic_tree(page): elements = await page.evaluate(""" () => { const interactiveSelectors = [ 'button', 'a', 'input', 'select', 'textarea', '[role="button"]', '[role="link"]', '[role="checkbox"]', '[onclick]', '[tabindex]:not([tabindex="-1"])' ]; const nodes = document.querySelectorAll(interactiveSelectors.join(',')); const result = []; nodes.forEach((node, index) => { const rect = node.getBoundingClientRect(); const style = window.getComputedStyle(node); if (rect.width === 0 || rect.height === 0) return; if (style.visibility === 'hidden' || style.display === 'none') return; result.push({ id: `elem_${index}`, tag: node.tagName.toLowerCase(), role: node.getAttribute('role') || node.tagName.toLowerCase(), text: (node.innerText || node.value || '').trim().slice(0, 100), placeholder: node.getAttribute('placeholder') || '', ariaLabel: node.getAttribute('aria-label') || '', bbox: {x: rect.x, y: rect.y, w: rect.width, h: rect.height}, visible: true, enabled: !node.disabled }); }); return result; } """) return elements

这段代码有几个设计考量。第一,只提取可交互元素,过滤掉纯展示节点,控制语义树规模。第二,过滤不可见元素,宽高为零或者 display none 的直接跳过,避免 LLM 定位到看不见的东西。第三,文本截断到 100 字符,防止超长文本撑爆上下文。第四,保留 bbox,为后续视觉校验做准备。

实测下来,一个中等复杂度的页面,语义树节点数在 30 到 80 之间,LLM 处理起来毫无压力。

4.3 视觉快照生成与标注叠加

视觉快照的生成分两步:先截图,再在截图上叠加语义元素的标注框。

from PIL import Image, ImageDraw, ImageFont async def generate_visual_snapshot(page, semantic_tree, output_path): screenshot_bytes = await page.screenshot(full_page=False) with open(output_path, 'wb') as f: f.write(screenshot_bytes) img = Image.open(output_path).convert('RGB') draw = ImageDraw.Draw(img) for elem in semantic_tree: bbox = elem['bbox'] x, y, w, h = bbox['x'], bbox['y'], bbox['w'], bbox['h'] draw.rectangle([x, y, x + w, y + h], outline='red', width=2) draw.text((x, y - 12), elem['id'], fill='red') img.save(output_path) return output_path

标注框用红色,ID 标在框的左上角。这样 LLM 看到截图时,能直接把视觉区域和语义树里的 ID 对应起来。实测这个做法对定位准确率提升非常明显,尤其是页面上有多个相似按钮时。

注意:截图要用视口截图,不要用全页截图。全页截图在长页面上会把元素压得很小,标注框也看不清。如果目标元素在视口外,先滚动到可见再截图。

4.4 定位仲裁的完整实现

定位仲裁是底座的核心逻辑,我把它写成一个独立的类:

class LocatorArbiter: def __init__(self, semantic_tree, visual_snapshot_path, llm_client): self.semantic_tree = semantic_tree self.visual_snapshot_path = visual_snapshot_path self.llm_client = llm_client async def locate(self, target_description): candidates = self._semantic_match(target_description) if not candidates: return await self._fallback_to_computer_use(target_description) verified = self._visual_verify(candidates) if not verified: return await self._fallback_to_computer_use(target_description) if self._has_ambiguity(verified): return await self._resolve_with_llm(target_description, verified) return verified[0] def _semantic_match(self, description): scored = [] for elem in self.semantic_tree: score = 0 if description in elem['text']: score += 0.5 if description in elem['ariaLabel']: score += 0.3 if description in elem['placeholder']: score += 0.2 if score > 0: scored.append((elem, score)) scored.sort(key=lambda x: x[1], reverse=True) return [elem for elem, _ in scored[:3]] def _visual_verify(self, candidates): img = Image.open(self.visual_snapshot_path) verified = [] for elem in candidates: bbox = elem['bbox'] if bbox['w'] > 0 and bbox['h'] > 0: verified.append(elem) return verified def _has_ambiguity(self, candidates): if len(candidates) < 2: return False return True

这段代码里,_semantic_match用的是简单的关键词匹配,实际项目里可以换成向量相似度。_visual_verify目前只检查宽高,更严格的做法是检查元素区域是否被其他元素遮挡。_has_ambiguity判断是否有多个候选,有的话就交给 LLM 重新决策。

4.5 操作执行与等待策略的落地

操作执行层我封装了一个ActionExecutor,核心方法如下:

class ActionExecutor: def __init__(self, page): self.page = page async def click(self, element_id, semantic_tree): elem = next(e for e in semantic_tree if e['id'] == element_id) locator = self._resolve_locator(elem) await locator.scroll_into_view_if_needed() await self._wait_for_stable() await locator.click(timeout=5000) await self._wait_after_action() def _resolve_locator(self, elem): if elem['text']: return self.page.get_by_text(elem['text'], exact=True).first if elem['ariaLabel']: return self.page.get_by_label(elem['ariaLabel']).first return self.page.locator(f"text={elem['text']}").first async def _wait_for_stable(self): await self.page.wait_for_load_state('networkidle') await self.page.wait_for_timeout(200) async def _wait_after_action(self): await self.page.wait_for_load_state('networkidle') await self.page.wait_for_timeout(300)

等待策略这里我用了三层:scroll_into_view_if_needed保证元素可见,wait_for_load_state('networkidle')等网络空闲,再加一个短延时等渲染稳定。实测这套组合比固定 sleep 快很多,而且稳定性更好。

5. 常见问题与排查技巧实录

5.1 定位飘移:为什么同一个按钮这次能点到下次就点不到

定位飘移是最高频的问题。我排查下来,原因主要有三类:

第一类,动态 ID 或动态 class。前端框架生成的 ID 每次刷新都不一样,如果你用 ID 做定位,必然飘。解决办法是语义化定位,用文本、角色、位置关系来定位,不用动态属性。

第二类,元素被遮挡。DOM 里元素存在,但被弹窗、浮层、固定导航栏挡住了。Playwright 的 click 会报“element is not visible”或者“element intercepts pointer events”。解决办法是点击前先检查遮挡,如果有遮挡先关闭弹窗或者滚动页面。

第三类,iframe 或 shadow DOM。元素在 iframe 里,主文档的定位器找不到。解决办法是先用page.frame_locator进入 iframe,再定位。shadow DOM 类似,需要用locator.locator穿透。

排查这类问题的通用思路是:先看语义树里有没有这个元素,再看视觉快照里可不可见,最后看操作句柄能不能解析。三步走下来,问题基本定位。

5.2 LLM 决策不稳定:同样的界面为什么给出不同的操作

LLM 决策不稳定通常是因为 prompt 里信息不足或者有歧义。我的经验是:

  • 给 LLM 的界面描述要结构化,不要丢一大段自然语言。用 JSON 格式列出可交互元素,每个元素带 ID、角色、文本、状态。
  • 操作指令要明确,比如“点击 ID 为 elem_5 的按钮”,而不是“点击提交按钮”。让 LLM 先选 ID,再由框架解析句柄,这样责任清晰。
  • 加 few-shot 示例,在 prompt 里给一两个正确决策的例子,LLM 的输出会稳定很多。

还有一个技巧:让 LLM 输出决策理由。比如“我选择 elem_5,因为它的文本是‘提交’,且位于表单底部”。理由不合理的决策可以直接拦截,不用执行。

5.3 视觉定位偏移:坐标对不上怎么办

视觉定位偏移的排查顺序:

  1. 检查截图分辨率。截图分辨率和实际屏幕分辨率不一致时,坐标需要缩放。我一般把坐标归一化成百分比,用的时候再乘当前视口宽高。
  2. 检查设备像素比。高 DPI 屏幕下,截图尺寸可能是逻辑尺寸的两倍,坐标要除以 devicePixelRatio。
  3. 检查滚动位置。截图是视口截图,如果页面滚动了,元素的 bbox 是相对于视口的,不是相对于文档的。定位时要确保滚动位置一致。

我踩过最坑的一次是设备像素比的问题,截图是 2 倍图,坐标没除 2,结果点击位置偏了一倍。后来在截图生成时统一记录 devicePixelRatio,坐标换算时自动处理,再没出过问题。

5.4 常见问题速查表

问题现象可能原因排查方法解决方案
元素找不到动态 ID/class检查语义树是否有该元素改用语义化定位
点击无效元素被遮挡检查视觉快照遮挡情况先关闭弹窗或滚动
坐标偏移分辨率/像素比不一致对比截图和实际尺寸坐标归一化处理
LLM 决策飘prompt 信息不足检查 prompt 结构结构化描述加 few-shot
断言误判LLM 过度宽容检查断言理由加严格判断标准
执行超时等待策略不当检查网络和渲染状态三层等待策略
iframe 内元素找不到未切换上下文检查元素是否在 iframe用 frame_locator
操作后状态未更新等待不足检查操作后界面变化加视觉稳定检测

这张表是我从实际项目里攒出来的,基本覆盖了 80% 的常见问题。遇到新问题时,我也会往表里补充,慢慢就形成了一套排查手册。

5.5 几个独家避坑技巧

技巧一:给每个元素加“稳定指纹”。除了语义描述,我还给每个元素算一个指纹,包括它在 DOM 树里的层级路径、相邻元素的文本、相对位置。定位时如果语义匹配有多个候选,用指纹做二次筛选,准确率提升明显。

技巧二:操作前做“干跑”。尤其是 Computer Use 场景,LLM 给出操作指令后,先不真正执行,而是模拟一遍——检查目标元素是否存在、是否可操作、操作后预期状态是否合理。干跑通过再执行,能拦掉大部分误操作。

技巧三:冲突样本自动归档。每次定位仲裁出现冲突时,把当时的语义树、截图、LLM 决策理由打包存下来。攒够一批之后,用这些样本微调定位模型或者优化 prompt,效果比拍脑袋改规则好得多。

技巧四:给 LLM 的上下文加“历史操作摘要”。LLM 在决策时,如果知道前面几步做了什么,能避免很多重复操作和逻辑错误。我一般把最近 5 步的操作摘要附在 prompt 里,包括操作类型、目标元素、执行结果。

6. 底座扩展:从 Web 到 App 再到桌面

统一底座的价值在于可扩展。Web 场景跑通之后,我把同样的架构迁移到了 App 和桌面场景,核心改动只在界面表征层和操作执行层。

App 场景下,语义树从 Appium 的 page source 提取,视觉快照用 Appium 的截图接口,操作句柄换成 Appium 的 WebElement。定位仲裁和等待策略的逻辑完全复用。实测下来,Android 原生 App 的语义化定位准确率比 Web 还高,因为原生控件的 accessibility 信息更规范。

桌面场景下,语义树从系统的 UI Automation 接口提取(Windows 用 UIA,macOS 用 AX),视觉快照用系统截图,操作句柄用坐标加输入模拟。桌面场景的挑战在于控件树不如 Web 规范,很多老软件根本没有 accessibility 信息,这时候 Computer Use 的占比会高很多。

我的建议是:先把 Web 场景做扎实,再往其他平台迁移。因为 Web 场景的调试工具最丰富,问题最容易定位,把底座的仲裁逻辑和等待策略在 Web 上打磨好,迁移到其他平台时只需要替换适配层,核心逻辑不用动。

这套底座我目前跑了大概三个月,覆盖了 Web 和 Android 两个平台,累计执行了上千条用例。定位成功率从最初的 70% 左右提升到了 95% 以上,剩下的 5% 主要是极端动态页面和验证码场景。后续我打算把冲突样本做成一个持续优化的闭环,让底座能自己从失败中学习。如果你也在做类似的事情,欢迎交流踩坑经验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/4 5:25:01

Android滑块验证码:自绘控件与服务端轨迹判定实践

1. 先搞清楚滑块验证码到底防的是谁注册页被脚本刷短信、登录接口被撞库、活动页被羊毛党批量扫&#xff0c;这几件事我这两年碰到的次数比过去五年加起来都多。一开始团队里的方案是四位数图形码&#xff0c;后来改成算式验证&#xff0c;再后来干脆上滑块。Android 滑块验证码…

作者头像 李华
网站建设 2026/11/4 5:24:45

乐鑫 ESP-Claw 上线:AI 直接看你板子上接了什么

通用 AI 写单片机代码最大的尴尬&#xff0c;不是代码烂&#xff0c;是它根本不知道你的板子上接了什么。引脚表靠你口述&#xff0c;外设靠你贴原理图&#xff0c;写出来跑不通&#xff0c;锅还归你。 2026 年 10 月底&#xff0c;乐鑫发布了 ESP-Mosaico 平台和配套的 ESP-Cl…

作者头像 李华
网站建设 2026/11/4 5:24:06

GEO优化与三维九项认证技术解析

1. 项目背景与行业趋势解析"GEO优化"这个概念最近在空间数据服务领域频繁出现&#xff0c;它本质上是指基于地理空间信息&#xff08;Geospatial&#xff09;的优化技术体系。我注意到2026年3月即将实施的三维九项认证标准&#xff0c;这标志着行业即将进入一个全新的…

作者头像 李华
网站建设 2026/11/4 5:23:41

三维ToF技术原理与工业机器视觉应用实战

1. 三维ToF技术的前世今生2004年索尼推出首款商用ToF传感器时&#xff0c;可能没想到这项技术会在20年后彻底改变机器视觉的维度认知。作为从业15年的工业视觉系统设计师&#xff0c;我亲眼见证了这项技术从实验室走向产线的全过程。与传统二维视觉相比&#xff0c;ToF&#xf…

作者头像 李华
网站建设 2026/11/4 5:23:05

百度网盘批量转存工具的技术原理与实践

1. 项目背景与核心功能解析最近在整理网盘资源时&#xff0c;发现一个痛点&#xff1a;当我们需要将大量文件从公开分享链接转存到自己账号时&#xff0c;传统的手动操作方式效率极低。特别是遇到需要批量转存到多个子账号&#xff0c;或者处理数百GB的大文件集合时&#xff0c…

作者头像 李华
网站建设 2026/11/4 5:21:49

Vite .env 多环境打包实战:加载顺序、产物分目录与运行时配置

Vite 项目里那套 .env 环境变量机制&#xff0c;是很多团队用得最多、也最容易用歪的一块功能。刚接触的时候觉得无非就是建个文件、加个前缀、import.meta.env 读一下&#xff0c;可真到要同时维护三套甚至四套部署环境&#xff0c;要在一个仓库里打出给不同客户、不同部署通道…

作者头像 李华