为什么流式:感知延迟的决定性技术
生成式输出的残酷之处:一个 500 token 的回答,完整生成要 10–30 秒——非流式模式下用户盯着空白屏幕等 30 秒(感知极差),流式模式下第一个字 1 秒内出现、后续持续滚动(感知是「即时响应」)。同样的总耗时,流式把感知延迟从「总时长」压缩到「首 token 延迟」——这是聊天类产品「体验好」的第一技术支柱,重要性高于模型本身的几毫秒差异。
技术栈的主流是 SSE(Server-Sent Events):HTTP 上的单向服务端推送——服务端每生成一个增量(一个或几个 token)就推送一个事件,客户端逐个接收拼接渲染。选 SSE 而非 WebSocket 的原因:单向推送够用(对话的「发送」走普通 HTTP)、兼容 HTTP 基建(代理、负载均衡、鉴权)、实现简单。各家模型 API 的流式接口都是 SSE 变体(stream: true),事件格式趋同(增量 delta + 结束标记)。
首 token 延迟(TTFT):流式时代的核心指标
TTFT(Time To First Token)——从请求发出到第一个 token 到达的时长——是流式场景的第一指标。TTFT 的构成拆解:排队时间(服务端并发高时的等待——推理引擎的批处理调度,本库 vLLM 篇)+ 预填充时间(prompt 的处理:全部输入 token 过一遍前向计算,长 prompt = 长 TTFT)。长 prompt 是 TTFT 的头号杀手:RAG 场景塞几千 token 的上下文、多轮对话的历史累积——prefill 的计算量与输入长度线性相关。对策按效排序:前缀缓存(相同的 system prompt 与历史前缀缓存 KV,只算增量——本库 KV Cache 篇,命中时 TTFT 大降)、prompt 瘦身(历史压缩、检索片段精选而非全文塞入)、以及引擎层的 chunked prefill(prefill 分块执行、与解码混合调度,降低长 prompt 对其他请求的阻塞)。
体验上的补充指标:生成速度(token/s,TPOT)决定「打字机的流畅感」(低于 10 token/s 会有卡顿感)、总时长决定「等多久能结束」——TTFT、TPOT、总时长三者构成流式体验的完整仪表盘,产品调优时按「用户在意哪段」定优先级(聊天在意 TTFT、长文生成在意 TPOT)。
流式的工程细节:中断、错误与统计
流式引入了非流式没有的工程问题,四个细节值得写进 checklist。中断处理:用户点击「停止生成」——客户端断开连接,服务端要检测断开并终止生成(否则模型继续生成到结束,浪费算力——「客户端走了、服务器还在烧钱」是最常见的浪费 bug);实现上轮询连接状态或在写入时捕获断开异常。错误处理的尴尬:流式响应的 HTTP 头已发出(200),生成中途出错不能再改状态码——错误要作为流内的特殊事件推送(error 事件),客户端要处理「流正常开始、中途报错」的形态。统计口径:流式的 token 统计在结束时的事件里(各家格式不同),日志系统要正确聚合(增量与累积的口径混乱是常见错误)。渲染的节奏:逐 token 渲染对 Markdown 有特殊性(未闭合的代码块、表格渲染会闪烁)——前端通常做「小缓冲」(按句子或按 50ms 批量渲染)平滑显示,以及 Markdown 的增量解析器(而非每次全量重渲)。这些细节都不难,但每一个没处理都会在真实用户面前暴露——流式的工程感,就是这些细节的总和。
什么场景该关掉流式
流式不是默认正确的,三类场景关掉更优。场景一:下游是程序不是人。机器调用的 API(智能体的工具调用、批处理管道)不关心打字机效果——流式的分块解析反而增加复杂度,非流式一次拿完整结果更简单可靠。场景二:需要后处理才能展示。输出要经过重处理(结构化抽取、翻译拼接、敏感词过滤)才能给用户——逐 token 展示「中间态」没有意义甚至有害(过滤前的内容闪现),攒齐再给。场景三:极短输出。回答只有几十 token 的场景(分类、判断),TTFT 与总时长差距不大,流式的收益单薄。判断式:给人看且长 → 流式;给机器用或要后处理 → 非流式。以及一个混合形态:对高 TTFT 的场景(长 prompt),可以先用「状态提示」填充等待(「正在阅读文档……」),这比强行流式更诚实。
流式输出的进阶形态:多通道流与结构化流的组合
流式输出(正文)的进阶形态出现在智能体与多模态场景:多通道流——一次生成同时流出多种内容通道(正文文本、工具调用事件、思考过程、进度状态),客户端按通道分别渲染。形态一:思考与回答分流。推理模型的思考过程(思维链)与最终回答分通道推送——客户端可以折叠展示思考(「深度思考中…」的可展开面板),这是推理模型产品的标配交互。形态二:工具调用的事件流。智能体的执行过程(调用了什么工具、参数、结果摘要)作为事件流推给前端——用户看到的不是「等待」,而是「正在搜索……搜索到 3 条……正在整理」的实时进度(Agent 产品的体验支柱)。形态三:多模态的分段流。图文混排的生成(先文后图再文),不同模态的分段以类型标记区分——客户端按类型分发渲染。多通道流对协议的要求:事件的类型标记(SSE 的 event 字段或 JSON 里的 type 字段)、通道间的顺序语义(思考必须先于回答?工具调用与文本的交错顺序?)、以及客户端的状态机(按通道聚合、按依赖渲染)。工程上的权衡:多通道流的实现复杂度显著高于单通道(协议设计、客户端状态管理、错误处理按通道分离),但它把「智能体的执行透明度」做成了产品体验——流式输出从「打字机效果」进化为「执行过程的实况转播」,这个进化是 Agent 产品体验的下一个分水岭。
流式的性能优化:从首 token 到长文的完整链路
流式的性能优化(正文 TTFT 三件套之外)有一份完整的链路清单,从请求到渲染全链路逐段优化。段一:网络与接入——HTTP 连接复用(keep-alive)、就近接入(边缘节点)、以及流式响应关闭代理缓冲(某些代理默认缓冲 SSE——首 token 被代理攒住,用户等到的是「一坨一次性到达」,代理配置是「流式不流」的头号元凶)。段二:服务端 prefill——前缀缓存命中(本库 KV Cache 篇)、prompt 瘦身、chunked prefill(正文)。段三:解码——引擎的批处理与调度(本库 vLLM 篇)、投机解码(低并发场景)。段四:传输与渲染——token 的批量推送(每 N 个 token 或每 50ms 推一次,减少事件数)、前端的增量渲染(Markdown 增量解析、虚拟滚动防长文卡顿)。全链路的时间预算示例(目标 TTFT < 1.5s):网络 < 100ms、排队 < 200ms、prefill(10K prompt + 缓存命中 80%)< 500ms、首 token 解码 < 100ms、传输 < 50ms——每段都有账,TTFT 超标时按段归因。流式优化的元方法与显存优化同构:全链路分段、每段记账、超标归因——性能工程的本质是「把延迟拆成账单」。
流式输出的兼容性地图:客户端与服务端的对接矩阵
流式输出(正文技术栈)的工程落地有一张兼容性地图:客户端形态 × 服务端形态的对接矩阵。服务端形态:厂商 API(SSE 标准格式)、自建服务(自选 SSE/WebSocket)、以及推理引擎直出(vLLM/ollama 的 SSE 兼容接口)。客户端形态:浏览器(EventSource/fetch 流读取)、移动端(各平台的流式 HTTP 客户端)、后端中转(Python/Node 的流式代理——把上游流转发给下游)。对接的三个注意点。其一,SSE 的代理兼容:Nginx/负载均衡的缓冲配置(X-Accel-Buffering: no 头 + proxy_buffering off)——「流式变一次性」的排查从这里开始。其二,流的中断语义:客户端断开后,服务端的感知延迟(TCP 半开的检测需要写超时)与上游的联动取消(本库正文的「停止生成」)——中断链路的每一环都要传递取消信号。其三,格式的统一抽象:客户端对接多家模型 API(格式各有差异)时,抽象层统一事件格式(delta/type/index 的自有协议),上游适配器各家各写——应用层只处理统一格式。兼容性地图的价值:流式对接的问题 80% 出在「中间层」(代理、网关、中转),地图在手,排障就有了网格坐标。
流式的用户体验细节:渲染层的三个人性化设计
流式的体验优化(正文渲染小缓冲之外)还有三个人性化的设计细节,它们是「能用」与「好用」的分界。细节一:光标与停顿的拟人化。逐字渲染时尾部带一个闪烁光标(「正在输出」的可视暗示)、token 到达的自然停顿保留(不追求匀速——模型的「思考停顿」是拟人的一部分,匀速反而机械)。细节二:中断后的残卷处理。用户点了停止——已生成的半句话要不要保留?实践的最优解:保留到最近的完整句(残句截断)、并标注「(已停止生成)」——丢弃全部会引发「我按晚了」的懊恼,保留残句给人「可控感」。细节三:错误时的优雅降级。生成中途出错——已生成的部分保留 + 明确的错误提示(「生成中断,以下为部分结果」+ 重试按钮)而非清空重来——用户的等待已经沉没,清空等于二次惩罚。三个细节的共同逻辑:流式体验的本质是「可控感」——让用户始终觉得「输出在我手里」(可停、可看、可续),比输出速度快慢更影响满意度。这些细节没有任何技术难度,缺的只是把它们写进需求清单。
小结
流式把感知延迟从总时长压缩到首 token 延迟——是聊天产品体验的第一技术支柱;技术栈主流是 SSE(单向推送够用且兼容 HTTP 基建)。TTFT 是核心指标:构成是排队 + prefill(长 prompt 是头号杀手),对策三件套(前缀缓存、prompt 瘦身、chunked prefill),体验仪表盘三件(TTFT、TPOT、总时长)。工程四细节:中断要终止生成(否则服务器空烧)、错误走流内事件、统计口径要聚合、渲染加小缓冲。三类场景关掉流式(给机器、要后处理、极短输出)——流式是体验技术不是默认真理,给人看且长才开。
读者留言
COMMENTS 暂无还没有留言,来说第一句?