首页 / 资讯中心 / 文章详情

基于 Web Vitals 构建面向 LLM 前端的“首字生成延迟(TTFT)”全链路追踪体系

基于 Web Vitals 构建面向 LLM 前端的“首字生成延迟(TTFT)”全链路追踪体系 ★ FEATURED ARTICLE
在生成式大语言模型LLM的全新人机交互范式下如果问哪一个指标最能直接决定用户的留存率与心理满意度答案毫无疑问是TTFTTime-to-First-Token首字生成延迟。认知心理学与人机工程学的大量实证研究表明当人类向另一个个体或智能系统发出提问后心理学意义上的“舒适倾听窗口”大约只有一秒钟。一旦超过一点五秒未得到任何语言回馈大脑便会开始滋生焦虑怀疑对方是否在倾听而当延迟突破三秒时绝大多数用户便会判定系统已经挂死或响应迟缓甚至愤怒地刷新页面或直接放弃任务。然而长期以来许多后端与算法团队习惯于在服务端的日志中自豪地宣称“我们的模型推理引擎非常强大首字 Prefill 耗时仅需 380 毫秒”但当这个指标呈现在真实用户的手机浏览器端时用户实际经历的等待时间往往长达整整两秒。为什么算法测出的 380ms 与用户感知的 2000ms 会产生如此巨大的认知鸿沟因为后端日志记录的仅仅是 GPU 内部的计算时间片而用户感知的是包含前端调度、全链路网络传输、七层网关转发以及最终浏览器像素光栅化在内的“端到端全物理链路闭环”。今天我们将依托 W3C Web Vitals 的设计规范在前端构建一套真正穿透全链路的端到端 TTFT 追踪体系。端到端 TTFT 的物理切片拆解消失的 1600 毫秒要彻底终结前后端团队在性能分析上的扯皮与盲人摸象首先必须把从用户按下回车键到第一个字符真正呈现在视网膜上的全过程以微秒为单位进行无死角的物理分段切片$T_1$ 前端调度与请求组装延时Client Pre-flight Delay从用户在文本框敲下回车keydown经历输入法IME合成事件完成、前端业务状态机响应、大型 Prompt 模板拼接与上下文 Token 估算直到fetch()真正发起。在主线程繁忙时这一阶段可能悄然消耗数十毫秒。$T_2$ 网络连接与 TLS 握手延时Network Handshake LatencyDNS 解析耗时、TCP 三次握手、以及 TLS 1.3 密钥协商。在移动弱网或跨洋跨可用区访问时仅握手阶段就能轻易消耗 300~600ms。$T_3$ 网关路由与排队时延Gateway Ingress Queuing请求抵达 API Gateway 后经历 JWT 验签、限流判定、安全风控扫描、以及在上游大模型推理集群连接池中的排队挂起时间。$T_4$ 模型 Prefill 与首字推理耗时Model Prefill Inference后端 GPU 集群完成多轮历史上下文KV Cache检索将数千个 Prompt Token 并行灌入 Transformer 神经网络计算并采样出第一个预测 Token。这正是后端算法人员通常所定义的窄义 TTFT。$T_5$ 传输分包与网络传输时延Downstream Network Transit推理服务吐出首个 SSE 分片网关进行封装数据经由骨干网、城域网、基站最终抵达客户端物理网卡。$T_6$ 客户端解析与主线程光栅化延时Client Render Paint浏览器网络线程将二进制字节流提交至 JavaScript 引擎ReadableStreamReader读取解密并完成 UTF-8 解码Markdown AST 解析器提取首个有效文本节点响应式框架触发局部 DOM 挂载浏览器执行样式计算、布局Reflow并在下一次 V-Sync 信号到来时经由 GPU 光栅化输出到显示器屏幕。唯有当这一整套链条在 $T_6$ 的终点完成像素绘制首个字符才真正映入用户眼帘。基于 Performance API 的端到端追踪 SDK 设计为了将这六个阶段的物理耗时进行高精度捕获并与 W3C 标准的 Navigation Timing 及 Resource Timing 无缝拼接我们构建了面向 LLM 的专属追踪 SDK。在网络握手阶段我们充分利用浏览器原生暴露的PerformanceResourceTiming在服务端下发的首个 SSE 数据帧头部要求网关注入服务端各阶段的纳秒级耗时指标Server-Timing Header在客户端接收到首字节后通过requestAnimationFrame锁定最终像素落地的物理时间戳export interface FullChainTTFTMetrics { sessionId: string; totalClientTTFT: number; // 用户真实感知的端到端首字延迟 clientPreflightMs: number; // T1: 前端输入调度 dnsTimeMs: number; // T2a: DNS 解析 tcpTlsTimeMs: number; // T2b: 网络建连与 TLS gatewayQueuingMs?: number; // T3: 服务端排队 modelPrefillMs?: number; // T4: 模型前向推理 downloadTransitMs: number; // T5: 首包网络传输 clientRenderPaintMs: number; // T6: 前端解析与光栅化 } export class FullChainTTFTTracker { private promptTriggerTime: number 0; private fetchStartTime: number 0; private firstByteReceivedTime: number 0; private firstPaintTime: number 0; private sessionId: string ; public markPromptTriggered(sessionId: string): void { this.sessionId sessionId; this.promptTriggerTime performance.now(); } public markFetchInitiated(): void { this.fetchStartTime performance.now(); } public markFirstChunkReceived(): void { if (this.firstByteReceivedTime 0) { this.firstByteReceivedTime performance.now(); } } public async recordFirstPaintAndFinalize(targetUrl: string): PromiseFullChainTTFTMetrics { // 利用 rAF 确保锁定在浏览器完成下一帧像素光栅化的真实时刻 await new Promisevoid((resolve) { requestAnimationFrame(() { // 双重 rAF 确保当前帧已提交至 GPU Compositor requestAnimationFrame(() { this.firstPaintTime performance.now(); resolve(); }); }); }); const totalClientTTFT this.firstPaintTime - this.promptTriggerTime; const clientPreflightMs this.fetchStartTime - this.promptTriggerTime; const clientRenderPaintMs this.firstPaintTime - this.firstByteReceivedTime; // 2. 从 Resource Timing 获取底层的网络握手细分指标 let dnsTimeMs 0; let tcpTlsTimeMs 0; let downloadTransitMs 0; const resources performance.getEntriesByName(targetUrl, resource) as PerformanceResourceTiming[]; if (resources resources.length 0) { const entry resources[resources.length - 1]; dnsTimeMs Math.max(0, entry.domainLookupEnd - entry.domainLookupStart); tcpTlsTimeMs Math.max(0, entry.connectEnd - entry.connectStart); downloadTransitMs Math.max(0, entry.responseStart - entry.requestStart); } const metrics: FullChainTTFTMetrics { sessionId: this.sessionId, totalClientTTFT: Math.round(totalClientTTFT), clientPreflightMs: Math.round(clientPreflightMs), dnsTimeMs: Math.round(dnsTimeMs), tcpTlsTimeMs: Math.round(tcpTlsTimeMs), downloadTransitMs: Math.round(downloadTransitMs), clientRenderPaintMs: Math.round(clientRenderPaintMs), }; this.reportMetrics(metrics); return metrics; } private reportMetrics(metrics: FullChainTTFTMetrics): void { // 异步低优先级上报 if (navigator.sendBeacon) { navigator.sendBeacon(/api/rum/ttft-metrics, JSON.stringify(metrics)); } } }全链路度量驱动的技术治理闭环当全链路端到端 TTFT 监控大盘在生产环境铺设完毕后原本模糊不清的性能迷雾瞬间被清澈的射线所穿透。在真实监控大盘的数据分析中我们经常能发现令人震惊的技术洞察某跨国业务线 P90 TTFT 高达 3.2 秒通过切片分析发现模型 Prefill 仅耗时 400ms而 $T_2$ 的 TLS 握手竟然高达 1100ms原因在于客户端未开启 HTTP/2 连接复用每次请求都在重新经历昂贵的 TLS 完整握手。通过在边缘节点配置连接保活池该指标瞬间暴跌 900ms低端 Android 设备 P90 渲染耗时异常切片显示 $T_6$ 前端光栅化竟然长达 450ms。深入排查发现由于 Markdown 解析器在接收到首个 Token 时同步加载并编译了巨大的数学公式库KaTeX阻塞了首次渲染。将其改为异步懒加载后$T_6$ 耗时瞬间收敛至 16ms 以内。结语没有精准的全链路度量所有的架构调优都只是盲目的运气游戏。基于 Web Vitals 理念构建的全链路 TTFT 追踪体系不仅是一套冰冷的打点代码更是连接用户真实心理感知、前端渲染管线、网关调度与后端算力矩阵的神经中枢。它让每一个参与系统构建的工程师都能够站在全局的至高点上以近乎苛刻的科学态度为消除用户眼前哪怕 10 毫秒的不适等待持续倾注最顶级的工程匠心。
阅读完成 · 觉得有帮助?
咨询建站