Next.js App Router 里做 AI 流式输出

最近把一个 AI 对话页迁到 Next.js App Router。流式输出在 Pages Router 时代我闭着眼都能写,到了 App Router + RSC + server action 这套新范式,卡了我大半天。把过关的路记下来。

第一个纠结:用 Route Handler 还是 Server Action

App Router 下做流式有两条路。

Route Handler( app/api/chat/route.ts 是最稳的,本质还是返回一个标准的 Response,body 塞一个 ReadableStream

javascript 复制代码
export async function POST(req: Request) {
  const stream = new ReadableStream({
    async start(controller) {
      const res = await callModel(/* ... */)
      for await (const chunk of res) {
        controller.enqueue(
          new TextEncoder().encode(chunk.delta)
        )
      }
      controller.close()
    },
  })
  return new Response(stream, {
    headers: { 'Content-Type': 'text/event-stream' },
  })
}

客户端老老实实 fetch + getReader() 读,跟以前没两样。

Server Action 看起来更"新潮",能直接在组件里 await 调用。但纯 server action 做逐字流式其实很别扭------它是为表单提交和数据变更设计的,不是天生为长连接流式准备的。要硬做得配合 useActionState 或返回一个可迭代的东西,绕。我试了一版,体感不如 Route Handler 直接。

我的选择

流式走 Route Handler,server action 只用来做非流式的副作用,比如把这轮对话存库、更新会话标题。各干各的,别让 server action 硬扛流式。

踩到的坑

坑一:动态渲染。 默认有缓存,对话接口必须显式 export const dynamic = 'force-dynamic',否则你会发现流式接口被缓存住,第二次问返回上次的内容,人都傻了。

坑二:客户端组件边界。 渲染对话气泡、维护输入状态的那部分必须 'use client'。我一开始忘加,useState 直接报错,App Router 默认是 server component 这点得时刻记着。

坑三:Edge runtime 的流式更顺,但部分 Node API 用不了,按需取舍。

没做完美的地方

server action 存库失败时的回滚我做得很糙,目前就打个日志,没做真正的补偿。先用着。

模型那一端我接的讯飞 MaaS,现成的推理服务直接调,不折腾自建。你们 App Router 下流式是走 Handler 还是 Action?评论区交个底。

相关推荐
一名普通的电源工程师6 分钟前
WRF2424S‑3WR2 适配优选 钡特电源 VF3‑24S24S|3W 工业 DC‑DC 模块电源选型拆解
人工智能·电源模块·工业电源
野三关彭于晏7 分钟前
Agent 时代,如何用 AI 重塑教育信息化
人工智能·agent
哔哩哔哩技术15 分钟前
IndexTTS 2.5 让声音跨越语言
人工智能
Mr_凌宇26 分钟前
codex开启1M上下文
人工智能·程序员
AI英德西牛仔27 分钟前
豆包导出 pdf 颜色不一样怎么办,选用 AI 导出鸭优化文档导出,结合行业白皮书数据解析色彩失真成因
人工智能·ai·chatgpt·pdf·deepseek·ai导出鸭
神奇小汤圆28 分钟前
RAG检索优化实战:从67%到92%,我做了这4步
人工智能
老司机张师傅42 分钟前
让 AI 之间自动对话:基于 TRAE Solo 的多智能体团队协作系统设计与实现
人工智能·aiaitrae
一知半解仙1 小时前
大国博弈与机器人纪元:我们正站在怎样的历史拐点?
大数据·人工智能·机器人
qpsj1 小时前
DeepSeek 缓存命中涨价 12 倍:4 个改动,账单回到涨价前
人工智能·llm
哈尔ai1 小时前
分布式锁没有失效:真正危险的是锁与事务的提交边界
人工智能