用一张时序图 讲清楚 Agent 一次完整 tool call -> tool result -> model resume 为什么更适合 Streamable HTTP
1. 先给结论
在 Agent 的一次完整链路里:tool call -> tool result -> model resume 更适合 Streamable HTTP,核心不是因为它比 SSE“更快”,而是因为这条链路本质上已经不是单纯的“文本事件推送”,而是一个 分阶段的协议交互过程 :
- 先流式输出模型状态
- 再产生结构化的 tool call 指令
- 再提交 tool result
- 再恢复模型执行
- 最后继续输出结果
这类交互更适合采用:
- HTTP 作为传输层
- 结构化 chunk 作为协议层
- 多个普通 HTTP 端点协同完成完整会话
而不是把所有内容都塞进一条 SSE 事件流。
2. 一张时序图
下面这张图展示一个典型 Agent 执行过程:

3. 这张图里最关键的点
3.1 这是“多阶段协议”,不是“一次文本输出”
这条链路至少分成两段主过程:
- 模型先流式输出,并声明要调用工具
- 工具结果返回后,模型再恢复执行并继续输出
也就是说,服务端并不是从头到尾只做一件事,而是在不断切换状态:
这类状态机式流程,本质更像 协议消息流 。
如果用 SSE,也不是不能做,但通常会变成:
- 外层
event: message - 内层
data: {type: ..., state: ..., payload: ...}
换言之,真正重要的协议已经不在 SSE 里,而在 data 里的 JSON 对象里。此时 SSE 只是一个额外包装层。
3.2 tool call 本身是结构化指令,不是适合展示的文本
例如图里的这几类消息:
tool_call.startedtool_call.arguments.deltatool_call.completed
它们都不是给人直接看的自然语言,而是给宿主程序消费的结构化事件。
宿主需要基于这些消息去做程序动作:
这就要求消息天然是 typed object ,而不是“文本事件里塞一段 JSON 字符串”。
Streamable HTTP 更适合直接传这样的结构化 chunk。
3.3 tool result 提交是新的 HTTP 动作,不是原流里自然解决的问题
图里有一个关键转折:
这是整个 Agent 链路里最重要的一点:
- 首次请求只负责启动 Agent
- 工具调用产生后,宿主执行工具
- 工具执行完,再发一个新的请求把结果提交回去
- 服务端再基于结果恢复模型
这说明完整链路不是“订阅一条事件流直到结束”那么简单,而是:
- 一次启动请求
- 若干状态流
- 一次工具结果提交
- 一次恢复执行
- 最终完成
这种设计天然更适合围绕一组普通 HTTP 端点来组织:
POST /responsesPOST /responses/{id}/tool_resultPOST /responses/{id}/cancelGET /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
解析过程变成:
- 先识别 SSE 事件边界
- 取出
data - 再反序列化 JSON
- 再按
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.startedtool_call_2.startedtool_call_1.resulttool_call_2.resultresume
再进一步,还可能涉及:
- 局部恢复
- 分支执行
- 结构化 UI 指令
- 图片/文件引用块
这些都更依赖一个可演进的 chunk schema,而不是固定文本事件格式。
7. 用一句话重新解释这张图
这张时序图想说明的是:
Agent 的一次完整 tool call 流程,本质是“流式协议消息 + 多次 HTTP 动作”共同组成的状态机,而不是“服务端往前端持续推文本”这么简单。
因此更合适的设计是:
- 用
Streamable HTTP承载流式协议块 - 用普通 HTTP 端点承载提交、取消、恢复、查询
- 让 SDK 基于结构化 chunk 驱动执行状态
而不是把一切都理解成 SSE 事件。
8. 最终总结
tool call -> tool result -> model resume 更适合 Streamable HTTP,主要因为它同时满足了四件事:
- 能流式返回模型过程 :例如 reasoning、delta、状态变更
- 能传结构化协议消息 :例如 tool call、call_id、arguments、resume signal
- 能与普通 HTTP 动作自然协同 :例如提交 tool result、取消、查询最终态
- 能作为跨浏览器、CLI、IDE、服务端的统一协议基座
所以从工程本质上看,这不是在比较“哪个更能流”,而是在比较:
SSE:更适合前端消费的事件流Streamable HTTP:更适合 Agent 会话级协议编排
更多推荐


所有评论(0)