前言
跟风折腾了一阵 OpenClaw,把过程中遇到的问题和解决办法整理成了这篇记录。环境是 Mac Mini (Apple Silicon),后端接了 千问、智谱、Anthropic、OpenAI 等主流模型,通过飞书 Bot 和 Web 控制台两个渠道对外服务。安装过程 OpenClaw 作者已经提供了一键脚本,这里不赘述,主要聊聊配置过程中那些文档里没写、搜也搜不到的坑。
基础环境
OpenClaw 通过 npm 全局安装,配置和插件统一放在 ~/.openclaw/ 下。Gateway 进程用 macOS 原生的 LaunchAgent 做守护,OpenClaw daemon install 会自动在 ~/Library/LaunchAgents/ 下生成 plist。
插件
OpenClaw 内置了多个插件,按需开启就好:
内置 Hooks
OpenClaw 有一套 Hook 机制,可以在特定生命周期节点注入逻辑。建议三个都启用:
-
• boot-md:启动时加载 SOUL.md 等工作区文件作为系统上下文 -
• command-logger:记录所有指令执行日志 -
• session-memory:会话级记忆,配合 Memory Core 插件实现跨会话信息持久化
还有一个 send-changelog 没在向导中出现——这东西会在版本更新后自动给用户发更新日志,对内部使用场景来说是噪音。
局域网访问
Mac Mini 跑 Gateway,日常在 Windows 上用浏览器访问 webchat 控制台。听起来很简单,实际踩了两个坑。
坑一:bind 配置的迷惑行为
Gateway 默认只听 127.0.0.1。配置里有个 gateway.bind 字段,文档暗示可以设成 "lan" 让局域网设备访问。实际试下来,设了 "lan" 配置文件倒是存进去了,但 Gateway 行为没有变化——依然只监听 localhost。"all" 也一样。试了一下直接填 "0.0.0.0" 倒是能开全通策略,但是新的问题又出现了。
坑二:裸 HTTP 访问 webchat 白屏
用 http://192.168.x.x:18789 访问 webchat 也不行,能看到页面侧边栏框架,但是白屏+红字报错穿插着来,webchat 前端用到了需要 Secure Context 的浏览器 API(crypto.subtle、Service Worker 等),而浏览器的 Secure Context 策略只认两种来源:HTTPS 和 localhost。裸 IP + HTTP 不在白名单里,前端直接起不来。
自签证书配起来麻烦,而且每台客户端都要信任,不值得。
最终方案:SSH 端口转发
SSH 隧道一箭双雕:既解决了跨机器访问,又让浏览器看到的是 localhost,天然满足 Secure Context。
1 2
# Windows 端执行
ssh -f -N -L 18789:127.0.0.1:18789 user@192.168.x.x
-f 放后台,-N 不开 shell,纯转发。之后浏览器访问 http://localhost:18789 就行。
原理是 SSH 在 Windows 本地监听 18789 端口,所有流量通过加密隧道转发到 Mac 的 127.0.0.1:18789。对浏览器来说请求目标就是 localhost,Secure Context 校验通过。
免密 + 一键启动
每次输密码太烦,配个 Ed25519 密钥对:
1 2
ssh-keygen -t ed25519
type %USERPROFILE%.sshid_ed25519.pub | ssh user@192.168.x.x "cat >> ~/.ssh/authorized_keys"
然后做个 bat 扔桌面:
1 2 3
@echo off
ssh -f -N -L 18789:127.0.0.1:18789 user@192.168.x.x
start http://localhost:18789
实际使用中有个小问题:如果上次的 SSH 隧道没断干净,端口被占用会报错。可以在脚本开头加一行 taskkill /F /IM ssh.exe 2>nul 暴力清理,或者用 ssh -O check 检测已有连接再决定是否新建。
模型配置与 Fallback 链
五个 Provider 的分工
配置里实际接入了五个 Provider。其中 DashScope 和 Qwen 虽然背后都是通义千问,但面向国内和国际是两套完全不同的端点和认证体系,不能混用:
其中 qwen 是运行 OpenClaw configure 配置向导时自动生成的——向导默认走国际版 OAuth 流程,通过 Qwen OAuth 插件完成认证。阿里云 Coding Plan 套餐(sk-sp- 开头的 key)需要专用端点,不能用国际端点和普通 DashScope API(智谱也是一样)。
端点地址:https://coding.dashscope.aliyuncs.com/v1/
配置命令:
1
OpenClaw config set models.providers.dashscope '{"baseUrl":"<https://coding.dashscope.aliyuncs.com/v1/","api":"openai-completions","models":[{"id":"qwen3-coder-plus","name":"Qwen3-Coder-Plus"},{"id":"qwen3-max-2026-01-23","name":"qwen3-max-2026-01-23>"}]}'
Auth 配置: 在 ~/.openclaw/agents/main/agent/auth-profiles.json 添加 dashscope:default profile
Coding Plan 套餐的 API Key 以 sk-sp- 开头,和普通 Key 不是同一套鉴权体系。最直观的区别是端点不同:
-
• 普通 Key : https://dashscope.aliyuncs.com/compatible-mode/v1/ -
• 国际版 OAuth:https://portal.qwen.ai/v1/ -
• Coding Plan :`https://coding.dashscope.aliyuncs.com/v1/ -
• Coding Plan支持模型: qwen3-coder-plus, qwen3-max-2026-01-23`
所以务必用正确的baseurl和完整的的模型 ID,不然换多少key尝试也一样 401 。
还有就是,OpenClaw 对 qwen/qwen-max 这种写法有内部路由逻辑,如果之前先一步配置了国际版,再想添加国内版,会把请求打到 qwen-portal,即便配通了,/model也是没办法正常切换模型的。
Fallback 编排
思路是把便宜量大的国产模型放前面扛日常流量,Claude 放最后兜底。日常聊天基本走不到 Fallback 4,除非前面全挂了。
不在Fallback中的模型也可以webchat/tui中手动切换,OpenClaw 支持给模型配别名:
1 2 3 4
"zai/glm-4.7": { "alias": "GLM" },
"anthropic/claude-opus-4-5": { "alias": "opus" },
"qwen/qwen-plus": { "alias": "qwen" },
"openai/gpt-5.2": { "alias": "gpt" }
比如临时想用 Claude 自查故障,直接 /model opus 切过去就行,不用记完整的 provider/model-id。
上下文管理
大模型对话的上下文窗口有限,长对话必须做裁剪。OpenClaw 提供了几种策略,当前用的是 cache-ttl 模式,TTL 设为 2 小时——超过 2 小时没被引用的上下文会被清理掉。配合 compaction: safeguard 模式,在裁剪前会做安全检查避免丢掉关键信息。
并发方面,主 Agent 限制 8 个并发请求,子 Agent(搜索、工具调用等)放宽到 16 个。对个人使用绰绰有余。
视觉模型
GLM-4.7 是纯文本模型,图片理解得用 GLM-4V 系列。但 OpenClaw 内部的模型前缀白名单(live-model-filter.js)默认只放行 glm-4.7 开头的模型 ID,glm-4v/glm-4.6v 直接被拦,返回 “Model not allowed”。
改法是往白名单数组里加一项:
1 2
sed -i '' 's/const ZAI_PREFIXES = ["glm-4.7"]/const ZAI_PREFIXES = ["glm-4.7","glm-4.6v"]/'
/opt/homebrew/lib/node_modules/openclaw/dist/agents/live-model-filter.js
这是 dist 下的编译产物,升级就被覆盖,想一劳永逸的话可以参考下文提到的补丁脚本。
Qwen3 Thinking 泄露:
几乎所有主流大模型都遵循 “不向用户暴露真实内部推理链” 的安全规则。像 DeepSeek 这样的「伪 CoT / 显式推理层」,本质上也是 解释性推理文本,而不是模型内部真实的推理轨迹。如果你在提示词写了“展示推理步骤”“详细分析”“结构化思考”之类(多见于第三方套壳应用,为了看起来“更高级、更拟人”),模型会模拟一个推理过程给你,但那只是经过训练的格式化输出,是“展示给用户看的推理解释”,不是内部真实权重更新。
qwen3 默认开启 Thinking 模式——类似 o1 的 Chain-of-Thought,模型先内部推理再给最终答案。问题是 OpenClaw 没有正确分离 thinking content 和 final answer,用户看到的回复里混进了推理过程,并且没有<think>...</think> 标签包裹,想过滤都难:
发送消息:
说句话
预期效果:
好的。
实际上:
用户让我说句话,这是明确的指令。我应该简单回应。好的。
排查过程
第一反应以为用 OpenClaw 自带的 /think off 就能关闭 Qwen3 的思考文本,但实践证明这完全无效。原因很简单:/think off 控制的是 OpenClaw 自身的推理增强逻辑,和 Qwen3 的原生 Thinking 模式毫无关系。
接着开始病急乱投医,尝试在 SOUL.md 里加入大量 Output Rules,明确禁止输出自我分析和内心独白,甚至列了具体的禁止模式(”我应该…”、”这是在测试…”之类的)。这些提示规则对普通格式确实有效,但对 Qwen3 的 Thinking 完全压不住——因为这不是提示控制范围的问题,而是模型在架构层启用的能力,根本不受 Prompt 影响。就像你无法仅通过提示让 o1 “停止思考”一样。
阅读了阿里千问的开发文档,hybrid-thinking 模式就是靠 enable_thinking 控制:true 会先思考再回答,false 直接回答。正确的做法是在 API 请求里传 enable_thinking: false。千问的 OpenAI 兼容接口支持这个参数。但在 OpenClaw 配置里加 extraParams 传这个字段,发现请求里死活没有。
在网上搜索了多个技术社区的openclaw相关话题,也有不少人提过:想把“厂商特有参数”原样塞进请求体,遗憾的是只有提问没看到解决方法,但是获得了一个重要线索,OpenClaw 在发 API 请求前有一个参数白名单过滤机制,只允许预定义的参数通过(temperature、top_p 这些),其他一律丢弃。enable_thinking 不在白名单里,被静默过滤了。
修复:打通参数透传的两个阻塞点
参数从用户配置到最终 API 请求经过两层处理,都要改。
第一层:extra-params.js(OpenClaw 的参数处理层)
位置:.../dist/agents/pi-embedded-runner/extra-params.js
在白名单校验之前插入透传逻辑:
1 2 3
if (typeof extraParams.enable_thinking === "boolean") {
streamParams.enable_thinking = extraParams.enable_thinking;
}
这里用 typeof === "boolean" 而不是简单的 truthy check 是有讲究的——enable_thinking: false 在 if (extraParams.enable_thinking) 里会被判为 falsy 直接跳过,恰恰是你最需要传的那个值传不出去。
第二层:openai-completions.js(底层 HTTP 请求构造层)
位置:.../node_modules/@mariozechner/pi-ai/dist/providers/openai-completions.js
这个文件是 pi-ai 库的一部分,负责组装最终发给 API 的 HTTP Body。在 buildParams 函数的 return params 前加:
1 2 3
if (options?.enable_thinking !== undefined) {
params.enable_thinking = options.enable_thinking;
}
注意:文件里有两处 return params,分别在 buildParams(约 385 行)和 convertMessages(约 639 行)函数中。只能改前者。convertMessages 的函数签名里没有 options 参数,在那里访问 options?.enable_thinking 虽然不会报错(可选链返回 undefined),但改错了等于白改——参数还是传不到 HTTP Body 里。
配置关闭 Thinking:
1 2 3 4
OpenClaw config set agents.defaults.models.dashscope/qwen3-max-2026-01-23
'{"params":{"enable_thinking":false}}'
OpenClaw config set agents.defaults.models.dashscope/qwen3-coder-plus
'{"params":{"enable_thinking":false}}'
补丁脚本
以上源码修改都在 node_modules 或 dist 目录下,升级就没了。维护了一个 ~/.openclaw/patches/apply-patches.sh 做自动化修补。
看一下脚本的设计思路:
1 2 3 4 5 6 7 8 9 10
# 幂等检查——grep 先看有没有打过
if ! grep -q 'enable_thinking' "$EXTRA_PARAMS" 2>/dev/null; then
sed -i '' '/if (Object.keys(streamParams).length > 0)/i
if (typeof extraParams.enable_thinking === "boolean") {
streamParams.enable_thinking = extraParams.enable_thinking;
}' "$EXTRA_PARAMS"
echo "[patch] enable_thinking added to extra-params.js"
else
echo "[patch] extra-params.js already patched"
fi
第二个补丁点比较复杂,用 sed 不好精确定位 buildParams 里的那个 return params(文件里有多处),所以用了 Python 做字符串替换,通过匹配上下文函数名(maybeAddOpenRouterAnthropicCacheControl)来锚定位置。
SOUL.md
SOUL.md 是 OpenClaw 的系统 Prompt 文件,相当于 AI 助手的”灵魂”。Gateway 启动时通过 boot-md Hook 加载到每个会话的上下文里。
人格基调
把 prompt 包装成”灵魂””人格””觉醒”,本质上还是在写 prompt。模型不会因为你告诉它”你正在成为某个人”就真的产生自我意识,它只是在统计概率上调整输出风格。问题是很多人把这当成在”培养 AI”,给它写使命宣言、人生哲学,觉得写得越感人效果越好。实际上模型只看到一串 token,”真诚”和”回复不超过 3 句话”相比,后者效果好十倍。
AI 工具的价值取决于你的使用方式,而不是怎么”感动”它。“智械危机”真要来了也轮不到咱们操心,与其写一堆”你是一个有帮助的AI助手”之类的废话,不如直接定义行为边界:
1 2 3 4 5
提供真正的帮助,而不是表演式的帮助。不要说“好问题!”或“我很乐意帮你!”之类的套话,直接解决问题。
保持有自己的观点。你可以不同意、可以表达偏好,也可以指出某些内容好笑、无聊或不重要。
在提问前先自行查找。优先自己动手:阅读文件、检查上下文、搜索相关信息。如果仍然无法解决,再提出问题。
核心思路是:少说多做,有事说事,别端着。”Great question!” 这种 AI 味的客套话在 IM 对话里特别违和,直接在 Prompt 里禁掉效果很好。
行为边界
给 AI 工具权限之后,哪些事情可以自主做、哪些必须先问,这条线要画清楚:
1 2 3
你的使用者把他们的内容和环境交给了你,别让他们后悔。
对外部行为要谨慎处理(例如邮件、发帖、任何公开动作),无论何时都要取得许可。
对内部行为可以大胆一些(阅读、学习),但凡涉及整理或修改内容,即使获得了用户许可,也必须事先做好备份。
翻译过来就是:读文件、搜索信息这些内部操作随便来;发邮件、发消息、发帖子这些对外操作必须谨慎,拿不准就先问。
语言偏好
1
永远说中文。除非用户明确要求使用其他语言。
简单粗暴但有效。不写这条的话,模型会根据上下文语言自动切换,在中英混排的技术讨论里经常突然蹦出英文回复。
工具调用规则
大模型调用工具时的参数传递经常有低级问题。与其每次排查 Bug,不如把踩过的坑直接写进 SOUL.md,比如:
1 2
## Tool Usage
发邮件时 bccRecipients 和 ccRecipients 传空数组 [] 而不是 null。
这条规则源于 mog 邮件工具的一个 Bug:接口定义了 bccRecipients: string[] 类型,但模型在判断”不需要密送”时倾向于传 null 而不是空数组,类型校验直接报错。在 SOUL.md 里写死规则后再没出过这个问题。
飞书集成
连接方式
飞书开放平台支持 WebSocket 长连接模式,不需要公网回调地址,对内网部署很友好。相比 Webhook 模式,WebSocket 省去了域名、证书、端口映射,已经有大佬开发了成熟的插件。
1 2 3
OpenClaw plugins install @m1heng-clawd/feishu
OpenClaw config set channels.feishu
'{"enabled":true,"appId":"your_app_id","appSecret":"your_secret"}' --json
消息触发机制
这里有两层开关,容易混淆:
-
1. 飞书后台的「接收群组中所有消息」权限——控制消息能不能到达你的应用。不开的话,只有 @机器人 的消息才会被推送过来。 -
2. OpenClaw 配置的 requireMention字段——控制收到消息后要不要处理。设为 true 则只响应 @消息,设为 false 则所有收到的消息都处理。
两层都打开才是真正的”全量消息”模式。只开飞书权限不关 requireMention,消息虽然到了但 OpenClaw 会忽略非 @消息;反过来只关 requireMention 不开飞书权限,消息根本到不了 OpenClaw。
全量模式要谨慎。开了之后机器人对群内每条消息都会尝试响应,包括表情包、”收到”、”好的”之类的,体验很差且浪费 Token。如果确实需要让机器人”旁听”做总结或知识沉淀,建议在 SOUL.md 里加明确的触发条件过滤。
另外 ackReactionScope: "group-mentions" 控制”已收到” Reaction 的发送范围,当前设置是只在群聊被 @ 时才发 Reaction 反馈,避免在全量模式下每条消息都打一个勾。
Markdown 渲染问题
大模型的输出天然带 Markdown 格式,但飞书的纯文本消息不渲染 Markdown。用户看到的是 **粗体** 和 - 列表项 这种原始标记。
解决这个问题的过程走了点弯路。一开始的思路是”既然飞书不渲染 Markdown,那就让模型别输出 Markdown”,于是配了 capabilities.markdown: false 告诉 OpenClaw 飞书不支持 Markdown,期望模型会自动调整输出格式。实际效果一般——模型偶尔还是会蹦出 Markdown 语法。
后来发现 OpenClaw 飞书插件支持 renderMode: "card" 配置,把消息以飞书卡片形式发送,卡片内容原生支持 Markdown 渲染。这才是正解——与其约束模型不用 Markdown,不如让渠道支持 Markdown。
1
OpenClaw config set channels.feishu.renderMode card
但这个配置只对群聊消息生效。私聊走 outbound.ts 的独立发送逻辑,这条路径没读 renderMode 配置。需要手动改 outbound.ts,在 sendText 方法里加 renderMode 判断,当值为 card 时调用 sendMarkdownCardFeishu 替代默认的纯文本发送。
零碎踩坑
网络配置
OpenClaw 集成了 Brave Search,但 Gateway 进程请求 api.search.brave.com 超时。本机浏览器正常,说明magic在工作。原因是 macOS 上 magic 的 TUN 模式虽然能接管大部分流量,但 LaunchAgent 启动的进程继承的是系统环境变量,不一定走 TUN 虚拟网卡。TUN 通过修改路由表来劫持流量,但如果进程在 TUN 启动之前就建立了连接,或者进程绑定了特定网卡,就可能绕过。这个问题容易忽略,顺便提一下。
最稳的办法是给 LaunchAgent 显式设置代理环境变量。编辑 plist 文件,加:
1 2 3 4 5 6 7
<key>EnvironmentVariables</key>
<dict>
<key>http_proxy</key>
<string>http://127.0.0.1:7890</string>
<key>https_proxy</key>
<string>http://127.0.0.1:7890</string>
</dict>
这样 Gateway 进程启动时就能拿到代理地址。Node.js 的 HTTP 客户端库(undici、node-fetch 等)大多会读这两个环境变量。
和风天气 API
还是端点问题,免费版端点是 devapi.qweather.com,不是文档里到处出现的 dev.qweather.com,也不是付费版的 api.qweather.com。搞错了返回 403,没有任何额外提示。
终端乱码
SSH 到 Mac 后方向键显示 [[D^[ 之类的转义序列,一般是 .zshrc 里 bindkey 配置覆盖了默认的 emacs 键位映射。粘贴时出现乱码控制字符是 Bracketed Paste 模式的问题,printf 'e[?2004l' 临时关掉,永久解决在 .zshrc 里加 unset zle_bracketed_paste。
运行效果



常用命令
日常用到的 launchctl 命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
# ============ 初始化与配置 ============
# 首次安装引导
openclaw onboard
# 交互式配置(凭证、设备、模型)
openclaw configure
# 初始化配置文件和workspace
openclaw setup
# 读配置(点路径)
openclaw config get agents.defaults.extraSystemPrompt
# 写配置
openclaw config set agents.defaults.extraSystemPrompt "你的prompt内容"
# 写JSON格式配置
openclaw config set channels.feishu '{"enabled":true,"appId":"cli_xxx"}' --json
# 删配置
openclaw config unset agents.defaults.extraSystemPrompt
# ============ Gateway 控制 ============
# 启动Gateway(前台)
openclaw gateway --port 18789
# 重启(通过LaunchAgent)
openclaw gateway restart
# 健康检查
openclaw health
# 查看日志
openclaw logs
# 查看渠道状态和最近会话
openclaw status
# 安全审计
openclaw security audit
# 打开控制台Web UI
openclaw dashboard
# ============ LaunchAgent 守护进程 ============
# 安装守护进程(自动生成plist)
openclaw daemon install
# 注册并启动
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
# 卸载
launchctl bootout gui/$(id -u)/ai.openclaw.gateway
# 原地重启(最常用,`kickstart -k` 的 `-k` 是 kill existing instance,相当于先杀后拉)
launchctl kickstart -k gui/$(id -u)/ai.openclaw.gateway
# 看日志(直接读文件)
tail -f ~/.openclaw/logs/gateway.log
# 打补丁后重启
bash ~/.openclaw/patches/apply-patches.sh && launchctl kickstart -k gui/$(id -u)/ai.openclaw.gateway
# ============ 模型管理 ============
# 查看当前模型配置(默认、fallback、别名、认证)
openclaw models
# 设置默认模型
openclaw models set zai/glm-4.7
# 管理fallback链
openclaw models fallbacks
# 管理别名
openclaw models aliases
# 添加模型认证(交互式,支持API key/OAuth/setup-token)
openclaw models auth add
# 登录GitHub Copilot(设备码流程)
openclaw models auth login-github-copilot
# ============ 插件与Skills ============
# 列出已加载插件
openclaw plugins list
# 安装插件
openclaw plugins install openai
# 启用/禁用插件
openclaw plugins enable <id>
openclaw plugins disable <id>
# 插件健康检查
openclaw plugins doctor
# 列出已加载skills
openclaw skills list
# 查看skill详情
openclaw skills info notion
# ============ 消息与Agent ============
# 发消息到指定渠道
openclaw message send --channel feishu --target <chat_id> --message "内容"
# 直接调用agent并投递回复
openclaw agent --to <target> --message "任务描述" --deliver
# 列出历史会话
openclaw sessions
# ============ 记忆系统 ============
# 查看记忆索引状态
openclaw memory status
# 重建索引
openclaw memory index
# 搜索记忆
openclaw memory search "关键词"
# ============ 渠道管理 ============
# 渠道总览
openclaw channels
# 登录WhatsApp Web(显示二维码)
openclaw channels login --verbose
# ============ 其他 ============
# 管理内置浏览器
openclaw browser
# 定时任务
openclaw cron
# CLI更新
openclaw update
# 看配置文件
cat ~/.openclaw/openclaw.json
最后:排查问题时 console.log 比 log.debug 好使,前者直接输出到 gateway.log,后者受日志级别控制可能被吞。建议先 curl 直接打 API 确认参数和响应格式,再回头对比 OpenClaw 的行为,能快速判断问题出在参数透传还是模型本身。
本篇文章来源于微信公众号: 猎户攻防实验室