🚀 从 GPT-5 流式输出看现代前端的流式请求机制(Koa 实现版)

260 阅读3分钟

一、前言:为什么要“流式输出”?

传统 HTTP 请求是「一次性返回完整结果」,而大模型(如 GPT-5)生成内容的过程往往比较慢。
如果要让用户看到“边生成边显示”的效果(像 ChatGPT 打字机一样),
就必须使用 流式响应(Streaming Response)


二、流式响应不是新协议

很多人会以为流式请求是某种新的 HTTP 方法,其实不是。

✅ 流式是 响应体的行为,不是请求方式的变化。
服务端仍然用普通的 POST,只是不会一次性 res.end(),而是持续往里写数据。

因此 GPT-5 的流式接口看起来是这样的:

POST /v1/chat/completions
Content-Type: application/json
Accept: text/event-stream

服务器边生成边 write(),客户端边读边显示。


三、为什么必须用 POST?

因为 GPT-5 请求通常包含复杂 JSON:

{
  "model": "gpt-5",
  "messages": [
    { "role": "user", "content": "解释量子纠缠" }
  ],
  "temperature": 0.7,
  "stream": true
}

只有 POST 请求 才能合法地携带请求体(body)。
GET 请求虽然也能带 query 参数,但长度有限、结构不适合复杂 JSON。

🧠 所以:“流式”是响应的特征,而“POST”是为了传递参数。


四、Koa 实现:服务端流式输出示例

下面是最小可运行的 Koa 流式响应示例

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"));

🧠 说明:

  1. ctx.set("Content-Type", "text/event-stream") 告诉浏览器使用 SSE 流式响应
  2. 每个 ctx.res.write() 会立即发送一部分内容;
  3. 客户端不需要多次请求,而是持续读取同一个响应流。

五、前端如何接收流

前端用原生 fetch() 即可实现流式读取:

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 传?

可以,但不推荐。
例如:

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 辅助生成,并由作者整理审核。