一、问题的提出
OpenClaw 支持多种 IM 通道(微信、QQ、Telegram),它们共享同一个 AI 引擎,但通信协议和认证方式完全不同。一个自然的疑问是:
微信 Bot 是怎么和微信服务器通信的?登录票据存在哪里?消息是怎么流转的?和 QQBot 有什么区别?
本文从源码出发,完整拆解微信 Bot 的通信链路,并与 QQBot 进行对比。
二、整体架构
2.1 微信 Bot 通信架构
消息流转步骤:
- 用户发消息 — 微信 App 发送消息到微信服务器
- 长轮询拉取 — 微信插件通过 HTTP 长轮询 (getUpdates) 从服务器拉取新消息
- 消息解析 — 插件解析消息格式,提取发送者、内容、附件等
- 会话路由 — Gateway 根据「通道 + 用户」匹配或创建对应的 Session
- AI 推理 — Session 加载 workspace 上下文,调用 GLM-5 生成回复
- 格式转换 — 插件将 Markdown 回复转为纯文本(微信不支持 Markdown)
- 发送回复 — 通过 HTTP POST (sendMessage) 将回复发回微信服务器
- 用户收到 — 微信 App 收到 Bot 回复
flowchart TB
subgraph User["① 用户侧"]
WXApp["📱 微信 App"]
end
subgraph WeChat["② 微信服务端"]
ILINK["ilinkai.weixin.qq.com"]
end
subgraph Local["③ 本地 OpenClaw"]
subgraph PluginLayer["插件层"]
WXPlugin["微信插件
openclaw-weixin"]
end
subgraph CoreLayer["核心层"]
GW["Gateway
会话路由"]
Session["Session Manager
上下文管理"]
end
subgraph ModelLayer["模型层"]
Model["GLM-5
AI 推理"]
end
end
WXApp -- "❶ 用户发消息" --> ILINK
ILINK -- "❷ getUpdates 长轮询" --> WXPlugin
WXPlugin -- "❸ 消息解析" --> GW
GW -- "❹ 会话路由" --> Session
Session -- "❺ AI 推理" --> Model
Model -- "❻ 生成回复" --> Session
Session -- "❼ 路由回复" --> GW
GW -- "❽ 交付消息" --> WXPlugin
WXPlugin -- "❾ 格式转换
Markdown → 纯文本" --> WXPlugin
WXPlugin -- "❿ sendMessage
HTTP POST" --> ILINK
ILINK -- "⓫ 投递回复" --> WXApp
style User fill:#228B22,color:#fff
style WeChat fill:#B22222,color:#fff
style PluginLayer fill:#E65100,color:#fff
style CoreLayer fill:#1565C0,color:#fff
style ModelLayer fill:#5E35B1,color:#fff
2.2 QQBot 通信架构
消息流转步骤:
- 用户发消息 — QQ App 发送消息到 QQ 服务器
- WebSocket 推送 — QQ 服务器通过 WebSocket 实时推送事件到插件
- 事件解析 — 插件解析 C2C_MESSAGE_CREATE 等事件,提取消息内容
- 会话路由 — Gateway 根据「通道 + 用户」匹配或创建对应的 Session
- AI 推理 — Session 加载 workspace 上下文,调用 GLM-5 生成回复
- 格式保持 — QQBot 支持原生 Markdown,无需格式转换
- 发送回复 — 通过 HTTP REST API 将 Markdown 回复发回 QQ 服务器
- 用户收到 — QQ App 收到 Bot 回复(支持流式显示)
flowchart TB
subgraph User["① 用户侧"]
QQApp["📱 QQ App"]
end
subgraph QQ["② QQ 服务端"]
QQAPI["bots.qq.com
Bot API"]
QQWS["WebSocket
Gateway"]
end
subgraph Local["③ 本地 OpenClaw"]
subgraph PluginLayer["插件层"]
QQPlugin["QQBot 插件
openclaw-qqbot"]
end
subgraph CoreLayer["核心层"]
GW["Gateway
会话路由"]
Session["Session Manager
上下文管理"]
end
subgraph ModelLayer["模型层"]
Model["GLM-5
AI 推理"]
end
end
QQApp -- "❶ 用户发消息" --> QQAPI
QQAPI -- "❷ WebSocket 推送
C2C_MESSAGE_CREATE" --> QQWS
QQWS -- "❸ 实时事件流" --> QQPlugin
QQPlugin -- "❹ 事件解析" --> GW
GW -- "❺ 会话路由" --> Session
Session -- "❻ AI 推理" --> Model
Model -- "❼ 生成回复" --> Session
Session -- "❽ 路由回复" --> GW
GW -- "❾ 交付消息" --> QQPlugin
QQPlugin -- "❿ POST
/v2/users/:openid/messages
(原生 Markdown)" --> QQAPI
QQAPI -- "⓫ 投递回复" --> QQApp
style User fill:#228B22,color:#fff
style QQ fill:#5E35B1,color:#fff
style PluginLayer fill:#E65100,color:#fff
style CoreLayer fill:#1565C0,color:#fff
style ModelLayer fill:#5E35B1,color:#fff
2.3 架构差异速览
| 维度 | 微信 Bot | QQBot |
|---|---|---|
| 消息接收 | HTTP 长轮询 (getUpdates) | WebSocket 实时推送 |
| 消息发送 | HTTP POST (sendMessage) | HTTP REST API |
| 认证方式 | 扫码授权 → Bot Token | appId + clientSecret → access_token |
| 票据类型 | 长期有效,手动刷新 | 有效期约 2 小时,自动刷新 |
| 协议基础 | iLink 私有协议 | QQ Bot 开放平台 API |
| 连接模式 | 无状态,每次请求独立 | WebSocket 持久连接 + HTTP 发送 |
三、登录与授权
3.1 微信 Bot 登录流程
微信 Bot 采用扫码授权模式,用户需要用微信扫描二维码完成 Bot 的身份绑定。
sequenceDiagram
participant CLI as OpenClaw CLI
participant Plugin as 微信插件
participant WX as ilinkai.weixin.qq.com
participant App as 微信 App
CLI->>Plugin: openclaw channels login
--channel openclaw-weixin
Plugin->>WX: GET ilink/bot/get_bot_qrcode?bot_type=3
WX-->>Plugin: {qrcode, qrcode_img_content}
Plugin-->>CLI: 显示二维码 + 扫码链接
Note over CLI,App: 二维码有效期 5 分钟
App->>WX: 扫码 → 确认授权
Plugin->>WX: GET ilink/bot/get_qrcode_status
(长轮询 35s)
WX-->>Plugin: {status: "scaned"}
Plugin->>WX: 继续轮询
WX-->>Plugin: {status: "confirmed",
bot_token, ilink_bot_id,
baseurl, ilink_user_id}
Plugin->>Plugin: 保存凭据到
~/.openclaw/openclaw-weixin/accounts/
Plugin-->>CLI: 登录成功 ✅
关键参数说明:
| 参数 | 说明 |
|---|---|
bot_type=3 | iLink Bot 类型标识 |
qrcode | 服务端生成的会话标识(非二维码图片本身) |
bot_token | 授权后获得的 API 调用凭证 |
ilink_bot_id | Bot 的唯一标识(如 863fc8fa1527-im-bot) |
ilink_user_id | 授权用户的微信 ID |
baseurl | API 基础 URL(可能因 IDC 调度而变化) |
3.2 QQBot 登录流程
QQBot 采用 appId + clientSecret 配置模式,无需扫码。
sequenceDiagram
participant Config as 配置文件
participant Plugin as QQBot 插件
participant API as bots.qq.com
Config->>Plugin: 读取 appId + clientSecret
Plugin->>API: POST /app/getAppAccessToken
{appId, clientSecret}
API-->>Plugin: {access_token, expires_in: 6022}
Plugin->>Plugin: 缓存 Token
启动后台刷新
Plugin->>API: GET /gateway
Authorization: Bearer {token}
API-->>Plugin: {url: wss://...}
Plugin->>API: WebSocket 连接
Identify + Intents
API-->>Plugin: Ready Event ✅
Note over Plugin,API: Token 约 2 小时过期
插件自动后台刷新
3.3 登录方式对比
| 对比项 | 微信 Bot | QQBot |
|---|---|---|
| 初始化方式 | 扫码授权 | 配置文件写入 appId + Secret |
| 用户交互 | 需要手机扫码 | 无需用户操作 |
| 凭据获取 | 服务端下发 bot_token | 客户端换取 access_token |
| 凭据有效期 | 长期有效(直到被撤销) | ~2 小时,需定期刷新 |
| 多账号支持 | 多次扫码,每个一个 token | 多组 appId + Secret |
3.4 跨通道协作:QQBot 渲染微信登录二维码
在 OpenClaw 2026.3.24 中,一个有趣的跨通道协作场景成为可能:通过 QQBot 完成微信 Bot 的扫码登录。
传统流程要求用户在终端执行 openclaw channels login --channel openclaw-weixin,然后查看终端中的 ASCII 二维码或复制链接到浏览器。但当用户不方便访问终端时(比如只有手机),这个流程就不太友好。
借助 OpenClaw 的跨通道能力,AI Agent 可以:
sequenceDiagram
participant User as 用户 (QQ)
participant QQ as QQBot
participant AI as OpenClaw Agent
participant CLI as 本地 CLI
participant WX as 微信服务器
User->>QQ: 帮我登录微信 Bot
QQ->>AI: 路由消息
AI->>CLI: exec: openclaw channels login
--channel openclaw-weixin
CLI->>WX: 请求二维码
WX-->>CLI: 返回二维码 + 扫码链接
CLI-->>AI: 输出扫码链接
AI->>AI: Python qrcode 库生成二维码图片
AI->>QQ: 发送二维码图片
QQ-->>User: 显示二维码图片 📱
Note over User,WX: 用户用微信扫码授权
User->>AI: 扫码完成
AI->>CLI: 检查登录结果
CLI-->>AI: 登录成功 ✅
AI->>QQ: 登录成功!
实现细节:
| 步骤 | 技术手段 |
|---|---|
| 1. 触发登录 | Agent 通过 exec 工具执行 openclaw channels login |
| 2. 获取链接 | 从 CLI 输出中提取 https://liteapp.weixin.qq.com/q/... 链接 |
| 3. 生成图片 | 使用 Python qrcode 库将链接转为 PNG 图片 |
| 4. 发送图片 | 通过 QQBot 的 <qqmedia> 标签发送本地图片 |
这个场景体现了 OpenClaw 的一个核心优势:Agent 具备本地执行能力。Agent 不只是一个聊天机器人,它可以执行命令、生成文件、跨通道传递内容。用户甚至不需要直接接触服务器,只需要在 QQ 上说一句话,Agent 就能完成整个登录流程。
⚠️ 注意:二维码有效期约 5 分钟。如果用户扫码不及时,Agent 需要重新启动登录流程获取新的二维码。
四、票据管理与安全
4.1 微信 Bot 票据存储
flowchart LR
subgraph Storage["本地存储"]
AcctJSON["accounts.json
账号 ID 列表"]
AcctDir["accounts/"]
Token["863fc8fa1527-im-bot.json
token + baseUrl + userId"]
CtxToken["...context-tokens.json
会话上下文 token"]
SyncBuf["...sync.json
消息同步游标"]
end
AcctJSON --> AcctDir
AcctDir --> Token
AcctDir --> CtxToken
AcctDir --> SyncBuf
style Storage fill:#1565C0,color:#fff
style Token fill:#B22222,color:#fff
style CtxToken fill:#E65100,color:#fff
style SyncBuf fill:#228B22,color:#fff
凭据文件内容(脱敏后):
| |
4.2 QQBot 票据存储
QQBot 的票据不落盘,完全在内存中管理:
flowchart LR
Config["openclaw.json
appId + clientSecret
(明文配置)"]
Memory["内存 Token 缓存
tokenCacheMap"]
API["QQ API"]
Config -->|"启动时读取"| Memory
Memory -->|"Token 过期"| API
API -->|"新 Token"| Memory
style Config fill:#B22222,color:#fff
style Memory fill:#1565C0,color:#fff
style API fill:#5E35B1,color:#fff
4.3 安全对比
| 安全维度 | 微信 Bot | QQBot |
|---|---|---|
| 凭据存储 | 本地文件 (600 权限) | 内存缓存(不落盘) |
| 敏感配置 | token 在独立目录 | appId + Secret 在主配置文件 |
| SecretRef 支持 | ❌ 不支持 | ❌ 不支持(clientSecret 明文) |
| 凭据轮换 | 需重新扫码 | 自动刷新 access_token |
| 泄露风险 | 文件读取 | 配置文件读取 + 内存 dump |
⚠️ 注意:两者的凭据都存在被本地进程读取的风险。微信 Bot 的 token 明文存储在磁盘上,QQBot 的 clientSecret 明文写在
openclaw.json中。如果对安全性有更高要求,建议使用文件权限控制和全盘加密。
五、消息收发流程
5.1 微信 Bot 消息收发
sequenceDiagram
participant User as 微信用户
participant WX as 微信服务器
participant Plugin as 微信插件
participant GW as Gateway
participant AI as GLM-5
Note over Plugin,WX: 1. 消息接收(长轮询)
loop 循环轮询
Plugin->>WX: POST getupdates
(长轮询,等待新消息)
WX-->>Plugin: {message_list: [...]}
end
Note over Plugin,AI: 2. 消息处理
Plugin->>GW: inbound message
from=userId@im.wechat
GW->>GW: 路由到对应 session
GW->>AI: 推理请求
(携带 workspace 上下文)
AI-->>GW: 生成回复
GW-->>Plugin: outbound message
to=userId@im.wechat
Note over Plugin,WX: 3. 消息发送
Plugin->>Plugin: Markdown → 纯文本转换
(微信不支持 Markdown)
Plugin->>WX: POST sendmessage
{to_user_id, item_list, context_token}
WX-->>Plugin: 发送确认
WX-->>User: 收到回复 ✅
关键细节:
- 长轮询机制:插件持续向
getupdates端点发送请求,微信服务器会 hold 住连接直到有新消息或超时 - 消息格式转换:微信不支持 Markdown,插件会将 AI 生成的 Markdown 回复转换为纯文本(去代码块、去图片语法、表格转空格分隔)
- context_token:微信使用上下文 token 来关联请求-响应对,确保消息的顺序性
- 消息同步游标:
sync-buf文件记录上次拉取位置,避免重复处理
5.2 QQBot 消息收发
sequenceDiagram
participant User as QQ 用户
participant QQ as QQ 服务器
participant Plugin as QQBot 插件
participant GW as Gateway
participant AI as GLM-5
Note over QQ,Plugin: 1. WebSocket 实时推送
User->>QQ: 发送消息
QQ-->>Plugin: WS: C2C_MESSAGE_CREATE
(主动推送,无需轮询)
Note over Plugin,AI: 2. 消息处理
Plugin->>GW: inbound message
GW->>GW: 路由到对应 session
GW->>AI: 推理请求
AI-->>GW: 生成回复
GW-->>Plugin: outbound message
Note over Plugin,QQ: 3. 消息发送(支持 Markdown)
Plugin->>QQ: POST /v2/users/:openid/messages
msg_type=2, markdown={content}
QQ-->>Plugin: 发送确认
QQ-->>User: 收到回复 ✅
Note over Plugin,QQ: 4. 流式输出(可选)
loop 流式分片
Plugin->>QQ: POST (partial markdown)
QQ-->>User: 逐步显示
end
5.3 消息收发对比
| 对比项 | 微信 Bot | QQBot |
|---|---|---|
| 消息接收 | HTTP 长轮询 (getUpdates) | WebSocket 实时推送 |
| 接收延迟 | 取决于轮询间隔(秒级) | 近实时(毫秒级) |
| 消息格式 | 纯文本(自动去 Markdown) | Markdown(支持富文本) |
| 流式输出 | ❌ 不支持 | ✅ 支持 partial streaming |
| 打字提示 | sendTyping API | sendC2CInputNotify |
| 主动推送 | ❌ 仅限回复用户消息 | ✅ 支持主动发送 |
六、会话管理
6.1 会话隔离模型
OpenClaw 对所有通道采用统一的会话隔离策略:
flowchart TB
subgraph Sessions["会话管理"]
QQSession["agent:main:qqbot:direct:{userId}"]
WXSession["agent:main:openclaw-weixin:direct:{userId}@im.wechat"]
TGSession["agent:main:telegram:direct:{userId}"]
end
subgraph Shared["共享资源"]
Soul["SOUL.md"]
User["USER.md"]
Memory["MEMORY.md"]
Tools["TOOLS.md"]
end
QQSession --> Shared
WXSession --> Shared
TGSession --> Shared
style Sessions fill:#1565C0,color:#fff
style Shared fill:#228B22,color:#fff
每个 通道 + 用户 组合对应一个独立的 session,但共享同一份 workspace 上下文(SOUL.md、MEMORY.md 等)。
6.2 会话存储结构
| |
JSONL 记录格式:
| type | 说明 |
|---|---|
session | 会话元数据(ID、工作目录、时间戳) |
message | 用户消息或 AI 回复(含完整内容) |
model_change | 模型切换记录 |
thinking_level_change | 推理级别变更 |
custom | 自定义事件(如模型快照) |
6.3 跨通道会话可见性
默认配置下,会话的可见性限制为当前会话树(tools.sessions.visibility=tree),即:
- QQBot 会话无法访问微信会话的历史
- 微信会话无法访问 Telegram 会话的历史
- 但都可以访问共享的 workspace 文件(MEMORY.md 等)
这意味着通过 MEMORY.md,不同通道之间可以间接共享记忆,但对话历史是隔离的。
七、插件生命周期
7.1 微信 Bot 启动流程
flowchart TB
Start["Gateway 启动"] --> LoadPlugin["加载 openclaw-weixin 插件"]
LoadPlugin --> Compat{"版本兼容检查
OpenClaw >= 2026.3.22?"}
Compat -->|"✅"| LoadAcct["读取 accounts.json
加载已保存的凭据"]
Compat -->|"❌"| Fail["拒绝加载"]
LoadAcct --> SetRuntime["设置 WeixinRuntime"]
SetRuntime --> StartPoll["启动 getUpdates 长轮询"]
StartPoll --> Ready["✅ 就绪,等待消息"]
style Start fill:#228B22,color:#fff
style Ready fill:#228B22,color:#fff
style Fail fill:#B22222,color:#fff
7.2 QQBot 启动流程
flowchart TB
Start["Gateway 启动"] --> LoadPlugin["加载 openclaw-qqbot 插件"]
LoadPlugin --> ReadConfig["读取 appId + clientSecret"]
ReadConfig --> GetToken["获取 access_token"]
GetToken --> GetGateway["获取 WebSocket URL"]
GetGateway --> WSConnect["建立 WebSocket 连接"]
WSConnect --> Identify["发送 Identify + Intents"]
Identify --> Ready["✅ Ready Event,等待消息"]
GetToken -->|"失败"| Retry["后台重试"]
style Start fill:#228B22,color:#fff
style Ready fill:#228B22,color:#fff
style Retry fill:#E65100,color:#fff
八、完整数据流总结
flowchart LR
subgraph Input["消息输入"]
WXIn["微信用户消息"]
QQIn["QQ 用户消息"]
end
subgraph Protocol["协议层"]
WXHTTP["HTTP 长轮询
getUpdates"]
QQWS["WebSocket 推送
C2C_MESSAGE_CREATE"]
end
subgraph Plugin["插件层"]
WXP["微信插件
消息解析 + 格式转换"]
QQP["QQBot 插件
消息解析 + @处理"]
end
subgraph Core["OpenClaw 核心"]
Router["会话路由器"]
Session["Session
(上下文管理)"]
LLM["LLM 推理
(GLM-5)"]
end
subgraph Output["消息输出"]
WXOut["HTTP POST
sendMessage"]
QQOut["HTTP REST
/v2/users/:id/messages"]
end
WXIn --> WXHTTP --> WXP
QQIn --> QQWS --> QQP
WXP --> Router
QQP --> Router
Router --> Session --> LLM
LLM --> Session --> Router
Router --> WXOut
Router --> QQOut
style Input fill:#228B22,color:#fff
style Protocol fill:#E65100,color:#fff
style Plugin fill:#5E35B1,color:#fff
style Core fill:#1565C0,color:#fff
style Output fill:#B22222,color:#fff
九、关键发现与思考
9.1 协议选择的权衡
| HTTP 长轮询(微信) | WebSocket(QQ) | |
|---|---|---|
| 优点 | 实现简单,无需维护连接状态 | 实时性好,服务端主动推送 |
| 缺点 | 延迟较高,资源浪费 | 连接管理复杂,需处理重连 |
| 适用 | 消息量小的场景 | 消息频繁、需实时响应 |
微信选择长轮询可能是因为 iLink 协议的设计历史——微信的 Bot 系统基于「微信读书」的 iLink 基础设施,而非专门为 Bot 场景设计。
9.2 凭据安全的矛盾
两个通道都存在凭据明文存储的问题:
- 微信:bot_token 明文存储在
~/.openclaw/openclaw-weixin/accounts/目录 - QQ:clientSecret 明文写在
openclaw.json主配置文件
OpenClaw 的 SecretRef 机制可以保护 API Key 等凭据(通过 Vault 引用),但 IM 通道的凭据目前不支持 SecretRef——这是插件实现层面的限制,而非框架限制。
9.3 会话隔离与记忆共享
OpenClaw 的会话设计体现了一个有趣的平衡:
- 对话隔离:不同通道的对话历史互相不可见(
visibility=tree) - 记忆共享:所有通道共享 MEMORY.md,可以间接传递上下文
这意味着在 QQ 上讨论的内容,如果写入了 MEMORY.md,微信 Bot 也能"记得"。但这种共享是显式的——需要 AI 主动决定写入什么内容。
本文基于 OpenClaw 2026.3.24 + openclaw-weixin 2.1.1 + openclaw-qqbot 1.6.6 的源码分析。