# 老莫微信消息 → serve session 路由设计方案 > 2026-05-20 > 目标:老莫给机器人号发微信,小小莫也能看到,不依赖莫荷转述 --- ## 现状 ``` 你微信 → wxhelper TCP (:19099) → wechat_agent.py ↓ Hermes API (:8642) ↓ 莫荷回复你 ``` 莫荷独占所有微信消息。小小莫只能被动等她转述。 --- ## 目标 ``` 你微信 → wxhelper TCP → wechat_agent.py ─┬→ Hermes API → 莫荷 (不变) └→ opencode serve (:4096) → 小小莫看到 ``` 新增一条岔路:老莫的消息同时写入 serve session,小小莫可主动查看。 --- ## 方案对比 ### 方案 A:subprocess 调用 `opencode run --attach --message` **做法**:wechat_agent.py 的 `process_msg()` 里加一段: ```python def fork_to_session(fu, ct): """将消息写入 opencode serve session (非阻塞)""" if fu != "wxid_c0a6izmwd78y22": return # 只转发老莫 try: import subprocess subprocess.run( ["opencode", "run", "--attach", "http://localhost:4096", "--password", "hermes123", "--session", SESSION_ID, "--message", f"[老莫] {ct}"], capture_output=True, timeout=10, env={**os.environ, "PYTHONHOME": ""} ) except Exception as e: log(f"FORK ERR (non-fatal): {e}") ``` | 方面 | 评估 | |------|------| | 复杂度 | ⭐ 低,~10 行代码 | | 对现有链路影响 | ❌ **无**,fork 是独立线程,失败不影响 Hermes | | session ID 稳定性 | ⚠️ serve 重启后 ses_xxx 会变 → 需想办法拿到当前 ID | | 性能开销 | subprocess 每次约 1-2 秒,但独立线程不阻塞主流程 | | serve 密码硬编码 | ⚠️ 已经在代码库(hermes123),无新增风险 | | 可靠性 | subprocess 可能因 PATH/PYTHONHOME 问题失败 | ### 方案 B:直接 HTTP POST 调用 serve API **做法**:抓包分析 `opencode run --attach` 的 HTTP 协议,直接用 `urllib.request` POST: ```python # 伪代码,serve API 协议未知,需逆向 urllib.request.urlopen("http://localhost:4096/api/session/inject", data=json.dumps({"session": SID, "message": "[老莫] xxx"})) ``` | 方面 | 评估 | |------|------| | 复杂度 | ⭐⭐⭐ 未知,需逆向 serve API | | 性能 | ✅ 纯 HTTP,无 subprocess 开销 | | 稳定性 | ⚠️ 非官方 API,版本更新可能不兼容 | ### 方案 C:写 inbox 文件 + 小小莫轮询 **做法**:wechat_agent 写文件,小小莫定期读取 | 方面 | 评估 | |------|------| | 复杂度 | ⭐ 最低 | | 即时性 | ❌ 需要轮询,无法实时 | --- ## 关键风险 ### 1. session ID 稳定性(最核心) `opencode run --attach` 需要 session ID。每次 serve 重启后,当前 TUI session 的 ID 可能变化: - 如果 serve 重启 → 老 session 消失 → 新 session 新 ID → 需要更新 wechat_agent 里的配置 - **解决思路**:用 session 名称而不是 ID,或每次启动时自动获取 ### 2. 不对莫荷通信造成任何影响 **铁律**:fork 到 session 的代码必须: - 在独立线程中运行 - 捕获所有异常 - 设置超时(≤10 秒) - 永远不阻塞 `call_hermes()` 和 `send_wx()` ### 3. 循环消息风暴 如果我不小心回了一条 `[老莫]` 到 session,wechat_agent 不能把它再 fork 一次。 - 现有的 `is_self` 检查已经过滤自己发送的消息 - 但如果 serve session 的消息被 serve 再推给 wechat_agent... 需要确认不会发生 ### 4. `opencode` CLI 在 wechat_agent 环境中是否可用 wechat_agent 以 `$env:PYTHONHOME=''` 启动,`opencode.cmd` 可能也依赖 Python。 - 需测试:从 Python subprocess 能否直接调用 `opencode run --attach --message` --- ## 推荐方案 **方案 A**(subprocess)最稳妥: 1. 对现有链路零影响 2. 改动最小 3. 可以逐步优化(先 subprocess,后改 HTTP API) ### 待确认事项 1. **serve session ID 如何维护?** - 能否用固定名称?还是每次启动获取? - 如果 serve 重启导致 ID 变了,wechat_agent 如何感知? 2. **`opencode run --attach --message` 是否支持在 Python subprocess 中调用?** - 需要验证 CLI 安装路径和调用方式 3. **是否需要前缀路由?** - 是全部消息都 fork? - 还是只有特定前缀(如 `[小小莫]` 开头的消息)才 fork? --- ## 验证清单 实现后验证: - [ ] 老莫发微信 → 莫荷正常回复(链路不变) - [ ] 老莫发微信 → serve session 能看到消息(新增) - [ ] `opencode run --attach` 超时/失败 → 莫荷通信不受影响 - [ ] 老莫连续发多条 → 都能看到 - [ ] wechat_agent 重启后依然工作 - [ ] serve 重启后 session ID 变化时能自动适配