核心要点

  • 流式输出(SSE 逐 token):用首字延迟取代总延迟,用户几百毫秒就看到内容开始往外吐,体感快得多

  • 占位与反馈:加载动画、打字光标、骨架屏,让等待“有进度”;乐观 UI 先回显用户输入与预期态

  • 减少请求:相同问题命中缓存(含语义缓存)、请求去抖合并、对热门问法预取/预热

  • 兜底:超时与降级(切更小模型或返回快速兜底答案)、长任务异步化(先返回受理再推送结果)

标准回答

先定位问题:真实延迟 vs 感知延迟

大模型生成是自回归的,端到端一两秒甚至更久往往是模型本身决定的,前端无法把它“算”得更快。所以前端优化的核心不是缩短真实延迟,而是缩短用户的感知延迟——让用户更早看到反馈、感觉系统在动。

第一招:流式输出(最关键)

把一次性等完整答案,改成 SSE / 逐 token 流式返回。这样关键指标从“总延迟”变成了 首字延迟(TTFT):模型只要生成出第一个 token 就开始往前端推,用户几百毫秒内就看到文字一个个冒出来。即使总时长不变,体感也会从“卡住了”变成“正在快速回答”。配合边输出边渲染 Markdown,让标题、列表、代码块随流增量成型。

第二招:占位、反馈与乐观 UI

在首字到达前,用打字动画、骨架屏、加载态占住界面,避免空白焦虑;能展示阶段性进度(如“正在检索资料…”“正在生成…”)更好。对用户的输入立即乐观回显——先把用户消息渲染上去、把助手气泡的等待态摆好,让交互“零延迟”地有了响应。

第三招:减少和加速请求

  • 缓存:相同或语义相近的问题直接命中缓存(前端内存缓存 + 服务端语义缓存),避免重复调用模型;
  • 去抖与合并:输入联想、自动补全等高频触发场景做去抖(debounce),把短时间内的多次请求合并为一次;
  • 预取 / 预热:对可预测的下一步(如点开某入口大概率会问的问题)提前发起或预热连接,把等待提前到用户察觉不到的时刻。

第四招:异步化与降级兜底

  • 长任务异步化:耗时很长的任务(长文档总结、批处理)先返回“已受理”,再通过 WebSocket / 轮询把结果推回来,不阻塞界面;
  • 超时与降级:设定超时阈值,超时后切换到更小更快的模型、返回预置的快速兜底答案,或提示用户重试,保证体验不会无限转圈。

小结

一句话:优化感知延迟,而非真实延迟。流式输出抢首字是性价比最高的一招,再叠加占位反馈、缓存预取、超时降级,就能在模型本身慢的前提下给出“快”的产品体验。

常见误区

⚠️ 常见踩坑

只盯着压缩端到端总时长、却不上流式输出,结果用户对着空白页干等;以为加个 loading 转圈就够了,而忽略首字延迟才是体感关键;盲目缓存导致返回过期或答非所问的结果;没有超时降级,模型偶发卡死时界面被无限等待拖垮。

追问

追问 1为什么首字延迟(TTFT)比总延迟更影响用户体感?

因为用户的焦虑主要来自“不确定系统是否在工作”。空白等待时,大脑无法判断进度,几百毫秒就开始觉得卡;而一旦文字开始逐字冒出,用户就确认“它在回答”,注意力转向阅读已生成的内容,对剩余生成时间的敏感度大幅下降。所以把指标从总延迟切到 TTFT,等于把“无反馈的焦虑等待”换成了“有反馈的阅读过程”,即使总时长不变,满意度也明显提升。

追问 2语义缓存和普通精确匹配缓存有什么区别,要注意什么?

精确缓存只在请求文本完全一致时命中,对“今天天气如何”和“今天天气怎么样”这种同义问法无能为力。语义缓存先把问题向量化,用相似度阈值判断是否“足够接近”已有缓存来命中。它能大幅提高命中率,但风险是阈值过松会把意思不同的问题误判命中、返回错误答案;还要处理时效性(带时间/个性化的回答不该缓存)和用户隔离。实践中要谨慎设阈值、对敏感或动态内容禁用缓存,并设置合理的过期时间。

追问 3流式输出场景下如何做错误处理和中断?

流式连接可能在中途断开或模型报错,前端要能区分“正常结束”和“异常中断”:监听 SSE 的 error/abort 事件,保留已生成的部分内容并提示“回答中断,可重试”,避免半截内容当成完整结果。同时要提供用户主动停止按钮(abort 掉请求,节省 token 与算力),并在重试时支持续写或重新生成。后端配合时还需考虑超时关闭空闲连接、断点续传等,保证流式体验在弱网下依然健壮。

🔗 相似问题

同一考点的不同问法,面试官可能换着问,一起刷更稳

没找到想看的面试题?把你想看的告诉我们 →

延伸学习

按主题分类的相关资源,便于系统复习