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

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

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

🧠 说明:

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

五、前端如何接收流

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

相关推荐
凸头2 小时前
Spring Boot接收前端参数的注解总结
前端·spring boot·后端
爱吃甜品的糯米团子2 小时前
JavaScript 正则表达式:选择、分组与引用深度解析
前端·javascript·正则表达式
excel2 小时前
Vue SSR 编译器源码深析:ssrTransformShow 的实现原理与设计哲学
前端
excel2 小时前
深入解析 Vue 3 SSR 编译管线:ssrCodegenTransform 源码全解
前端
excel2 小时前
深入解析 Vue SSR 编译器的核心函数:compile
前端
IT_陈寒2 小时前
Vue 3性能优化实战:7个关键技巧让我的应用加载速度提升50%
前端·人工智能·后端
excel2 小时前
Vue SSR 错误系统源码解析:createSSRCompilerError 与 SSRErrorCodes 的设计原理
前端
excel2 小时前
Vue SSR 源码解析:ssrTransformModel 深度剖析
前端
excel2 小时前
Vue SSR 运行时辅助工具注册机制源码详解
前端