结论先行:700ms 是怎么做到的(Quick Answer)

实时 AI 应用(语音助手、面试 copilot、客服机器人)里,用户耐心阈值是 1 秒,超过 1.5s 就会显著感觉"卡顿"。我们把端到端延迟从 2300ms 优化到 700ms,核心五招:

  1. ASR 流式分段:放弃 VAD 整段送,改用 200ms partial result 触发推理,砍掉 600ms 等待。
  2. Prompt 模板预热 + KV Cache 复用:System prompt 哈希命中走 prefix cache,prefill 阶段从 350ms 降到 80ms。
  3. Token-by-token streaming:第一个字 ≤500ms 出现,用户感知 latency 从 "总耗时" 变成 "首字耗时"。
  4. 模型分层:Qwen Flash 跑高频低复杂度,复杂解题切 Claude,平均 TTFT 砍半。
  5. SSE + HTTP/2:取消 polling、用持久连接,传输层节省 80~150ms。

下面把每一招的实测数据 + 工程取舍写清楚。

一、延迟从哪来:把 2.3s 拆成 7 段

优化前先做"延迟归因"。我们在生产环境抓了 1000 次请求的火焰图,把端到端拆成 7 段(Chrome 麦克风 → 应用层渲染):

阶段 耗时 (P50) 占比
音频采集 + WebSocket 上传 120ms 5%
ASR 等待 VAD 端点 650ms 28%
ASR 识别 + 文本回传 180ms 8%
业务侧拼 prompt + 鉴权 90ms 4%
LLM prefill (System+History) 360ms 16%
LLM decode (输出 200 tokens) 820ms 36%
前端渲染 + DOM update 80ms 3%

结论很清楚:VAD 等待 + Prefill + Decode 是三大头,占了 80%。任何不动这三段、只在传输层优化的方案,最多砍 100ms,没意义。

二、ASR 流式:从"等说完"到"边说边推"(节省 600ms)

传统做法是等 VAD(Voice Activity Detection)判定句子结束才送文本去推理,问题是中文里"嗯"、"那个"这种语气词会让 VAD 误判,导致额外 200~800ms 等待。

我们在做即答侠(一款实时面试 AI copilot)时遇到了同样的瓶颈——面试场景下用户对延迟极度敏感,候选人多停顿 1 秒都会被面试官察觉异常。我们最终的方案是放弃 VAD 整段,改用 Deepgram Nova-2 的 partial_results 模式,每 200ms 推一次中间结果,业务层用一个简单的 stable-suffix 判定:

function isStableEnough(partial, prev) {
  // 连续 2 次 partial 末尾 8 个字符未变 = 稳定,可以预热推理
  if (!prev) return false;
  const tailA = partial.slice(-8);
  const tailB = prev.slice(-8);
  return tailA === tailB && partial.length >= 6;
}

当 stable-suffix 命中时,我们不等用户说完就把当前文本(带 [partial] 标记)送进 LLM 做 speculative prefill。如果用户继续说话改变了语义,下一轮 prefill 用新文本覆盖;KV cache 复用率实测在中文长句下能到 78%。

三、Prompt 预热 + KV Cache:Prefill 从 350ms 到 80ms

面试场景的 prompt 结构非常稳定:

[System: 你是面试助手 + 候选人简历 + 目标 JD] (≈ 1800 tokens, 几乎不变)
[History: 最近 10 轮 Q&A]                  (≈ 600 tokens, 慢变化)
[Current Question]                          (≈ 50 tokens, 每次变)

System 段在整个面试过程中是常量,但传统做法每次请求都重发,prefill 要重新算 1800 tokens 的 attention,光这一步在 H100 上都得 280~350ms。

我们做了两层优化:

  1. 客户端预热:用户启动 session 的瞬间发一个空 user message 触发 prefill,让 KV cache 提前驻留。
  2. 服务端 Prefix Cache:用 vLLM 的 prefix caching 或 SGLang 的 RadixAttention,按 system+history 的哈希命中。命中后 prefill 只算 Current Question 的 50 tokens,实测 80ms 完成。

注意:Prefix cache 命中率受 history 影响。我们做了一个历史归一化器:把"嗯"、"对了"这类口水词和时间戳剥离再做哈希,命中率从 41% 提到 73%。

四、Token Streaming:用户感知的 latency 是"首字时间"

这是心理学层面的优化,效果比技术优化还猛。生成 200 tokens 在 50tps 的模型上需要 4 秒,但只要第一个字在 500ms 内出现、后面以 60+ tps 流式吐出,用户会觉得"很快"。

实现上要注意三点:

  • SSE 不要 buffer:Nginx 默认 proxy_buffering on 会把 SSE 攒到 4KB 才下发,必须显式 proxy_buffering off; chunked_transfer_encoding on;
  • 前端逐字渲染:用 requestAnimationFrame 节流,避免每个 token 都触发 React reconcile(80ms 渲染抖动就是这么来的)。
  • 结尾稳定性:流式吐出过程里,用户可能根据前 50 个字就行动了,所以前 50 个字的内容质量比后 150 个更重要——prompt 设计要求模型"先给结论再展开"。

五、模型分层 + SSE 链路调优(最后 200ms)

不是所有问题都需要最强模型。我们在路由层做分流:

  • 问候、闲聊、简单事实问题 → Qwen 3.5 Flash(TTFT 180ms)
  • 结构化回答、STAR 行为题 → GPT-4.1-mini(TTFT 320ms)
  • 代码题 / 系统设计 → Claude Sonnet(TTFT 450ms,但深度值得)

路由器本身用一个 50ms 的小模型分类。看起来加了一步反而慢,但因为 70% 流量走 Flash,平均 TTFT 砍掉一半。

传输层最后两个细节:

  • HTTP/2 多路复用,避免 head-of-line blocking
  • 把 ASR、LLM、用户客户端放同一 Region,跨区 RTT 30ms 加起来不少

六、踩过的坑

坑 1:Prefix cache miss storm。 System prompt 里带时间戳"当前时间是 2026-04-24 14:30:21"会让每次请求都 miss。改成"今天日期 2026-04-24"按天聚合,命中率直接拉满。

坑 2:Speculative prefill 资源浪费。 用户说话中途送 partial 去推理,如果用户后续修改了说法,前面的计算白做。控制好 speculative 触发频率(≥2 次 stable-suffix 才触发),浪费率从 35% 压到 8%。

坑 3:流式中重传。 网络抖动时 SSE 连接断了,前端要做"已渲染 token offset"记录,重连后从 offset 继续,而不是整段重生成。

常见问题

Q1:为什么不直接用 GPT-4o 一把梭?
A:GPT-4o 单次 TTFT 在 600~900ms,已经超过我们 700ms 的总预算。强模型的成本和延迟都太高,分层路由是更工程化的解。

Q2:流式输出过程中如果用户中途打断怎么办?
A:我们维护一个 session_id + turn_id,新 turn 进来直接 AbortController.abort() 掉旧 stream,前端立刻清空展示区。SSE 客户端用 fetch + ReadableStreamEventSource 更好控制,因为 EventSource 没法主动 abort。

Q3:KV Cache 命中率怎么监控?
A:vLLM 暴露了 cache_hit_rate metric,每个 prefix 有独立计数。我们在 Grafana 上画两条线:cache_hit_rate 和 P99 prefill latency,前者下降时后者会立刻上涨,是最早的报警信号。

Q4:1800 tokens 的 system prompt 是不是太长?能不能压缩?
A:可以,但收益有限。简历 + JD + 偏好至少 1200 tokens 是必要信息。我们试过用 embedding 检索动态拼,准确率掉了 6%(候选人简历里的关键经历可能被检索漏掉)。最终还是全量塞 + prefix cache。

Q5:移动端怎么办?SSE 在 4G 下稳定吗?
A:移动端我们额外做了 keep-alive 心跳(每 15s 一次空 SSE event),并且在客户端对 token 增量做本地缓冲,断网恢复后从最后收到的 token offset 续传。实测 4G 弱网下首字延迟会到 1.2s 左右,比 WiFi 慢一倍但仍可用。

写在最后

实时 AI 应用的延迟优化是个系统工程,不是单点能解决的。归因 → 分层优化 → 体感对齐,三步走完才能把 2.3s 压到 700ms。流式架构 + KV cache 是骨架,模型分层和 prompt 预热是肌肉,剩下就是不断调试和取舍。希望这篇拆解对做实时 AI 工程的同行有用。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐