1. 先给结论

在 Agent 的一次完整链路里:tool call -> tool result -> model resume 更适合 Streamable HTTP,核心不是因为它比 SSE“更快”,而是因为这条链路本质上已经不是单纯的“文本事件推送”,而是一个 分阶段的协议交互过程 :

  • 先流式输出模型状态
  • 再产生结构化的 tool call 指令
  • 再提交 tool result
  • 再恢复模型执行
  • 最后继续输出结果

这类交互更适合采用:

  • HTTP 作为传输层
  • 结构化 chunk 作为协议层
  • 多个普通 HTTP 端点协同完成完整会话

而不是把所有内容都塞进一条 SSE 事件流。

2. 一张时序图

下面这张图展示一个典型 Agent 执行过程:

 

3. 这张图里最关键的点

3.1 这是“多阶段协议”,不是“一次文本输出”

这条链路至少分成两段主过程:

  1. 模型先流式输出,并声明要调用工具
  2. 工具结果返回后,模型再恢复执行并继续输出

也就是说,服务端并不是从头到尾只做一件事,而是在不断切换状态:

这类状态机式流程,本质更像 协议消息流 。

如果用 SSE,也不是不能做,但通常会变成:

  • 外层 event: message
  • 内层 data: {type: ..., state: ..., payload: ...}

换言之,真正重要的协议已经不在 SSE 里,而在 data 里的 JSON 对象里。此时 SSE 只是一个额外包装层。

3.2 tool call 本身是结构化指令,不是适合展示的文本

例如图里的这几类消息:

  • tool_call.started
  • tool_call.arguments.delta
  • tool_call.completed

它们都不是给人直接看的自然语言,而是给宿主程序消费的结构化事件。

宿主需要基于这些消息去做程序动作:

这就要求消息天然是 typed object ,而不是“文本事件里塞一段 JSON 字符串”。

Streamable HTTP 更适合直接传这样的结构化 chunk。

3.3 tool result 提交是新的 HTTP 动作,不是原流里自然解决的问题

图里有一个关键转折:


这是整个 Agent 链路里最重要的一点:

  • 首次请求只负责启动 Agent
  • 工具调用产生后,宿主执行工具
  • 工具执行完,再发一个新的请求把结果提交回去
  • 服务端再基于结果恢复模型

这说明完整链路不是“订阅一条事件流直到结束”那么简单,而是:

  • 一次启动请求
  • 若干状态流
  • 一次工具结果提交
  • 一次恢复执行
  • 最终完成

这种设计天然更适合围绕一组普通 HTTP 端点来组织:

  • POST /responses
  • POST /responses/{id}/tool_result
  • POST /responses/{id}/cancel
  • GET /responses/{id}

这就是 Streamable HTTP 的优势: 流式响应只是整体会话协议的一部分 ,而不是全部。

4. 为什么这比 SSE 更自然

4.1 更容易把“流”和“动作”分开

在 Agent 场景中,至少有两类东西:

  • 流式输出 :状态、delta、事件、结果片段
  • 离散动作 :提交 tool result、取消、恢复、查询最终结果

Streamable HTTP 更适合把两类能力拆开:

  • 流,用 streaming body 表达
  • 动作,用普通 POST / GET 表达

这样设计后,接口职责会非常清楚:

表格

类别

例子

更适合的表达

流式消息

reasoning delta / output delta

HTTP stream

离散操作

submit tool result / cancel

普通 HTTP request

状态查询

get final response

普通 HTTP GET

而如果强行以 SSE 为中心来思考,容易把系统误建模成:

  • 一条主事件流承载一切

这对复杂 Agent 并不理想,因为工具结果提交、取消、恢复这些动作,本来就更像独立 API 行为。

4.2 更容易做“暂停—恢复”

一次 tool call 实际上把模型执行切成了两半:

  • 第一半:模型推理到“需要工具”
  • 第二半:工具结果回来后继续推理

所以链路天生带有:

  • checkpoint
  • pending state
  • resume point

这些语义。

Streamable HTTP 更容易把它做成显式协议:

  • 响应流里发出 tool_call.completed
  • 服务端进入 waiting_for_tool_result
  • 客户端提交 tool_result
  • 服务端发出 response.resumed

这里每一步都可以是明确的结构化对象。

如果用 SSE,也可以发这些事件,但“暂停”“恢复”“等待结果”这些关键语义并不是 SSE 原生概念,只能靠上层 JSON 自己定义。这样一来,真正起作用的仍是自定义协议,不是 SSE 本身。

4.3 更容易跨宿主和跨语言实现

图中的 Client / Host 可能并不是浏览器,而可能是:

  • IDE 插件
  • 本地 Agent 宿主
  • CLI
  • 桌面应用
  • 另一个服务

这些环境实现“POST 一个 JSON 请求并读取流式响应 body”通常都很自然;但实现“严格依赖 SSE 事件语法及 EventSource 心智模型”则未必是最佳方案。

因此对框架作者来说,更统一的抽象是:

  • 发 HTTP 请求
  • 读取结构化 chunks
  • 按 chunk 类型驱动状态机
  • 在需要时发下一次 HTTP 请求

这套模型比 SSE 更适合作为通用协议基座。

5. 这张图如果用 SSE 来做,会别扭在哪里

假设把图中的第一段流写成 SSE,大致会像这样:


event: message data: {"type":"response.started"} event: message data: {"type":"reasoning.delta","delta":"..."} event: message data: {"type":"tool_call.completed","call_id":"tc_1","args":{...}}

表面上也能工作,但问题在于:

5.1 SSE 退化成“字符串外壳”

真正协议字段全在 data 的 JSON 里;event: message 往往已经没太大意义。

5.2 SDK 解析要先过一层 SSE,再过一层 JSON

解析过程变成:

  1. 先识别 SSE 事件边界
  2. 取出 data
  3. 再反序列化 JSON
  4. 再按 type 分发协议逻辑

如果底层直接就是 Streamable HTTP + JSON chunks,就只需要做第 3、4 步。

5.3 提交 tool result 仍然要回到普通 HTTP

SSE 只解决了“服务端持续往外推”的问题,并不能优雅覆盖:

  • 提交工具结果
  • 取消请求
  • 查询最终状态
  • 恢复执行

这些动作最终还是会落回普通 HTTP API。所以如果整体系统已经必须围绕多端点 HTTP 协议设计,那么把核心流式部分也统一为 Streamable HTTP,通常更一致。

6. 从框架设计角度看,为什么 Streamable HTTP 更整洁

6.1 更像一个正式 API,而不是前端技巧

SSE 很适合 UI 层“实时刷字”;但 Agent 的 tool call -> tool result -> resume 已经是正式协议流程。

框架更希望定义的是:

  • 请求对象是什么
  • 流式 chunk schema 是什么
  • 工具结果如何提交
  • 取消如何表达
  • 恢复如何表达
  • 最终态如何查询

这时,最自然的体系就是:

  • 普通 HTTP API + 流式 body

而不是:

  • 以 SSE 为中心,再在上面拼协议

6.2 更利于中间层做治理

在图里的每一个阶段,中间层都可能需要介入:

  • 记录 tool call
  • 审计工具参数
  • 拦截危险工具执行
  • 注入 tracing id
  • 做重试与恢复
  • 在恢复点继续执行

这些治理动作对结构化 HTTP 消息更友好;因为它们要处理的是 协议对象 ,不是单纯字符串事件。

6.3 更利于未来扩展多模态和多工具并发

如果以后一个 Agent 同时发起多个工具调用,链路会变成:

  • tool_call_1.started
  • tool_call_2.started
  • tool_call_1.result
  • tool_call_2.result
  • resume

再进一步,还可能涉及:

  • 局部恢复
  • 分支执行
  • 结构化 UI 指令
  • 图片/文件引用块

这些都更依赖一个可演进的 chunk schema,而不是固定文本事件格式。

7. 用一句话重新解释这张图

这张时序图想说明的是:

Agent 的一次完整 tool call 流程,本质是“流式协议消息 + 多次 HTTP 动作”共同组成的状态机,而不是“服务端往前端持续推文本”这么简单。

因此更合适的设计是:

  • 用 Streamable HTTP 承载流式协议块
  • 用普通 HTTP 端点承载提交、取消、恢复、查询
  • 让 SDK 基于结构化 chunk 驱动执行状态

而不是把一切都理解成 SSE 事件。

8. 最终总结

tool call -> tool result -> model resume 更适合 Streamable HTTP,主要因为它同时满足了四件事:

  1. 能流式返回模型过程 :例如 reasoning、delta、状态变更
  2. 能传结构化协议消息 :例如 tool call、call_id、arguments、resume signal
  3. 能与普通 HTTP 动作自然协同 :例如提交 tool result、取消、查询最终态
  4. 能作为跨浏览器、CLI、IDE、服务端的统一协议基座

所以从工程本质上看,这不是在比较“哪个更能流”,而是在比较:

  • SSE:更适合前端消费的事件流
  • Streamable HTTP:更适合 Agent 会话级协议编排
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐