大模型流式输出Streaming Output
流式输出(Streaming Output)是大模型应用中的关键技术,它通过实时分批次返回生成内容,显著提升用户体验。相比传统一次性返回模式,流式输出能降低首字延迟,让用户感知生成过程,减少等待焦虑。技术实现上主要采用SSE、WebSocket等方式,其中SSE因其单向通信和自动重连特性成为首选方案。文章详细解析了流式输出的工作原理、实现方法(含Java/Spring示例代码)及生产环境注意事项,
·
1. 概述
1.1 什么是流式输出(Streaming Output)
流式输出(Streaming Output) 是一种服务端响应模式:
服务端在大模型生成内容的过程中,将已生成的部分实时、分批次返回给客户端,而不是等待全部内容生成完毕后再一次性返回。
在大模型(LLM)场景中,生成过程通常是 token-by-token 的,因此天然适合流式返回。
1.2 为什么需要流式输出
在非流式模式下:
- 用户必须等待模型完全生成结束
- 请求时间稍长就会被误认为“卡死”
流式输出的核心目标是:
- 降低首字延迟(TTFB)
- 显著提升交互体验
- 让用户感知模型正在工作
2. 流式输出的核心价值
2.1 用户体验层面
| 指标 | 非流式 | 流式输出 |
|---|---|---|
| 首字响应 | 高 | 极低 |
| 可感知过程 | 否 | 是 |
| 等待焦虑 | 高 | 低 |
| 交互感 | 弱 | 强 |
2.2 技术层面
- 避免一次性返回超大 JSON
- 减少内存峰值占用
- 支持“边算边推”,提升吞吐
- 更符合大模型的生成机制
3. 工作原理(机制解析)
3.1 非流式请求流程
Client Request
↓
LLM 全量生成
↓
Server 一次性返回完整结果
缺点:
- 首字延迟 = 总生成时间
3.2 流式输出请求流程
Client Request
↓
LLM 生成 token1 → 推送
LLM 生成 token2 → 推送
LLM 生成 token3 → 推送
...
LLM 结束 → 结束信号
关键点:
- 同一个连接
- 多次写响应
- 不中断 HTTP 会话
4. 流式输出的技术本质
4.1 并不是多次请求
流式输出不是轮询,也不是多次 HTTP 请求,而是:
- 一次请求
- 一个连接
- 多次写出(chunk)
4.2 常见底层实现机制
| 技术 | 说明 | 适用性 |
|---|---|---|
| Chunked Transfer | HTTP 分块传输 | 底层基础 |
| SSE | Server-Sent Events | ⭐⭐⭐⭐⭐(推荐) |
| WebSocket | 全双工通信 | ⭐⭐⭐ |
| Reactive Stream | Flux / Flow | ⭐⭐⭐⭐ |
5. 主流实现方式对比
5.1 SSE(Server-Sent Events)——首选
特点:
- 基于 HTTP
- 单向(Server → Client)
- 原生支持自动重连
- 非常适合 AI 文本流
AI 场景适配度:★★★★★
5.2 WebSocket
特点:
- 双向通信
- 支持中途打断生成
问题:
- 实现复杂
- 运维成本高
- 对短连接不友好
5.3 StreamingResponseBody(Servlet)
特点:
- 传统 Spring MVC 可用
- 不依赖 WebFlux
限制:
- 线程阻塞
- 并发能力有限
6. Java / Spring 后端实现示例
6.1 基于 WebFlux + SSE(推荐)
@GetMapping(value = "/ai/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream() {
return Flux.just("你", "好", ",", "世", "界")
.delayElements(Duration.ofMillis(300));
}
返回效果(客户端实时接收):
你
好
,
世
界
6.2 基于 StreamingResponseBody
@GetMapping("/ai/stream")
public StreamingResponseBody stream() {
return outputStream -> {
for (String s : List.of("你", "好", "世界")) {
outputStream.write(s.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
Thread.sleep(300);
}
};
}
7. 前端接收方式
7.1 SSE(EventSource)
const es = new EventSource('/ai/stream');
es.onmessage = (event) => {
console.log(event.data);
};
7.2 fetch + ReadableStream
const response = await fetch('/ai/stream');
const reader = response.body.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log(new TextDecoder().decode(value));
}
8. 大模型 API 中的流式输出
8.1 通用请求参数
{
"stream": true
}
8.2 通用返回结构(示意)
{"delta": "你"}
{"delta": "好"}
{"delta": "世界"}
{"finish_reason": "stop"}
9. 典型业务场景
- AI 对话系统(ChatGPT 类)
- 长文本生成
- 代码生成 / 补全
- 推理过程可视化
- 实时分析 / 日志输出
10. 生产级注意事项(重点)
10.1 错误处理
-
可能在中途失败
-
已经返回部分内容
-
需要:
- error event
- finish_reason
10.2 连接生命周期管理
-
客户端主动断开
-
服务端必须:
- 停止模型生成
- 释放资源
10.3 超时与限流
- 设置最大生成时间
- 防止长连接耗尽资源
11. 架构设计建议(AI 场景)
Client
↓ SSE
API Gateway
↓
AI Service
↓ stream
LLM Provider
建议:
- 网关支持长连接
- AI 服务单独限流
12. 总结
一句话总结:
流式输出不是为了更快完成生成,而是为了让用户更早、更持续地“看到结果”,
用架构复杂度换取极大的交互体验提升。
13. 延伸阅读方向
- Spring AI Streaming ChatModel
- SSE vs WebSocket 选型
- AI 生成中断与取消设计
- Token 级别计费与统计
欢迎关注公众号FishTech Notes,收藏点赞关注哦
更多推荐


所有评论(0)