[控场AI]
· 6 分钟阅读· 3,482 字

点击偏差20像素:截图与无障碍树之争

点击偏差20像素:截图与无障碍树之争

计算机使用代理应树优先、截图后备,以元素语义替代像素猜测。

文章探讨了计算机使用代理在执行点击前如何理解屏幕内容,核心主张是优先使用无障碍树而非截图。无障碍树由操作系统为辅助技术维护,提供每个控件的角色、名称、状态和精确位置,让代理直接按元素点击,避免坐标偏移,并且文本表示远比截图省 token。但树的质量取决于应用的无障碍实现程度,在 Canvas、远程桌面、未激活无障碍的 Electron/Chromium,以及无名称自绘控件等场景下会失效。截图则能应对任何绘制像素的内容,不过从截图点击常因 Retina 物理像素与逻辑点、系统显示缩放等原因发生静默偏移,需要统一的坐标转换函数。文章推荐执行顺序:树优先、OCR 文本其次、截图最后、动作后验证。

计算机使用代理(computer-use agent)在执行任何点击之前,都必须先回答一个问题:屏幕上现在有什么?

答案有两条路。把截图发给模型,让它在像素里找按钮;或者直接问操作系统——因为每一个正经的 UI 工具包都会为屏幕阅读器发布无障碍树(accessibility tree),其中列出了每个控件的角色、名称、状态和位置。

多数演示选择截图,因为它到处都能用。但实际落地时,你需要两者配合,并且顺序很关键。

计算机使用代理的两种视觉输入方式

无障碍树到底给了你什么

截图是一堆像素网格,而无障碍树是一组明确的事实:

button  "Export"        enabled   @ (812, 64)
textbox "File name"     focused   value "report-q3"
menuitem "CSV"          @ (830, 140)

这对代理意味着三件事:

它点击的是元素,不是猜出来的坐标。 代理说“按下 Export 按钮”,操作系统直接返回它的精确位置。不会在拥挤的工具栏上出现偏差 20 像素的未命中。

它知道状态。 禁用、勾选、聚焦、展开、字段当前值。从截图中,模型只能根据稍微浅一点的灰色去推断一个按钮被禁用了。

发送成本低。 一个包含 40 个控件的屏幕,用文本表示只有几十行短句。而在 Claude 上,截图大约消耗 width x height / 750 个 token(据 Anthropic 视觉文档),一张 1092x1092 的图像每一步就要约 1600 token。文本还可以被模型精确搜索和引用。

无障碍树(accessibility tree) 是操作系统为辅助技术(屏幕阅读器、语音控制、自动化工具)维护的一份结构化 UI 描述。它由各平台的原生 API 暴露:Windows 上是 UI Automation(UIA),macOS 上是 AXUIElement,Linux 上是 AT-SPI,Android 上是 AccessibilityNodeInfo。应用开发者通过系统控件库或显式标注(如 Web 中的 ARIA 属性、iOS 的 accessibilityLabel)向这棵树填充每个元素的信息。它的关键价值在于:描述的是控件的语义而非像素——一个按钮即便换了主题、改了圆角、加了阴影,树里的角色名、标题、启用状态和逻辑位置依然稳定。代理因此可以像人一样“按名字找控件”,而不必依赖视觉特征。这也意味着树反映的是开发者的无障碍投入程度,投入越多,代理可用的信息就越丰富。

树会为空,也会撒谎

无障碍树的质量完全取决于应用自身的无障碍实现程度。常见的缺口包括:

  • Canvas 应用。 设计工具的画布、游戏、地图、图表控件。整个窗口就是一个大元素,里面什么都没有。
  • 远程屏幕。 VNC、RDP 或 Citrix 窗口本质上只是一段视频,树在窗口边框处就停止了。
  • Electron 和 Chromium,在被请求之前。 Chromium 只有在检测到辅助技术时才会构建完整的 Web 无障碍树。在 macOS 上,Electron 应用要等客户端设置 AXManualAccessibility 后才暴露树;Chrome 本身需要 --force-renderer-accessibility。在此之前,你看到的是几乎没有子节点的外壳。
  • 没有名称的自绘控件。 你会连续得到 12 个 button ""。
  • 动画和滚动期间的位置过期。 读取树后要快速动作,或者重新读取。

这就是截图始终留在工具箱里的原因:它能应对任何绘制像素的东西。

这里提到的 Chromium 无障碍树延迟构建 是一个容易被忽视的机制。出于性能考虑,Chromium 默认不构建完整的 Web 无障碍树,只有检测到辅助技术通过平台 API 发出查询信号时才激活,macOS 上通过 AXManualAccessibility 属性声明,Chromium 命令行需要 --force-renderer-accessibility。Electron 应用继承了这一行为,所以自动化工具首次连接时往往拿到的是一个几乎为空的壳。绕过的方法是:在启动浏览器或 Electron 应用前主动开启无障碍模式,或让代理在初始阶段显式请求一次无障碍树,触发惰性构建。否则你可能会得到“有时能读到、有时读不到”的不稳定结果,误判为应用本身不支持无障碍。

截图的陷阱:坐标与缩放

如果你确实从截图出发去点击,最常见的静默失败不是模型视力问题,而是缩放。

在 Retina Mac 上,截图使用物理像素,而点击使用逻辑点(一半)。在 Windows 上,125% 或 150% 的显示缩放同理。许多代理在把截图发给模型之前还会先降采样,于是模型的坐标处于第三种坐标空间。

症状是:点击持续性地落在目标的左上方或右下方,偏移比例固定。修复方法是:保持一个从“模型看到的图像”到“屏幕点”的转换函数,并让每一次点击都经过它。用已知窗口的四个角来测试。

亲自查看无障碍树

在决定你的代理对某个应用需要哪种方案之前,花两分钟用平台检查器看看:

  • macOS: Accessibility Inspector(Xcode,Open Developer Tool)。悬停任意元素即可看到角色、标题和框架。
  • Windows: Accessibility Insights for Windows,或 Windows SDK 中的 Inspect.exe。两者都读取 UI Automation。
  • Linux: Accerciser,读取 AT-SPI。大部分假设基于 X11;Wayland 有意限制了其他程序可以观察和注入的内容。
  • Android: adb shell uiautomator dump /sdcard/view.xml && adb pull /sdcard/view.xml。你会得到带有文本、资源 ID 和边界的视图层级。

如果检查器显示出有名称的按钮和字段,那么树优先的代理在这里就会又快又准。如果它只显示一个大面板,那就准备截图和 OCR。

有效的执行顺序

  1. 树优先。 列出带有名称、角色和位置的交互元素。让模型按 ID 选择。
  2. 文本其次。 当树没有标签时,对窗口做 OCR,匹配可见文本。
  3. 截图最后。 用于画布、没有名称的图标,或者“这是正确页面吗?”这类检查。
  4. 动作后验证。 每个重要步骤后重新读取树或文本,不要假设点击成功了。

把截图当作后备而非默认,可以让大多数步骤更便宜、更精确,同时仍然覆盖那些完全自绘的应用。

为什么这件事值得关注

Auten 是一个 MCP 服务器,为 Claude Code、Codex、Cursor 或任何 MCP 客户端提供操作真实屏幕的能力:你的电脑(Mac、Windows、Linux)以及通过 USB 连接的 Android 手机(beta)。它的工作顺序正是上述逻辑:代理先从无障碍树获得真实按钮和字段的编号列表,OCR 和截图则在应用没有无障碍信息时顶上。

你自动化过哪些无障碍树最糟糕的应用?欢迎在评论区分享。

背景补充

OCR 在这里的定位是“文本后备层”,不是通用视觉识别。它的工作方式是:对当前窗口截图进行文字检测与识别,得到每个文本块的内容和边界框,然后把文本框中心作为可点击坐标返回给模型。相比完整截图发给模型,OCR 输出的成本更低、坐标更明确——模型只需要从“文件 (F)”“另存为”这类文本标签中选择目标。它弥补的是无障碍树里无标签控件的空白,例如自绘工具栏上的“保存”图标虽无名称,但旁边的文字或图标下方的小字仍可被 OCR 捕获。局限在于:纯图标按钮、非拉丁字形、低对比度或艺术字体场景识别率会下降,因此 OCR 通常与截图并存,用于处理“文本能解释的画面”。

#computer-use agent#无障碍树#accessibility tree#截图点击#UI自动化#MCP服务器#坐标缩放#OCR
分享:

相关推荐