精炼回答
因为 Agent 的工作方式是流式的、双向的、多客户端的------而这三点,HTTP 轮询从设计上就满足不了。
OpenClaw 的 Gateway 运行在 ws://127.0.0.1:18789,CLI、WebChat UI、macOS App、iOS/Android Node 全都通过 WebSocket 连到这个控制平面。这不是随手选的技术,而是 Agent 运行模型倒逼出来的结论。
当你发一条消息给 OpenClaw,Brain 进入 ReAct 推理循环:Thought → Action → Observation → Thought......这个过程可能持续几秒到几十秒,中间会不断产生中间状态。如果用 HTTP 请求-响应,你只能等整个循环跑完拿到最终结果;用 WebSocket,Gateway 可以把每一步的状态实时流向所有连接中的客户端------这就是你在 Telegram 看到"正在思考......"然后逐步更新的效果背后的机制。
HTTP 本质是"一问一答",问完连接就关了,服务端没有办法主动联系客户端。轮询是对这个限制的绕行方案:客户端每隔几秒问一次"有消息吗"。这个方案能用,但 99% 的请求换来的是空响应,每次都要重新走 TCP 握手和 HTTP 头解析的完整流程。对于 Agent 这种执行过程本身就是连续事件流的系统,这个代价完全不值得付。
扩展分析
OpenClaw 面对的是什么样的通信模型
要理解为什么选 WebSocket,先得看清楚 OpenClaw 的消息流长什么样。

注意图里有个细节:Telegram 和 WhatsApp 走的是 HTTP,CLI/WebChat/App 走的是 WebSocket。这不是矛盾,而是各归其位------外部平台的协议由平台决定,OpenClaw 自己控制的通道全部选择了 WebSocket。
Gateway 在这里不是消息转发器,而是整个系统的神经中枢:它接收来自各个渠道的输入,驱动 Brain 推理,再把推理过程中产生的每一个中间状态实时广播出去。这个"实时广播"的需求,是选型的核心出发点。
为什么轮询在这个场景下撑不住
把三种常见方案放在一起比较,差距会非常直观:

短轮询在 Agent 场景下问题很明显:推理过程可能持续 30 秒,轮询间隔设 1 秒就有 30 次空请求,设 5 秒消息延迟又高得难以接受。而且每次请求都要重新建 TCP 连接,在本地环回地址还好,一旦上云或走移动网络,开销就完全不可接受。
长轮询是 OpenClaw 在 Telegram 通道上的选择------但这是一个刻意的权衡,不是通用最优解。Telegram Bot 的长轮询模式(由 grammY runner 实现)之所以适合,是因为本地部署场景下无需公网 IP、无需 HTTPS 证书,零配置开箱即用。但长轮询本质上仍然是"等服务端回应后立刻发下一个请求",本地维持一条连接的逻辑比较绕,高并发下服务端也需要维持大量挂起的连接。
WebSocket 一次握手,连接永久保持。对 OpenClaw 来说,这意味着三件事同时成立:推理中间状态可以实时 stream 出来、用户确认可以随时插入流程、多个客户端能同步看到同一个 Agent 的状态变化。
三个具体场景,三个"HTTP 做不到"的理由
场景一:流式推理输出
Brain 执行一次任务的过程不是"输入 → 等 → 输出",而是连续的状态机:
vbnet
Thought: 用户想看 PR 列表,需要调用 github skill
Action: exec("gh pr list --state open --json number,title,author")
Observation: [{"number":42,"title":"Fix login bug"...}]
Thought: 拿到数据了,整理成自然语言回复
Final Answer: "当前有 3 个 open PR..."
每一步都是一个事件。WebSocket 让这些事件实时流向客户端,用户能看到 Agent"正在干什么",而不是盯着转圈等结果。HTTP 做不到这个------你必须等整条链路跑完,才能发出那个唯一的响应。
场景二:双向确认交互
Heartbeat 触发了一个定时任务,任务执行到一半发现需要合并一个 PR,OpenClaw 需要向用户确认:"要不要 merge #42?"用户回复"可以",任务继续执行,最终结果回推。
这整个过程里,服务端和客户端都需要随时主动发消息。HTTP 的单向性(服务端不能主动发)在这里根本撑不住------你必须用轮询来"模拟"服务端的主动推,而 WebSocket 原生就支持。
场景三:多客户端状态同步
你同时在手机 Telegram 发了一条消息,在电脑上的 WebChat 看进度。Gateway 通过 WebSocket 把同一个 Agent 的执行状态广播给所有已连接的客户端------Telegram 那边显示"正在执行",WebChat 这边同步更新,两边看到的是同一个任务的同一个进度。轮询方案里,每个客户端只能各自问、各自拿,状态同步需要额外的协调层。
不可忽视的代价:断线重连
WebSocket 不是没有成本。选择持久连接,就必须认真处理"连接断了怎么办"。
移动网络切换、服务端重启、网络抖动都会导致 WebSocket 断开。一个生产可用的客户端必须实现指数退避重连,并在重连后重新同步状态:
kotlin
class ReconnectingWebSocket {
constructor(url) {
this.url = url;
this.retryDelay = 1000; // 初始等 1s
this.maxDelay = 30000; // 最长等 30s
this.connect();
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
this.retryDelay = 1000; // 连上了,重置退避计时
};
this.ws.onclose = () => {
setTimeout(() => {
this.retryDelay = Math.min(this.retryDelay * 2, this.maxDelay);
this.connect();
}, this.retryDelay);
};
this.ws.onmessage = ({ data }) => {
this.handleMessage(JSON.parse(data));
};
}
}
OpenClaw 本地运行的场景下,这个问题相对简单------主要是 Gateway 重启。但如果通过 Tailscale Funnel 把 Gateway 暴露到公网,移动客户端就必须把断线重连做扎实。
小结:不是"WebSocket 更好",而是"Agent 模型需要它"
| 短轮询 | 长轮询 | WebSocket | |
|---|---|---|---|
| 服务端主动推 | ✗ | △ 模拟 | ✓ 原生 |
| 流式中间状态 | ✗ | ✗ | ✓ |
| 双向通信 | ✗ | ✗ | ✓ |
| 多客户端广播 | 需要额外层 | 需要额外层 | 原生支持 |
| 连接开销 | 高(每次重建) | 中 | 低(一次握手) |
| OpenClaw 用在哪 | 不用 | Telegram 通道 | CLI / WebChat / App |
OpenClaw 的选择不是"WebSocket 在所有场景都比轮询好"------Telegram 通道至今用的还是 long polling,因为那个场景下零配置比低延迟更重要。技术选型从来不是找最优解,而是找最匹配约束的解。Agent 的流式推理、双向交互、多客户端同步这三个约束加在一起,WebSocket 是唯一能同时满足的方案。