你定义‘首个 Token’的位置决定了你的延迟 SLO 是否真实
我上季度合作的一个团队在周二发布了推理层升级,周三就开始收到支持工单。用户反映助手感觉“坏了”、“冻结了”或“卡住了”。值班工程师查看了延迟仪表盘,没发现任何异常。p99 首字延迟(first-token latency)为 612 毫秒——远低于团队花了一个季度建立的 800 毫秒 SLO。仪表盘是一片绿色。电话却响个不停。
问题的根源在于 14 个月前做出的一个埋点决策,当时生产环境中还没有推理模型。标记为 “first token” 的指标测量的是供应商发出的第一个数据块(chunk)的时间戳。升级后,第一个 chunk 变成了推理 token——这对用户不可见,也从未渲染,但在 SLO 中却被计为“首个”。模型在流式传输第一个用户可见字符之前,会先发出 4 到 7 秒的内部思考过程。每个图表依然是绿色的。每个用户却在黑暗中等待。
这不是一个关于指标好坏的故事。指标对于它最初设计的模型来说是正确的。这是一个关于当你的埋点边界不再是用户的感知边界时会发生什么的故事——以及在不知不觉中发布这种偏差是多么容易且危险。
“首字”现在代表三个不同的数字
在推理模型出现之前,“首字”只有一个含义:模型响应的第一个字符。供应商边界、流式传输边界和用户可见边界都是重合的。你可以对其中任何一个进行埋点,并得到相同的结果。
推理模型将这一个数字拆分成了三个:
- 首个发出的 token —— 模型产生的第一个 chunk,这可能是一个永远不会展示给用户的内部推理 token。一些供应商将其作为单独的流暴露;另一些则将其折叠到与回答相同的 SSE 通道中,通过 chunk 上的字段进行区分。
- 首个可见 token —— 实际出现在用户聊天窗口中的第一个字符。在非流式 UI 上,这是响应卡片从加载状态变为填充内容的时刻。在流式 UI 上,它是你的渲染流水线写入 DOM 的第一个字符。
- 首个有效 token —— 在诸如“让我想想”或“我会检查三件事”之类的开场白之后的实质性回答内容的第一个字符。许多团队忽略了这种区别,但对于那些开场白只是占位符、用户正在等待实际答案的任务来说,这一点至关重要。
延迟 SLO 承诺的是用户体验。你在制定 SLO 时埋点的这三个数字中的哪一个,SLO 实际上指的就是哪一个。如果你针对非推理模型制定了 “p99 首字延迟低于 800 毫秒”,你就隐含地将首字定义为用户可见的——因为这是当时唯一的含义。然后推理模型出现了,改变了仪表盘读取的数字。SLO 的文字没变。它所做 的承诺却变了。
供应商边界是衡量用户体验的错误位置
当团队建立 LLM 可观测性时,最简单的埋点位置是 SDK 回调。OpenAI 客户端在第一个流式 chunk 上为你提供了一个钩子。Anthropic SDK 也提供了类似的功能。大多数可观测性供应商会自动对该钩子进行埋点,将指标标记为 gen_ai.server.time_to_first_token,然后大功告成。
对于基础设施问题来说,这是正确的指标——供应商响应有多快?——但对于产品问题来说,这是错误的指标——我们的应用感觉有多快?这两个问题过去通常有相同的答案。现在不再如此,而且差距正在扩大。
供应商边界是错误的用户体验衡量层的三个原因:
- 第一个 chunk 可能是推理 token。 推理 API 通常将思考内容作为同一流的一部分发出,并标记内容类型。天真的埋点方式会记录第一个 chunk 的时间戳,而不管你是否渲染它。推理模型在发出第一个回答 token 之前,可以从容地花费 4–15 秒发出隐藏的思考。你的仪表盘会将其记录为一次正常请求,因为第一个 chunk 按时到达了。
- 你的渲染流水线增加了 API 永远看不到的延迟。 流式 chunk 会经过 SSE 缓冲区、可选的反向代理、前端的 chunk 处理器、React 的状态批处理以及浏览器的绘制循环。 “边缘节点接收到 chunk”和“用户看到字符”之间的差距虽然很小但并非为零——在典型配置中为 50 到 300 毫秒,如果你使用了批量更新或路径中有 service worker,则会更长。
- 开场白污染了指标,但没有同等地污染体验。 如果你提示模型在推理前发出“让我想想...”,现在第一个可见 token 会在几毫秒内到达,你的 SLO 看起来非常完美。但有效的首个 token 仍然在几秒钟之后。一个点击发送并阅读“让我想想...”长达八秒的用户,其感受与盯着空白屏幕八秒的用户完全相同。你的 SLO 却说你已经修复了它。
埋点位置决定了你能做出什么样的承诺。SDK 回调承诺网络工作正常。边缘计时器承诺你的基础设施工作正常。渲染时间标记则承诺用户得到了一些东西。只有最后一个才是真正的用户体验(UX)SLO。
路线图是如何悄悄打破定义的
我描述的那个团队并没有更改他们的 SLO,也没有更改他们的埋点。他们只是更改了流量路由到的模型,而这一改变触及了一个他们甚至没意识到已经做出的定义。
- https://www.spheron.network/blog/llm-inference-slo-ttft-itl-latency-budget-guide-2026/
- https://developers.openai.com/api/docs/guides/latency-optimization
- https://developers.openai.com/api/docs/guides/reasoning
- https://developers.openai.com/api/docs/guides/prompt-guidance
- https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/reasoning
- https://openrouter.ai/docs/guides/best-practices/reasoning-tokens
- https://futureagi.com/glossary/time-to-first-token/
- https://www.codeant.ai/blogs/ai-first-token-latency
- https://aimultiple.com/llm-latency-benchmark
- https://docs.anyscale.com/llm/serving/benchmarking/metrics
- https://bentoml.com/llm/inference-optimization/llm-inference-metrics
- https://aws.amazon.com/about-aws/whats-new/2026/03/amazon-bedrock-observability-ttft-quota/
- https://oneuptime.com/blog/post/2026-01-30-latency-percentile-slos/view
- https://www.traceloop.com/blog/granular-llm-monitoring-for-tracking-token-usage-and-latency-per-user-and-feature
- https://www.uxtigers.com/post/think-time-ux
