AI图鉴

AI图鉴首页 › AI 资讯 › 桌面 Agent 缺的不是视觉:Vercel 开源 agent-desktop,

桌面 Agent 缺的不是视觉:Vercel 开源 agent-desktop,用无障碍树 ref 替代截图坐标

来源:掘金 · 发布时间:11-03 · 由 AI图鉴每小时自动聚合

11 月 3 日的 GitHub Trending 榜首换成了一个 Rust 项目:vercel-labs/agent-desktop。仓库 11 月 3 日中午还在推代码,累计 11874 星,np

11 月 3 日的 GitHub Trending 榜首换成了一个 Rust 项目:vercel-labs/agent-desktop。仓库 11 月 3 日中午还在推代码,累计 11874 星,npm 上的 agent-desktop 包上周下载量 20713。它的自我介绍只有一句话:让 AI agent 通过操作系统无障碍树去读写桌面应用。

截图加视觉模型早就能够点按钮,所以这句话本身不算新鲜。真正值得拆开看的是它把「看」和「点」拆成了两个独立且可验证的步骤,并且把每一步失败的原因写进了 JSON 契约。这决定了它能不能被无人值守的流水线直接调用,而不是只能在演示视频里跑一遍就结束。

桌面上没有 DOM,只有一棵无障碍树

网页 agent 的日子好过,因为有 DOM。一个按钮有 id、有 class、有可读文本,选择器写错会立刻报找不到元素,错误信息直接指到出错的那一行。桌面应用没有这套东西,窗口里的控件活在进程内存里,外部进程拿不到句柄之外的东西。

于是只剩两条路。一条是截图交给视觉模型判断坐标,另一条是读操作系统自己暴露的无障碍树。前者通用,代价是脆;后者覆盖面窄,但每个元素自带语义。agent-desktop 走的是后者,分别对接 macOS 的 Accessibility API、Windows 的 UI Automation 和 Linux 的 AT-SPI。

无障碍树的核心好处是定位依据不是像素。窗口换了主题、DPI 从 100% 调到 150%、系统切成深色模式,树的层级和节点名字不会跟着变,一个 ref 仍然指向同一个按钮。截图方案在这些变化下需要重新校准,校准一旦失效,整条流水线就停在等待坐标超时上,而且日志里只剩一句超时,看不出究竟是界面变了还是模型看错了。

这也是为什么它的文档反复强调 ref 属于某一次快照。快照是一次真实观测的结果,不是一份可以长期缓存的元素地图。UI 变了,旧的 ref 立即失效,工具会明确告诉你失效,而不是猜一个位置点下去。这个差别在长时间运行的批处理里非常关键,因为缓存坐标的做法会在界面更新后的某个清晨静默失败,而缓存 ref 的做法会当场报错。

一条 snapshot 命令换回一张 ref 清单

核心命令是 snapshot,最常用的形态是只列可交互元素。加上 -i 参数后,返回的 JSON 里每个可操作节点都带 role、name、ref_id 和 available_actions 四项。ref_id 形如 @s8f3k2p9:e8,前面的短串是这次快照的编号,后面是节点序号,两者绑定在一起,单写后半段会被拒绝。

信封结构固定为 version、ok、command 三个顶层字段,成功时数据落在 data 里,失败时落在 error 里。当前契约版本是 2.6。一次典型的返回里,data 会给出 app 名、窗口 id、窗口标题、snapshot_id、ref_count 和一个 complete 布尔值。

complete 这个字段在 2.2 版本才出现,意义不小。旧版遇到超大窗口会直接抛 TIMEOUT 且不给任何树,调用方只能靠加大预算硬试。新版改成返回 ok 为 true、complete 为 false、truncated 为 true,附上已经观测到的树和 nodes_observed 计数,被截断的父节点各自带 subtree_truncated 标记,读者可以沿着根一路走到每一处边界。调用方只要读 complete 就能判断手里这棵树是不是完整的,不用再从超时错误里反推。

窗口多的场景不会替你选。同一应用开了多个窗口时,快照返回 AMBIGUOUS_TARGET,候选窗口 id 放在 error.details 里,需要显式传 window-id 指定一个。对密集应用,官方建议先用 skeleton 加 compact 拿到一张骨架图,再用 root 参数从一个节点往下钻。root 下钻是替换已有快照里的 ref,不是新建快照,所以观测不完整时它仍然返回 TIMEOUT,不会给你半棵树。这条区别很容易被忽略,写循环的时候要按不同语义处理:第一次快照不完整可以加大预算重来,下钻不完整只能退回上一层重新选节点。另一种常见浪费是把骨架图当成最终结果直接拿去点,骨架里的节点往往没有 available_actions,点了会被判动作不可用,白白多一轮快照。

动作认 ref,不认坐标

拿到 ref 之后,动作命令的写法很直白:click 接一个 ref,type 接文本,set-value 直接改值。ref 必须带快照前缀,单独写 @e8 会被判 INVALID_ARGS 拒绝,这条规则防的是跨快照误用。

真正需要提前想清楚的是三种失败态。STALE_REF 表示这个 ref 在当前的 RefMap 里找不到,说明页面或窗口已经变了,恢复策略是重新快照再拿新 ref。AMBIGUOUS_TARGET 表示目标不唯一,多窗口场景常见。第三个不是错误而是结果状态:动作交付成功但没有验证,返回值里 delivery 为 delivered_unverified,同时 verification_scope 为 unavailable。文档说得很清楚,这种情况只说明动作发出去了,不证明应用层真的落库,所以不能拿它当成功依据往下跑。

默认是 headless 行为。ref 动作全部走无障碍 API,不移动鼠标指针,也不抢窗口焦点。要物理输入必须显式加 --headed。对后台跑批的任务,这一点比速度重要得多,因为抢焦点会打断用户正在做的事,而无人值守的第一原则就是不打扰。

表格最后一列那句话是这套方案的天花板。自绘画布、游戏画面、远程桌面里的内容,无障碍树里没有对应节点,ref 无从生成,这条路直接走不通。选型时先问一句:要操作的那个应用,它的控件有没有语义。

信封里的 disposition 决定该不该重试

这套工具最省事的设计是把「能不能重试」做成了机器可读字段,而不是留给人去猜。每个错误都带 disposition 对象,里面两个值:delivery 和 retry。delivery 有五种取值,unknown、not_delivered、delivery_uncertain、delivered_unverified、delivered_verified。retry 有三种,unknown、safe、unsafe。

规则是推导出来的,不是拍出来的。delivery 为 not_delivered 时 retry 一定是 safe,其余非 unknown 的取值一律 unsafe。unknown 意味着这次调用什么也没证明出来,比如一个可能已经落地的操作超时了,这种情况按不安全处理,因为重试可能造成重复提交。

所以正确的调用方代码里只该有一行判断:只有 error.disposition.retry 等于 safe 时才自动重试,其余分支读 error.code 和 message 走人工或换策略。recovery 对象只在 retry 为 safe 时出现,里面给出策略名和是否需要新快照,TIMEOUT 与 APP_UNRESPONSIVE 还会带 retry_after_ms,可以直接拿来做退避间隔。

2.4 版本又补了一层。set-value、type、clear、check、uncheck、toggle、expand、collapse 这些有状态的动作用一次交付后就校验后置条件,发现矛盾直接报错,details.kind 标为 post_action_verification。这一条针对的是最阴的一类事故:动作返回成功,界面看着变了,实际状态没改,于是后续每一步都基于错的前提往下跑,最后失败的位置离真正的根因差了十几个步骤。校验失败时工具不会替你重试,它把矛盾摆出来,因为此时再点一次很可能把错误状态放大。读这个错误时先看 details.kind,再决定是重新快照核对界面真实状态,还是回滚到上一步的业务动作。把这套约定接进流水线之后,重试逻辑就从「失败就重试三次」变成了「按错误码决定重试或停机」,后者的好处是可以事后统计哪类错误最多,而不是只留下一个笼统的失败率。

三个平台的权限账不一样

Windows 最省事,不需要任何授权,前提是目标应用和终端的完整性级别一致。管理员权限运行的应用可以被读取,但只有同样提权的 agent 才能往里输入。这个差异在混合权限环境里会表现为「看得到点不动」,不是权限没给对,而是级别不匹配,排查时要先看两个进程的完整性级别。

macOS 的门槛是辅助功能权限,截图另需屏幕录制,操作通知中心还要给 System Events 自动化权限。permissions 子命令可以只读地报告当前状态,加 request 才会发起请求,加 guide 会直接打开系统设置的对应页面。装完第一件事应该是跑一次 permissions,而不是等动作失败再逐个排查。

Linux 的前提是跑在用户图形会话里,并且装了 at-spi2-core。纯 TTY 或 SSH 会话看不到任何应用,因为没有那个会话的总线、显示和运行时环境。官方给的主目标很明确:Ubuntu 24.04 加 GNOME 46,glibc 地板 2.35。Ubuntu 22.04 的 ATK 桥接在 Action.GetActions 上会直接崩,属于已知不兼容,不是配置能修好的问题。

还有一个容易踩的坑是二进制签名。发布包没有签名,从浏览器下载会触发 SmartScreen 提示,杀毒软件可能直接隔离,全新装的 Windows 11 上 Smart App Control 会拦下来。走 npm 安装不触发这一套,所以 Windows 上优先 npm 而不是手动下载压缩包。如果确实要用压缩包,可以用 gh attestation verify 核对来源,因为发布页的校验和只能证明文件没损坏,证明不了它来自这个仓库。

接进自己的循环:从 CLI 到 FFI

安装和第一轮验证只需要几条命令。注意 npm 12 默认屏蔽安装脚本,被屏蔽时包会退回 Node 启动器,每次调用多出约 125 毫秒,用 install-scripts approve 或 --allow-scripts 放行后才是原生二进制的约 21 毫秒。这个差距在批量任务里会累积成分钟级差别。

npm install -g agent-desktop

agent-desktop version

agent-desktop permissions

agent-desktop snapshot --app Finder -i

agent-desktop click @s8f3k2p9:e8

agent-desktop snapshot --app Finder -i

PowerShell 用户要把每个 ref 用引号包住,因为裸写的 @ 开头标记会被当成变量展开运算符,命令会被解析成完全不同的东西。这是官方文档单独拎出来讲的一条,Windows 上第一次跑就撞的概率很高,而且报错信息完全不指向真正的原因。

二进制自带文档是这套工具另一个实用设计:skills get desktop 讲循环、ref、验证和重试规则,skills get platform 讲当前操作系统上的能力差异,skills get ffi 讲语言绑定的入口、内存归属和线程规则。文档跟着二进制版本走,不会出现照着网页上的旧示例写出跑不通的代码,这一点比很多只在 README 里写用法的项目要靠谱。

要在进程内调用而不是每条命令起一次子进程,可以走发布包里带的 libagent_desktop_ffi。以 Python 为例,用 ctypes 加载库,调用 ad_init 并核对返回值,再创建 adapter 句柄:

import ctypes

lib = ctypes.CDLL("./lib/libagent_desktop_ffi.dylib")

lib.ad_adapter_create.restype = ctypes.c_void_p

lib.ad_adapter_destroy.argtypes = [ctypes.c_void_p]

assert lib.ad_init(7) == 0

adapter = lib.ad_adapter_create()

lib.ad_adapter_destroy(adapter)

那段 assert 校验的是 ABI 主版本号,头文件里的 AD_ABI_VERSION_MAJOR 必须和库匹配,否则后面所有调用都是未定义行为,可能不报错但结果全错。命令总数是 60 个,其中 56 个可直接用,剩下四个 key-down、key-up、mouse-down、mouse-up 在守护进程能释放被按住的键或按钮之前拒绝运行。这个限制是有意为之,防止一次崩溃把系统留在按键卡死的状态。

它替代不了什么

第一件事是隐私。快照内容、剪贴板读取、截图、命令输出和调试产物都可能带着私人桌面内容,官方文档专门提醒不要把它们放进公开日志或上传。可选的 JEV helper 会把屏幕描述发给你配置的模型服务商,那是独立组件,不是原生 CLI 的一部分,接不接要单独判断,不能因为装了 CLI 就默认同意了这件事。

第二件事是覆盖面。它只处理有语义的控件,自绘界面和游戏画面不在射程内。浏览器里做自动化,Playwright 那种按 DOM 选择器操作的方式仍然更精确,因为 DOM 比无障碍树信息量大得多,能读属性、能拦截请求、能等到具体网络事件。两套东西不是替代关系,是按场景分工。

第三件事是节奏。这套工具把一次操作拆成观察、动作、再观察三步,每次观察都是一次真实快照。对单人手工调试的脚本,这比一次性点完所有按钮要慢;对需要留下可审计轨迹的自动化任务,这三步就是它的价值所在。慢下来的部分正是可以被验证的部分,也是事后能复盘的部分。

放在 2026 年 11 月的这个时间点看,agent-desktop 的意义不在它多快,而在于它把桌面操作从「看得见就点得到」改成了「有语义才点得到,点完能证明」。前面那半句限制了适用范围,后面那半句决定了它能不能进生产流水线。

本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。

相关 AI 工具

相关免费羊毛

同源资讯

阅读原文(juejin.cn)

标题、摘要与正文均来自该信源的公开页面,版权归原作者所有;本站自动聚合与翻译,仅作出处跳转。

← 返回 AI 资讯列表AI工具图鉴首页

返回 AI图鉴首页 · 提交工具 · 商务合作 · 关于本站 · 608 款 AI 工具分类导航 · 每日 AI 热门项目与动态