CLI agent-desktop 让 AI 代理真正操作桌面应用的无障碍树命令行工具
大多数所谓的“AI 操作电脑”方案,本质上是在玩一场猜谜游戏:截一张图,让视觉模型猜按钮在哪,然后模拟一次点击,再截一张图看看猜没猜对。这种方案不仅慢,还极不靠谱——分辨率一变、主题一换、弹窗一挡,模型就彻底懵了。Vercel Labs 开源的 agent-desktop 走了一条完全不同的路:它直接读取操作系统给屏幕阅读器用的那棵无障碍树,把每个可交互元素变成一个有确定引用的节点。代理看到的不再是像素,而是结构化的 JSON。这意味着点击一个按钮不再靠运气,而是靠精确的语义寻址。
项目基本信息
| 信息项 | 详情 |
|---|---|
| 项目名称 | agent-desktop |
| GitHub地址 | https://github.com/vercel-labs/agent-desktop |
| 项目描述 | Computer use CLI for AI agents |
| 作者 | vercel-labs |
| 开源协议 | Apache License 2.0 |
| 开源状态 | 公开状态 |
| Languages | Rust / TypeScript |
| 支持平台 | Windows / macOS / Linux |
| 最后更新 | 2026-11-10 |
一、项目介绍
agent-desktop 是一个用 Rust 编写的命令行工具,它本身不是一个代理,也不内置任何语言模型。它的职责非常纯粹:把一个桌面应用的无障碍树拍平成一份带引用的 JSON 快照,让外部的代理(比如 Claude Code、Codex,或者你自己写的脚本)能够精准地知道界面上有什么、每个东西叫什么、能对它做什么动作。
整个工作循环只有三步:观察、操作、再观察。代理先跑 snapshot 拿到当前界面的结构化描述,每个可操作元素都带着一个类似 @s8f3k2p9:e8 的引用。代理拿着这个引用去执行 click 或 set-value,然后必须再拍一次快照来验证操作是否生效。这种设计把“验证”的责任明确交给了调用方,而不是让工具自己去猜结果。
之所以敢这么做,是因为无障碍树提供的是一种确定性的观察。一个按钮的 role、name 和可用动作是操作系统层面的元数据,不会因为窗口被遮挡了半个像素就改变。代理拿到的引用在快照有效期内是稳定的,UI 一变,旧引用就会返回 STALE_REF,强迫代理重新观察,而不是盲目地往原来的坐标上再点一次。
二、核心优势
不靠像素,靠语义。 这是 agent-desktop 和绝大多数 computer-use 方案最本质的区别。截图方案需要视觉模型去“理解”界面,而 agent-desktop 直接把界面的结构读出来。一个 treeitem 叫 “Documents”,它的引用是 @s8f3k2p9:e8,可用的动作是 Click——这些信息不需要任何模型去推断。附带的好处是,喂给模型的观察数据从一张高分辨率图片变成了一小段 JSON,token 消耗和延迟都低得多。
默认无头,不抢你的鼠标。 代理执行引用操作时,走的是无障碍 API 的语义调用路径,不会移动你的鼠标指针,也不会抢走焦点。你在这边写代码,代理在后台点它的按钮,互不干扰。只有当你显式加上 --headed 标志时,才会动用真实的物理输入。这个设计选择直接决定了它能不能用在日常工作的机器上,而不是一台专门跑自动化的虚拟机。
失败就报错,不瞎猜。 每个操作的结果都包在一个 JSON envelope 里,成功带 ok: true 和 data,失败带 error。错误里有 code、disposition,还有 suggestion。关键在于 disposition.retry:只有当它的值是 safe 时,重试才是安全的。如果操作可能已经落地了(比如点击已经发出但结果不确定),重试可能导致双击,工具会明确拒绝让你盲目重试。这种“宁可报错也不乱动”的保守策略,在自动化场景里比激进重试珍贵得多。
自带给代理看的说明书。 二进制文件里内置了版本匹配的技能文档。代理第一次使用前跑一下 agent-desktop skills get desktop 和 agent-desktop skills get platform,就能拿到针对当前操作系统和当前版本的完整使用规则。你不需要在提示词里长篇大论地教它怎么用,它自己会读。
三、适用场景
后台批处理桌面任务。 比如定时从某个只有桌面客户端的财务软件里导出报表,或者批量在一个内部管理系统里录入数据。这些应用没有 API,但有无障碍树。代理在后台默默完成,你的鼠标和键盘全程不受影响。
让编码代理帮你操作开发工具。 你正在用 Claude Code 写代码,需要它在模拟器里点一下“允许”按钮,或者在 Xcode 里触发一次构建。agent-desktop 让编码代理有了伸手够到桌面应用的能力,而不只是待在终端里。
无障碍树可见的常规应用自动化。 Electron 应用、原生 macOS 应用、大多数 Win32/WinForms 应用都暴露了可用的无障碍信息。代理可以读取表单字段、勾选复选框、展开树节点、切换开关,用 set-value 直接写入文本而不是模拟键盘输入。
不适合的场景也说清楚。 如果一个应用的无障碍树是空的(某些游戏、自绘 UI 的旧软件),agent-desktop 就无能为力了。网页自动化应该用 agent-browser 而不是它。另外,它不内置模型,你需要自己有一个能跑 shell 命令的代理框架来驱动它。
四、安装教程
agent-desktop 通过 npm 分发,包含了对应平台的预编译二进制。macOS、Windows、Linux 的 x64 和 ARM64 都覆盖了。
# 全局安装
npm install -g agent-desktop
# 验证安装和版本
agent-desktop version如果你用 Bun、Yarn 或 pnpm,装的是同一个包,行为一致。
平台权限准备:
macOS 需要授予辅助功能权限。装完后跑一下引导命令,系统会弹出设置面板:
agent-desktop permissions --guideWindows 通常不需要额外授权,只要目标应用和你的终端运行在相同的完整性级别即可。Linux 则需要 AT-SPI 正常运行,并且你在一个图形会话里(at-spi2-core 已安装)。
给代理装技能(可选但推荐):
如果你用的是支持 skills.sh 的代理框架,可以直接把 agent-desktop 的技能装进去:
npx skills add https://github.com/vercel-labs/agent-desktop --skill agent-desktop或者在项目的 AGENTS.md 里加一段指引,告诉代理第一次使用前先读取内置文档。
五、使用示例
下面用一个 macOS 上的 Finder 场景走一遍完整循环。假设你想让代理在 Finder 里选中 “Documents” 文件夹。
第一步:确认环境和可用应用。
agent-desktop status
agent-desktop list-appsstatus 会告诉你辅助功能权限是否就绪,list-apps 列出当前有窗口的图形应用。
第二步:观察,拿到带引用的快照。
agent-desktop snapshot --app Finder -i-i 表示只返回可交互元素,过滤掉静态文本和装饰性容器。输出是一个 JSON envelope,其中 data 里有一棵节点树,每个可操作节点长这样:
{
"role": "treeitem",
"name": "Documents",
"ref_id": "@s8f3k2p9:e8",
"available_actions": ["Click"]
}第三步:对引用执行操作。
agent-desktop click @s8f3k2p9:e8在默认的无头模式下,这个点击通过无障碍 API 触发,你的鼠标纹丝不动。
第四步:再观察一次,验证结果。
agent-desktop snapshot --app Finder -i检查新的快照里,Finder 的当前选中项或窗口内容是否已经变成了 Documents 下的文件列表。如果 UI 已经变化,旧的 @s8f3k2p9:e8 就作废了,任何拿它做的后续操作都会返回 STALE_REF。
一个更实际的代理脚本示例(概念性):
import subprocess, json
def run(cmd):
out = subprocess.check_output(cmd, shell=True, text=True)
return json.loads(out)
# 观察
snap = run("agent-desktop snapshot --app 'Notes' -i --compact")
# 在返回的树里找到新建笔记按钮的 ref
# ... 解析逻辑 ...
ref = "@s8f3k2p9:e12"
# 操作
result = run(f"agent-desktop click {ref}")
# 检查错误
if not result["ok"]:
if result["error"]["code"] == "STALE_REF":
# UI 变了,重新观察,不要盲目重试
pass
elif result["error"]["disposition"]["retry"] == "safe":
# 可以安全重试
pass
# 验证
snap2 = run("agent-desktop snapshot --app 'Notes' -i --compact")这个循环的核心纪律是:绝不跳过“再观察”这一步。代理的每一次操作都应该以一次新的观察收尾,用观察到的状态决定下一步,而不是假设操作一定成功了。
六、常见问题
快照返回空树或者缺少我要的元素。 先确认应用是否真的暴露了无障碍信息。有些 Electron 应用需要开启 accessibility 支持,某些自绘界面则根本不提供树。另外,-i 过滤掉了非交互节点,如果你需要读取静态文本作为上下文,去掉 -i 再试。
点击报 STALE_REF。 这是正常的保护机制。快照拍完之后 UI 变了(比如异步加载完成、弹窗出现),旧引用失效。重新 snapshot,用新引用操作。不要尝试复用旧引用。
报 AMBIGUOUS_TARGET。 有多个元素匹配了同一个引用路径。常见于有多个窗口的应用。快照的错误详情里会列出候选的 window-id,用 --window-id 指定目标窗口,或者用 --root 把观察范围缩小到某个容器内。
PowerShell 里引用被吞了。 PowerShell 把裸的 @token 当成 splatting 操作符。给引用加上单引号:agent-desktop click '@s8f3k2p9:e8'。
操作成功了但界面没变。 有些应用对语义调用的响应是异步的,或者需要额外的焦点事件。先跑一次 wait 命令等目标状态出现,而不是立刻再拍快照:
agent-desktop wait --element @s8f3k2p9:e12 --predicate value --value "Done"Linux 上提示找不到 AT-SPI。 确认 at-spi2-core 已安装,并且你是在图形会话里运行的。SSH 连进去的纯终端环境没有无障碍总线,agent-desktop 无法工作。
七、总结
agent-desktop 做的事情在概念上极其简单:把操作系统的无障碍树暴露成一个 CLI 接口,让代理能读能点。但它的价值恰恰在于这种克制。它没有试图做一个“智能代理”,没有内置模型去猜界面,也没有用截图和视觉推理来掩盖底层的不确定性。它把确定性的部分(界面结构、元素引用、操作结果)用严格的方式交给调用方,把不确定的部分(下一步该做什么)留给真正擅长决策的代理。
如果你正在构建需要触碰桌面应用的代理工作流,而且目标应用的无障碍树不是空的,agent-desktop 是目前最干净的一条路。它不抢你的鼠标,不在失败时瞎重试,还把使用说明塞进了二进制里让代理自己学。对于一个 Apache 2.0 的开源项目来说,这种完成度值得放进你的工具箱。
无障碍树这个思路确实比截图靠谱太多了。之前用某视觉方案点一个按钮十次能错三次,看了这篇去试了 agent-desktop,同样的应用一次就对,而且后台跑完全不打扰我干活。
Good writeup. The retry disposition thing is underrated. Most automation tools just blindly retry and you end up double-clicking things.
林间风|安装挺顺利的,npm 一条命令搞定,macOS 权限引导也清楚。不过 Linux 那部分写得有点简略,我这边 AT-SPI 折腾了一会才通。
The Python example is a bit too conceptual but it gets the point across. The loop discipline is the key takeaway.
老张头|用了几天,最惊喜的是 set-value 直接写表单字段,不用模拟键盘一个个敲。速度快了不止一倍。
Does this work with Electron apps? I saw it mentioned but want to confirm before trying.
Electron 可以的,但有些需要开 accessibility 标志。我试的 VS Code 没问题。
“不抢鼠标”这个特性太关键了。以前用别的工具,跑自动化的时候电脑基本没法用,现在边跑边写代码毫无压力。
阿飞|文章里说它不内置模型,这点很多人会误解。它就是个工具,得你自己有代理去驱动。想开箱即用的可能要失望。
Been waiting for something like this from Vercel. agent-browser was good for web, nice to have the desktop equivalent.
The stale ref error handling is smart but takes getting used to. Your agent logic has to actually handle it, otherwise you’ll just crash.
陈默|试了 Finder 的示例,click 之后确实没动我的光标,打开新窗口了才反应过来。有点神奇。
Is there a way to batch multiple actions without round-tripping a snapshot each time? The batch command is mentioned briefly but no example.
Built in Rust and it shows. Snapshot on a dense app comes back fast.
数据搬运工|请问 Windows 上对高 DPI 缩放有要求吗?我这边界面元素有时候找不准。
The privacy warning at the end of the repo is worth noting. Snapshots can contain sensitive stuff. Make sure your logs are clean.
小白学自动化|文章对新手挺友好的,循环那个概念讲得很清楚。之前一直以为自动化就是录屏回放,原来还有这种结构化的方式。
Used it to automate a legacy WinForms app at work. set-value on text boxes works perfectly, no physical input needed at all.
清风|macOS 的辅助功能权限有时候会重置,升级系统后记得重新授权。别问我怎么知道的。
60 commands sounds like a lot but the ones you actually use daily are maybe 10. The rest are there for edge cases.
午夜编码|PowerShell 那个引号的坑我踩过,文档里要是没写我估计得折腾半天。
Does the --headed flag actually move the real cursor? I need drag for some canvas app.
Yes, --headed uses physical input. For canvas apps without accessibility nodes, it’s the only option, but it does steal focus.
老猫|Agent 每次操作完必须重新 snapshot 这点挺烦的,但确实安全。习惯了就好,总比点错强。
The skeleton + root drill approach for dense apps is smart. Slack is a mess without it.
星尘|用这个配合 Claude Code 做了个小工具,让它帮我操作内部的管理后台导数据。虽然不是网页,但无障碍树读起来比抓包稳定。
Apache 2.0 协议很友好,可以放心在公司项目里用。有些自动化工具 license 卡得很死。
The installation section should mention that on Windows the binary is unsigned. SmartScreen will block it if you download directly.
小鹿|文章写得很实在,优缺点都说了。不像有些教程吹得天花乱坠,实际用起来一堆坑。
Can this read notifications? I have a workflow that depends on parsing desktop alerts.
Yes, macOS notifications are supported with list-notifications and dismiss commands. Windows I’m not sure.
大熊|Rust 写的性能确实好,snapshot 一个复杂的 Electron 窗口大概一两百毫秒,完全可以接受。
The “tool, not an agent” framing is important. Too many people expect it to think for them.
夜航船|看完最大的收获是理解了“观察-操作-观察”这个循环的纪律性。以前写自动化脚本总是假设操作成功了,现在知道验证才是核心。
Good article. Would love to see a follow-up on how to integrate this with a specific agent framework, like an actual Claude Code skill setup.