流式输出与SSE
本页深入介绍 LLM 应用中不可或缺的交互模式——流式输出(Streaming):模型生成的文本逐 token 实时返回给用户,而非等全部生成完才一次性返回。它通过 Server-Sent Events(SSE)等协议实现,是 ChatGPT、Cursor 等产品流畅体验的技术基石,与LLM 推理优化中的延迟优化紧密相关。
流式输出 = 打字机 vs 传真机。 非流式像传真机——你发完后干等,对方整页接收完才看到内容(LLM 生成 500 字要等 10 秒,用户面对空白屏幕)。流式像打字机——每个字打出来你立刻看到,用户几乎感觉不到等待。
- Token-by-Token:模型每生成一个 token 就发送一次,延迟最低。大多数 LLM API 的默认模式。
- Chunk-by-Chunk:攒几个 token 再一起发,减少网络请求。适合高并发。
- SSE(Server-Sent Events):HTTP 长连接,服务器单向持续推送数据。LLM 流式输出的标准传输方式。
- WebSocket:全双工通信,适合需要客户端也实时发消息的场景(如实时语音对话)。
为什么需要流式输出
Section titled “为什么需要流式输出”LLM 是自回归生成(Autoregressive Generation)的——逐 token 依次生成,每个 token 依赖前面所有 token,不可能并行。非流式模式下 200 token 的回答要等 2-5 秒,用户面对空白屏幕,感知延迟等于总生成时间。流式模式下第一个 token 生成后立刻发送,用户感知的延迟是首 token 延迟(TTFT, Time To First Token),通常只有 200-500ms。虽然总时间不变,但信息持续流动,大脑对”有进展”的等待容忍度远高于”干等”。这与解码策略中的自回归解码过程直接相关。
流式输出的核心指标
Section titled “流式输出的核心指标”| 指标 | 全称 | 含义 | 典型值 | 优化手段 |
|---|---|---|---|---|
| TTFT | Time To First Token | 请求发出到第一个 token 返回 | 200-800ms | Prefill 加速、Prompt 缓存、缩小模型 |
| TPS | Tokens Per Second | 生成阶段每秒输出 token 数 | 30-150 t/s | 投机解码、量化、更优硬件 |
| 完成率 | Completion Rate | 成功完整输出的请求比例 | >99% | 重连机制、超时调优 |
用户体验的”快”主要由 TTFT 主导——看到第一个字后心理等待感就大幅降低。详见LLM 推理优化。
流式传输协议深度解析
Section titled “流式传输协议深度解析”SSE 线缆格式(Wire Format)详解
Section titled “SSE 线缆格式(Wire Format)详解”SSE(Server-Sent Events)是 HTML5 标准定义的基于 HTTP 的服务器推送协议。它的线缆格式极其简单——纯文本,按行解析。理解这个格式是掌握所有 LLM 流式 API 的基础。
一个 SSE 事件由若干行组成,以**两个换行符(\n\n)**结束。每行是一个”字段: 值”对:
id: 42event: tokenretry: 5000data: {"choices": [{"delta": {"content": "你好"}}]}SSE 规范定义了五个字段:
| 字段 | 作用 | LLM 场景中的使用 |
|---|---|---|
data | 事件的数据载荷,可以有多行(多行 data: 会用 \n 拼接) | 核心——存放 JSON 格式的 token delta |
event | 事件类型(客户端可按类型注册不同监听器) | Anthropic 用它区分 message_start、content_block_delta 等事件 |
id | 事件 ID,浏览器会自动记住最后一个 ID,重连时通过 Last-Event-ID 头发回 | 可用于断点续传(但 LLM 流通常不续传,而是重新生成) |
retry | 建议的重连等待时间(毫秒),浏览器 EventSource 自动遵守 | 用于控制客户端重连节奏 |
: (注释) | 以冒号开头的行是注释,被忽略 | 常用作 keepalive 心跳——发送 : heartbeat\n\n 防止代理超时断连 |
关键规则:
- UTF-8 纯文本:整个流是
text/event-stream,不是二进制(与 WebSocket 不同)。 \n\n分隔事件:客户端靠双换行切分事件边界。- 自动重连:浏览器
EventSource在断开时按retry间隔自动重连并带Last-Event-ID头。但 OpenAI 等用 POST 的 API 不能用EventSource(只支持 GET),需手动fetch+ 重连。 - 注释行做心跳:LLM prefill 阶段可能 1-3 秒不产出 token,中间发
:\n\n让代理知道”连接还活着”。
SSE 工作流程
Section titled “SSE 工作流程”SSE vs WebSocket vs WebTransport vs HTTP/2 流式
Section titled “SSE vs WebSocket vs WebTransport vs HTTP/2 流式”| 特性 | SSE | WebSocket | WebTransport | HTTP/2 Streaming |
|---|---|---|---|---|
| 方向 | 服务器→客户端(单向) | 全双工 | 全双工 | 双向 |
| 底层 | HTTP/1.1 或 HTTP/2 | HTTP 升级到 ws:// | HTTP/3 (QUIC) | HTTP/2 |
| 自动重连 | ✅ 浏览器内置 | ❌ 手动 | ❌ 手动 | ❌ 手动 |
| 代理/CDN 友好 | ✅ 普通 HTTP | ⚠️ 需升级 | ⚠️ 需 HTTP/3 | ✅ |
| 典型 LLM 场景 | 文本流式输出 | GPT-4o 实时语音 | 未来低延迟场景 | 自建推理服务 |
SSE 为什么赢了 LLM 文本流式:够用(文本生成只需单向推送)、HTTP 兼容(所有代理、CDN、防火墙天然支持)、简单(纯文本,前端一个 fetch 就能解析)、EventSource 内置自动重连。什么时候用 WebSocket:只有需要客户端→服务器也实时发消息时——典型是 GPT-4o 实时语音、AI 多人协作、实时游戏。
连接生命周期管理
Section titled “连接生命周期管理”SSE 是长连接,需要精心管理整个生命周期:
- Keepalive 心跳:在 prefill 等待阶段或思考模型(如 o1)的长推理阶段,服务器可能数秒不产出 token。定期发送注释行(
: keepalive\n\n)防止中间代理因空闲超时断连,推荐间隔 15-30 秒。 - 重连:客户端检测到断开后需重新发请求。但 LLM 流不支持断点续传(KV-Cache 已丢失),重连后只能从头重新生成。浏览器
EventSource会自动重连(并带Last-Event-ID头),但手动fetch不会——需在read()出错时手动重试。 - 优雅关闭:流结束时发送明确标记(OpenAI 用
data: [DONE],Anthropic 用event: message_stop),让客户端知道是正常结束而非网络中断。 - 客户端取消:用户点”停止生成”时,客户端应
AbortController.abort()断开,并通知服务器停止生成以节省 GPU。
主流厂商流式协议
Section titled “主流厂商流式协议”不同 LLM 厂商的流式 API 在 SSE 之上定义了各自的 JSON 事件格式。理解这些格式差异对于做多模型适配层非常重要。
OpenAI 流式格式
Section titled “OpenAI 流式格式”OpenAI 的流式 API 使用统一的 data: 行,每行是一个 JSON 对象,核心是 choices[].delta:
data: {"choices":[{"delta":{"role":"assistant"},"finish_reason":null}]} ← 第一个 chunk 只有 roledata: {"choices":[{"delta":{"content":"你"},"finish_reason":null}]} ← 后续 chunk 是 content 增量data: {"choices":[{"delta":{"content":"好"},"finish_reason":null}]}data: {"choices":[{"delta":{},"finish_reason":"stop"}]} ← 最后一个 chunk 给 finish_reasondata: [DONE] ← 纯文本结束标记,不是 JSON要点:
- 第一个 chunk 只有
role(如{"delta": {"role": "assistant"}}),此时还没有内容。 finish_reason在最后一个 chunk:值为stop(正常结束)、length(达到 max_tokens)、tool_calls(触发函数调用)等。- Tool call 也是流式的:
delta.tool_calls逐步给出函数名和参数,参数是 JSON 字符串的片段(如先到{"ci,再到ty":"Beijing"}),需要客户端拼接后再解析。这与结构化输出和工具使用紧密相关。
Anthropic 流式格式
Section titled “Anthropic 流式格式”Anthropic(Claude)的流式协议更细粒度,使用 SSE 的 event: 字段区分多种事件类型,是一个真正的事件流模型:
event: message_start ← 消息开始,含模型信息和 input_tokensdata: {"type":"message_start","message":{"id":"msg_1","model":"claude-3.5-sonnet","usage":{"input_tokens":25}}}
event: content_block_start ← 一个内容块开始(text / tool_use / thinking)data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta ← 内容块增量文本(核心,重复多次)data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"你好"}}
event: content_block_stop ← 内容块结束data: {"type":"content_block_stop","index":0}
event: message_delta ← 消息级更新(stop_reason、累计 output_tokens)data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":15}}
event: message_stop ← 消息完全结束data: {"type":"message_stop"}事件类型包括:message_start(消息开始,含模型信息和 input_tokens)、content_block_start/content_block_delta/content_block_stop(内容块生命周期,每个块有独立 index)、message_delta(stop_reason 和累计 output_tokens)、message_stop(结束)、ping(心跳)。
Anthropic 的设计更适合混合内容(一段回复中既有文本又有工具调用,甚至有 thinking 思考块),因为每个内容块有独立的 index 和生命周期。OpenAI 则用扁平的 choices[].delta 表达。
Token 用量统计的差异
Section titled “Token 用量统计的差异”流式模式下 token 计费统计各家不同:OpenAI 标准流式需 stream_options: {include_usage: true} 才在最后一个 chunk 返回 usage(早期完全不返回);OpenAI Responses API(2024 年新架构)在响应末尾返回完整 usage;Anthropic 分阶段给出——message_start 给 input_tokens,message_delta 给累计 output_tokens,更利于实时成本监控。如果上游不提供流式 usage,只能用 tokenizer(如 tiktoken)本地估算。
前端渲染挑战
Section titled “前端渲染挑战”流式输出的前端渲染远比想象中复杂——你要渲染的是一段正在生长的、不完整的 Markdown/代码/表格。
渐进式 Markdown 渲染
Section titled “渐进式 Markdown 渲染”LLM 回复包含 Markdown,但流式过程中语法是不完整的——收到 ```python\nimport os 时代码块未闭合,**加粗 缺少闭合 **,| 列1 | 列2 | 时表格还没闭合。常用策略:每次收到新 token 都用 markdown-it、marked、remark 等库全文重新解析(O(n),对几千 token 可接受);好的解析器会容错处理未闭合语法。解析后用 requestAnimationFrame 合并到下一帧渲染,避免每个 token 都触发 DOM 更新。
代码高亮 / 表格 / LaTeX 的流式处理
Section titled “代码高亮 / 表格 / LaTeX 的流式处理”流式中还有几类内容需要特殊处理:
- 代码语法高亮:高亮库(Shiki、Prism、highlight.js)假设输入是完整代码,流式中括号匹配和字符串闭合判断不稳定,导致高亮每帧剧烈跳动。实用做法:代码块未闭合时用灰色纯文本显示,收到结束
```后再一次性高亮。 - 表格:分隔行(
|---|---|)到达前无法确认是表格。可以把疑似表格的行缓冲起来,分隔行到达后再一次性渲染。之后数据行到达一行渲染一行。 - 列表:列表项逐个到达,相对简单。注意有序列表的编号会随新项到达而变化。
- LaTeX 数学公式:未闭合的
$$E=mc直接渲染 KaTeX 会报错。检测到$$开始后进入”公式缓冲模式”,攒起来不渲染,直到收到闭合$$后一次性渲染。
token 到达速度可能不均,每个 token 都立即更新 DOM 会导致高频重绘卡顿和视觉闪烁(尤其不稳定的高亮和布局变化)。关键技术:
requestAnimationFrame(rAF)合并更新:不每个 token 都渲染,而是攒起来每个动画帧(约 16ms)只渲染一次——最核心的防闪烁手段。- 双缓冲(Double Buffering):内存中构建好新 DOM 结构,一次性替换,避免中间态闪烁。
- 限制重解析频率:Markdown 全文重新解析可限制为每 50-100ms 一次。
- CSS containment:用
contain: content限制 reflow 范围。 - 智能自动滚动:用户在底部附近时自动滚动,手动上滚查看历史时不滚。
结构化输出的流式解析
Section titled “结构化输出的流式解析”当 LLM 被要求输出 JSON 时(如 function calling、JSON mode),流式收到的是不完整的 JSON 片段——这是流式场景中最棘手的工程问题之一。
问题:不完整的 JSON 无法解析
Section titled “问题:不完整的 JSON 无法解析”标准 JSON.parse() 遇到任何不完整片段都会抛异常——{"name": "张三", "a(字符串未闭合)、"age": 2(值未完成)、{"name":"张三"(缺少 })。但 UI 上我们希望”名字一到就显示名字,年龄一到就显示年龄”。
增量 JSON 解析
Section titled “增量 JSON 解析”思路:用容错解析器从不完整 JSON 中提取已完整的部分(best-effort / partial JSON parsing):
from partial_json_parser import allow
def on_token(delta: str): buffer += delta try: partial = loads(buffer, allow.ALL) # 容忍未闭合的括号、引号 update_ui(partial) except: pass # 等更多 token容错策略:
| 不完整情况 | 容错做法 |
|---|---|
| 字符串/数组/对象未闭合 | 自动补上闭合符后解析 |
值未完成("age": 2) | 不确定是 2 还是 25——保守地不返回该字段 |
键未完成("name) | 无法判断键名——跳过 |
常用库:JS/TS 端有 partial-json、best-effort-json-parser、jsonrepair;Python 端有 partial-json-parser。
流式 JSON Schema 校验
Section titled “流式 JSON Schema 校验”如果用 JSON Schema 约束输出(详见结构化输出和受限解码),可以在流式过程中做增量 Schema 校验:每收到一个新字段就校验是否符合 Schema,不符合的提前报错。一些推理引擎(如 llama.cpp 的 GBNF 语法、outlines)甚至在生成阶段就强制符合语法——但这属于服务端能力,前端只能做”事后校验”。
实用模式:字段到达即展示
Section titled “实用模式:字段到达即展示”UI 上最常见的模式是”字段到达即展示”——不等整个 JSON 完整,已解析出的字段立即渲染到对应 UI 区域。例如,一个查询天气的工具调用,UI 可以在 city 字段完整时立即显示”正在查询北京天气”,而不必等整个 JSON(含 date、unit 等字段)完整。
LLM 生成 token 的速度(GPU 端 100-500 t/s)可能远快于客户端消费的速度(网络 + 渲染)。当上游产出快于下游消费时,多余数据必须在某处缓冲,无限缓冲会撑爆内存——这就是**背压(Backpressure)**问题。
当下游跟不上时有三种策略:排队(无限缓冲全部保留——LLM 流几乎都用这个,token 不能丢)、丢弃(丢多余数据——不适合文本流)、节流(上游减速——LLM 生成速度受硬件决定,难主动降速)。实际中背压沿 GPU → 网络 → 代理 → 客户端 → DOM 逐级传导:客户端 rAF 合并是最关键的节流(每帧只渲染一次);TCP 接收窗口满时操作系统自动让 send() 阻塞,天然传导回引擎;高级引擎(vLLM)在客户端消费慢时暂停 token 产出,释放 GPU。一般短回复无问题,长回复 + 慢渲染 + 高并发才需关注。
实时语音/视频流式(2025)
Section titled “实时语音/视频流式(2025)”文本流式用 SSE 足矣,但实时语音/视频需要双向、低延迟的流——这超出了 SSE 的能力,必须用 WebSocket 或 WebTransport。
GPT-4o Realtime API
Section titled “GPT-4o Realtime API”OpenAI 在 2024 年底推出的 GPT-4o Realtime API 是实时语音交互的标杆。它基于 WebSocket,支持音频同时双向流式传输——客户端和服务器同时收发音频帧,SSE 的单向推送做不到这一点。
关键技术点:WebSocket 全双工让客户端和服务器同时收发音频帧;音频分帧(每 ~100ms 一帧)持续传输降低延迟;230ms 端到端延迟接近人类对话反应速度(约 200ms),这是”像真人对话”的关键阈值;实时对话中也能触发 function calling。
打断处理(Barge-in)
Section titled “打断处理(Barge-in)”Realtime API 支持 barge-in(打断):用户开始说话时客户端发送 interrupt 事件,服务器立即截断正在播放的 AI 语音并转而处理新输入。这在 SSE 单向流上无法实现——客户端没有低延迟反向通道,这正是实时语音必须用 WebSocket 的根本原因。
低延迟的关键
Section titled “低延迟的关键”230ms 端到端延迟的实现靠三点:直接流式音频(省去 TTS 延迟);GPT-4o 原生处理音频(流式 ASR + LLM 一体化,不需先 ASR 再送 LLM);小帧快传(音频切成 ~100ms 小帧而非等整句传完)。
基础设施考量
Section titled “基础设施考量”流式输出是长连接,这与传统短连接 API 在运维上有本质区别。以下是各层的配置要点。
反向代理配置
Section titled “反向代理配置”Nginx(最经典的坑:默认开启 proxy_buffering,会把小数据块攒成大块再发,破坏流式效果——用户看到”等很久然后一大段出现”而非”逐字出现”):
location /chat { proxy_pass http://backend; proxy_buffering off; # 关键:关闭响应缓冲 proxy_http_version 1.1; # 保持长连接 proxy_read_timeout 300s; # 读超时设长(默认 60s 会切断长流) gzip off; # 压缩会缓冲数据,破坏流式}Caddy(对 SSE 较友好,设 flush_interval -1 立即转发即可):
reverse_proxy /chat localhost:8000 { flush_interval -1}Cloudflare / CDN:大多数 CDN 默认缓冲响应,需关闭缓冲或启用 streaming 模式;注意 Cloudflare 免费版有 100 秒超时限制,长流会被切断——心跳尤其重要。
负载均衡长连接
Section titled “负载均衡长连接”SSE 一个用户占用一个连接数分钟,与传统短连接 API 迥异:连接持续累积(而非快速释放)、节点故障会导致已建立连接全部断开。因此负载均衡器应用 least-connections(最少连接数)策略而非轮询,监控每节点活跃连接数;节点下线前做连接耗尽(connection draining);单服务器可支撑的连接数受限于文件描述符(ulimit -n)和内存,通常几万到几十万。
各层超时管理
Section titled “各层超时管理”流式请求从客户端到 LLM 引擎经过多层,任何一层超时小于流的实际时长都会导致切断:浏览器 fetch(用 AbortController 设为流最长时长+余量)、Cloudflare(免费版 100 秒——升级或用心跳绕过)、Nginx proxy_read_timeout(默认 60s,建议 300s+)、应用框架(显式设大)。心跳是万能保险:定期发数据(如 : keepalive)能重置各层空闲计时器。
流式监控指标
Section titled “流式监控指标”| 指标 | 含义 | 告警阈值参考 |
|---|---|---|
| TTFT | 首 token 延迟 | P99 > 1-2s |
| TPS | 生成阶段每秒 token 数 | P50 突降 |
| 完成率 | 成功完整输出的比例 | < 99% |
| 中断率 | 流中途断开的比例 | > 1% 排查代理 |
| 连接数 | 活跃 SSE 连接数 | 接近上限 |
流式响应每个 token 是独立 chunk,难以像传统 API 那样记录”完整 response”。建议在服务端拼接完整文本后统一记日志(注意脱敏),同时单独记录上述流式指标。
Vercel AI SDK(2025)
Section titled “Vercel AI SDK(2025)”Vercel AI SDK(也称 AI SDK)是 2024-2025 年构建 LLM 流式前端的事实标准库。它封装了多厂商的 SSE 解析、流式 UI 状态管理和渐进式渲染,极大降低了前端开发成本。
streamText:核心 API,调用 LLM 并返回流式结果。自动处理各厂商的 SSE 格式差异。useCompletion/useChat(React Hooks):封装了”发送消息→接收流→渲染”的全流程状态管理,包括 loading 状态、错误处理、停止生成等。- UI Protocol:AI SDK 定义了一套自己的流式 UI 协议(data stream protocol),前端无需关心底层是 OpenAI 还是 Anthropic 的格式。
示例:流式聊天接入
Section titled “示例:流式聊天接入”// 前端 React 组件——useChat 封装了发送消息、接收流、渲染、停止生成的全流程import { useChat } from 'ai/react';
function Chat() { const { messages, input, handleInputChange, handleSubmit, isLoading, stop } = useChat({ api: '/api/chat', }); return ( <div> {messages.map(m => <div key={m.id}>{m.role}: {m.content}</div>)} <form onSubmit={handleSubmit}> <input value={input} onChange={handleInputChange} /> <button type="submit">发送</button> {isLoading && <button onClick={stop}>停止</button>} </form> </div> );}
// 后端(Next.js)——streamText 自动处理 SSE 转发import { streamText } from 'ai';import { openai } from '@ai-sdk/openai';export async function POST(req: Request) { const { messages } = await req.json(); return streamText({ model: openai('gpt-4o-mini'), messages }).toDataStreamResponse();}AI SDK 替你处理了 SSE 解析、token 拼接、loading/error 状态、停止生成、多模型切换、工具调用流式渲染。一套 API 适配 OpenAI、Anthropic、Google 等数十个 provider。
Python 后端:流式调用 LLM 并用 SSE 转发
Section titled “Python 后端:流式调用 LLM 并用 SSE 转发”from fastapi import FastAPIfrom fastapi.responses import StreamingResponsefrom openai import OpenAI
app = FastAPI()client = OpenAI()
@app.post("/chat")def chat(prompt: str): """SSE 端点:前端用 fetch 或 EventSource 接收""" def generate(): stream = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}], stream=True, # 关键参数:开启流式 ) for chunk in stream: delta = chunk.choices[0].delta.content if delta: yield f"data: {delta}\n\n" # SSE 格式:data: 内容 + 两个换行 yield "data: [DONE]\n\n" # 流结束标记
return StreamingResponse(generate(), media_type="text/event-stream")JavaScript 前端:接收 SSE 流并渲染
Section titled “JavaScript 前端:接收 SSE 流并渲染”// 用 fetch + ReadableStream 解析 SSE(EventSource 只支持 GET,POST 需手动处理)const res = await fetch("/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ prompt: "解释什么是流式输出" }),});const reader = res.body.getReader();const decoder = new TextDecoder();let buffer = "";while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const parts = buffer.split("\n\n"); // 双换行分隔每个 SSE 事件 buffer = parts.pop(); // 最后一段可能不完整,保留 for (const part of parts) { const line = part.replace(/^data: /, "").trim(); if (line === "[DONE]") return; if (line) document.getElementById("output").textContent += line; }}- 默认开流式:任何面向用户的 LLM 应用都应默认开启流式输出。大多数 LLM API 只需加
stream=True,TTFT 从 3 秒降到 0.3 秒。 - 前端用 rAF 合并渲染:用
requestAnimationFrame按帧渲染(每帧只绘制一次),是最核心的防闪烁手段。详见上方”防闪烁技术”。 - 心跳防超时:在 prefill 等待阶段定期发
: keepalive\n\n注释行,防止 Nginx、Cloudflare 等中间层因空闲超时断连——这是 SSE 运维的头号坑。 - 反向代理关闭缓冲:Nginx 必须
proxy_buffering off,Caddy 设flush_interval -1,否则流被攒成一团发送。 - 结构化输出做增量解析:要求 JSON 输出时用
partial-json等库逐块解析不完整 JSON,UI 上渐进式展示已确定的字段。详见结构化输出。 - 日志记录完整文本:流式响应每个 token 是独立 chunk,建议服务端拼接完整文本后记日志,同时单独记录 TTFT/TPS 指标。
- ChatGPT / Claude / Gemini 对话界面:回答逐字出现,代码块逐行渲染——流式输出最经典的场景,也是用户对”AI 速度”感知的主要来源。
- Cursor / GitHub Copilot:代码建议流式输出到编辑器,开发者可在生成过程中即时判断方向,不对就提前中断。
- AI 搜索引擎(Perplexity / 秘塔):搜索结果摘要流式输出,引用来源链接逐步出现,信息逐步展开。
- 实时语音助手:GPT-4o 用 WebSocket 双向流式传输音频,约 230ms 端到端延迟,支持 barge-in 打断。
- AI Agent 工具调用:Agent 系统执行多步工具调用时,流式输出让用户看到”调用搜索→读取结果→组织答案”的实时进展。
典型类库与工具
Section titled “典型类库与工具”| 类库 / 工具 | 语言 | 说明 |
|---|---|---|
| OpenAI / Anthropic SDK | Python / TS | 内置 stream=True,自动处理 SSE 解析 |
| Vercel AI SDK | TypeScript | 2025 年 LLM 流式 UI 的事实标准,封装多厂商 SSE 解析和渐进式渲染 |
| FastAPI StreamingResponse | Python | 快速搭建 SSE 后端 |
| EventSource API | JavaScript | 浏览器原生 SSE 客户端(仅 GET) |
| partial-json | JS / TS | 流式不完整 JSON 增量解析 |
| Markdown-it / remark | JavaScript | 容错 Markdown 渲染,适合流式 |
| 术语 | 英文 | 解释 |
|---|---|---|
| 流式输出 | Streaming | 模型逐 token 生成并实时返回,而非等全部生成完一次性返回 |
| 服务器推送事件 | SSE (Server-Sent Events) | 基于 HTTP 的服务器单向推送协议,LLM 流式输出的标准传输方式 |
| 首 token 延迟 | TTFT (Time To First Token) | 从请求发出到第一个 token 返回的延迟,衡量响应速度 |
| 每秒 token 数 | TPS (Tokens Per Second) | 流式生成时每秒输出的 token 数,衡量生成速度 |
| 增量内容 | Delta | 流式传输中每个数据块相对于上一块的增量部分 |
| WebTransport | WebTransport | 基于 HTTP/3 (QUIC) 的现代双向通信协议,面向未来低延迟场景 |
| 背压 | Backpressure | 下游消费速度慢于上游产出速度时,向上游传导的”减速”压力 |
| 心跳 | Heartbeat / Keepalive | 定期发送的空数据,防止长连接因空闲超时被中间代理断开 |
| 打断 | Barge-in | 实时语音对话中,用户打断 AI 说话,AI 立即停止并处理新输入 |
| 增量 JSON 解析 | Partial JSON Parsing | 从不完整的 JSON 文本中提取已完整部分的技术 |
- SSE 规范:HTML5 标准的一部分,MDN 的 EventSource API 教程是理解
data/event/id/retry字段的最佳起点。 - Vercel AI SDK:2025 年构建 LLM 前端的事实标准,封装了多厂商 SSE 解析、渐进式 Markdown 渲染和 UI 状态管理。
- OpenAI / Anthropic Streaming Guide:各自官方文档详述了 delta 对象格式、事件类型、
[DONE]标记及示例代码。 - GPT-4o Realtime API:OpenAI 文档对 WebSocket 实时语音 API 的说明(音频分帧、barge-in、双向流)。
- TTFT 与推理优化:如何降低 TTFT(KV-Cache、prefill 优化、投机解码)详见LLM 推理优化。
- 结构化输出与流式 JSON:详见结构化输出。解码策略:token 序列质量取决于解码策略(temperature、top-p 等)。