# 04 — Bridge(转接器)架构设计指南 > 目标读者:要把微信 ClawBot 接到自己已有 AI 项目的开发者。 > 本文档不绑定具体语言,给出模块划分、状态模型、对接模式和可靠性设计。 ## 1. Bridge 的职责边界 Bridge 是一个**薄适配层**,职责只有: ``` iLink 长轮询 ──▶ 消息规范化 ──▶ 调用你的 AI 后端 ──▶ 回复格式化 ──▶ sendmessage ``` Bridge **不负责**: - 对话历史/记忆管理(那是 AI 后端的事,除非后端无状态) - 业务逻辑、工具调用编排 - 用户体系、计费 保持薄的好处:协议变了只改 Bridge;AI 后端可以独立演进、独立测试。 ## 2. 推荐模块划分 ``` bridge/ ├── ilink/ │ ├── client.* # HTTP 封装:请求头、鉴权、X-WECHAT-UIN、超时 │ ├── auth.* # 扫码登录流程(get_bot_qrcode / get_qrcode_status) │ ├── poller.* # 长轮询主循环、游标管理、退避重试 │ ├── sender.* # sendmessage / sendtyping / 分片发送 │ └── media.* # getuploadurl、CDN 上传下载、AES-128-ECB ├── store/ │ ├── account.* # bot_token / ilink_bot_id / baseurl 持久化 │ ├── cursor.* # get_updates_buf 持久化(每次更新立即落盘) │ └── context.* # peer_id → context_token 映射持久化 ├── normalize.* # WeixinMessage → 内部统一消息模型 ├── backend.* # 你的 AI 项目的调用封装(HTTP / SDK / 进程) └── main.* # 装配 + 启动 + 优雅退出 ``` 参考:官方包 `@tencent-weixin/openclaw-weixin` 的目录结构(`auth/ api/ cdn/ messaging/ monitor/ config/ storage/`)几乎就是这个划分;Java SDK 的门面模式(`OpenClawWeixinSdk` 统一封装 `QrLoginFlowService / WeixinApiClient / WeixinLongPollMonitor / MediaUploadService`)也值得对照。 ## 3. 必须持久化的三类状态 | 状态 | 内容 | 更新时机 | 丢失后果 | |---|---|---|---| | 账号凭据 | `bot_token`、`ilink_bot_id`、`ilink_user_id`、`baseurl` | 扫码成功时 | 需重新扫码 | | 同步游标 | `get_updates_buf` | **每次 getupdates 响应后立即** | 重复收到历史消息 | | 上下文令牌 | `peer_id → context_token` | 每条入站消息 | 无法回复/主动消息送达不到 | 实现建议: - 单机用 JSON 文件或 SQLite 就够(官方包就是文件存储,Java SDK 用 `FileAccountStore / FileContextTokenStore / FileSyncCursorStore`) - 游标落盘要**同步写**(或至少 flush),崩溃丢游标 = 重启后消息轰炸 - 文件加个简易锁,防止多开实例写坏状态 ## 4. 与 AI 后端的对接模式 按你的 AI 项目形态选一种: ### 模式 A:HTTP API 后端(最常见) ``` poller.onMessage(msg): reply = http_post("http://127.0.0.1:8000/chat", { "session_id": msg.peer_id, # 用微信用户 ID 当会话 ID "text": msg.text, "attachments": msg.media_paths }) sender.send_text(msg.peer_id, reply.text, msg.context_token) ``` - 会话 ID 直接映射微信 `from_user_id`,天然一对一私聊 - 后端无状态的话,Bridge 也不存历史;有状态则后端自己管 ### 模式 B:本地 SDK / 库调用 Bridge 与 AI 项目同进程(或同语言直接 import),把消息对象传给 Agent 函数,拿回字符串。参考 [cc-weixin](https://github.com/hao-ji-xing/cc-weixin) 把 Claude Agent SDK 的 `query()` 包一层。 ### 模式 C:异步任务队列 AI 处理慢(深度研究、图像生成)时: ``` 1. 收到消息 → 立即回复「收到,处理中」(可选:sendtyping) 2. 任务入队,worker 处理 3. 完成后用保存的最新 context_token 主动 sendmessage ``` ⚠️ 注意 24h 窗口限制(见 06 文档):任务完成后主动推送必须在窗口内才送得到。慢任务一定要设计超时兜底文案。 ### 模式 D:多通道网关(进阶) 如果以后还要接 Telegram/飞书/钉钉,把「规范化消息模型 + 统一网关」抽出来,iLink 只是其中一个 adapter。直接参考 [OpenAkita 的架构](https://github.com/openakita/openakita/blob/main/docs/im-channels.md):`Adapter → UnifiedMessage → Gateway → Agent`,发送路径反向。第一版不用做,但内部消息模型(见 03 文档第 7 节)建议一开始就用。 ## 5. 可靠性设计 ### 5.1 长轮询主循环 伪代码: ``` loop: try: resp = getupdates(cursor, timeout=server_timeout + 10s) if resp.ret == 0: if resp.get_updates_buf: cursor.save(resp.get_updates_buf) for msg in resp.msgs: dispatch(msg) backoff.reset() elif resp.errcode == -14: pause_account(1h); notify("需重新扫码") else: backoff.retry() # 2s 快速重试,连续 3 次后 30s except HttpTimeout: continue # 长轮询正常超时 except NetworkError: backoff.retry() ``` ### 5.2 消息去重 三层兜底: 1. 游标正确持久化(治本) 2. 应用层按消息 ID 去重(`client_id` / 服务端消息 ID,存最近 N 条的 set) 3. 发送侧带唯一 `client_id`(防自己重发) ### 5.3 回复超时 - AI 后端设硬超时(如 60s),超时回「处理超时,请重试」 - 有资料称 24h 窗口外消息送达不到——对「用户发问 → bot 回复」场景这不是问题(立即回复),只影响主动推送 ### 5.4 优雅退出 - 捕获 SIGTERM/SIGINT:停 poller → 等正在处理的消息发完 → flush 状态 → 退出 - 不要在 dispatch 中途杀进程,游标已推进但回复没发 = 用户消息被吞 ### 5.5 可观测性 - 结构化日志:收消息(peer、类型、长度)、发消息、游标推进、错误码、退避事件 - 关键指标:轮询错误率、-14 出现次数、AI 后端耗时分布 - token、context_token 不要打进日志(打 hash 前缀即可) ## 6. 安全注意 - `bot_token` 等于这个微信 bot 的全部权限:**不入 git、不进日志、文件权限 600** - 谁扫码谁就是这个 bot 的「主人」,但任何拿到 token 的人都能收发消息——服务器上注意文件权限 - 入站消息内容视为不可信输入:如果 AI 后端有工具调用能力(执行命令、读写文件),提示词注入风险要自己评估 - 建议加白名单:只响应指定 `from_user_id`(比如就你自己),防止别人给 bot 发消息白嫖你的模型 API 额度 ## 7. 容量与限制设计 - 微信侧不支持 Markdown:发送前转纯文本 - 长回复分片:按段落或固定字数(如 1500 字)切多条,注意保持顺序 - 媒体大小:入站大图/大文件设上限,超限回复提示而不是硬下载 - AI 后端并发:一个微信号只有一个 bot,但用户可能连发多条——dispatch 建议串行 per-peer(或按 peer 加锁),避免回复乱序 ## 8. 测试策略 - 协议层:把 iLink client 抽象成接口,测试时用 mock server 回放抓包 JSON - 规范化层:构造各类 WeixinMessage JSON(文本/图片/语音/图文混排/多 item)做单元测试 - 端到端:真机扫码,用另一个微信号发消息验证 - 回归重点:游标持久化(重启不重复收)、context_token 传递、-14 恢复流程 ## 参考资料 - [openclaw-weixin-sdk 架构图与失败恢复策略](https://github.com/openclaw-java/openclaw-weixin-sdk) - [OpenAkita 多通道网关架构](https://github.com/openakita/openakita/blob/main/docs/im-channels.md) - [cc-weixin — 接入 Claude Code Agent 的完整范例](https://github.com/hao-ji-xing/cc-weixin)