开源agent-desktop告别AI截图:一棵树省掉96%Token消耗


agent-desktop 是一个专为 AI 代理设计的原生桌面自动化 CLI 工具,使用 Rust 编写。它通过操作系统无障碍树(accessibility tree)为 AI 代理提供对任意应用程序的结构化访问——不需要截图、像素匹配或浏览器。

核心理念:观察 → 决策 → 执行(OBSERVE. DECIDE. ACT.)

核心特性

高性能原生实现

  • 纯 Rust 编写的单个二进制文件,无运行时依赖
  • 提供 C-ABI cdylib 动态库(libagent_desktop_ffi),可从 Python、Swift、Go、Ruby、Node、C 等语言加载调用,避免每次调用都启动新进程
基于无障碍树的精确控制
  • 58 个命令名称,54 个操作命令:涵盖观察、交互、键盘、鼠标、通知、剪贴板、窗口管理、会话生命周期等
  • 适用于 Finder、Safari、系统设置、Xcode、Slack 等任何支持无障碍树的应用程序
极低的 Token 消耗
  • 渐进式骨架遍历(Progressive skeleton traversal):对密集型应用可实现 78–96% 的 Token 缩减
  • 浅层概览 + 定向下钻,AI 代理只需获取必要的上下文
智能引用系统
  • 快照 ID + 限定元素引用:@s8f3k2p9:e1、@s8f3k2p9:e2
  • 引用在操作时重新识别目标,支持移动、变化后的元素重新定位
  • 多个匹配目标时返回 AMBIGUOUS_TARGET 而非随意选择
结构化 JSON 输出
  • 机器可读的响应格式,包含错误码和恢复提示
Chromium 应用支持(CDP)
  • 通过 launch --cdp 为 Chromium 应用(Slack、VS Code、Discord、Obsidian、Notion)打开 Chrome DevTools Protocol 端点
  • 可与 Playwright、Puppeteer、agent-browser 等任何支持 CDP 的框架集成


人人都说AI操控电脑就该用截图,但这件事错得离谱!

让AI看懂电脑屏幕,根本不需要“看”!

AI操控电脑的底层逻辑,正在被一个15MB的Rust程序彻底推翻。当全世界都在教大模型“看图识按钮”的时候,有人发现操作系统早就把答案写在了明处——只是没人去看。


截图操控电脑,这个思路一开始就歪了

过去两年,所有让AI替你操作电脑的工具,走的是同一条路:截图,识别,点坐标,再截图。

这套逻辑听起来很直觉——人怎么用电脑,AI就怎么学。人看屏幕,AI也看屏幕;人点按钮,AI也算坐标。完美模仿,对吧。

但问题出在一个没人细想的地方:人不是靠“看图”来操作电脑的。

人看到的是一个按钮,知道它叫“发送”,知道点了它会发消息。AI看到的是一堆像素,它要干的是从这堆像素里猜出哪个区域对应“发送”这个语义。中间隔着一层巨大的鸿沟。

更麻烦的是,这套截图-识别-点坐标的循环,每一步都在累积误差。屏幕分辨率变了怎么办。窗口拖动了怎么办。深色模式和浅色模式切了一下怎么办。按钮被键盘焦点高亮了一下怎么办。

每多一个变量,AI就多一分猜错的可能。而每一次猜错,都要重新截一张图、重新识别一遍、重新算一次坐标。

这套玩法能跑起来,全靠大模型硬扛。但硬扛是有代价的——慢,贵,脆。

操作系统早就有答案,只是没人当回事

有个东西叫“无障碍树”(accessibility tree)。

屏幕阅读器靠它给视障用户读屏。系统设置里的“辅助功能”靠它让键盘操控界面。这个树里装着当前屏幕上每一个UI元素的角色(按钮、输入框、复选框)、名称(“发送”“取消”“保存”)、状态(可用、禁用、选中)、层级关系。

换句话说,操作系统一直都知道界面上有什么、在哪里、能干嘛。 它只是没主动告诉你。

而所有截图-识别-点坐标的方案,都在干同一件蠢事:放着现成的语义信息不用,非要让大模型从像素里反向推断这些信息。

这就像面前摆着一本已经翻译好的书,非要去破译原版天书。

agent-desktop做的事情极其简单:直接读这棵无障碍树,把里面的信息用JSON格式吐出来。


agent-desktop snapshot --app Finder -i

就这一行命令,Finder里所有按钮、菜单、输入框的完整结构,带着编号,整整齐齐摆在面前。AI不需要猜,不需要识别,不需要算坐标。读就行了。

一棵树能省掉96%的Token,这事谁敢信

但无障碍树也有个问题——它有时候太tm大了。

Slack的完整无障碍树,轻松超过5万个Token。一个快应用(Electron应用)的树又深又吵,全塞给大模型,光是“看清界面”这一步就能烧掉小半条上下文。

agent-desktop的解法叫“渐进式骨架遍历”(progressive skeleton traversal)。

第一轮只返回浅层结构——深度3层,容器节点只告诉你“这里头有N个子元素”,不展开。就像一个地图,先只看主干道,不标每条小巷子。

AI看完浅层结构,说“我对第三层那个叫‘消息列表’的容器感兴趣”。于是agent-desktop只展开那一块,其他区域保持折叠。

一轮浅层概览加几轮定向下钻,Token消耗比全量dump少了78%到96%。

在Slack、VS Code、Notion这些Electron应用上,这个数字稳定得可怕。

这意味着什么。意味着AI操控桌面应用的成本,从“烧钱”变成了“几乎不花钱”。意味着可以把桌面自动化塞进任何一个大模型的上下文里,而不用心疼那点Token。

引用系统:让AI不再“手抖”

截图方案还有一个致命伤:坐标是死的。

AI算出了“发送”按钮在(450, 720)。但窗口被拖动了100像素。或者列表滚动了一下,按钮位置变了。或者弹出了一个对话框,整个界面布局都变了。

坐标还是那个坐标,但按钮已经不是那个按钮了。

agent-desktop的解法是给每个元素发一个“身份证号”。

一次snapshot命令会生成一个快照ID(比如s8f3k2p9),树里每个可交互元素会分配一个引用(比如@e1@e12)。AI想点“发送”按钮,就说click @e12 --snapshot s8f3k2p9

引用绑定的是元素在无障碍树里的身份,不是坐标。窗口拖动了。列表滚动了。界面重绘了。只要这个元素还在树上,agent-desktop就能重新找到它。

如果元素真的消失了,会返回STALE_REF错误码,附带恢复建议。AI收到后重新snapshot一次,拿到新引用,继续操作。

稳定的身份,胜过任何精确的坐标。

一个15MB的二进制,干掉了整个截图流水线

agent-desktop是一个Rust写的命令行工具,单个二进制文件,大约15MB,没有任何运行时依赖。

安装只需要一行:

bash
npm install -g agent-desktop

或者不安装直接用npx:

bash
npx agent-desktop snapshot --app Finder -i

58个命令名称,54个可操作命令——观察、交互、键盘、鼠标、通知、剪贴板、窗口管理,全齐了。

更狠的是它还提供了一个C-ABI动态库(libagent_desktop_ffi)。这意味着Python、Swift、Go、Ruby、Node、C都可以直接加载这个库,在进程内调用,而不是每次操作都启动一个新进程。

python
import ctypes
lib = ctypes.CDLL("./lib/libagent_desktop_ffi.dylib")
lib.ad_init(3)
adapter = lib.ad_adapter_create()
# snapshot -> 解析引用 -> execute
lib.ad_adapter_destroy(adapter)

进程内调用 vs 每次fork新进程。前者快一个数量级。对于需要频繁操作的AI代理来说,这不是优化,是必需品。

Chromium应用:一个例外,但被完美接住了

Electron应用(Slack、VS Code、Discord、Obsidian、Notion)的无障碍树有一个特点:又深又吵。

agent-desktop对这类应用开了一扇后门——launch --cdp

这个命令会为Chromium应用打开一个Chrome DevTools Protocol端点。之后任何支持CDP的框架——Playwright、Puppeteer、agent-browser——都可以驱动Web内容,而原生菜单、对话框、窗口仍然走无障碍树路径。

Web部分交给CDP,原生部分交给无障碍树。 各取所长,不打架。


Finder会撒谎

agent-desktop真正让人意外的,不是发现了CDP这条捷径。而是在处理真正难的问题时,用了几个反常识的设计决策。

第一个反常识决策:不相信应用的返回码。

大多数自动化工具的逻辑是:调用一个API,检查返回值,成功就继续,失败就重试。但agent-desktop的作者发现了一个令人不安的事实——应用会撒谎。

Finder对一个已经成功的操作返回错误码,对一个什么都没做的操作返回成功码。如果自动化工具迷信返回码,就会出现两种情况:要么报告失败但实际上成功了,要么报告成功但实际上什么都没发生。这对AI来说是灾难性的——AI会根据错误的反馈做出错误的决策,一路错下去。

agent-desktop的做法是:不看返回码,看界面状态变化。一次点击不是一次API调用,而是一条有序的链——AXPress、AXOpen、激活内部单元格、写入选择、AXConfirm。每一步只在元素支持时才执行,成功与否的判断标准是应用的状态是否发生了变化。哪个机制先产生可观察到的效果,哪个就是赢家。

这就像跟一个人说话,不看嘴型,只看有没有按照说的去做。嘴型可以骗人,动作骗不了。

第二个反常识决策:不返回完整树。

无障碍树的结构化信息虽然比像素强,但有一个问题——太大了。Slack的一棵完整无障碍树可以轻松超过五万个token。把五万个token塞进上下文窗口,让AI在里面找一个按钮——这是在逼AI玩大家来找茬。

agent-desktop的方案是渐进式骨架遍历。第一次快照只返回深度为3的浅层树,深层容器被截断并标注孩子数量。有名字的容器获得引用ID,AI可以针对性地请求某个子树的详细信息。操作完成后,AI只需要重新查询被操作的那个区域,而不是重新扫描整个应用。

78%到96%的token节省。这不是优化,这是把不可能变成可能。

第三个反常识决策:引用不是指针,是身份证据。

大多数自动化工具用指针或坐标来标记元素。指针在UI变化后会失效,坐标在窗口移动后会偏移。agent-desktop的引用是一组身份证据:角色、路径、稳定文本、边界哈希。每次操作前,系统拿这组证据去和当前的实时界面做匹配。

如果界面变了,返回STALE_REF。如果两个元素都匹配,返回AMBIGUOUS_TARGET。它从不猜测。

从不猜测这四个字,是整件事的核心。


但有一个问题,agent-desktop暂时回答不了

以上所有能力,目前只在macOS上完整可用。

Windows和Linux的无障碍树支持、点击输入、鼠标操作、截图、剪贴板、窗口管理、通知——全部标记为“Planned”(计划中)。

一个跨平台CLI,目前还不是真跨平台。

但这恰恰是最有意思的地方——不是技术做不到,而是没人做。 Windows有UIA(UI Automation),Linux有AT-SPI,都是现成的无障碍树接口。agent-desktop的架构从一开始就做了平台抽象(PlatformAdapter trait),Windows和Linux的适配层是现成的空壳,只等人去填。

问题变成了:谁会去填。什么时候填。填完之后,Windows和Linux的桌面自动化格局会变成什么样。

没人知道答案。但可以确定的是,一旦这三个平台全部打通,截图操控电脑这条路径,就彻底沦为一种行为艺术。


盲人摸象和X光片

把这一切串起来看,agent-desktop做的事情很简单:把桌面自动化从一个视觉问题还原成了一个结构化数据问题。

截图方案的本质是让AI扮演盲人摸象的角色——摸到鼻子说像蛇,摸到腿说像柱子。无障碍树方案的本质是直接给AI一张大象的X光片——骨骼、肌肉、血管,清清楚楚。

整个行业花了三年时间,用最先进的大模型去做最原始的图像识别,而操作系统早就把答案摆在桌面上没人看。

为什么会这样?因为让AI看屏幕这个叙事太有诱惑力了。它符合人类对智能的直觉——能看懂屏幕的才是真智能。但智能不等于模仿人类的感知方式。一个能读懂无障碍树的AI,和一个需要靠截图猜坐标的AI,哪个更智能?答案显而易见!