在 SpringBoot 后端中,如果前端发送请求后,后端依次调用 Dify 工作流A(含工具调用+大模型) 和 工作流B,并希望最终以 流式输出(Streaming) 返回给前端,是可以实现的。但需要注意 异步编排、数据流合并、协议兼容性 等问题。以下是完整实现方案:
1. 整体架构设计
2. 关键实现步骤
(1) SpringBoot 配置 SSE(Server-Sent Events)
@RestController
@RequestMapping("/api")
public class StreamController {
@GetMapping(path = "/stream-chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat() {
// 合并多个工作流的流式响应
return Flux.merge(
callDifyWorkflowA(),
callDifyWorkflowB()
);
}
private Flux<String> callDifyWorkflowA() {
// 调用 Dify 工作流A(假设返回 Flux<String>)
return WebClient.create("http://dify-api/workflow-a")
.get()
.accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(String.class)
.map(data -> "[Workflow A] " + data); // 添加标记
}
private Flux<String> callDifyWorkflowB() {
// 调用 Dify 工作流B(工具调用可能在此触发)
return WebClient.create("http://dify-api/workflow-b")
.get()
.accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(String.class)
.map(data -> "[Workflow B] " + data);
}
}
(2) 前端接收流式数据
const eventSource = new EventSource("/api/stream-chat");
eventSource.onmessage = (e) => {
console.log("收到流式数据:", e.data);
// 示例输出:
// [Workflow A] 工具调用: 计算完成
// [Workflow A] 模型回复: 结果是42
// [Workflow B] 模型回复: 补充说明...
};
(3) Dify 工作流的关键配置
- 工具调用与流式兼容:
Dify 工作流需配置为 流式返回工具中间结果(如先返回{"tool_used": "calculator"}
,再返回模型生成的文本)。 - 超时处理:
在 SpringBoot 中设置超时(避免流无限挂起):.timeout(Duration.ofSeconds(30)) // 超时设置 .onErrorResume(e -> Flux.just("【超时】"));
3. 解决核心问题
(1) 多个工作流的流式合并
Flux.merge()
:并行合并多个流(谁先返回数据谁先输出)。Flux.concat()
:串行合并(先完整返回A,再返回B)。
(2) 工具调用的流式交互
- Dify 工作流内部:
工具调用结果需通过yield
逐步返回(而非一次性返回),例如:# Dify 工作流示例(Python伪代码) def workflow_a(): yield "正在调用计算工具..." tool_result = execute_tool("1+1") # 同步调用 yield f"工具结果: {tool_result}" yield model.generate("解释结果...") # 流式生成
(3) 协议一致性
- 统一数据格式:
所有工作流返回的数据需保持一致格式(如纯文本或 JSON 包裹的{type: "tool/text", data: ...}
)。
4. 高级优化方案
(1) 动态工作流链
- 根据条件调用不同工作流:
Flux<String> dynamicFlow = requestParam.equals("A") ? callDifyWorkflowA() : callDifyWorkflowB();
(2) 背压(Backpressure)控制
- 防止前端处理不过来:
.onBackpressureBuffer(100) // 缓冲100条数据
(3) 断线重连
- 前端监听
onerror
并重新连接:eventSource.onerror = () => { setTimeout(() => new EventSource(...), 1000); };
5. 完整示例场景
- 用户提问:
“计算3的平方,然后解释数学意义。” - 流式响应:
[Workflow A] 工具调用: square(3)=9
[Workflow A] 模型回复: 3的平方是9,代表...
[Workflow B] 模型回复: 补充:平方运算在几何中...
6. 注意事项
- Dify 工作流需支持流式:确认其 API 是否返回
Transfer-Encoding: chunked
。 - 性能监控:使用 Micrometer 跟踪流式请求的延迟和吞吐量。
- 错误隔离:某个工作流失败时不影响其他流(如用
onErrorContinue
)。
如果需要更具体的 Dify 工作流配置或 SpringBoot 调优技巧,可以进一步讨论!