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
开源状态公开状态
LanguagesRust / 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 --guide

Windows 通常不需要额外授权,只要目标应用和你的终端运行在相同的完整性级别即可。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-apps

status 会告诉你辅助功能权限是否就绪,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 的开源项目来说,这种完成度值得放进你的工具箱。