FastAPI的 StreamingResponse在被Next.js的 fetch调用时,会把语言模型输出的token缓冲到完成才会输出。如何解决?

人工智能 2026-07-08

我正在用Next.js作为前端、FastAPI作为后端,构建一个AI聊天功能。

后端从LLM提供商获取token,并通过StreamingResponse将它们返回给浏览器。我的目标是让浏览器在后端产出每个token/分块时就渲染:

chunk 1
chunk 2
chunk 3
...

问题在于前端只有在生成完成后才收到整条响应。后端日志显示分块一个接一个地产出,但浏览器在完整流完成之前不会显示任何内容:

chunk 1 chunk 2 chunk 3 ...

后端示例:

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio

app = FastAPI()

async def token_generator():
    for i in range(10):
        chunk = f"token-{i}\n"
        print("yielding:", chunk.strip(), flush=True)
        yield chunk
        await asyncio.sleep(0.5)

@app.get("/stream")
async def stream():
    return StreamingResponse(
        token_generator(),
        media_type="text/plain",
        headers={
            "Cache-Control": "no-cache",
            "X-Accel-Buffering": "no",
        },
    )

前端示例:

export default async function readStream() {
  const response = await fetch("http://localhost:8000/stream", {
    method: "GET",
    cache: "no-store",
  });

  if (!response.body) {
    throw new Error("No response body");
  }

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { value, done } = await reader.read();

    if (done) break;

    const chunk = decoder.decode(value, { stream: true });
    console.log("received:", chunk);
  }
}

我尝试了以下方法:

  1. 使用 StreamingResponse 代替返回一个普通的JSON响应。
  2. 在产出之间添加 await asyncio.sleep() 以确认生成器不会立即完成。
  3. 在后端日志中添加 flush=True
  4. 设置 Cache-Control: no-cache
  5. 添加 X-Accel-Buffering: no
  6. 使用 response.body.getReader() 读取响应,而不是 await response.text()

后端日志确认生成器每500ms产出一次,但浏览器仍然在完整响应完成后才接收数据。

在这样的设置中,缓冲的常见原因有哪些?

具体来说,我想了解问题很可能由以下哪些因素引起:

  • FastAPI/Starlette行为
  • 浏览器 fetch() 行为
  • text/plaintext/event-stream 的差异
  • 本地开发服务器行为
  • 反向代理缓冲,例如Nginx
  • Next.js路由/运行时行为
  • 缺少换行符或SSE格式的问题

生产环境中从FastAPI向 Next.js前端无缓冲地流式传输LLM tokens的正确且安全的方法是什么?

解决方案

Use “text/event-stream” instead of a content type of “text/plain”.

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章