让 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 元素树。每个窗口的每个按钮、输入框、文本标签,在这棵树里都有一个节点,带有语义信息。
这意味着什么?
- 不需要视觉模型。 纯文本 LLM 就能驱动 Windows 自动化——它读的是结构化数据,不是图片
- 不受分辨率影响。 元素 ID 就是元素 ID,不管屏幕是 720p 还是 4K
- 能访问隐藏内容。 即使元素被其他窗口遮挡或在视口外,a11y tree 里依然存在
- 天然绕过坐标精度问题。 操作的是"编辑框 #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 对话轮次结束(无论正常/中断/错误),三件事必须发生:
- 取消隐藏之前为了截屏而隐藏的窗口
- 注销 ESC 热键
- 释放会话锁 + 发送系统通知"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 还在非常早期的阶段。今天的四种哲学,未来可能会收敛成一到两种主流方案。但在那之前,它们各自的探索都值得被认真对待。
参考项目
- CursorTouch/Windows-MCP — MCP Server for Computer Use in Windows (~4.6k stars)
- claude-did-this/MCPControl — MCP server for Windows OS automation (~306 stars)
- domdomegg/computer-use-mcp — Give AI models complete control of your computer (~179 stars)
- Claude Code Computer Use 源码分析基于还原版本
restored-src/src/utils/computerUse/