一、我做了一个新的 Skill:MacOS Desktop Control
- 它的目标很简单:让 AI Agent 能够在 macOS 上更稳定、更可控地完成桌面操作,比如打开应用、识别屏幕内容、点击按钮、输入文字、拖拽元素,以及读取窗口状态。
- 这个 skill 已经发布,下载地址如下:
- ClawHub(官方Skill平台):ClawHub——MacOS Desktop Control
二、为什么要写这个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识别到了会话栏,并且点击打开了会话
执行流程
- 可以看到OpenClaw用Skill的能力完成了整个流程
2.新建备忘录并输入“我是KD”
执行效果
- 直接打开并新建&输入,执行的很快,因为完全是本地操作
执行流程
- Applescript负责切备忘录到前台,keyboard负责快捷键和文件输入
五、目录结构
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 的做法是,在执行桌面操作前先跑一次坐标初始化脚本,通过下面两个信息计算比例:
pyautogui.size()获取当前屏幕的逻辑尺寸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 的视觉层主要做两件事:
- 看见屏幕上有什么
- 把目标元素定位成可点击坐标
它没有走复杂的 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 切到前台
- 看当前是不是微信在最前面
- 读一下当前窗口标题
而视觉和输入模拟擅长的是“我知道屏幕上哪个地方该点”,比如:
- 找到“确定”按钮
- 找到聊天列表入口
- 找到输入框并粘贴文字
所以它们组合起来的典型流程就是:
- AppleScript 激活目标应用
- AppleScript 获取基础窗口状态
- 截图
- OCR 或模板匹配定位目标
- 鼠标点击 / 键盘输入执行操作
这种分层方式,比单纯依赖 AppleScript UI scripting 要稳得多,也更适合写成可复用的通用 skill。
七、写在最后
欢迎大家下载使用!有意见或建议可以留言,我会持续完善这个Skill