乐于分享
好东西不私藏

我终于解决了微信同时用 OpenClaw 和 Claude Code 的问题

我终于解决了微信同时用 OpenClaw 和 Claude Code 的问题


上篇文章用 Agent SDK 搞定了微信接管 Claude Code,手机上随时对话,很爽。

但我时不时也要用 OpenClaw——主要是 Cron定时任务。Claude Code 自己的 loop 太弱了,Cloud 版虽然有成熟的计划任务,国内又封得厉害。OpenClaw 的 Cron 刚好补上这块。

问题来了:微信一个账号只能绑定一个 bot token。你配了 wechat-claude-code,再配 openclaw-weixin,后者的 token 直接把前者挤掉。想用 OpenClaw 的定时任务和 WebUI,就得把 Claude Code 断了。来回切,烦。

本着 AI 时代有点子就能解决的心态,花了小半天搞定:一个微信入口,/switch 一句话切来切去,两个 AI 随时用。

为什么不让 OpenClaw 当主入口

先说一个关键决策:谁当默认路由?

我日常 Claude Code 占 90%,OpenClaw 偶尔用。如果 OpenClaw 做主入口,每条 Claude Code 消息都多一次中转——wcc → bridge → OpenClaw → bridge → wcc,白白加了延迟。而且 OpenClaw + ACPX 之类的方案太重了,我不需要它接管整个微信消息循环,只需要在需要的时候把消息转过去就行。

反过来,Claude Code 做默认,大部分消息零延迟直达,只有切到 OpenClaw 时才走 bridge。轻量、直接。

所以答案很简单:谁用得多,谁做默认。

这也是为什么我基于 wechat-claude-code 扩展而不是基于 openclaw-weixin 修改——把微信消息监听放在用量大的那一边,减少不必要的转发。

怎么做到的:三件事

1. openclaw-bridge 插件——让 OpenClaw 能接 HTTP 消息

参考了原先 openclaw-weixin 的 Channel 实现,写了一个 OpenClaw channel 插件 openclaw-bridge,监听 localhost:3847。

工作原理:wcc 把微信消息 POST 到 bridge,bridge 注入 OpenClaw 的消息处理管道,AI 处理完,回复通过 HTTP 响应原路返回给 wcc,wcc 再发回微信。

整个过程是同步的:发消息 → 等 OpenClaw 处理 → 收到回复 → 发回微信。用户体验跟直接用 Claude Code 一样。

2. /switch 路由——微信里一句话切换

在 wcc 的命令系统里加了 /switch

  • • /switch openclaw → 切到 OpenClaw(自动检查 bridge 是否活着)
  • • /switch claude → 切回默认(Claude Code)
  • • /whoami → 看当前路由和目标端可用性

切换是即时生效的。切到 OpenClaw 后,后续所有微信消息走 bridge 转发;切回 Claude Code,消息直达 Agent SDK 处理。

3. @wechat 跨渠道推送——WebUI/Cron 的回复也能到微信

这是意外收获。

在 OpenClaw WebUI 或 Cron 定时任务里发消息,回复停在网页上,手机看不到。加了个 @wechat 标注:消息里带 @wechat,AI 回复自动推到微信。

比如在 WebUI 里输入”帮我总结今天的日程 @wechat”,AI 的回复会同时出现在网页和微信上。

但这里有个容易搞混的地方:

来源
处理方式
微信 → bridge → OpenClaw
同步 HTTP 回复,不额外推送
WebUI / 其他渠道 + @wechat
hook 标记 → 推送到微信
Cron + @wechat
agent_end 检测 → 推送到微信

为什么微信来源不推送?因为已经在 HTTP 响应里拿到回复了,再推一次就是同一条消息发两遍。

踩的坑

OpenClaw Hook 机制:翻了半天源码才找到

我需要拦截 AI 的回复——不管是 Cron 触发的还是 WebUI 触发的——然后推到微信。但 OpenClaw 的文档里没有直接告诉你”怎么 hook AI 的输出消息”。翻了半天 plugin-sdk 源码,才发现两个关键 hook:message_received 可以拦截外部输入(如 WebUI),agent_end 可以拿到完整的消息数组(包括 AI 的回复)。

本以为用 message_received 就够了,结果 Cron 的 systemEvent 根本不触发这个 hook。最终方案:在 agent_end 里检查 messages 数组的输入消息是否含 @wechat,WebUI 和 Cron 两种场景统一处理。理解了这套 hook 机制,后面的实现就顺了。

userId 大小写

OpenClaw 把 userId 统一转小写,微信原始 ID 有大写字母。推送到微信时 ID 对不上,消息发不出去。最后的方案是从 wcc 的原始消息追踪正确的大小写,bridge 里单独保存一份。

联系人重启丢失

push-server 需要知道推给谁(userId + contextToken)。原来放内存,进程重启就没了。加了磁盘持久化——contact.json,每次收到微信消息自动保存,重启后自动加载。

安装

一个 Makefile 搞定:

git clone https://github.com/edwin19861218/claude-openclaw-wechatcd claude-openclaw-wechatmake install    # 构建 + 安装 bridge 插件 + 复制 wcc 到 skills

首次需要扫码绑定微信:

cd ~/.claude/skills/wechat-claude-code && npm run setup

启动方式:

cd ~/.claude/skills/wechat-claude-code && npm run daemon -- start   # wcc 独立进程openclaw gateway start                                              # bridge 随 gateway 自动启动

wcc 用 launchd(macOS)/ systemd(Linux)守护,开机自启、崩溃自动重启。bridge 作为 OpenClaw 插件,gateway 启动时自动加载,不用单独管。

前置条件:Node.js >= 18、macOS 或 Linux、个人微信账号(需扫码绑定)、Claude Code(含 Agent SDK)、OpenClaw CLI。详细配置参考项目 README。

局限性

作为懒人,够用即可。这里还是澄清边界,不做过度设计:

  • • 单用户模式:当前设计假设一个人用。多用户同时用,@wechat 推送可能发错人
  • • 端口无认证:3847/3848 仅监听 localhost,但没用 token 认证,同机别的进程理论上可调用
  • • 推送无重试:push-server 不可用时推送直接丢弃,没有重试队列
  • • 国内模型:用智谱 GLM 等国内模型需要手动导出环境变量,参考上篇文章

写在最后

一个微信账号,两个 AI,/switch 一键切换。@wechat 打通所有渠道到微信。

代码基于 wechat-claude-code 扩展,开源在 GitHub,https://github.com/edwin19861218/claude-openclaw-wechat。