在 Spring AI Alibaba Graph 中,流式输出通过以下方式工作:
流式输出的层次结构
┌─────────────────────────────────────────────────────────────────┐
│ 图级别流式输出 (Graph Level) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Node A │ ───> │ Node B │ ───> │ Node C │ │
│ │ (普通节点) │ │ (流式LLM节点) │ │ (普通节点) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ NodeOutput ┌─────┴─────┐ NodeOutput │
│ │ │ │
│ StreamingOutput StreamingOutput │
│ (Token 1) (Token 2) │
│ │
│ 获取方式: │
│ • graph.stream() → Flux<NodeOutput> │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 节点级别流式输出 (Node Level) │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ StreamingNode (LLM 节点) │ │
│ │ │ │
│ │ chatClient.prompt() │ │
│ │ .user(query) │ │
│ │ .stream() │ │
│ │ .chatResponse() │ │
│ │ │ │
│ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │
│ │ │Token1│→ │Token2│→ │Token3│→ │Token4│→ │Token5│→ ... │ │
│ │ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │ │
│ │ │ │
│ │ 这些 Token 会作为整个图流的一部分输出 │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 流输出数据类型层次 (Data Types) │
│ │
│ NodeOutput │
│ (基类/接口) │
│ │ │
│ ┌─────────────────────┼─────────────────────┐ │
│ │ │ │ │
│ ┌────▼────┐ ┌───────▼───────┐ ┌─────▼─────┐ │
│ │NodeOutput│ │StreamingOutput│ │CustomOutput│ │
│ │(普通节点) │ │ (LLM流式节点) │ │ (用户自定义) │ │
│ └─────────┘ └───────────────┘ └───────────┘ │
│ │
│ 包含内容: │
│ • OverallState (全局状态) │
│ • Message (节点消息) │
│ • Node ID (节点标识) │
└─────────────────────────────────────────────────────────────────┘
详细说明
1. 节点级别流式输出
单独看图中一个具体的 Node 节点,它可能会产生流式输出,比如调用模型得到流式 token 输出,这些 token 会作为整个流输出的一部分。
示例流程:
LLM 节点内部:
Query → LLM API → Token1 → Token2 → Token3 → ... → TokenN
(流式响应)
这些 Token 会被包装成 StreamingOutput,成为图流的一部分
2. 图级别流式输出
站在整个图的视角,图有多个节点且每个节点都会有输出,那么执行图的多个节点自然就形成一个流式过程。有两种方法可以获取整个图的执行流(注意,这包含节点的流式输出内容)。
方法对比:
| 方法 | 返回类型 | 使用场景 |
|---|---|---|
| graph.stream() | Flux<NodeOutput> | 普通图执行,直接获取节点输出流 |
| graph.graphResponseStream() | Flux<GraphResponse<NodeOutput>> | 嵌套子图场景,需要 GraphResponse 包装 |
图流执行示例:
graph.stream() 返回的流:
NodeOutput(node="A", state={...}) ← 节点 A 输出
↓
StreamingOutput(chunk="Hello") ← LLM 节点流式 Token 1
↓
StreamingOutput(chunk=" World") ← LLM 节点流式 Token 2
↓
StreamingOutput(chunk="!") ← LLM 节点流式 Token 3
↓
NodeOutput(node="C", state={...}) ← 节点 C 输出
↓
NodeOutput(node="__END__", state={...}) ← 图执行完成
3. 流输出数据类型
流式输出的核心数据类型是 NodeOutput,代表节点的输出,NodeOutput 中包含整个图的当前全局 OverallState 状态、当前节点的 Message 输出等,不同的节点可能返回不同子类型:
类型层次:
// 基类:所有节点输出的基础类型
NodeOutput {
- node: String // 节点 ID
- state: OverallState // 全局状态
- message: Object // 节点消息
}
// 子类型 1:LLM 流式输出(框架内置)
StreamingOutput extends NodeOutput {
- chunk: String // 流式 Token 内容
}
// 子类型 2:普通节点输出(框架默认)
NodeOutput (普通实例)
// 子类型 3:用户自定义(可扩展)
CustomOutput extends NodeOutput {
- customField: Object // 用户自定义字段
}
使用场景:
- StreamingOutput:框架自动为 LLM 流式节点创建,标识流式 Token 输出块
- NodeOutput:普通节点的标准输出类型
- 自定义类型:用户可以基于 NodeOutput 扩展任意类型,在自定义节点中返回
如果你有多个并行节点(普通节点或者嵌套子图),可以参考 并行节点的流式处理 来了解详情。
- 使用适当的订阅方式:根据需求选择 subscribe()、blockLast() 或其他 Reactor 操作符
- 错误处理:始终使用 doOnError() 处理流式输出中的错误
- 资源清理:确保在流完成或取消时正确清理资源
- 性能考虑:对于大量数据,使用背压(backpressure)机制控制流的速度
- 快速入门 – Graph 基础使用
- Spring Reactor 文档 – Reactor 流式处理参考
本文整理自 Spring AI Alibaba 官方文档《流式输出》:https://java2ai.com/docs/frameworks/graph-core/core/streaming

AI项目博客






