Openclaw 在 Mac mini 上的安装、启动、使用、完全卸载与清理
在自己的Mac mini搭建专属的Openclaw
环境说明
- 设备:搭载 Apple Silicon M4 芯片的 Mac,32GB 统一内存
- 本地大模型:Qwen 3.5 27B(通过 Ollama 运行,零 API 费用)
- 云端备用模型:DeepSeek(或其他兼容 OpenAI 格式的 API)
- 模型路由中间件:LiteLLM(统一管理本地和云端模型)
- 控制入口:飞书(长连接 WebSocket 模式)
- 进程守护:PM2(替代 macOS 原生 LaunchAgent,防止两者冲突)
第一阶段:准备基础环境
1.1 安装 Homebrew
打开 Mac 的终端(Terminal.app),运行:
/bin/bash -c "$(curl -fsSL <https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh>)"
注意(M4 芯片必做):安装结束后,终端底部会打印两行以
eval开头的命令。请将这两行复制出来,粘贴到终端里运行,否则后续所有brew命令会报找不到命令的错误。
1.2 安装 Node.js 和 Python
brew install node python
1.3 解决 nvm 与 Homebrew 的 Node.js 版本冲突
如果 Mac 上已安装 nvm(Node 版本管理器),Homebrew 安装的 Node 和 nvm 管理的旧版 Node 会产生冲突,导致 OpenClaw 安装时报 requires Node.js 20+ 错误。
使用 nvm 安装并固定 Node 20 LTS 版本:
nvm install 20
nvm use 20
nvm alias default 20
运行以下命令确认版本输出为 v20.x.x 后再继续:
node -v
1.4 安装 PM2
npm install -g pm2
第二阶段:安装本地大模型(Qwen 3.5 27B)
2.1 安装 Ollama
前往 ollama.com 下载并安装 Mac 客户端。安装完毕后,打开 Ollama.app,确认屏幕右上角菜单栏出现羊驼图标。
2.2 拉取 Qwen 3.5 27B 修正版
注意:请勿直接运行官方
ollama pull qwen3.5。官方版本存在工具调用(Tool Calling)缺陷,会导致 OpenClaw 无法正确识别 AI 的指令。请使用以下社区修正版本:
ollama pull bazobehram/qwen3.5-flash-27b
拉取完毕后,测试是否可以正常对话:
ollama run bazobehram/qwen3.5-flash-27b
收到正常回复后按 Ctrl+D 退出。Ollama 会继续在后台运行,监听本地 http://localhost:11434。
第三阶段:安装并配置 LiteLLM
3.1 安装 LiteLLM
pip3 install litellm[proxy]
3.2 创建 LiteLLM 配置文件
vim ~/litellm_config.yaml
按 i 进入插入模式,粘贴以下内容(将 DeepSeek 的 API Key 替换为真实密钥;如暂无云端 Key,可删除 cloud-fallback 节点):
model_list:
# 本地 Qwen 3.5 27B (修正版)
- model_name: local-qwen27b
litellm_params:
model: ollama/bazobehram/qwen3.5-flash-27b
api_base: http://localhost:11434
# 云端备用大模型 (DeepSeek)
- model_name: cloud-fallback
litellm_params:
model: deepseek/deepseek-chat
api_key: sk-你的真实DeepSeekAPIKey
按 Esc 退出插入模式,输入 :wq 保存并退出。
3.3 找出 LiteLLM 的绝对路径
PM2 作为后台服务不会继承终端的环境变量,必须使用绝对路径:
which litellm
记下输出的路径(例如 /opt/homebrew/bin/litellm)。
3.4 创建 LiteLLM 启动脚本
vim ~/start_litellm.sh
按 i 进入插入模式,粘贴以下内容(将路径替换为上一步找到的真实路径):
#!/bin/bash
source ~/.bash_profile 2>/dev/null || true
source ~/.zshrc 2>/dev/null || true
/opt/homebrew/bin/litellm --config ~/litellm_config.yaml --port 4000
按 Esc,输入 :wq 保存退出,然后赋予执行权限:
chmod +x ~/start_litellm.sh
3.5 测试 LiteLLM 是否正常启动
~/start_litellm.sh
确认输出中包含 LiteLLM: Proxy initialized 和 Uvicorn running on http://0.0.0.0:4000,按 Ctrl+C 退出。
第四阶段:安装并配置 OpenClaw
4.1 全局安装 OpenClaw
npm install -g openclaw
4.2 授予 Mac 系统权限
点击屏幕左上角苹果图标 -> 系统设置 -> 隐私与安全性,依次找到以下三项,为"终端(Terminal)"打上勾:
- 辅助功能(Accessibility)
- 屏幕录制(Screen Recording)
- 完全磁盘访问权限(Full Disk Access)
4.3 创建 OpenClaw 配置文件
mkdir -p ~/.openclaw
vim ~/.openclaw/openclaw.json
按 i 进入插入模式,粘贴以下内容(将飞书的 appId 和 appSecret 替换为真实值,获取方式见第五阶段):
{
"models": {
"providers": {
"litellm": {
"baseUrl": "http://localhost:4000",
"api": "openai-completions",
"apiKey": "sk-placeholder",
"models": [
{
"id": "local-qwen27b",
"name": "Local Qwen 27B",
"input": ["text"]
}
]
},
"deepseek": {
"baseUrl": "https://api.deepseek.com/v1",
"api": "openai-completions",
"apiKey": "sk-你的真实DeepSeekAPIKey",
"models": [
{
"id": "deepseek-chat",
"name": "DeepSeek V3",
"input": ["text"]
}
]
}
}
},
"agents": {
"defaults": {
"model": {
"primary": "litellm/local-qwen27b",
"fallbacks": ["deepseek/deepseek-chat"]
},
"contextTokens": 16000
}
},
"gateway": {
"mode": "local"
},
"channels": {
"feishu": {
"enabled": true,
"dmPolicy": "open",
"allowFrom": ["*"],
"accounts": [
{
"appId": "cli_xxxxxxxxxxxxxx",
"appSecret": "xxxxxxxxxxxxxxxxxxxx",
"region": "china"
}
]
}
}
}
按 Esc,输入 :wq 保存退出。
4.4 验证配置文件
openclaw doctor
确认输出中没有红色 ERROR 报错后继续。
第五阶段:对接飞书(Feishu)
5.1 创建飞书自建应用
- 浏览器打开 飞书开放平台,使用飞书账号登录。
- 点击创建企业自建应用,填写应用名称(例如"Mac 管家")和图标。
5.2 获取 App ID 和 App Secret
在左侧菜单点击凭证与基础信息,复制 App ID 和 App Secret,填入 openclaw.json 的对应字段。
填完后,重新打开配置文件更新:
vim ~/.openclaw/openclaw.json
5.3 添加机器人能力
在左侧菜单点击添加应用能力,选择并添加机器人(Bot)。
5.4 配置事件订阅
在左侧菜单点击事件与回调:
-
订阅方式选择:通过长连接接收事件(WebSocket)
此步骤为机器人能否收到消息的关键,选错会导致 400 连接错误,机器人完全无响应。
-
点击添加事件,搜索并添加
im.message.receive_v1(接收消息)。
5.5 配置权限
在左侧菜单点击权限管理,搜索并开通以下权限:
- 在左侧菜单点击权限管理。
- 点击页面右上角的批量导入(Batch Import)按钮。
- 将以下 JSON 完整粘贴进去,点击批量申请开通:
{
"scopes": {
"tenant": [
"im:message",
"im:message:send_as_bot",
"im:message:readonly",
"im:message.p2p_msg:readonly",
"im:message.group_at_msg:readonly",
"im:chat.members:bot_access",
"im:chat.access_event.bot_p2p_chat:read",
"im:resource",
"contact:user.employee_id:readonly",
"contact:contact.base:readonly",
"contact:user.email:readonly",
"cardkit:card:read",
"cardkit:card:write",
"application:bot.menu:write",
"application:application:self_manage"
],
"user": [
"im:chat.access_event.bot_p2p_chat:read"
]
}
}
| 权限名称 | 用途 |
|---|---|
im:message |
读取与发送消息(核心权限) |
im:message:send_as_bot |
以机器人身份发送消息 |
im:message:readonly |
读取消息历史 |
im:message.p2p_msg:readonly |
读取私聊消息 |
im:message.group_at_msg:readonly |
读取群聊中被 @ 的消息 |
im:chat.members:bot_access |
机器人加入群聊 |
im:resource |
读取消息中的图片/文件资源 |
contact:user.employee_id:readonly |
读取用户工号(用于身份识别) |
contact:user.email:readonly |
读取用户邮箱 |
cardkit:card:read / write |
读取和发送飞书消息卡片 |
application:bot.menu:write |
配置机器人菜单 |
如果还希望 OpenClaw 能操作飞书文档、多维表格、云盘,还需要额外开通以下权限(可选):
docs:doc— 读写飞书文档sheets:spreadsheet— 读写飞书电子表格bitable:app— 读写多维表格drive:drive— 读取云盘文件calendar:calendar— 读写日历
5.6 发布版本
在左侧菜单点击版本管理与发布 -> 创建版本,填写版本号(如 1.0.0),点击发布。
如需管理员审核,前往飞书客户端工作台 -> 应用管理通过审核后方可生效。
注意:每次修改权限、事件订阅或应用能力后,均需重新发布一个新版本才能生效。
第六阶段:用 PM2 托管所有服务
6.1 创建 OpenClaw 启动脚本
找出 OpenClaw 的绝对路径:
which openclaw
创建启动脚本:
vim ~/start_openclaw.sh
按 i 进入插入模式,粘贴以下内容(将路径替换为实际路径):
#!/bin/bash
source ~/.bash_profile 2>/dev/null || true
source ~/.zshrc 2>/dev/null || true
/usr/local/bin/openclaw gateway run
按 Esc,输入 :wq 保存退出,赋予权限:
chmod +x ~/start_openclaw.sh
6.2 使用 PM2 启动所有服务
# 启动 LiteLLM 路由代理
pm2 start ~/start_litellm.sh --name litellm
# 等待 LiteLLM 就绪后,启动 OpenClaw
sleep 3 && pm2 start ~/start_openclaw.sh --name openclaw
6.3 配置开机自启动
pm2 startup
注意:该命令会在终端中打印一行以
sudo env PATH=...开头的新命令。请将该命令完整复制后,粘贴回终端运行,输入 Mac 开机密码确认授权。
然后保存当前进程列表:
pm2 save
6.4 验证运行状态
pm2 status
确认 litellm 和 openclaw 均显示为绿色的 online。
第七阶段:飞书使用与模型切换
日常使用
打开飞书,搜索并打开创建的机器人,发送任意消息。机器人会使用本地 M4 Mac 上的 Qwen 27B 进行回复,数据不经过任何外部服务器。
飞书内快速切换模型
| 飞书指令 | 效果 |
|---|---|
/model |
查看当前使用的模型 |
/model local-qwen27b |
切换到本地 Qwen 模型 |
/model deepseek-chat |
切换到云端 DeepSeek 模型 |
/status |
查看网关运行状态 |
附录一:日常维护命令速查
| 操作 | 命令 |
|---|---|
| 查看所有服务状态 | pm2 status |
| 查看 OpenClaw 实时日志 | pm2 logs openclaw |
| 查看 LiteLLM 实时日志 | pm2 logs litellm |
| 重启 OpenClaw | pm2 restart openclaw |
| 重启 LiteLLM | pm2 restart litellm |
| 临时停止所有服务 | pm2 stop all |
| 取消开机自启 | pm2 unstartup |
附录二:关闭与卸载步骤
临时停止服务
pm2 stop openclaw
pm2 stop litellm
取消开机自启
pm2 unstartup
# 将终端输出的 sudo 命令复制并运行
pm2 save --force
完全卸载
按照以下顺序执行,确保彻底清理:
# 1. 停止并删除 PM2 进程
pm2 delete openclaw
pm2 delete litellm
pm2 save --force
# 2. 运行官方卸载命令
openclaw gateway uninstall
openclaw uninstall --all --yes --non-interactive
# 3. 卸载 npm 包和 Python 包
npm uninstall -g openclaw
pip3 uninstall litellm
# 4. 清除配置文件与隐私数据
rm -rf ~/.openclaw
rm -rf ~/.clawdbot
rm -rf ~/.moltbot
# 5. 清除 macOS 系统启动项残留
rm -f ~/Library/LaunchAgents/ai.openclaw.gateway.plist
rm -f ~/Library/LaunchAgents/com.openclaw.gateway.plist
清除系统权限:进入系统设置 -> 隐私与安全性,在"辅助功能"、"屏幕录制"、"完全磁盘访问权限"列表中,选中并删除"终端"的授权。
附录三:常见报错速查表
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
requires Node.js 20+ |
nvm 锁定了旧版本 | 运行 nvm install 20 && nvm alias default 20 |
Unrecognized key: "provider" |
配置文件格式为旧版 | 将根节点改为 models.providers |
models.providers.litellm.models: expected array |
缺少 models 数组 |
在 litellm 配置块中添加 models: [...] |
Gateway start blocked: set gateway.mode=local |
未指定网关模式 | 运行 openclaw config set gateway.mode local |
Cannot destructure property 'tenant_access_token' |
App ID/Secret 错误或应用未发布 | 核对密钥,并在飞书后台重新发布版本 |
Request failed with status code 400 (ws) |
飞书未开启长连接订阅 | 在飞书开发者后台将订阅方式改为"长连接"并重新发布 |
Restarted LaunchAgent 无限循环 |
PM2 与 macOS LaunchAgent 冲突 | 运行 launchctl unload ~/Library/LaunchAgents/ai.openclaw.gateway.plist |
PM2 [ERROR] File not found |
PM2 将命令字符串当文件名 | 用绝对路径启动脚本代替内联命令 |
pm2 status: errored |
Python 环境路径不匹配 | 用 which litellm 找绝对路径,并写入启动脚本 |
/bin/bash: gateway: No such file or directory |
PM2 无法识别子命令参数 | 将 openclaw gateway run 写入 Shell 脚本,由 PM2 执行脚本 |
| 飞书点开机器人没有输入框 | 事件未订阅或版本未发布 | 添加 im.message.receive_v1 事件后重新发布版本 |
| 发消息机器人不回复 | dmPolicy 限制或权限未开通 |
配置文件中设置 "dmPolicy": "open" 和 "allowFrom": ["*"] |