1. 为什么AI模型推理延迟是个“难缠”的监控对象
先讲个真实场景。上个月有个做私有化部署的朋友半夜给我打电话,说他们的模型服务告警疯了,P99延迟从50ms飙到1.8s,值班同事被连续叫起来三次,结果登录服务器一看,GPU利用率才40%,显存也正常,日志没有任何报错。折腾到天亮才发现,告警规则里用的延迟口径把客户端排队等待的时间也算进去了——那会儿正好有个定时任务在批量拉数据,把入口带宽占满了,推理引擎本身压根没问题。
这种事在AI模型监控里太常见了。很多人第一反应是“延迟监控不就是加个指标、画张图、设个阈值的活儿吗”,等真做起来才发现,模型推理延迟和普通后端接口延迟完全是两个物种。如果你也在做AI模型推理延迟监控,或者正打算搭这套东西,这篇文章把我从踩坑到落地整个过程的思路、配置、还有那些文档里不会写的细节都梳理一遍。
先说清楚,这里聊的“推理延迟监控”指的是:对AI模型从收到推理请求到产出结果的全过程进行延迟指标的采集、存储、可视化、告警和分析。它适合三类人看:一是自己部署了大模型或传统深度学习模型服务的工程师,二是给AI应用做稳定性保障的SRE,三是想从零搭一套监控体系、但不确定从哪下手的团队。
为什么我说它“难缠”?因为它和普通监控有一个本质区别:延迟数据呈现很强的长尾分布和非线性特征。
普通接口的延迟,大多数情况下集中在一个狭窄区间,平均值和P99差距不大,画出来的曲线是平稳的一团。模型推理不一样,尤其生成式模型场景下,输入长度、batch大小、显存碎片、并发排队、动态shape,任何一个因素变化都会让延迟产生数量级的跳变。可能99%的请求在40ms内返回,剩下1%的请求直接飙到2s以上,平均值被拉高50%,而P99看起来还“很正常”——反过来也一样,P99爆了,可能只是某一个长文本请求拖了后腿,其他用户体验完全没受影响。
这就是延迟监控的第一个坑:告警看哪个分位数,直接决定你会不会被误报折磨死。
用平均值监控延迟,等于给自己戴了副墨镜上路——大问题全被抹平了;用P99监控也不够,因为P99对极端长尾不敏感。真正要做的是多维分位数组合:P50看整体体验,P95/P99看长尾问题,P999看极端异常,再加一个最大值用来捕捉瞬时雪崩。这个后面讲指标设计的时候我再展开。
还有一个容易忽略的点:模型推理延迟不是一个孤立指标,它和数据面、控制面、硬件层指标强关联。GPU利用率高不代表延迟低,可能只是并发都挤在一起了;显存占用正常也可能出现推理速度劣化,比如显存碎片化导致cudaMemoryPool分配变慢。所以“模型推理延迟”这套监控,本质上是一个关联分析系统,不是单一指标能回答的问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先分清楚你在监控哪一段延迟:五种口径别搞混
做AI模型推理延迟监控,第一步不是选工具,而是先在团队里把“延迟”这个词的语义对齐。我在好几个项目里都遇到过类似的尴尬:业务方说“模型太慢了”,算法说“模型推理只用了20ms”,运维查完说“服务响应是300ms”——三个人说的都是真话,但因为口径不同,讨论了一上午没有任何结论。
一段典型的AI推理请求,从用户触达到最终返回,会经历下面几个阶段:
第一阶段是网络传输。请求从客户端到服务端,响应再回来,这段时间取决于网络链路质量,和模型本身无关。
第二阶段是排队等待。请求到了服务端但没有空闲的worker处理,在队列里等着。这里包括网关层的连接池等待、模型服务内部的请求队列、以及推理框架的batching等待(比如vLLM会把多个请求攒在一起做continuous batching,攒的过程本身也是延迟)。
第三阶段是预处理。文本要做tokenize,图像要做resize和normalize,这些在进模型之前完成。很多团队把这些计算忽略不计,但它们在batch输入时还挺费时间的。
第四阶段才是真正的模型计算,也就是模型前向推理时间。这一段时间和模型结构、输入shape、batch大小、硬件直接相关。在LLM场景下,又分为首Token延迟(TTFT)和每个Token的平均生成延迟(TPOT):前者决定了用户“第一个字多久出来”,后者决定了每秒能吐出多少个字。
第五阶段是后处理和响应序列化。比如从logits解码、做beam search的最终选择、把tensor转成JSON等等。
这五个阶段加起来才是用户感知到的端到端延迟。不同角色关心不同阶段:用户关心端到端,算法优化关心纯推理时间,SRE要关注排队和预处理。监控方案里这五段必须分开埋点、分开存储、分开可视化,否则出问题的时候你根本不知道锅该甩给谁。
我自己习惯用五个标签把这五个阶段打散在链路追踪系统里,同时在Prometheus里分别记录五个histogram。这里给出一套可以直接用的指标命名规则:
| 阶段 | 指标名 | 说明 |
|---|---|---|
| 端到端 | model_latency_end2end_seconds |
从收到请求到完全返回响应 |
| 网络传输 | model_latency_network_seconds |
可选,通常由网关统计,客户端和服务端难以精确对应 |
| 排队等待 | model_latency_queue_seconds |
请求在队列里等待的时间 |
| 预处理 | model_latency_preprocess_seconds |
tokenize / resize 等 |
| 模型计算 | model_latency_inference_seconds |
纯模型前向推理时间 |
| 后处理 | model_latency_postprocess_seconds |
解码、序列化 |
| 流式场景TTFT | model_latency_ttft_seconds |
首Token耗时 |
| 流式场景TPOT | model_latency_tpot_seconds |
每个Token平均生成耗时 |
指标单位统一用“秒”,加到prometheus的histogram里,bucket不要拍脑袋设,要根据你服务的真实延迟分布来定。我见过有人bucket设成0.1、0.2、0.5、1,结果服务大多数时候是5ms,所有请求全落在第一个bucket里,分位数计算出来全是0.1,等于白采。正确做法是先裸跑一天,用summary或者直接打印日志的方式采集原始值,看到真实分布后再定bucket边界,原则上要让P50落在中间某个
