「OpenClaw」我写了个桌面控制Skill,让龙虾接管电脑!(MacOS版)

82 阅读7分钟

一、我做了一个新的 Skill:MacOS Desktop Control

  • 它的目标很简单:让 AI Agent 能够在 macOS 上更稳定、更可控地完成桌面操作,比如打开应用、识别屏幕内容、点击按钮、输入文字、拖拽元素,以及读取窗口状态。
  • 这个 skill 已经发布,下载地址如下

二、为什么要写这个Skill

  • MacOs兼容性:现有Skill都没有专门针对macOS做兼容性配置。比如很多MacBook是Retina屏,截图是像素坐标(pixels),但鼠标点击是逻辑坐标(point)。这里不做兼容的话,Agent就会“看得准,点不准”
  • 中文兼容性:现有Skill没有对中文输入和识别的兼容。比如我要打字“你好”,其他Skill会无法正确输入,因为它们默认是键盘一个一个敲英文字母
  • MacOs特性使用:现有Skill没有利用到macOS一些独有的特性。比如AppleScript很擅长打开app和读app标题,Apple自带的Apple Vision很适合读文字...等等

三、一个清晰的操作流水线

flowchart TD
    A[用 AppleScript 激活 App<br/>或读取窗口状态]
    B[初始化坐标映射]
    C[截取屏幕]
    D[用 OCR 或 OpenCV 找到目标]
    E[用鼠标或键盘执行动作]
    F[结束 / 验证结果]

    A --> B --> C --> D --> E --> F

四、这个Skill能做什么?

这里我举两个简单的例子

1.点击侧边栏会话按钮

执行效果
  • OpenClaw识别到了会话栏,并且点击打开了会话 image.png
执行流程
  • 可以看到OpenClaw用Skill的能力完成了整个流程 image.png

2.新建备忘录并输入“我是KD”

执行效果
  • 直接打开并新建&输入,执行的很快,因为完全是本地操作 image.png
执行流程
  • Applescript负责切备忘录到前台,keyboard负责快捷键和文件输入 image.png

五、目录结构

desktop-control-for-macos/
├── SKILL.md
├── _meta.json
├── requirements.txt
├── .DS_Store
└── scripts/
    ├── applescript_app.py
    ├── applescript_window.py
    ├── calibration.py
    ├── capture_screen.py
    ├── crop_image.py
    ├── init_coordinate_mapping.py
    ├── keyboard.py
    ├── locate_image_opencv.py
    ├── locate_text_ocr.py
    └── mouse.py

六、一些关键技术点

1. Retina 坐标映射

在 macOS 桌面自动化里,一个很容易踩坑的问题就是,截图坐标和鼠标点击坐标不一定是同一套坐标系

原因是 Retina 屏幕通常同时存在两套尺寸概念:

  • 逻辑坐标(point),鼠标控制工具更常用
  • 物理像素坐标(pixel),截图结果更常用

比如一块 Retina 屏幕在自动化工具里看起来可能是 1512 × 982,但实际截图尺寸却是 3024 × 1964。也就是说:

  • 逻辑宽高: 1512 × 982
  • 截图宽高: 3024 × 1964
  • 缩放比例: 2.0 × 2.0

如果不处理这个映射关系,视觉识别返回的坐标直接拿去点击,就会发生明显偏移。

这个 skill 的做法是,在执行桌面操作前先跑一次坐标初始化脚本,通过下面两个信息计算比例:

  1. pyautogui.size() 获取当前屏幕的逻辑尺寸
  2. pyautogui.screenshot() 获取截图的像素尺寸

然后计算:

scale_x = shot_w / screen_w
scale_y = shot_h / screen_h

并把结果保存到本地 JSON 文件里,例如:

{
  "screen_width_points": 1512,
  "screen_height_points": 982,
  "screenshot_width_pixels": 3024,
  "screenshot_height_pixels": 1964,
  "scale_x": 2.0,
  "scale_y": 2.0,
  "mode": "retina"
}

对应的换算关系也很直接:

  • 像素坐标转逻辑坐标:point = pixel / scale
  • 逻辑坐标转像素坐标:pixel = point * scale

不过这个 skill 进一步做了一层简化:默认把整个工作流统一到逻辑坐标系里。 也就是说,截图后会主动把图像 resize 到逻辑分辨率,这样 OCR、模板匹配、鼠标点击都基于同一套坐标,后续流程更稳定,也更不容易出错。

所以这部分的关键思想可以概括成一句话:

不是每次都手动换算,而是尽量把“识别”和“执行”统一到同一个逻辑坐标系里。


2. 视觉实现

这个 skill 的视觉层主要做两件事:

  1. 看见屏幕上有什么
  2. 把目标元素定位成可点击坐标

它没有走复杂的 UI 自动化树,而是采用了更通用的“截图 + 视觉定位”方案。这样做的好处是,对很多 自绘界面、聊天窗口、按钮图标、非标准控件 更稳。

整个视觉实现可以分成两类:

第一类,OCR 文本定位

当目标是一个有文字的按钮,比如“确定”“发送”“会话”,最直接的方法就是 OCR。

这个 skill 用的是 Apple Vision,通过 PyObjC 调用 macOS 原生文本识别能力,而不是依赖 Tesseract。这样做有几个优点:

  • macOS 原生支持,依赖更轻
  • 中文识别友好
  • 与系统图像栈兼容性更好

实现上会先对截图做一次放大:

img.resize((img.width * upscale, img.height * upscale))

默认放大 3 倍,再送进 Vision OCR。这样可以提升小字号文本的识别率。识别时同时启用了多语言:

['zh-Hans', 'zh-Hant', 'en-US']

识别到文本后,会遍历结果,寻找与目标文本最接近的候选,并输出中心点坐标以及文本框信息,例如:

  • 文本内容
  • 中心点 x, y
  • 边界框 left, top, width, height
  • 识别置信度

这种方式很适合处理:

  • 中文按钮
  • 菜单项
  • 标签栏
  • 聊天列表中的文字入口

第二类,OpenCV 模板匹配

如果目标不是文字,而是某个固定图标,比如关闭按钮、头像入口、特定视觉元素,就更适合走模板匹配。

这个 skill 使用 OpenCV 的 matchTemplate

cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)

然后通过阈值判断是否命中:

if max_val < threshold:
    return None

命中后同样输出中心点坐标和边界框。

它适合的场景包括:

  • 图标按钮
  • 固定样式的 UI 元素
  • OCR 不稳定、但视觉样式稳定的目标

为什么这种视觉方案很实用

因为很多现代桌面应用,尤其是 Electron、游戏化界面、聊天类界面,并不总能从系统无障碍树里稳定拿到目标控件。 这时“先截图,再识别,再点击”反而更通用。

所以这个 skill 的视觉思路其实很明确:

不强依赖应用内部结构,而是把界面当作一张图来理解。

这让它对跨应用、跨界面类型的自动化任务更有泛化能力。


3. AppleScript 应用

这个 skill 里 AppleScript 的定位很克制,也很明确,它只负责语义明确的应用级控制,不负责深度 UI 点按。

换句话说,AppleScript 在这里不是拿来“点按钮”的,而是拿来做这些更稳定的事情:

  • 打开应用
  • 激活应用
  • 判断应用是否在运行
  • 获取当前前台应用
  • 读取窗口标题
  • 统计窗口数量
  • 列出窗口标题

比如激活一个应用时,脚本会执行类似:

tell application "Google Chrome" to activate

为了确保应用真正到前台,还会结合 System Events

tell application "System Events"
    tell process "Google Chrome" to set frontmost to true
end tell

窗口信息读取也是类似思路,比如获取前台窗口标题:

tell application "System Events"
    tell process "Google Chrome"
        if (count of windows) > 0 then
            return name of front window
        else
            return ""
        end if
    end tell
end tell

为什么不直接用 AppleScript 做整个 UI 自动化

因为 AppleScript UI scripting 虽然理论上能操作按钮、菜单、输入框,但在现实里常常会遇到这些问题:

  • 不同应用的无障碍层级差异很大
  • 自绘界面经常拿不到稳定控件
  • 脚本容易因为 UI 结构变化而失效
  • 可移植性和鲁棒性都一般

所以这个 skill 刻意划了一条边界:

  • AppleScript 负责应用和窗口层面的“语义控制”
  • 视觉识别 + 鼠标键盘 负责界面层面的“视觉操作”

这其实是一个很实用的工程折中。

AppleScript 擅长的是“我知道我要操作哪个 app”,比如:

  • 把 Chrome 切到前台
  • 看当前是不是微信在最前面
  • 读一下当前窗口标题

而视觉和输入模拟擅长的是“我知道屏幕上哪个地方该点”,比如:

  • 找到“确定”按钮
  • 找到聊天列表入口
  • 找到输入框并粘贴文字

所以它们组合起来的典型流程就是:

  1. AppleScript 激活目标应用
  2. AppleScript 获取基础窗口状态
  3. 截图
  4. OCR 或模板匹配定位目标
  5. 鼠标点击 / 键盘输入执行操作

这种分层方式,比单纯依赖 AppleScript UI scripting 要稳得多,也更适合写成可复用的通用 skill。

七、写在最后

欢迎大家下载使用!有意见或建议可以留言,我会持续完善这个Skill