为什么 Agent Runtime 常用 WebSocket?
潘忠显 / 2026-07-07
前两篇我们讲清了 Agent Runtime 是什么、它和 Agent / Skill 的分工。这一篇聚焦 Runtime 的一个具体侧面:它到底怎么和外界通信。
很多人在接触 OpenClaw、Hermes 这类把 Runtime 独立部署的 Agent 框架时,会发现一个现象:它们几乎都会使用 WebSocket 与客户端通信。
本文会用“为什么选 WebSocket”做切口,真正想讲的是:Agent Runtime 的通信模型,如何从一次性的 Token Streaming,演变成 长期 Session + 双向事件流。
我最开始怀疑:是不是因为这些 Agent 框架接入了微信、Slack、Telegram 等 IM,所以才大量使用 WebSocket?
但在 Agent Runtime 的设计语境里,真正让 WebSocket 成为自然选择的,并不是 IM 接入本身,而是 Agent Runtime 的通信模型发生了变化。
IM 只是最容易让这个变化显现出来的入口。
从 Chat UI 到 Agent Runtime
要理解为什么是 WebSocket,需要先看 AI 应用的通信模型是怎么演进的。
Chat UI 时代:HTTP + SSE 就够了
在早期的 ChatGPT 类产品里,系统形态其实很简单:
浏览器发起一次请求,服务端开始调用模型。模型生成过程中,服务端不断把增量 Token 推给浏览器,浏览器负责拼接展示。
Server 推送 Browser 呈现
──────────────────────────────────────────────────
"Hello" → Hello
", how" → Hello, how
" are" → Hello, how are
... → ...
这时的核心需求只有一个:服务端持续向客户端推送内容。
因此,SSE(Server-Sent Events)就足够了。它简单、浏览器原生支持、非常适合 Token Streaming。
早期很多 Chat UI,例如 Open WebUI、LobeChat,本质上都是围绕“对话展示”设计的。它们关注的是如何把模型输出流畅地呈现给用户,而不是管理一个持续运行的任务。
Tool Calling 时代:事件变多了,但仍然是单向的
后来,大模型开始支持 Tool Calling。
比如用户说「帮我查一下今天上海天气」,执行流程变成:
服务端返回的内容不再只是 Token,还会包含 Thinking、Tool Call、Tool Result 等事件。
但从通信模型上看,这件事并没有发生根本变化:这些事件依然可以由服务端按照顺序持续推送给客户端。
也就是说,事件类型变丰富了,但方向仍然主要是:
所以在这个阶段,很多系统继续使用 SSE 是合理的。今天很多模型 API 仍然沿用这种设计。
Agent Runtime 时代:客户端也要不断发回控制事件
真正的转折点,是 Agent Runtime 的出现。
Agent Runtime 是什么,前两篇已经讲透了,这里直接用结论:它不只是把请求转发给模型,而是会管理 Session、工具调用、用户确认、暂停恢复、上下文状态和任务生命周期。在 OpenClaw、Hermes 这类系统里,它就是承载 Agent 任务执行的后台层。
也就是说,Agent 不再只是生成一段文本,也不只是完成一次工具调用,而是在 Runtime 里执行一个可以持续推进的任务:
这时,客户端已经不只是“接收内容”的地方了。它还需要随时向 Runtime 发回控制命令,例如:
- Approve:批准一次工具调用
- Stop:中止当前任务
- Resume:恢复一个暂停的 Session
- Continue:补充新的指令
- Upload:上传文件或上下文
- Retry:重试失败的步骤
于是通信模型从“服务端单向推送”,变成了“客户端与 Runtime 之间持续交换事件”:
这时,WebSocket 开始变得自然。
WebSocket 解决的是长期双向连接
Agent Runtime 需要的不是一次请求、一次响应,而是一个可以长期存在的通信通道。
一个 Agent Session 往往会持续几十秒,甚至几分钟。期间 Runtime 会不断向客户端发送 Token Streaming、Thinking、Tool Call、Tool Result、Approval Request、Interrupt、Memory Update 等事件。
与此同时,客户端也会不断把 Continue、Stop、Approve、Upload、Retry 等控制事件发回 Runtime。
这种模式的本质是:Client 和 Agent Runtime 之间需要持续、低延迟、双向的事件通信。
WebSocket 正好适合这个模型:
连接建立一次之后,双方都可以随时发送消息。Runtime 不需要等客户端重新发起 HTTP 请求,客户端也不需要为每一种控制动作额外维护一套请求与状态同步逻辑。
这并不是说 Agent Runtime 只能使用 WebSocket。HTTP polling、SSE + HTTP command 等方案也可以实现类似能力;如果是在后端服务之间,还可能会考虑 gRPC streaming。
简单对比一下几种方案:
| 方案 | 通信方向 | 浏览器支持 | 重连成本 | 状态对账 | 生态适配 |
|---|---|---|---|---|---|
| SSE | Server → Client | 原生支持 | 较低 | 简单 | 很适合 Token Streaming |
| SSE + HTTP command | 下行 SSE,上行 HTTP | 原生支持 | 中等 | 上下行要关联 | 适合中等复杂度 |
| WebSocket | Client ↔ Runtime | 原生支持 | 中等 | 同一连接内做事件对账 | 前后端和网关生态成熟 |
但 WebSocket 的优势在于:
- 浏览器原生支持,接入成本低
- 双向通信模型直接,不需要为上下行分别维护两套传输逻辑
- 生态成熟,前后端、网关、代理都比较熟悉
- 很适合表达 Agent Runtime 的事件总线模型
更准确地说,WebSocket 的好处不是“功能上唯一能做”,而是它把 Agent Runtime 最核心的交互抽象成了一件事:
同一个 Session 里,Client 和 Runtime 持续交换有序事件。
Runtime 事件协议长什么样
注意,这里说的“事件”不是 WebSocket 协议本身,而是运行在 WebSocket 之上的应用层消息。
比如 Runtime 发给客户端一条事件:
{
"type": "tool_call.created",
"session_id": "sess_123",
"run_id": "run_456",
"event_id": "evt_001",
"payload": {
"tool": "shell",
"args": {
"cmd": "npm test"
},
"requires_approval": true
}
}
客户端再发回一条命令:
{
"type": "tool_call.approved",
"session_id": "sess_123",
"run_id": "run_456",
"reply_to": "evt_001",
"payload": {
"approved": true
}
}
这里的关键不是 JSON 格式本身,而是消息之间的关系:
session_id标识一个长期存在的 Agent Sessionrun_id标识 Session 中的一次具体执行event_id标识 Runtime 发出的事件reply_to把客户端命令关联回某个 Runtime 事件type表示这是 Token、Tool Call、Approval、Resume 还是 Interrupt
通常可以这样理解:Session 是一段持续存在的上下文,Run 是一次用户指令触发的执行过程,而一个 Run 里会推进多个 Step;每个 Step 又会产生若干 Event,比如模型输出、工具调用、审批请求和工具结果。
一旦系统进入这种模型,客户端和 Runtime 之间传递的就不再是简单的“请求”和“响应”,而是一组围绕 Session 生命周期展开的事件。
WebSocket 并不会自动帮你设计好这些字段,但它提供了一个更贴近这个模型的传输通道:双方在同一条连接上发送事件,应用层只需要关注事件语义本身。
这和 Agent Runtime 的内部模型更接近。
比如一次工具审批,大概会是这样的事件往返:
这里的 reply_to 不是装饰字段,而是把“用户批准了哪个工具调用”这件事明确关联回去。否则一旦同一个 Session 里同时存在多个工具调用,客户端和 Runtime 就很容易对不上状态。
为什么不用 SSE + HTTP command
最容易想到的替代方案,是继续使用 SSE 接收 Runtime 事件,再用普通 HTTP 请求发送控制命令。也就是这样:
这个方案不是不能用。在简单场景里,它甚至很合理。比如客户端只需要接收模型输出,偶尔发送一次 Stop 或 Continue,那么 SSE + HTTP command 完全可以支撑。
但 Agent Runtime 的复杂度上来之后,上下行通信被拆成两套通道,复杂度就会逐渐显现出来。
前端不再只是维护一个事件连接,而是要同时处理:
- 一个 SSE 连接
- 一组 HTTP command API
- command 与 event 的对应关系
- 命令发送后 Runtime 状态已经变化的冲突
这些问题并不是 SSE + HTTP command 独有的,WebSocket 下同样会遇到竞态、幂等和状态对账。区别在于,SSE + HTTP command 把上下行拆成两套通道之后,前端更容易同时面对“事件流”和“命令 API”两套抽象,顺序、状态同步和恢复逻辑会显得更分散。
需要注意的是,断线重连后的 Session 恢复并不是 SSE + HTTP command 独有的问题。WebSocket 断开之后,同样要重新鉴权、恢复 Session、对齐最后收到的 Event,必要时还要做事件重放或状态快照。两种方案都逃不开应用层协议设计。
相比之下,WebSocket 让双向事件天然处在同一条连接里。它不消除重连、对账、幂等这些应用层复杂度,但能减少传输层和客户端抽象上的割裂感。
Runtime 独立出来之后,入口都变成客户端
很多人会觉得:OpenClaw 接入了微信、Slack、Telegram,所以才用了 WebSocket。
但更准确的说法是:IM 只是 Agent Runtime 的一个客户端入口。理解这一点之后,很多 AI 产品的架构变化就会变得清楚。
过去,我们常见的结构是 UI 直接连接模型或模型服务,模型返回流式输出,UI 负责展示:
但 Agent 应用出现之后,UI 不再直接连接模型,而是连接 Runtime。Runtime 负责管理 Session、Tool 调度、MCP、Memory、多 Agent 协作、生命周期管理和 Event Bus。UI、CLI、IDE、微信、Slack 都只是 Runtime 的不同客户端。结构变成:
这里要把两段链路分清楚:本文讨论的 WebSocket,主要是 Client / Gateway ↔ Runtime 这一段事件与控制通道。Runtime ↔ LLM 这一段通常仍然是 HTTP / SSE streaming,Runtime 只是把模型输出、工具调用、审批请求等信息重新组织成自己的事件流。
例如微信场景里,架构可能是这样:
Gateway 负责接收微信消息,并把消息转交给 Runtime。进入 Runtime 之后,它就不再是一条普通消息,而会变成一个可持续推进的 Agent 任务:
这个生命周期可能持续很长时间。Gateway 与 Runtime 之间使用 WebSocket,自然是合理的。
事实上,WebSocket 常常同时出现在两段链路上:IM 平台到 Gateway(例如 Slack Socket Mode),以及 Gateway 到 Runtime。前者是平台规定的接入方式,后者才是本文关注的 Runtime 通信模型。两段链路都可能用 WebSocket,但动机不同,不要混为一谈。
但关键点在于:如果把微信换成 Browser、VS Code、CLI 或手机 App,通信需求其实并没有变化。这些客户端都需要接收 Runtime 的执行事件,也都可能向 Runtime 发回控制事件。所以,真正需要 WebSocket 的不是 IM,而是 Runtime。
这也是为什么 OpenClaw、Hermes 这类系统会倾向于把 Runtime 独立出来,让 Browser、CLI、微信、Slack、VS Code 等不同入口都能连接进来。
与之相对,Cursor、Codex CLI、Claude Code 也具备 Session、Tool Calling、Approval、Continue、Patch 等 Runtime 能力,但这些能力通常被嵌入到自己的产品内部。它们更像是一体化的 Agent Client:
是否把 Runtime 独立出来,是产品形态上的选择;但只要 Runtime 需要和外部客户端持续交互,双向事件通信就会成为核心需求。
总结
在 Agent Runtime 场景下,真正推动 WebSocket 成为默认选择的,是 Runtime 自身的通信模型——从「问答」 Request → Response,演变成「Agent」 长期 Session + 双向 Event。IM 只是 Agent Runtime 的一个客户端。
WebSocket 并不是 Agent Runtime 唯一可以采用的技术方案。SSE + HTTP command 等方案也能实现类似能力,只是它们通常需要在应用层处理更多连接、顺序、状态同步和恢复逻辑。同时,WebSocket 拥有成熟的浏览器支持、广泛的生态兼容性,以及非常贴合 Agent 交互模式的双向通信能力。因此,它成为越来越多 Agent Runtime 的默认选择。
后续文章,我们将讨论如何创建真正有用的 Agent 以及多 Agent 交互。
