OpenClaw 接微信时,为什么 CLI 模式总比前端慢

0 阅读5分钟

技术支持 wechatapi.net

3abbe0c82f47860393497b96f64cb857.png

最近我在做一件比较实际的事情:
把微信接到 OpenClaw,让用户直接在微信里和 Agent 对话。

这个过程里,我碰到一个非常明显的现象:

  • OpenClaw 前端里,同样一句话,大概 8~10 秒
  • 微信里通过 openclaw agent --session-id --message 去调,常常要 20~30 秒,甚至更久

一开始我以为是微信回调慢、网络慢、或者回消息慢。
后来把日志拆开以后,我确认了一个事实:

真正慢的,不是微信网关,而是 OpenClaw CLI 模式本身。

ddc6f24821de97d8a3c30bf6739e93a9.jpg

先看我最后是怎么定位的

我在网关里打印了两个关键时间点:

logger.info("[OpenClaw CHAT] sid=%s | %s", sid, short_text(message))
# ...
logger.info("[发送文本] TO=%s | %s", to_wxid, short_text(text))

结果非常清楚:

  • 收到微信消息时间:09:58:56
  • 发回微信时间:09:59:28

中间大约 32 秒。

而且这 32 秒不是卡在回调,也不是卡在 reply_text()
而是卡在:

subprocess.run(
    ["openclaw", "agent", "--session-id", sid, "--message", message],
    capture_output=True,
    text=True,
    timeout=180
)

所以问题一下就明确了:
CLI 调用链本身很重。

为什么 CLI 会比前端慢这么多

1. 每条消息都是一次冷启动

前端通常是已经打开着的,很多状态是热的。
但 CLI 模式下,每来一条消息都要重新做一遍:

  • 启动进程
  • 读取配置
  • 恢复 session
  • 初始化 provider
  • 再去请求模型
  • 然后退出

这和前端的运行方式根本不是一个级别。

2. Session 恢复也有成本

我在调用时用了 --session-id

openclaw agent --session-id wechat_dm_xxx --message "你现在都会什么技能"

这意味着 OpenClaw 不只是单纯问模型,
还要恢复这个会话的历史上下文。

如果这个 session 已经比较长,恢复成本会更明显。

3. 某些问题会触发更多内部处理

比如“你现在都会什么技能”这种问题,本身就容易让系统去读取:

  • 技能列表
  • 工具定义
  • workspace
  • 说明文件

所以它天然比一句“你好”更重。

我后来怎么验证这个判断

最有效的方式其实很简单:
绕过微信,直接在终端测同样的 CLI 命令。

time openclaw agent --session-id test123 --message "你好"
time openclaw agent --session-id test123 --message "你现在都会什么技能"

如果 CLI 自己都慢,那微信层再怎么优化,也不可能把整体体感做得很快。

这个测试做完以后,我基本就不再怀疑微信回调了。

这件事对网关设计有什么启发

启发 1:不要把“微信慢”误判成“网关慢”

很多人一看到微信里回复慢,就先怀疑:

  • 回调没处理好
  • 发消息接口慢
  • 本地网络慢

但真正拆日志后,经常会发现:

  • 回调几乎是秒收
  • 发送接口也很快
  • 真正慢的是 AI 内核调用链

这就是为什么我建议一定要在网关里把阶段耗时打出来。

启发 2:CLI 模式适合验证,不适合长期追求低延迟

CLI 模式的优点是接入非常直接:

cmd = ["openclaw", "agent", "--session-id", sid, "--message", text]

但它的问题也同样直接:

  • 冷启动
  • 上下文恢复
  • 无流式返回
  • 无常驻状态

所以它非常适合做 MVP、做验证、做第一版。
但如果你后面真的要追求更低延迟,还是得往常驻化方向走。

启发 3:要区分“入口层优化”和“内核层优化”

入口层能做的优化包括:

  • 回调立即返回
  • 多 worker 并发
  • 同 session 顺序处理
  • 去重
  • 更清晰的 session 设计

这些都值得做,但它们解决的是入口层问题
CLI 冷启动是 内核层问题,这两个不能混在一起看。

我现在在代码里是怎么处理的

虽然 CLI 模式无法从根上解决,但外围还是可以做得更合理。

1. 按 session 分片

def shard_index_for_session(session_id: str, worker_count: int) -> int:
    h = int(hashlib.md5(session_id.encode("utf-8")).hexdigest(), 16)
    return h % worker_count

2. 限制单会话积压

if session_pending[session_id] >= config["MAX_PENDING_PER_SESSION"]:
    reply_text(chat_id, "⚠️ 当前会话任务过多,请稍后再试。", config)
    return {"status": "too_many_pending"}

3. 日志分阶段

logger.info("[收到消息] chat_id=%s text=%s", chat_id, short_text(actual_text))
logger.info("[OpenClaw CHAT] sid=%s | %s", sid, short_text(message))
logger.info("[发送文本] TO=%s | %s", to_wxid, short_text(reply))

这些改动不能让 CLI 变成前端那种速度,
但至少能让你知道问题到底卡在哪。

我的结论

如果你现在也在做“微信 + OpenClaw”这种接法,那我会给你一个很直接的判断:

微信网关可以做好,但只要底层还是“每条消息起一次 openclaw agent”,它的延迟就很难和前端一样。

这不是代码写错了,而是调用方式决定的。

所以正确的预期应该是:

  • 第一阶段:CLI 接法,先验证场景
  • 第二阶段:如果场景成立,再想办法做常驻化、服务化、热状态化

最后一句话

很多人会把“AI 入口慢”归因到入口层。
但我这次做下来最大的体会是:

入口层最多帮你减少混乱,真正决定体感上限的,往往还是内核调用方式。