Skip to content

流式

流式不是把文本"慢慢输出"这么简单。Runtime 的流式契约定义了四种强类型模式、终止帧、反压,以及 fail-closed 语义。App 把流当作权威事件时间线消费,不是任意 chunk 拼盘。

四种流式模式

模式承载内容关闭语义
Mode A文本与语音生成;连续 chunk 直到终止帧显式 done=true 终止帧
Mode B状态事件流(工作流事件、状态更新)终态状态后关闭
Mode C审计导出eof 标记后关闭
Mode D长连接订阅(健康、App 消息、实时事件)长连接;只在会话拆除时关闭

每种模式的关闭语义都是显式的。消费 Mode A 的 App 等 done=true;消费 Mode B 的 App 等终态状态。流的模式由声明给出,App 不需要猜。

终止帧

如果一个流没有按准入的终止信号结束,那就是契约违反。Runtime 会发出强类型的失败终止帧,而不是悄悄截断。

模式终止信号
Mode Adone=true
Mode B终态状态事件
Mode Ceof 标记
Mode D会话拆除

如果某个 provider 在流中违反契约(形状错误、缺必填字段、schema 违例),流式契约会发一个强类型失败终止帧。工作流切到 FAILED。不存在静默截断。

反压

流式具备端到端反压。预算从生产者贯通到消费者;消费者慢就向上施压,而不是丢帧。

属性
预算每流独立
方向生产者 → 消费者
溢出处理App 通过 SDK 看到反压;Runtime 不会静默丢帧

这点在长生成里很关键。用户在聊天窗口停手 30 秒,消费者向上施压,生产者暂停。消费者恢复后,流继续。一帧不丢。

Fail-Closed 语义

流式契约失败按 fail-closed 处理:

失败类型行为
帧形状错误强类型失败终止帧;工作流 FAILED
缺必填字段强类型失败终止帧
Schema 违例强类型失败终止帧
MIME 不匹配强类型失败终止帧
瞬时传输错误按传输策略重试;可恢复则流继续
需要刷新鉴权按鉴权策略刷新;可恢复则流继续

重试只救得回传输级失败,不救契约失败。schema 违例属于契约失败,按 fail-closed 处理,不会被重试救成"成功"。

场景:Mode A 文本流

App 发起一次会流式返回的文本生成。

  1. 流打开。 Mode A。Runtime 开始发送文本 chunk。
  2. Chunk 到达。 每个 chunk 都是强类型形状。App 增量渲染。
  3. Provider 抖动。 出现一次瞬时传输错误。Runtime 按传输策略重试。流从合适的边界恢复。
  4. Provider 继续返回内容。 流继续。
  5. 生成完成。 Runtime 发出 done=true 终止帧。
  6. App 标记响应完成。 用户可以进入下一轮。

没有发生过的事情:流从未静默截断。要么到达 done=true,要么发出了强类型失败。

场景:Mode B 工作流事件流

App 订阅某个工作流的事件流。

  1. 流打开。 Mode B。Runtime 开始发送工作流事件。
  2. 事件到达。 STARTED → NODE_STARTED → NODE_PROGRESS → NODE_COMPLETED → ...
  3. 工作流到达终态。 Runtime 发出终态状态事件(COMPLETED / FAILED / CANCELED / SKIPPED)。
  4. 流关闭。 Mode B 关闭语义达成。

App 的 UI 随事件到达增量更新。无须轮询;事件流就是"这个工作流在干什么"的真值来源。

场景:慢消费者引发反压

App 正在消费一段长 Mode A 流。用户打开了一个重型 modal,渲染暂停。

  1. 消费者变慢。 App 的 chunk 处理速率下降。
  2. 反压上行。 SDK 流消费方向上游发出反压信号。
  3. 生产者暂停。 Runtime 按预算暂停 provider 流消费。
  4. 用户关闭 modal。 消费者恢复。
  5. 生产者恢复。 流从合适的边界继续。

无丢帧,无队列爆涨。反压是端到端的。

来源依据

Nimi AI open world platform documentation.