一、前言:为什么要"流式输出"?
传统 HTTP 请求是「一次性返回完整结果」,而大模型(如 GPT-5)生成内容的过程往往比较慢。
如果要让用户看到"边生成边显示"的效果(像 ChatGPT 打字机一样),
就必须使用 流式响应(Streaming Response) 。
二、流式响应不是新协议
很多人会以为流式请求是某种新的 HTTP 方法,其实不是。
✅ 流式是 响应体的行为 ,不是请求方式的变化。
服务端仍然用普通的
POST,只是不会一次性res.end(),而是持续往里写数据。
因此 GPT-5 的流式接口看起来是这样的:
bash
POST /v1/chat/completions
Content-Type: application/json
Accept: text/event-stream
服务器边生成边 write(),客户端边读边显示。
三、为什么必须用 POST?
因为 GPT-5 请求通常包含复杂 JSON:
json
{
"model": "gpt-5",
"messages": [
{ "role": "user", "content": "解释量子纠缠" }
],
"temperature": 0.7,
"stream": true
}
只有 POST 请求 才能合法地携带请求体(body)。
GET 请求虽然也能带 query 参数,但长度有限、结构不适合复杂 JSON。
🧠 所以:"流式"是响应的特征,而"POST"是为了传递参数。
四、Koa 实现:服务端流式输出示例
下面是最小可运行的 Koa 流式响应示例:
javascript
import Koa from "koa";
import Router from "@koa/router";
import bodyParser from "koa-bodyparser";
const app = new Koa();
const router = new Router();
router.post("/stream", async (ctx) => {
const { prompt } = ctx.request.body;
ctx.set("Content-Type", "text/event-stream");
ctx.set("Cache-Control", "no-cache");
ctx.set("Connection", "keep-alive");
// 模拟 GPT-5 边生成边输出
const text = `你发送的提示词是:${prompt}。\n下面是流式输出示例:`;
for (const ch of text) {
ctx.res.write(`data: ${ch}\n\n`);
await new Promise((r) => setTimeout(r, 50)); // 模拟生成延迟
}
ctx.res.write("data: [DONE]\n\n");
ctx.res.end();
});
app.use(bodyParser());
app.use(router.routes());
app.listen(3000, () => console.log("🚀 Server on http://localhost:3000/stream"));
🧠 说明:
ctx.set("Content-Type", "text/event-stream")告诉浏览器使用 SSE 流式响应;- 每个
ctx.res.write()会立即发送一部分内容; - 客户端不需要多次请求,而是持续读取同一个响应流。
五、前端如何接收流
前端用原生 fetch() 即可实现流式读取:
javascript
const res = await fetch("http://localhost:3000/stream", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "你好,GPT-5!" }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder("utf-8");
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
console.log("部分响应:", chunk);
}
控制台会实时打印字符流。
在真实应用中,你可以把这些内容插入编辑器或页面。
六、EventSource 可以替代吗?
不行(至少不完全行)。
EventSource 是浏览器内置的 SSE 客户端,但它:
- ❌ 只支持
GET请求; - ❌ 不能带请求体;
- ✅ 适合广播、通知,而不是 GPT 请求。
所以如果你只要订阅服务器事件(例如"系统状态更新"),可以用 EventSource;
但要给模型发 prompt、传 JSON,就必须用 fetch + ReadableStream。
七、能不能用 ?query 传?
可以,但不推荐。
例如:
javascript
const source = new EventSource(`/stream?prompt=${encodeURIComponent("你好")}`);
这种方式仅适合短文本、教学演示。
如果 prompt 很长,或者要传多条 messages,URL 会溢出或被记录到日志中。
所以 GPT-5 这类接口建议还是:
✅
POST携带 JSON body✅ 服务端用
text/event-stream返回流式内容
八、对比三种实时通信方式
| 技术 | 请求体 | 响应方式 | 实时性 | 双向通信 | 适用场景 |
|---|---|---|---|---|---|
| fetch + ReadableStream | ✅ 支持 | SSE / chunk | ✅ 高 | ❌ 单向 | GPT-5 / AI 输出 |
| EventSource | ❌ 不支持 | SSE | ✅ 高 | ❌ 单向 | 系统日志、状态广播 |
| WebSocket | ✅ 支持 | Binary/Text | ✅ 最高 | ✅ 双向 | 聊天、协作、游戏 |
💡 GPT-5 这类接口的最佳实践:
POST + fetch + ReadableStream
九、结语
流式请求的核心并不在于"请求方式",
而在于响应体的 分块(chunked transfer) 与浏览器对流的解析能力。
现代浏览器对流的支持越来越好,
这让我们能在前端直接用标准 API 实现 ChatGPT 那样的实时输出体验。
所以------
流式输出依然是 POST 请求,
只是响应体变成了连续的数据流。
本文部分内容借助 AI 辅助生成,并由作者整理审核。