我以为 SSE 渲染就是 Agent 流式,直到它停在用户确认前

我以为 SSE 渲染就是 Agent 流式,直到它停在用户确认前

我做过 SSE 渲染:服务端不断推送事件,前端拿到增量文本就更新页面。所以刚开始学 Agent 的 Streaming 时,我的判断很直接:这不就是模型流式输出,Runtime 接一下,前端渲染一下吗?

这个判断没有错,但它只覆盖了最容易看见的那一段。

真正让我重新理解这件事的,不是"你好"两个字怎样逐步显示,而是下面这个场景:模型已经流式返回了待写入内容,进程却在用户确认之前重启了。此时新启动的 Agent 到底该知道什么?又凭什么不能直接把内容写出去?

顺着这个问题,我把 Streaming、上下文和会话状态连成了一条线。

流式首先是一次尚未结束的调用

一个简化的正常过程可能是:

vbscript 复制代码
response.created
→ output_text.delta
→ output_text.delta
→ response.completed

SSE 前端通常消费的是中间的 output_text.delta:拿到"你"就显示"你",再拿到"好"就显示"你好"。这正是流式能改善体验的原因------用户不必等全部结果生成完才看到第一个字。

但这里有一个容易混淆的点:首字更早出现,不等于完整结果更早结束。模型仍需要生成剩余内容,所以流式通常降低首字延迟,却不保证总耗时更短。

而且,Agent 的流里不只有文本。中间还可能混进工具参数增量、错误事件和取消事件。于是我不再把流式理解成"前端的渲染技巧",而是理解成 Runtime 正在处理一条尚未结束的事件生命周期。

我补了一个 SSE 增量渲染 Demo。它逐帧解析 SSE,并验证三件事:

  • 收到文本增量时,视图立即更新;
  • response.failed 可以保留已显示的部分文本,但不能伪装成完成;
  • response.cancelled 同样不是成功结束。

这段 Demo 不需要我重新学习 SSE。它只是把我已有的渲染经验变成一份可检查的工程证据。

重启后,不是把全部历史重新塞给模型

接下来问题变得更具体:如果会话重启,模型没有旧进程的记忆。Runtime 当然要根据会话 ID 找回原来的记录,但找回记录不等于把所有内容都发给 LLM。

本次分析停在 waiting_confirmation:模型已经提出了一个待写入意图,但用户还没有确认。

这时真正会影响下一步判断的信息包括:

复制代码
当前目标是什么
本次有哪些约束
会话仍在等待用户确认
待执行的写入意图是什么
这个意图是否已经执行过

反过来,上周聊过的电影、午饭、已经与当前任务无关的旧闲聊,即使也存在于持久化会话中,也不应该因为"重启了"就自动成为模型上下文。

这里我把两个层次分开了:

复制代码
Runtime 恢复完整会话状态
→ 从中选出影响下一步判断的语义信息
→ 再把这部分交给 LLM

例如原始幂等键属于 Runtime 防止重复执行的机制;但"这次待写入意图尚未执行"这个事实,会影响模型和 Runtime 接下来应该停在确认、继续还是拒绝。前者不必原样出现在 Prompt 中,后者可能是有效上下文。

重启确认前的上下文 Demo 把这个场景固定下来:它保留目标、约束、等待确认、待执行意图和执行状态,排除无关旧闲聊。这里的预算数字只是确定性示例,不代表任何真实模型的 token 计算;它证明的是选择规则,而不是某个模型的实际价格或延迟。

模型流结束,不代表 Runtime 可以执行

我原本还会把下面几件事靠得太近:模型流结束、工具参数完整、真正写入发生。

现在看,它们至少是三道不同的门:

vbscript 复制代码
response.completed
→ 模型这次流正常结束

工具参数可解析且通过 Schema
→ Runtime 可以理解这个调用

用户确认已持久化
→ Runtime 获得产生外部副作用的授权

例如工具参数可能分两段到达:

json 复制代码
{"title":"
学习记录"}

Runtime 不能在第一段时提前执行;等 JSON 完整后,还要做 Schema 和业务校验。即使参数完全合法、response.completed 也已经到达,只要会话仍是 waiting_confirmation,写入就仍然不能发生。

这也是重启场景最容易出问题的地方:如果新进程只看见"模型曾经要写入",却没有恢复"用户尚未确认",它就可能越权执行;如果只看见"待写入",却不知道是否已经执行过,又可能重复执行。

为此我运行了 状态恢复 Demo

bash 复制代码
node examples/2026-07-27-streaming-state-recovery/demo.mjs all

它覆盖正常、错误、取消、重启恢复四条路径。它不代表某个厂商的协议,也不接真实模型 API;它只验证一个 Runtime 最小原则:事件终态、参数校验、确认状态和防重复状态,必须由 Runtime 显式维护。

我今天真正改掉的理解

今天不是重新学习怎样写 SSE,而是把已有经验放回 Agent 的完整链路中。

我现在会这样理解一次带工具的流式调用:

复制代码
模型持续产生事件
→ Runtime 决定怎样消费和向前端交付
→ Runtime 选择哪些旧状态仍值得交给下一次模型调用
→ Runtime 在确认、校验和幂等边界上阻止越权执行

所以,Agent 的流式体验不只是"字能不能先出来"。真正决定它是否可靠的,是 Runtime 能不能在流没有完成、调用失败、用户取消、进程重启和等待确认这些节点上,始终知道自己处于什么状态。

下一步我会进入 L13,从用户点击发送消息开始,追到 Node/TS 后端、模型、工具、确认节点和聊天 UI 的完整调用链。

相关推荐
阿里云云原生1 小时前
告别“玄学”调参:如何让 Agent 从真实执行路径中自动挖掘优化经验?
agent
ZhengEnCi2 小时前
Pi Agent 深度解析:最小化编码 Agent 的哲学、争议与未来
agent
JaydenAI2 小时前
[A2A协议与实现-08]基于MAF的A2A Server设计与实现
ai·agent·a2a·maf
张反手3 小时前
一个能讲清的 Loop + Tool 小 demo
agent
大龄码农有梦想3 小时前
Codex、Claude Code 等 AI 编程工具对软件工程的启发
人工智能·软件工程·agent·ai编程·ai agent·智能体·智能体平台
玉鸯3 小时前
向量检索不是记忆:Agent 记忆的三层进化
python·agent
思考着亮4 小时前
11.Rag
agent
不一样的少年_4 小时前
原来 AI Agent 的核心循环这么简单:手搓一个 Agent Loop
前端·后端·agent
Bigger4 小时前
🔥每天最难的问题不是做饭,而是今天到底吃什么——我做了「烟火食间」
前端·人工智能·agent