如何基于 OpenTelemetry 采集 token 级延迟并上报 Prometheus?
解读
在国内大模型落地场景中,token 级延迟是衡量用户体验与成本的核心指标:它直接决定首包时间、流式输出节奏与 GPU 利用率。面试官问“如何基于 OpenTelemetry 采集并上报 Prometheus”,表面看是监控技术题,实则考察三点:
- 是否理解生成式 AI 推理链路(预填充、解码、流式返回)与 token 的对应关系;
- 能否在不侵入模型代码的前提下,用OTel 语义规范把“token 延迟”表达为可聚合的指标;
- 是否熟悉国内可观测性基础设施(阿里云 ARMS、腾讯云 TPS、自建 Thanos+Grafana)的集成方式。
答得太浅(只讲 counter 和 histogram)会被认为没做过大模型;答得太深(魔改 CUDA kernel)会被认为过度设计。必须给出工程可落地、云原生友好、符合国内合规要求的完整路径。
知识点
-
Token 级延迟定义
- TTFT(Time To First Token):用户侧首包时间,对应预填充阶段结束。
- TPOT(Time Per Output Token):首 token 之后每 decode 一步的间隔。
- E2E Latency = TTFT + (TPOT × token 数) + 网络抖动。
-
OpenTelemetry 指标模型
- Instrument:Histogram 最贴合,可自动产生 _bucket、_sum、_count,供 Prometheus quantile 计算。
- 属性维度:model_name、gpu_type、quantization、request_id、user_id(脱敏后)、scene(chat/embedding/rerank)。
- 单位:秒(s)或毫秒(ms),必须显式声明,避免 Prometheus 默认秒级假设导致数量级错误。
-
采集点选择
- 服务端拦截:在 Triton Inference Server / vLLM / FastAPI 的流式 generate 接口里,用 Python decorator 或 C++ callback 在每次 yield token 时打时间戳。
- 零侵入方案:若模型服务不可改,可在边车(Sidecar) 里解析 HTTP Chunk 的 SSE 事件,用正则提取 "data: {"token":"xxx"}" 并计算间隔;该方案对国产芯片(华为昇腾、寒武纪)同样适用,无需 GPU 代码。
-
合规与脱敏
- 根据《个人信息保护法》,user_id 必须做哈希或 tokenization 后才能写入 label;高基数场景改用 span attribute,指标维度仅保留低基数字段。
- 若客户为金融或国企,需支持国密 SM4 脱敏与专网隔离,OTel Collector 需开启 TLS 双向认证(国密套件)。
-
上报链路
- OTel Collector → Prometheus Remote Write:国内云厂商已支持,阿里云可直写 ARMS Prometheus,腾讯云通过 TPS 实例接入;自建场景用 Thanos Receiver。
- 采样策略:高并发下开启头部采样(1%~5%),防止 Prometheus 内存爆炸;关键 VIP 客户可基于属性采样(user_tier=vip 全采)。
-
Grafana 大盘
- 用 histogram_quantile(0.95, rate(ttft_seconds_bucket[5m])) 展示 P95 TTFT;
- 用 increase(output_token_total[1m]) / increase(output_token_duration_seconds_sum[1m]) 计算实时吞吐(token/s);
- 告警规则:TTFT>2s 持续 5 分钟且 QPS>50 时触发钉钉、飞书、企业微信,并自动扩容 GPU 节点(HPA 基于自定义指标)。
答案
-
定义指标 在 Python 服务启动时注册 OTel Histogram:
from opentelemetry import metrics meter = metrics.get_meter("llm", version="1.0.0") ttft_histogram = meter.create_histogram( name="llm_ttft_seconds", description="Time to first token", unit="s" ) tpot_histogram = meter.create_histogram( name="llm_tpot_seconds", description="Time per output token", unit="s" ) token_counter = meter.create_counter( name="llm_output_token_total", description="Number of output tokens", unit="1" ) -
注入采集逻辑 在 FastAPI 流式接口里,用生成器包装 yield:
async def generate(request): first = True async for token in model.stream(request.prompt): now = time.time() if first: ttft_histogram.record(now - request.start_time, attributes={"model": request.model}) first = False else: tpot_histogram.record(now - last_time, attributes={"model": request.model}) last_time = now token_counter.add(1, attributes={"model": request.model}) yield token -
启动 OTel Collector 国内镜像加速:
exporters: prometheusremotewrite: endpoint: "https://arms-grafana.aliyuncs.com/prometheus/v1/write" headers: Authorization: "Bearer your-arms-token"开启 batch processor 与 memory_limiter,单实例 8 卡 A100 场景下,batch=1024 可降 30% 网络带宽。
-
Prometheus 验证 查询
{__name__=~"llm_.*"}确认指标到达;若发现 cardinality explosion,立即检查是否把 request_id 写进 label,及时改为 attribute。 -
压测对齐 用 wrk2 + lua 脚本 模拟 100 并发流式请求,对比 TTFT、TPOT 曲线与 NVIDIA nvidia-smi dmon 的 GPU 利用率,确保监控值与真实用户体验误差 <5%。
拓展思考
- 多 LoRA 动态加载 场景下,同一张卡可能服务多个微调模型,此时需在属性里加上 lora_id 与 base_model,否则 histogram 聚合会掩盖慢模型。
- 国产芯片(昇腾 910B)使用 CANN 栈时,stream 回调接口与 CUDA 不同,需在 C++ 侧用 aclrtLaunchCallback 插入时间戳,再通过 opentelemetry-cpp 的 OTLP gRPC exporter 发送到 Collector,避免 Python GIL 带来的误差。
- 成本治理:把 token 延迟与 GPU 功耗(DCGM 指标)联合建模,可算出每 token 能耗(J/token),在华东/华北电力差价大的机房做绿色调度,实现“双碳”合规。
- 审计回溯:对金融客户,需把同一 request 的 trace_id 写入日志与指标,事后可重放整条链路;此时推荐把 trace_id 作为 exemplar 挂在 histogram 上,Grafana 10 以上支持一键下钻到 Jaeger 链路。
- 下一步演进:当业务规模到 10 亿级 token/天 时,Prometheus 本地存储成为瓶颈,可切换到 Apache Doris/StarRocks 作为 OLAP 指标仓库,通过 OTel Collector 的 dorisexporter 直写,实现 1 分钟级大规模聚合,支撑实时计费与 SLA 对账。
掌握以上思路,面试时既能给出可落地的代码级方案,又能结合国内合规、国产芯片、成本优化侃侃而谈,将“监控 token 延迟”这一单点问题升维到LLMOps 体系化建设,可稳稳拿到高分。