← 大模型应用基础

大模型应用为什么要做流式输出、取消和超时控制?

高频 中等 流式输出 · 第 1 / 3 问 更新于 2026/09/29
LLMOps流式输出SSE超时用户体验
本题落地项目AI Agentic RAG高级企业知识库平台

简化版

流式输出能显著降低用户感知延迟,取消和超时控制能避免无效生成继续消耗 token 与连接资源。生产系统通常用 SSE 或 WebSocket 返回增量 token,同时在服务端做超时、断连检测、重试和幂等处理。

详细版

  • 首 token 延迟决定用户是否觉得系统“有反应”。
  • 流式输出不等于总耗时变短,但能改善等待体验。
  • 取消生成要通知模型服务或停止读取,否则可能继续计费。
  • 超时要分连接超时、首 token 超时、总生成超时和工具调用超时。
  • 前端、网关、后端和模型服务的超时要协调,否则容易出现半断连。

完整版教学

易错点:流式输出解决的是“感知等待”,取消和超时解决的是“资源泄漏”。两者必须一起设计。

一、流式、取消和超时分别解决什么

流式输出改善的是感知延迟:用户更早看到首 token,但模型仍需完成已生成 token 的计算。取消必须从浏览器沿网关、编排、模型客户端和工具传播,否则前端断开后后台仍在计费。

超时则定义各阶段和整条任务最多等待多久,防止连接永久占用。三者组合后,系统才能既早反馈、又允许用户停止,并在依赖失控时确定性收尾。

二、核心原理和工程边界

自回归模型逐 token 生成,天然适合边生成边返回。应用层通过 SSE 或 WebSocket 把增量 token 推给前端,前端边收边渲染。与此同时,后端要监听客户端断开和用户取消,把取消信号传到上游模型或中断请求链路。

三、带数字的工程算例

一次回答总生成耗时 12 秒。如果非流式,用户 12 秒后才看到内容;如果首 token 1.2 秒返回,之后每秒 20 token,用户会明显觉得系统更快。若 20% 用户在 3 秒内取消,不做取消传播会浪费大量输出 token。

用户感知延迟 ≈ 首 token 延迟,而不是完整生成耗时
浪费 token = 取消后继续生成的 token 数 * 取消请求量
总超时 >= 首 token 超时 + 预期输出长度 / 生成速率

四、典型链路怎么跑

可以把这类问题拆成下面的链路来理解:

前端发起请求
   |
后端创建模型流
   |
模型返回增量 token
   |
SSE/WebSocket 推送前端
   |
用户取消或断连
   |
服务端传播 abort 信号并记录日志

五、方案对比和选择标准

方式优势适用场景
普通 HTTP实现简单短回答、批处理
SSE浏览器友好、单向流聊天、生成式问答
WebSocket双向实时协同编辑、语音交互
轮询兼容性强异步任务进度

什么时候该流式、什么时候必须等完整结果,见「什么时候用流式输出,什么时候必须等模型完整返回?」;流式对话的消息什么时候落库、中途失败怎么收尾,见「流式对话的消息什么时候落库?中途失败、被截断或用户停止时怎么处理?」。

六、上线后最容易出问题的地方

  • 只在前端隐藏加载状态不算取消,后端和上游仍可能继续执行。

  • 代理层缓冲会破坏流式体验,需要确认 Nginx/CDN 是否关闭缓冲。

  • TTFT 从请求到首个内容 token,不能把连接确认或空心跳当首 token;TPOT 则要排除工具等待并单独报告。

七、Deadline 怎样跨层传播

入口生成绝对 Deadline,各下游根据 remaining = deadline - now 决定是否启动,并把更短的阶段超时传递给模型与工具。各层独立设置 30 秒会串行叠加,不能保证用户看到的总时限。

取消信号与 Deadline 都应幂等传播;写工具超时后若执行状态未知,不能简单重试,而要先查询 operation_id。超时是控制边界,不等于证明外部动作没有发生。

八、常见误区与追问

  • 误区:流式输出能降低模型计算成本。 仅分块返回不会减少生成计算;只有用户提前取消且取消真正传到推理端时才可能节省后续 token。
  • 误区:用户关页面后请求自然就完全停止。 代理和服务端可能继续读取上游响应,必须传播 cancel signal 并关闭工具与模型调用。
  • 误区:所有超时设置成同一个值最简单可靠。 连接、首 token、token 间隔、工具和总 deadline 的故障含义不同,应分层设置且服从总预算。
  • 追问:SSE 和 WebSocket 在 LLM 场景怎么选? 单向文本流优先 SSE,需双向实时控制或多路事件时选 WebSocket,同时考虑代理与重连语义。
  • 追问:如何统计首 token 延迟? 从服务端接收请求开始,到收到并成功写出第一个非空内容 token,按 P50/P95/P99 报告。
  • 追问:工具调用阶段如何向前端反馈进度? 发送受控状态事件如 searching/executing,不泄露思维链;事件要有序号并支持取消。

九、加强记忆

记住“流式管体验,取消管浪费,超时管边界”。面试时把前端、服务端、模型上游、代理层四处都讲到,答案就很完整。

项目实战落地

项目里怎么做的

《AI Agentic RAG高级企业知识库平台》的问答应用用 SSE 把答案流式推给页面:后端用 LangChain 的 astream 一段段取模型输出,每段立刻转成一个 delta 事件发出去,同时收集起来,跑完拼成完整答案落库。前端这一侧有几处要点:

  • 不用浏览器原生的 EventSource。 它只支持 GET,也没法自定义请求头带 token,而问答接口是 POST 且要带 token,所以用 fetch 读流、自己解析 SSE。
  • 处理残缺报文。 网络分片和 SSE 的事件边界没有关系,一次 read() 可能读到两个半事件:
buffer += decoder.decode(value, { stream: true })
// 空行是一个事件的结束标志,按它切分
const blocks = buffer.split('\n\n')
// 最后一块可能是残缺的,留到下一轮再拼
buffer = blocks.pop()
  • 中文不被切坏。 decode 带上 stream: true:一个中文字符占三个字节,可能被分片切开,不加这个参数会解码出乱码。
  • 失败也发事件。 回答过程中抛异常时,把原因写进消息记录,再发一个 error 事件。
  • 光标在 finally 里关。 正在接收时末尾显示一个闪动的竖条,接收完消失;成功失败都要关掉。

《AI Agent 智慧医院智能导诊就诊系统》后端用的是回调式的流式模型:回调在框架自己的线程上执行,方法要等生成结束才能拿到完整正文,所以用 CountDownLatch 等待并设超时,回调里收到的异常先存进 AtomicReference,回到本线程再统一翻译抛出。《AI智能客服与工单处理系统》的「停止生成」按钮调用 abort(),让 reader.read() 抛出异常、读取循环结束,页面不再追加新内容;教程写明这个按钮只作用在前端这一侧。

为什么这样取舍

  • SSE 出错必须显式发事件。 普通接口抛异常,前端的 .catch() 能收到;SSE 流中途出错,前端只知道流断了、不知道为什么,会一直转圈。
  • 流式光标是标配。 没有它,用户分不清是在慢慢输出还是卡住了;失败时不关,光标会一直闪。
  • 等待一定要带超时。 模型既不返回内容也不回调错误时,不带超时的等待会让线程一直挂着,连着的 SSE 连接也不会释放。

面试官还会追问

  • 引用来源为什么在正文开始生成之前就发给前端?
  • 一条资料都没召回时,这一轮还会调模型吗?页面上看到的是什么?
  • 自动化测试怎么验证「答案被拆成很多段发出去,拼起来还是完整的」?

学完《AI Agentic RAG高级企业知识库平台》,上面这些追问你都会迎刃而解。

本题落地项目地狱锤炼AI Agentic RAG高级企业知识库平台基于企业知识库、PGVector 向量检索、Agentic RAG、FastAPI + LangGraph、Function Calling,实现向量 + BM25 混合检索与 RRF 融合、Rerank 重排、HyDE 与多查询改写、父子分块召回、LangGraph 状态图自适应检索、Agent 执行时间线和 LLM-as-judge 四指标评测,覆盖从基础 RAG 到高级 RAG 调优的完整闭环。FastAPILangChainLangGraphRAGPGVector源码+SQL喂饭学习教程配套面试文档环境安装文档项目运行文档 学习这个项目 也可以学AI Agent 智慧医院智能导诊就诊系统地狱锤炼 查看项目 也可以学AI智能客服与工单处理系统登峰造极 查看项目