Next.js接大模型流式SSE实操踩坑

先把结论甩出来:Next.js App Router 里做大模型流式输出,服务端用 Route Handler 直接返回一个 ReadableStream,把模型 SSE 透传给浏览器;前端别急着上 EventSource(它只支持 GET,传不了 body),用 fetch + response.body.getReader() 自己读流就行。整套打通其实就两个文件的事,难点都在那些没人提的细节里。

事情起因是上周我给自己那个内部小工具加了个对话框。之前一次性等模型把几百字吐完再渲染,白屏五六秒,我自己都嫌卡。改成流式,逐字蹦出来,体感立刻不一样了。

服务端:route handler 把流透传出去

app/api/chat/route.ts 长这样:

javascript 复制代码
export const runtime = 'edge' // node 也行,edge 冷启动快点

export async function POST(req: Request) {
  const { messages } = await req.json()

  const upstream = await fetch('https://你的模型网关/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${process.env.LLM_KEY}`,
    },
    body: JSON.stringify({ model: 'xxx', messages, stream: true }),
  })

  // 直接把上游的 SSE 流当成自己的响应体往下游灌
  return new Response(upstream.body, {
    headers: {
      'Content-Type': 'text/event-stream; charset=utf-8',
      'Cache-Control': 'no-cache, no-transform',
      Connection: 'keep-alive',
    },
  })
}

注意 no-transform 那一行,别省。我一开始没写,本地好好的,一上 Nginx 反代就变成等全部生成完才一次性返回------代理把流给缓冲了,排查了快一个钟头才反应过来是这破玩意儿。

如果上游格式跟你前端想要的对不上,就别裸透传,用 TransformStream 中间拆一道 data: 行再重新拼,逻辑也就十几行,这里不展开了。

前端:fetch 读流,逐字渲染

javascript 复制代码
const res = await fetch('/api/chat', {
  method: 'POST',
  body: JSON.stringify({ messages }),
})

const reader = res.body!.getReader()
const decoder = new TextDecoder()
let buf = ''

while (true) {
  const { done, value } = await reader.read()
  if (done) break
  buf += decoder.decode(value, { stream: true })

  // SSE 一条条 \n\n 切,注意半截 chunk 要留着下一轮拼
  const lines = buf.split('\n\n')
  buf = lines.pop() ?? ''
  for (const line of lines) {
    if (!line.startsWith('data:')) continue
    const data = line.slice(5).trim()
    if (data === '[DONE]') return
    const delta = JSON.parse(data).choices[0].delta.content ?? ''
    setText((t) => t + delta) // 蹦字
  }
}

那个 buf.pop() 留半截的细节是真容易翻车------一个 chunk 不保证正好切在 \n\n 上,UTF-8 中文还可能被切两半,TextDecoder 的 { stream: true } 帮你兜住后半个问题,前半个得自己用 buffer 接。

一点题外的取舍

代码不长,但调通那天我顺手干了件偷懒的事。模型这块我没自己折腾,挂了个零代码就能配智能体的平台------拖一拖把提示词、知识库配好,发布成一个 API,我前端这边只管 fetch 那个地址。说实话第一版我让那小助手当客服答疑,回答干巴巴像背说明书,回去把知识库文档喂细了点、温度调低,第二版才像样。它也就帮我省了搭后端和调模型的活,前端流式这套该写还得写,逐字渲染的边界问题它一个都不替你扛。

学习曲线倒不算陡,但配 RAG 那块文档分块策略我摸了大半天才找到手感,不是点两下就完美。

模型/API 我走的讯飞 MaaS,现成调,没自己部署算力,省了一台 GPU 机器钱。

你们接流式踩过哪些代理缓冲、中文截断的坑?评论区聊聊,我赌一半人栽在 no-transform 上。

相关推荐
码农飞哥7 小时前
WorkBuddy 两个月没动,今晚测试红了 2 条
人工智能·效率·ai工具·workbuddy
硅谷秋水7 小时前
面向科学的科学AI智能体:任务、架构、评估与挑战综述
人工智能·科技·语言模型
叼烟扛炮7 小时前
[LangChain] 学习笔记(04):手搓 ChatGPT 打字机——流式输出(SSE)与模型初始化选型
人工智能·笔记·chatgpt·langchain
奶油话梅糖7 小时前
2,615 个知识点、3,003 条先修边:6,800 Star 的开源知识图谱,能顺着错题从高中回溯到小学
人工智能·知识图谱
seacracker8 小时前
不重造 S3 轮子:PowerFS 如何把 RustFS 变成自己的 S3 服务面
人工智能·kv存储·rustfs·powerfs·s3对象存储
IT_陈寒8 小时前
SpringBoot实战技巧总结
前端·人工智能·后端
lucas_AI8 小时前
Agent 基准也会烂:Terminal-Bench 三成任务有缺陷,修复后分数集体上涨 10 个点
人工智能·agent
AI源码8 小时前
基于深度学习的日常服饰分类与推荐系统设计
人工智能·深度学习·分类
johnsong16 小时前
AI前沿日报 · 2026-10-31 | 安全失守——当AI工具被下毒、当云端门户洞开
人工智能·安全
得帆低代码PaaS17 小时前
企业级 Agent 架构:MCP、Integration、数据与治理能力
大数据·人工智能·架构·ai agent·企业数字化·mcp