目录
[二、集成 SSE(Server-Sent Events)实战](#二、集成 SSE(Server-Sent Events)实战)
[五、流式 vs 非流式对比](#五、流式 vs 非流式对比)
一、为什么需要流式输出?
-
大模型响应通常需要几秒甚至几十秒,如果等全部生成完再返回,用户体验极差
-
流式输出让用户看到 AI "边想边写",首字响应时间大幅降低(通常在 1 秒内)
二、集成 SSE(Server-Sent Events)实战
- SSE 是 HTML5 标准,服务器向客户端推送事件流,单向通信,适合流式输出场景
java
@RestController
@RequestMapping("/ai")
public class StreamChatController {
@Autowired
private ChatClient chatClient;
/**
* 简洁的写法:直接返回 Flux<String>(Spring 自动转 SSE)
*/
@GetMapping(value = "/stream-simple", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamSimple(@RequestParam String message) {
return chatClient.prompt()
.user(message)
.stream()
.content();
}
}
三、ReactAgent流失输出
java
/**
* Agent 流式对话接口
* 通过 Server-Sent Events (SSE) 实时推送 AI 回复内容
*
* @param message 用户输入的对话消息
* @param response HTTP 响应对象,用于设置响应头防止乱码
* @return Flux<ServerSentEvent<String>> SSE 流式响应,包含 AI 生成的文本片段和结束标记
*/
@GetMapping(value = "/agent_stream")
public Flux<ServerSentEvent<String>> agentStream(@RequestParam String message, HttpServletResponse response) {
// 解决浏览器乱码问题
response.setContentType("text/event-stream;charset=UTF-8");
return Flux.create(sink -> {
try {
// 构建 ReactAgent 实例,配置模型和系统指令
ReactAgent agent = ReactAgent.builder()
.name("StreamAgent")
.model(chatModel)
.instruction("你是智能助手")
.build();
// Agent 流式输出:订阅数据流并处理每个输出片段、错误和完成事件
agent.stream(message)
.subscribe(
// 正常数据块:将每个输出片段转换为 SSE 事件推送给客户端
chunk -> sink.next(ServerSentEvent.<String>builder()
.data(String.valueOf(chunk))
.build()),
// 异常情况:将错误传递给 Sink
error -> sink.error(error),
// 完成回调:发送结束标记并完成流
() -> {
sink.next(ServerSentEvent.<String>builder()
.data("[DONE]").build());
sink.complete();
}
);
} catch (Exception e) {
sink.error(e);
}
});
}
四、前端对接SSE示例
java
// 前端接收 SSE 流式输出
function streamChat(message) {
const eventSource = new EventSource(
`/ai/stream?message=${encodeURIComponent(message)}`
);
let fullContent = '';
eventSource.onmessage = (event) => {
if (event.data === '[DONE]') {
eventSource.close();
console.log('流式输出完成:', fullContent);
return;
}
// 逐字追加到页面
fullContent += event.data;
document.getElementById('output').innerText = fullContent;
};
eventSource.onerror = (error) => {
console.error('SSE 连接错误:', error);
eventSource.close();
};
}
五、流式 vs 非流式对比
| 维度 | 非流式(call) | 流式(stream) |
|---|---|---|
| 等待时间 | 等待全部生成完成 | 立即开始接收第一个片段 |
| 用户体验 | 长时间白屏 | 打字机效果,流畅自然 |
| 适用场景 | 简短回答、需要完整处理结果 | 长文本生成、对话交互 |
| 技术实现 | 普通 HTTP 请求 | SSE / WebSocket |
| 错误处理 | 统一异常处理 | 需要处理流中断 |
六、注意事项
-
SSE 是单向推送(服务器→客户端),无法客户端→服务器实时发送;双向需用 WebSocket
-
Nginx 反向代理时需要关闭缓冲:
proxy_buffering off;,否则 SSE 不会实时推送 -
流式输出中发生异常,要及时关闭流并通知客户端,避免客户端永久等待