标准回答
后端:开启流式
调用大模型 API 时传 stream=true,模型不再等全部生成完,而是一边生成一边把 token 分片推回来。后端用 SSE(text/event-stream)把这些分片逐条转发给前端,每条形如 data: {...}。
前端:边收边渲染
前端用 EventSource 或 fetch 的 ReadableStream 接收,每收到一片就把文本累加到当前消息末尾并重新渲染,视觉上就是「逐字蹦出」的打字机效果。用 requestAnimationFrame 或节流控制刷新频率,避免高频重渲染卡顿。
关键细节
- 分片是按 token 切的,可能把一个 markdown 语法(如 ``` 代码块)或一段 JSON 切成两半。要先把分片缓冲累积成完整文本,等收到结束信号再做 markdown 渲染或 JSON 解析。
- 用 AbortController 支持用户「停止生成」,主动断开连接。
- 监听错误事件,网络中断或服务报错时停止动画并提示「生成中断,可重试」,别让光标一直闪。
常见误区
⚠️ 常见踩坑
对每个分片单独做 JSON.parse 或 markdown 渲染,结果因分片把语法截断而频繁报错或乱码;正确做法是累积成完整文本后再解析。
追问
追问 1:SSE 和 WebSocket 在这个场景怎么选?
流式输出是单向的(服务端持续推、客户端只接收),SSE 足够且更轻量:基于 HTTP、自动重连、实现简单。需要双向实时交互(如语音对话、协同编辑)才上 WebSocket。多数大模型聊天 UI 用 SSE。
追问 2:流式输出怎么统计 token 和成本?
流式响应通常在最后一个分片或结束事件里带 usage(输入/输出 token 数),后端在流结束时读取并记账。如果 API 不返回 usage,可在服务端累积完整输出后用 tokenizer 估算输出 token 数。
延伸学习
与本题相关的知识库文章、术语、工具与行业资讯。