别让桌面 Agent 靠截图猜按钮:agent-desktop 用可失效的引用把原生 GUI 操作变成可验证步骤
浏览器自动化有 DOM、选择器和开发者工具;可一旦任务落到 Finder、系统设置、Slack、Xcode 或 Electron 的原生菜单上,Agent 常常只剩截图、坐标和 OCR。它看似能“看见”界面,实际上却很难稳定回答三个问题:目标元素究竟是什么、操作是否真正命中、界面变化后旧坐标还能不能继续用。
agent-desktop 是一个 Rust 编写的桌面自动化 CLI。它的核心输入不是像素,而是操作系统暴露的无障碍(accessibility)树:先把窗口结构导出为 JSON,再为可交互节点分配引用,最后让 Agent 基于引用读、点、填、等。项目采用 Apache-2.0 许可证;仓库的 workspace 目前声明版本为 0.8.1。需要先说明边界:目前真正实现无障碍树和交互能力的是 macOS;Windows 与 Linux 在项目的平台支持表中仍标为 Planned。不要把它当成跨平台桌面自动化承诺。
为什么截图加坐标会让 Agent 变得不可靠
坐标脚本隐含了大量未写出来的前提:窗口位置和缩放比例不能变,遮挡物不能出现,按钮文案不能换行,前一次点击不能改变布局。截图模型即使识别出了“保存”按钮,也未必能区分同名按钮属于哪一个窗口。更麻烦的是,失败后的恢复没有结构化线索:Agent 只能重新截图并再次猜测。
无障碍树的价值并不在于“替代视觉”,而在于提供角色、名称、值、可用状态和层级关系。agent-desktop 的 snapshot 会输出这种结构;可交互的 button、textfield、checkbox、link、menuitem、tab 等节点会获得类似 @s8f3k2p9:e3 的限定引用。前半段是快照 ID,后半段是节点 ID,因此引用明确绑定于一次观察,而不是一个永远有效的 CSS selector。
这也形成了正确的 Agent 循环:观察 → 决策 → 动作 → 再观察。如果窗口刷新、节点消失或稳定身份发生改变,项目的可靠性约定是返回 STALE_REF 或 AMBIGUOUS_TARGET,而不是静默点击一个“差不多”的目标。对带有删除、发送、授权等副作用的桌面任务而言,显式失败通常比误操作更有价值。
从权限检查开始,而不是直接让 Agent 点击
安装包可通过 npm 获取;README 同时提供从源码构建方式,并注明构建需要 Rust 1.89+、macOS 13.0+。macOS 的无障碍权限是前置条件;截图还需要屏幕录制权限,通知中心相关操作还可能需要 Automation 权限。先检查权限能把“命令没有反应”与“定位不到元素”区分开:
npm install -g agent-desktop agent-desktop permissions agent-desktop permissions --request
permissions --request 的用途是请求缺失权限,文档特别说明它在隔离 helper 中执行。自动化系统不应把授权弹窗当作普通界面继续盲点;应由运行者确认授权,再回到任务流程。纯检查不会主动弹出授权请求,这对于预检和 CI 式的环境诊断也更安全。
用快照、限定引用和复查完成一次编辑
以 Finder 中输入一个字段为例,下面的命令展示的是项目 README 中的实际命令形态。真实任务应当把应用名和元素引用替换成刚刚拿到的结果,不要把示例里的 @s8f3k2p9:e5 硬编码进生产脚本:
agent-desktop snapshot --app Finder -i --compact agent-desktop click @s8f3k2p9:e3 agent-desktop type @s8f3k2p9:e5 "quarterly report" agent-desktop press cmd+s agent-desktop snapshot --app Finder -i --compact
-i 是 --interactive-only,用于把观察结果收敛到可操作节点;--compact 省去空的结构节点。第一份快照负责取证和定位,后一次快照负责验证状态是否改变。若返回 stale 或 ambiguous 错误,不应对旧引用重试:重新执行 snapshot,根据新树定位,再决定是否继续。这一恢复策略把 GUI 自动化的“不确定性”留在决策层,而不是用循环点击把它扩大成事故。
面对 Slack、VS Code、Notion 这样节点很多的应用,完整树本身会消耗上下文。项目提供 --skeleton 生成三层概览,并以 children_count 标出被截断容器;随后可用 --root 加已有快照 ID 钻取局部区域:
agent-desktop snapshot --skeleton --app Slack -i --compact agent-desktop snapshot --root @e3 --snapshot s8f3k2p9 -i --compact agent-desktop click @e12 --snapshot s8f3k2p9 agent-desktop snapshot --root @e3 --snapshot s8f3k2p9 -i --compact
这不是单纯的 token 优化。先获得骨架、再只展开目标区域,能减少同名控件和无关内容进入 Agent 上下文,也让“依据何处找到此控件”更容易审计。README 将这种渐进式 skeleton traversal 描述为在复杂应用中降低 78–96% token 使用量;这个范围是项目自身的说明,应视为其场景化指标,而不是所有 GUI 的通用基准。
把会话记录做成可审阅的运行证据
如果任务跨多个操作,建议先创建会话。session start 返回的 ID 可以通过全局 --session 或 AGENT_DESKTOP_SESSION 显式传入;在 trace 启用的会话中,命令会写入 JSONL 片段,并共享该会话的最新快照命名空间。结束后可用 trace show 读取有界 JSON 时间线,或导出单文件 HTML:
session_id=$(agent-desktop session start --screenshots | jq -r '.data.session_id') export AGENT_DESKTOP_SESSION="$session_id" agent-desktop snapshot --app Finder -i --compact agent-desktop trace show --limit 500 agent-desktop trace export --out run.html agent-desktop session end "$AGENT_DESKTOP_SESSION"
注意 --screenshots 和 artifacts: full 可能把屏幕内容带入导出的证据;README 明确建议像处理截图一样处理导出的 HTML。涉及客户资料、密钥或聊天内容时,应限制会话范围、降低工件级别,并把 HTML 纳入访问控制与保留策略。多 Agent 并行时,独立 Agent 最好使用各自的 session;若确实共享同一个 session,每个 Agent 都应只操作自己刚取得的限定引用,不能假设“最新快照”仍属于自己。
还有一个容易被忽略的实现细节:Agent 不应只根据错误文本决定重试。项目的 JSON 输出约定会在错误对象中给出 code、suggestion、recovery 和 disposition。例如 STALE_REF 的恢复策略是刷新快照后再重试原操作,但只有 disposition.retry 为 safe 时,消费者才应采纳这类策略;旧版响应里曾存在的自由文本 retry_command 已被移除。换句话说,调用方应把错误信号建模为状态机:权限不足转人工授权;找不到目标转为重新观察或结束任务;目标歧义转人工确认或增加定位条件;明确安全可重试的陈旧引用才重新观察后继续。不要把所有非零退出都接到同一条“再试一次”分支。
这套边界还适合放进审批流程。可以将 snapshot 的 JSON 作为“准备执行”的证据,让策略层在 click、type 或 press 前检查应用名、窗口、元素角色和名称;对发送消息、覆盖文件、修改设置等动作要求人工放行。动作完成后的第二次快照与 trace 则形成“实际结果”的证据。这样,即使上层是自然语言 Agent,底层的桌面执行仍能保留可检查的输入、决策点和输出,而不是只有一张无法解释的最终截图。
实际接入时还应设置业务级的停止条件。比如只允许目标应用在允许名单内、只允许写入指定目录、对 press cmd+s 前后的文件名或窗口标题做复查;遇到密码框、系统权限页、支付或删除确认框则退出自动流程。无障碍树能改善“我点到了什么”的确定性,却不能替业务系统判断“现在是否应该点”。将 UI 定位的确定性与领域策略、人工审批分开,才能避免把技术能力误当作授权能力。
与 CDP 的分工:网页内容走协议,原生表面走无障碍树
很多桌面应用是 Chromium 外壳。agent-desktop 的 launch "Obsidian" --cdp 可以在新启动时开启并校验一个本地 DevTools 端口,随后 Playwright、Puppeteer 或其他 CDP 客户端可操作 web contents;原生菜单、对话框和窗口仍由无障碍路径处理。端点绑定在 127.0.0.1,但同一用户下的本地进程仍可访问它,因此任务完成后应关闭应用以结束暴露。
这条边界很实用:不要为了点击 Electron 的原生“打开文件”对话框而强行用 CDP,也不要为了读网页正文而把所有内容塞进无障碍树。前者适合语义化桌面引用,后者适合浏览器协议和页面工具。
agent-desktop 最适合的并不是无人值守地接管整台电脑,而是为有明确应用、权限和复查点的桌面步骤提供结构化执行层。把快照当作操作前提、把引用当作会失效的能力凭证、把 trace 当作事后证据,Agent 才能从“看图点按钮”升级为可停止、可恢复、可审查的 GUI 自动化参与者。