← 返回文章列表← Back to posts

让 AI 操控你的电脑:四个项目,四种哲学Let AI Control Your Computer: Four Projects, Four Philosophies

从 Claude Code 源码逆向到三个开源项目,深度对比 Computer Use 的四种技术实现——像素驱动、结构驱动、极简跨平台、Provider 模式,揭示安全模型、坐标精度和自动化哲学的根本分野。Reverse-engineering Claude Code and comparing three open-source projects to reveal four distinct approaches to Computer Use — pixel-driven, structure-driven, minimal cross-platform, and provider pattern — with deep analysis of security models, coordinate accuracy, and automation philosophy.

·35 分钟阅读min read

让 AI 操控你的电脑:四个项目,四种哲学

从 Claude Code 源码逆向到开源社区,深度拆解 Computer Use 的技术实现与设计分野


引子

想象一下:你对着终端说"帮我打开 Safari,搜索今天的天气,然后把结果截图发给同事"。

然后你看着鼠标自己动了起来。它滑向 Dock 栏,点开 Safari,在地址栏输入 URL,等待页面加载,按下 Cmd+Shift+4 截图——整个过程你的手没有碰过键盘和触控板。

这不是科幻。这是 2025 年已经在多个项目中落地的技术:Computer Use——让 AI 直接操控你的电脑桌面。

但"让 AI 操控电脑"这件事,看似简单,实现起来却分裂出了截然不同的技术路线。我花了相当长的时间逆向分析 Claude Code 的源码,又横向拆解了三个知名开源项目,发现了一些令人意外的东西:

  • 有人用 Rust + Swift 写了两个原生模块,还手搓了一个 CFRunLoop 泵来桥接 Node.js 和 macOS
  • 有人直接读 Windows 的无障碍树(Accessibility Tree),根本不需要"看"屏幕
  • 有人把所有逻辑塞进一个文件,用一行 npx 就能跑
  • 有人搞了三种自动化后端(键盘发送器 / PowerShell / AutoHotkey),让你自己选

这四个项目分别是:

项目 Stars 平台 一句话定位
Claude Code (Anthropic 内部) 非公开 macOS 商业产品的内置功能,安全到偏执
CursorTouch/Windows-MCP ~4.6k Windows a11y tree 驱动的通用自动化框架
MCPControl ~306 Windows Provider 模式的实验性桌面控制
domdomegg/computer-use-mcp ~179 跨平台 极简主义——一个文件解决问题

接下来,我会带你从最简单的方案看起,逐步深入到工业级实现的内部,最后揭示这四个项目背后完全不同的设计哲学。


第一章:什么是 Computer Use?

概念

Computer Use 的核心循环非常简单:

AI 截取屏幕 → 理解画面内容 → 决定下一步操作 → 执行操作 → 再截屏验证

比如 AI 看到屏幕上有一个"确定"按钮在坐标 (340, 520),它就发出一个指令:"在 (340, 520) 处左键单击"。执行完后再截屏看看按钮是不是真的被点了。

听起来像是给 AI 装了一双眼睛和一双手。但魔鬼在细节里:

  • 怎么截屏? 直接截全屏?排除某些窗口?格式和分辨率怎么选?
  • 坐标怎么对齐? 截屏图片可能被缩放过,AI 看到的 (340, 520) 对应屏幕上的哪个点?
  • 安全怎么保证? AI 要是被 Prompt Injection 攻击了,开始乱点呢?
  • 多个 AI 会话同时操控同一台电脑怎么办?

这些问题的不同回答方式,造就了四个截然不同的项目。

MCP 协议:统一的"工具语言"

四个项目都使用了 MCP(Model Context Protocol) 作为 AI 与工具之间的通信协议。你可以把 MCP 理解为"AI 的 USB 接口"——它定义了一套标准的方式,让 AI 模型发现和调用外部工具。

一个 MCP 工具调用看起来像这样:

{
  "tool": "mcp__computer-use__left_click",
  "input": { "coordinate": [340, 520] }
}

AI 发出这个请求,MCP server 收到后执行点击操作,然后把结果(比如一张截屏)返回给 AI。

但有意思的是,Claude Code 对 MCP 的使用方式和其他三个项目完全不同——它的 MCP server 是假的。这个后面细说。


第二章:先看最简单的——domdomegg 的极简方案

我们从最简单的项目看起,建立一个基线认知。

一个文件,一行命令

domdomegg/computer-use-mcp 可能是地球上最简洁的 Computer Use 实现。核心逻辑几乎全在一个文件里:src/tools/computer.ts。安装?一行命令:

npx -y computer-use-mcp

它能做什么?

  • 截屏 — 用 nut-js 的 grabScreen(),macOS 上 fallback 到 screencapture 命令
  • 鼠标操作 — 移动、左/右/中键点击、双击、拖拽、滚动
  • 键盘操作 — 按键组合(如 Ctrl+C)、文字输入
  • 光标定位 — 获取当前光标位置

技术栈也很简洁:TypeScript + nut-js(跨平台桌面自动化库)+ Zod(输入验证)。

截屏与坐标处理

这里有一个值得注意的细节。domdomegg 的截屏流程是这样的:

grabScreen()
  → 获得原始截图(可能非常大,比如 5K 显示器)
  → 缩放到 API 限制内(长边 1568px,总像素 ≤ 1.15MP)
  → 记录缩放比例 ratio
  → 发送给 AI

AI 返回坐标 (x, y)
  → x_actual = x / ratio
  → y_actual = y / ratio
  → 在实际屏幕坐标执行点击

它还做了一个贴心的事情:在截屏上用红色十字准星标记当前光标位置,这样 AI 能看到鼠标在哪。

跨平台的代价

nut-js 让这个项目成为四个中唯一真正跨平台的方案(macOS / Windows / Linux)。但跨平台是有代价的:

  • macOS 上 nut-js 的截屏质量不如原生 SCContentFilter,无法排除特定窗口
  • Linux 上它会检测 xdotool 是否可用——如果有就用 xdotool 输入(更好的键盘布局支持),否则 fallback 到 nut-js
  • 所有平台上都无法做窗口管理、应用管理、剪贴板操作

这就是极简主义的取舍:什么都能用,但什么都不深。

不过对于"我就想快速试试 AI 操控电脑是什么感觉"这个需求,它是最佳选择——零配置,即开即用。


第三章:Windows 最强——CursorTouch 的 a11y tree 革命

如果说 domdomegg 是瑞士军刀——小巧通用,那 CursorTouch/Windows-MCP 就是一整套工业级工具箱。

不需要"看"的 AI

这是我在对比过程中最惊讶的发现。

其他三个项目(Claude Code、MCPControl、domdomegg)都遵循同一个范式:

截屏 → AI 视觉理解 → 输出坐标 → 点击坐标

这叫**"像素驱动"**——AI 必须"看到"屏幕内容,才能决定操作。

CursorTouch 走了一条完全不同的路。它有两个核心工具:

Screenshot — 快速截屏,和其他项目一样:

拍照 → 发给 AI → AI 看图决策

Snapshot — 这才是杀手锏:

读取 Windows Accessibility Tree (a11y tree)
  → 提取所有可交互元素(按钮、输入框、菜单...)
  → 每个元素带有 ID、名称、角色、位置
  → 返回结构化数据给 AI
  → AI 通过元素 ID 操作,而非坐标

什么是 Accessibility Tree?它是 Windows 系统为无障碍功能(屏幕阅读器等)维护的一棵 UI 元素树。每个窗口的每个按钮、输入框、文本标签,在这棵树里都有一个节点,带有语义信息。

这意味着什么?

  1. 不需要视觉模型。 纯文本 LLM 就能驱动 Windows 自动化——它读的是结构化数据,不是图片
  2. 不受分辨率影响。 元素 ID 就是元素 ID,不管屏幕是 720p 还是 4K
  3. 能访问隐藏内容。 即使元素被其他窗口遮挡或在视口外,a11y tree 里依然存在
  4. 天然绕过坐标精度问题。 操作的是"编辑框 #37",不是"坐标 (340, 520)"

DOM 模式——浏览器自动化的妙招

CursorTouch 还有一个 use_dom=True 模式。在这个模式下,Snapshot 工具不读 a11y tree,而是直接提取浏览器中网页的 DOM 结构,过滤掉浏览器自身的 UI 元素(地址栏、标签栏等),只返回网页内容。

这对于网页自动化来说非常聪明——AI 看到的是干净的网页结构,而不是一堆浏览器 chrome(注意这里的 chrome 是小写,指浏览器边框装饰,不是 Google Chrome)。

17 种工具——最丰富的能力集

除了基础的点击、输入、滚动,CursorTouch 还提供了:

  • App — 启动、调整大小、移动应用窗口
  • Shell — 执行 PowerShell 命令
  • Process — 列出和终止进程
  • Registry — 读写 Windows 注册表
  • Notification — 发送 Windows toast 通知
  • Scrape — 提取网页内容
  • MultiSelect / MultiEdit — 批量选择和编辑操作
  • Clipboard — 剪贴板读写

这让它不只是一个"替你点鼠标"的工具,而是一个完整的 Windows 系统操控框架。

远程模式

CursorTouch 还支持远程模式——连接到 windowsmcp.io 云服务,在远程 Windows VM 上执行操作。这对于 CI/CD 集成测试、远程运维场景很有价值。

但是……

CursorTouch 的 a11y tree 方案有一个根本性限制:它依赖 UI 框架的无障碍实现质量。

Windows 原生控件(WPF、WinForms)的 a11y tree 通常很完整。但自定义 UI(游戏、Electron 应用的某些部分、画布渲染的内容)可能在 a11y tree 里完全不存在。这时候就只能 fallback 到 Screenshot + 坐标点击。

而且它是 Windows-only。不支持 macOS,不支持 Linux。


第四章:拆解 Claude Code——工业级 Computer Use 长什么样

这是本文最硬核的部分。我逆向分析了 Claude Code 的还原源码,在 src/utils/computerUse/ 目录下发现了 15 个文件、约 2000 行代码。

如果说 domdomegg 是"大学生的课程作业",CursorTouch 是"创业公司的核心产品",那 Claude Code 的 Computer Use 就是"NASA 的航天器控制系统"——每一个细节都经过深思熟虑,每一个边界条件都有处理。

架构全景

Claude API 后端
  │ (检测 mcp__computer-use__* 工具名 → 注入系统提示)
  │
query.ts (主查询循环)
  │
wrapper.tsx (会话上下文绑定)
  │
@ant/computer-use-mcp (Anthropic 内部共享包)
  │
hostAdapter.ts (宿主适配器)
  │
executor.ts (操控引擎)
  ╱                ╲
Rust NAPI            Swift NAPI
(@ant/computer-      (@ant/computer-
 use-input)           use-swift)
  │                      │
enigo 库              SCContentFilter
(鼠标/键盘)           (截屏/窗口管理)
  ╲                  ╱
   macOS HID / WindowServer

两个独立的原生模块——这是和其他所有项目最大的架构差异。Claude Code 没有用 nut-js 或 pyautogui 这样的跨平台库,而是用 Rust(enigo 库)做鼠标/键盘输入,用 Swift(SCContentFilter)做截屏和窗口管理。

为什么要这么折腾?因为它只需要支持 macOS,可以把每个平台能力都压榨到极致。

MCP 是"假的"——但有充分的理由

这是一个非常有趣的设计决策。

其他三个项目都是真正的 MCP server——它们作为独立进程运行,通过 stdio 或 SSE 与 AI 客户端通信。

Claude Code 的 Computer Use MCP server 是在进程内运行的。setup.ts 创建了一个 MCP 配置,看起来像是要 spawn 一个子进程,但 client.ts 在连接时检测到是 computer-use server,就直接创建了一个 in-process server。所有调用都在同一个进程内完成,不走进程间通信。

为什么要穿这件"MCP 的外衣"?

源码注释写得很清楚:

// The MCP layer isn't ceremony: the API backend detects
// `mcp__computer-use__*` tool names and emits a CU availability
// hint into the system prompt. Built-in tools with different
// names wouldn't trigger it.

Claude API 后端会检测 mcp__computer-use__* 这个命名模式,然后自动在系统提示中注入 Computer Use 的使用指南。如果用其他名字,后端不会识别。所以 MCP 协议在这里纯粹是为了名字——这大概是我见过最务实的"名字驱动架构"了。

CFRunLoop 泵——最精妙的技术点

这是 Claude Code 中我最欣赏的一段工程。

问题:Swift 的 @MainActor 方法(截屏、窗口管理)和 Rust 的 key()/keys() 都会把工作 dispatch 到 DispatchQueue.main(macOS 的主队列)。在 Electron 里,CFRunLoop 会自动消费这个队列——一切正常。但 Claude Code 跑在 Node.js 里,主队列永远不会被消费,这些调用会永远挂起。

解决方案 (drainRunLoop.ts):

// 引用计数的 CFRunLoop 泵
let pump: ReturnType<typeof setInterval> | undefined
let pending = 0

function retain(): void {
  pending++
  if (pump === undefined) {
    // 每 1ms 调用一次 Swift 的 _drainMainRunLoop
    pump = setInterval(drainTick, 1, requireComputerUseSwift())
  }
}

function release(): void {
  pending--
  if (pending <= 0 && pump !== undefined) {
    clearInterval(pump)
    pump = undefined
  }
}

// 包装任何需要主队列的操作
async function drainRunLoop<T>(fn: () => Promise<T>): Promise<T> {
  retain()  // 启动泵
  try {
    const work = fn()
    const timeout = /* 30 秒超时 */
    return await Promise.race([work, timeout])
  } finally {
    release()  // 释放泵
  }
}

简单说:每当有操作需要 macOS 主队列时,启动一个 1ms 间隔的定时器不断 pump CFRunLoop。用引用计数管理——多个并发调用共享同一个泵,最后一个完成时停泵。还有 30 秒超时兜底防止永久挂起。

这段代码的存在本身就说明了一个问题:在错误的平台上做正确的事情,代价是惊人的。Electron 自动做的事,Node.js 里你得手搓一个 RunLoop 泵。但 Claude Code 不用 Electron——它是命令行工具。

坐标一致性——被所有开源项目忽视的关键

截屏被发给 AI,AI 基于图片上的坐标决定点击位置。如果截屏在传输过程中被缩放了,坐标就会偏移。

domdomegg 的做法是"截完再缩,点击时反算"——先截全屏,缩放到 API 限制以内,记录比例,点击时乘以反比例。问题是浮点精度会累积误差。

MCPControl 的做法更粗暴——推荐你把分辨率调到 1280x720,从源头回避问题。

Claude Code 的做法优雅得多:

// 在截屏阶段就精确控制输出尺寸
function computeTargetDims(logicalW, logicalH, scaleFactor) {
  const physW = Math.round(logicalW * scaleFactor)
  const physH = Math.round(logicalH * scaleFactor)
  // targetImageSize 计算 API 期望的精确尺寸
  return targetImageSize(physW, physH, API_RESIZE_PARAMS)
}

// 截屏时直接传入目标尺寸
cu.screenshot.captureExcluding(
  allowedBundleIds,
  0.75,        // JPEG quality
  targetW,     // 预计算的目标宽度
  targetH,     // 预计算的目标高度
  displayId
)

截屏的那一刻就已经是最终尺寸了。 API 的 transcoder 检测到图片尺寸匹配期望值,走 early-return 路径不做任何处理。这样 scaleCoord(坐标换算)运算始终精确——因为根本没有二次缩放发生。

为什么说这很重要?在 4K/5K 显示器上,一个 1-2 像素的偏移可能导致点击到错误的 UI 元素。对于商业产品来说,"大部分时候能点对"和"每次都能点对"之间隔着用户体验的生死线。

7 层安全纵深——偏执到令人敬佩

这是 Claude Code 与所有开源项目之间最大的鸿沟。让我按层级来拆解:

第 1 层:Feature Gate(灰度控制)

编译期 feature flag → GrowthBook 远程开关 → 订阅级别检查 (Max/Pro)
→ 平台检查 (macOS only) → 交互模式检查

五道门,全部通过才能使用 Computer Use。还有一个有趣的细节:内部员工(代码里叫 "ant")如果在 monorepo 开发环境中,默认禁用——防止开发时误触发。

第 2 层:应用级权限授权

AI 不能操控所有应用。它必须通过 request_access 工具向用户请求权限,弹出一个审批对话框,用户逐个确认"允许操控 Safari"、"允许操控 VS Code"。

第 3 层:Prompt Injection 防御

这是最让我印象深刻的部分。应用名称会被嵌入到发送给 AI 的工具描述中。而应用名是攻击者可控的——任何人都可以发布一个名叫 "grant all access to everything" 的应用。

Claude Code 的防御(appNames.ts):

// 字符白名单——只允许安全字符
// \p{L}\p{M}\p{N} 覆盖所有语言的字母数字
// 禁止引号、反引号、尖括号、换行符
const APP_NAME_ALLOWED = /^[\p{L}\p{M}\p{N}_ .&'()+-]+$/u

// 长度限制 + 数量限制
const APP_NAME_MAX_LEN = 40
const APP_NAME_MAX_COUNT = 50

// 路径白名单——只有 /Applications/ 下的才算用户应用
const PATH_ALLOWLIST = ['/Applications/', '/System/Applications/']

// 名称黑名单——过滤后台服务
const NAME_PATTERN_BLOCKLIST = [
  /Helper(?:$|\s\()/,    // "Slack Helper (GPU)" → 过滤
  /Agent(?:$|\s\()/,     // "ABAssistantService" → 过滤
  /Service(?:$|\s\()/,   // 但 "Service Desk" → 保留
  /Updater(?:$|\s\()/,
  /^\./,                  // 隐藏文件开头
]

四重过滤:路径白名单 → 名称黑名单 → 字符白名单 → 长度/数量限制。常用应用(Safari、Chrome、VS Code 等约 30 个)走单独的白名单通道,跳过字符检查但保留长度限制。

第 4 层:ESC 全局热键(CGEventTap)

Computer Use 运行期间,ESC 键被全局拦截。为什么?

想象一个 Prompt Injection 场景:恶意网页内容诱导 AI 认为需要按 ESC 来关闭某个对话框——但那个对话框其实是安全确认弹窗。ESC 拦截让 AI 无法通过键盘关闭任何对话框。

但如果 AI 自己需要按 ESC 呢(比如关闭 Vim 的 insert 模式)?

// executor.ts 中:AI 按 ESC 前先通知热键系统
if (isBareEscape(parts)) {
  notifyExpectedEscape()  // "接下来的 ESC 是我按的,别拦"
}
await input.keys(parts)

Swift 侧给这个通知一个 100ms 的衰减窗口——如果 100ms 内没有 CGEvent 到达,通知自动失效,下次用户按 ESC 依然会触发 abort。

第 5 层:会话独占锁

// O_EXCL 原子创建——操作系统保证只有一个进程能成功
await writeFile(lockPath, JSON.stringify(lock), { flag: 'wx' })

加上 PID 存活检测(process.kill(pid, 0)),死进程的锁会被自动回收。两个会话同时 recover 同一个 stale lock?O_EXCL 保证只有一个能赢。

第 6 层:剪贴板安全(6 步序列)

当 AI 需要通过剪贴板粘贴文本时:

1. 保存用户剪贴板       (pbpaste)
2. 写入目标文本         (pbcopy)
3. 回读验证——写入成功了吗?(pbpaste == text?)
   └→ 不匹配?绝不按 Cmd+V(否则会粘贴垃圾内容)
4. Cmd+V 粘贴
5. 等待 100ms           (粘贴生效 vs 恢复的竞态窗口)
6. 恢复用户剪贴板       (在 finally 中,异常也能恢复)

第 3 步的回读验证是关键——剪贴板写入可能静默失败(比如另一个应用正在使用剪贴板),不验证就粘贴会把错误内容写入用户的文档。

第 7 层:Turn 结束清理

每次 AI 对话轮次结束(无论正常/中断/错误),三件事必须发生:

  1. 取消隐藏之前为了截屏而隐藏的窗口
  2. 注销 ESC 热键
  3. 释放会话锁 + 发送系统通知"Claude 已完成使用你的电脑"

这三步有严格顺序:先让用户看到窗口 → 再解除 ESC 拦截 → 最后释放锁。任何一步失败都不能阻塞后续步骤。

终端作为"代理宿主"

还有一个有趣的工程挑战。Claude Code 是命令行工具,没有窗口。但 Cowork(Claude 桌面版)有 Electron 窗口。共享包 @ant/computer-use-mcp 的很多逻辑假设宿主有窗口——比如"截屏时排除宿主窗口"、"操控前隐藏无关窗口但保留宿主"。

Claude Code 的解决方案:把终端当作代理宿主。

// 检测当前终端的 bundleId
export function getTerminalBundleId(): string | null {
  // 优先用 macOS 的 __CFBundleIdentifier 环境变量
  const cfBundleId = process.env.__CFBundleIdentifier
  if (cfBundleId) return cfBundleId
  // fallback 到手动映射表
  return TERMINAL_BUNDLE_ID_FALLBACK[env.terminal ?? ''] ?? null
}

映射表覆盖了主流 macOS 终端:iTerm2、Apple Terminal、Ghostty、Kitty、Warp、VS Code。检测到后,这个 bundleId 被传递给 Swift 侧,用于:

  • 截屏时排除终端窗口(你不想让 AI 看到自己的命令行界面)
  • 隐藏窗口时豁免终端(不然终端被隐藏了你什么都看不到)
  • 激活目标应用时跳过终端(终端在前台不应该吃掉点击事件)

第五章:MCPControl——Provider 模式的可能性

MCPControl 是四个项目中体量最小的,但它有一个有趣的架构思路。

三种后端,一套接口

// 通过环境变量选择自动化后端
const provider = createAutomationProvider()
// 可能是 keysender、PowerShell、或 AutoHotkey v2

// 无论哪个后端,接口是统一的
await provider.screen.getScreenshot()
await provider.mouse.click(x, y)
await provider.keyboard.type("hello")

这个 Provider Factory 模式的优势是:

  • keysender(默认)—— 原生 Windows 自动化,速度快但需要编译
  • PowerShell —— 纯脚本,无需编译,但慢
  • AutoHotkey v2 —— 社区生态丰富,很多现成脚本可复用

用户可以根据场景选择最合适的后端,甚至可以为不同操作配置不同后端。

远程部署

MCPControl 支持 SSE + HTTPS 远程部署:

mcp-control --sse --https --cert /path/to/cert --key /path/to/key

这让它可以运行在远程 VM 上,AI 客户端通过网络连接过来——适合测试环境和 CI/CD 场景。

局限

但坦率地说,MCPControl 目前还很粗糙:

  • 推荐 1280x720 分辨率(在其他分辨率下点击精度下降)
  • 多显示器支持有限
  • 无任何权限控制——AI 可以操控任何东西
  • 无并发管理——多个客户端同时连接会出问题
  • README 大字标注:"THIS SOFTWARE IS EXPERIMENTAL AND POTENTIALLY DANGEROUS"

它更像是一个"可能性展示"——Provider 模式如果配上 Claude Code 级别的安全层,会是一个很好的架构。


第六章:四种答案的深度对比

安全模型——最大的鸿沟

安全维度 Claude Code CursorTouch MCPControl domdomegg
灰度发布控制 5 层 gate 无 无 无
应用级权限授权 用户审批 无 无 无
Prompt Injection 防御 字符白名单+ESC拦截 无 无 无
会话独占锁 O_EXCL 原子锁 无 无 无
剪贴板保护 6 步安全序列 无 无 无
按键卡住防护 pressed 跟踪+finally 无 无 无
操作超时 30s 兜底 无 无 无
窗口自动恢复 turn 结束 unhide 无 无 无
OS 权限检查 TCC API 无 无 无
退出通知 系统通知 无 无 无

Claude Code 的安全相关代码占总代码量的 40% 以上。三个开源项目的安全措施加起来不超过"try/catch + 坐标边界检查 + README 里写个 WARNING"。

这不是开源项目的疏忽——是定位差异。Claude Code 出了事,Anthropic 要承担品牌风险和法律责任。开源项目出了事,README 里写了"use at your own risk"。

坐标精度

方案 策略 精度
Claude Code 截屏时预缩放到 API 精确尺寸 像素级精确
domdomegg 截屏后缩放,反算坐标 有浮点误差累积
MCPControl 推荐固定 1280x720 回避问题
CursorTouch a11y tree 用元素 ID 绕过坐标问题

CursorTouch 的方案最聪明——它直接绕过了坐标精度这个问题。当你操作的是"按钮 #37"而不是"坐标 (340, 520)"时,分辨率变化、DPI 缩放、窗口移动都不会影响操作准确性。

性能

操作 Claude Code CursorTouch domdomegg
截屏 <100ms (SCContentFilter 硬件加速) 0.2-0.9s (含 a11y tree) 较慢 (nut-js)
点击 ~50ms 0.2-0.9s 100ms+
键盘 8ms/次 (125Hz USB polling) 未公开 依赖 nut-js
拖拽 60fps ease-out-cubic 动画 无动画 无动画

Claude Code 在原始速度上最快——毕竟是直接调系统 API。但 CursorTouch 的 0.2-0.9s 延迟包含了 a11y tree 提取的开销,换来的是不需要二次截屏验证(因为操作的是元素,不是坐标),总体工作流可能反而更快。

能力覆盖

domdomegg:     ████░░░░░░░░░░░░░░░░  基础鼠标键盘截屏
MCPControl:    ██████░░░░░░░░░░░░░░  + 窗口管理 + 剪贴板
Claude Code:   ████████████░░░░░░░░  + 应用管理 + 区域截屏 + 批量操作 + 权限系统
CursorTouch:   ████████████████████  + a11y tree + DOM + Shell + Registry + Process + 远程

第七章:一个问题背后的三种哲学

拆解完四个项目,我发现 Computer Use 这个领域存在三种根本不同的技术哲学。

哲学一:像素驱动——"用眼睛看,用手操作"

截屏 → AI 视觉理解 → 坐标 → 点击

Claude Code、domdomegg、MCPControl 都属于这个阵营。AI 扮演的是一个"远程操控员"——它看着屏幕截图,决定在哪里点击。

优点:通用性极强。任何有 UI 的东西都能操控——原生应用、网页、游戏、甚至图片编辑器。 缺点:脆弱。分辨率变了坐标就偏了。UI 变了可能认不出来了。截屏有时间成本。

这也是为什么 Claude Code 在坐标一致性上下了这么大功夫——在像素驱动的世界里,精度就是一切。

哲学二:结构驱动——"读代码,不看画面"

读取 UI 结构树 → AI 分析结构 → 选择元素 → 操作元素

CursorTouch 独属于这个阵营。AI 扮演的是一个"自动化脚本"——它读的是 UI 的结构描述,操作的是具名元素。

优点:精确、不受视觉干扰、不需要视觉模型、天然无坐标问题。 缺点:依赖 UI 框架的无障碍实现。自定义控件、Canvas 渲染、游戏 UI 完全失效。

这其实是 Selenium / Playwright 那套 Web 自动化思路在桌面上的复刻——只是用 AI 替代了硬编码的选择器。

哲学三:混合驱动——"能读结构就读结构,不行就看图"

CursorTouch 同时提供 Screenshot 和 Snapshot 两个工具,让 AI 自己决定什么时候"看图"、什么时候"读结构"。这是最灵活的方案,但也给 AI 增加了一层决策负担。

更深层的分野:商业产品 vs 开源社区

四个项目的安全差异不是技术能力的差异,而是激励结构的差异。

Claude Code 的安全投入(7 层纵深、40% 代码量)在开源社区看来几乎是"过度工程"。但对 Anthropic 来说:

  • AI 误操控导致用户数据丢失 → 法律诉讼 + 品牌灾难
  • Prompt Injection 导致 AI 发送恶意邮件 → 安全事故 + 信任崩塌
  • 两个会话同时操控导致行为不可预测 → 用户体验碎裂

这些风险中任何一个的损失都远超安全开发的成本。

开源项目呢?MIT 许可证的免责条款已经把风险转移给了用户。安全投入的 ROI 远低于新功能开发。所以我们看到 CursorTouch 把精力放在了 17 种工具和 a11y tree 创新上,而不是锁机制和 PI 防御。

两种策略都是理性的。 只是优化的目标函数不同。

一个有趣的注释

在 Claude Code 的 executor.ts 头部,有一大段注释解释了 CLI 版本与 Cowork(桌面版)的每一处差异。不是简单的 TODO 或 FIXME,而是完整的 rationale——为什么这里和桌面版不同,差异会导致什么后果,有什么副作用。

在 drainRunLoop.ts 里,注释解释了为什么超时获胜后要给孤儿 Promise 附加 .catch(() => {})——因为原生层的延迟 rejection 会变成 unhandledRejection。

在 typeViaClipboard 里,注释解释了为什么在 Cmd+V 后要等 100ms 再恢复剪贴板——因为粘贴是异步的,恢复太快会导致目标应用粘贴到恢复后的内容。

这些注释的存在本身就传递了一个信息:这段代码是写给下一个要维护它的工程师看的。每个看似奇怪的数字(1ms、8ms、50ms、100ms)背后都有一个 HID 轮询周期或 AppKit 事件传播延迟的解释。

这种代码风格在开源项目中极为罕见。不是因为开源工程师不够好,而是因为商业项目中"下一个人读这段代码"的概率接近 100%,而个人项目中这个概率接近 0%。


结语:四种答案,都是对的

让 AI 操控你的电脑——同一个问题,四个项目给出了四种回答:

  • domdomegg 说:"用最少的代码,让最多的人能试一下。"
  • CursorTouch 说:"不要看屏幕,直接读 UI 结构——更快、更准、更稳。"
  • MCPControl 说:"让用户选择自己的工具——keysender 也好,AutoHotkey 也好。"
  • Claude Code 说:"先确保不会出事,再谈怎么做事。"

没有哪个是错的。它们在各自的约束条件下,都做出了最优解。

如果你想 5 分钟内体验 AI 操控电脑——用 domdomegg。 如果你想 在 Windows 上做严肃的 AI 自动化——用 CursorTouch。 如果你想 理解工业级 Computer Use 是什么样子——读 Claude Code 的源码。 如果你想 在自己的产品中集成 Computer Use——把 Claude Code 的安全模型和 CursorTouch 的 a11y tree 结合起来。

Computer Use 还在非常早期的阶段。今天的四种哲学,未来可能会收敛成一到两种主流方案。但在那之前,它们各自的探索都值得被认真对待。


参考项目