下游依赖门诊三件套+延迟:调用量分布 / 失约率 / 在途通话 / 等待时长。gpt-5 长输出 60s+ 属正常别当故障;>10% 错误率=限流(429)/key 失效——切流量不是修 bug。覆盖核心盘 🟡 组 4 张面板(c12–c15)。
我们雇了几家"外包医生"(gpt-5 / xAI / MiniMax / Seedance)给用户看病。这四张图就是门诊台账:c12 挂号量(每家每秒被 call 几次=钱花在谁家)、c13 失约率(失败次数÷总次数)、c14 占线中的电话(在途调用)、c15 等回话时长(P50/P95/P99,单位秒)。
关键心法:供应商是别人家的服务,坏了修不了只能切。所以 >10% 错误率的处置动作是"切流量或换 key",不是通宵修 bug;而 gpt-5 长输出跑到 60s+ 是正常现象,别当成故障拉响警报。
想象你开了一家连锁诊所,把看病外包给三家机构。你关心四件事:每家每天接多少单(调用量——也是你的成本账);约了没来的比例(失约率,也就是错误率);此刻有几通电话占着线(在途并发——电话占线说明新的预约只能排队);以及病人平均等多久能看上(延迟)。这四件事各对应一张图,全按 provider(哪家供应商)拆开看。
失约率怎么算?把每家的失败次数和总次数都是计数器(_total 只增不减),各自求速度再相除:rate(失败)/rate(总数)——这就是 c13。分母要小心:凌晨没人打电话时总数趋近 0,除以接近 0 的数会爆炸,所以面板用了 clamp_min(..., 1) 把分母兜底。阈值口径:<5% 绿、5–10% 黄、>10% 红。红的含义要想清楚:多半是 provider 限流(429)或 key 失效——不是我们的 bug,切流量或换 key 才是止血。
延迟为什么要看三个分位数?因为"平均等待"会被海量快请求稀释:一个 60 秒的长输出混在 999 个 1 秒的短请求里,平均只有约 1.06 秒——看起来岁月静好,其实有人等了整整一分钟。P50 告诉你典型体验,P95/P99 告诉你"最惨的那批人"有多惨。还有一条铁三角在暗中生效:并发 ≈ QPS × 延迟——模型慢一倍,同样流量下占线的电话就多一倍,新请求排队更久,恶性循环。这就是 c14 持续高位时为什么危险。
| 类比里的东西 | 系统里对应的东西 |
|---|---|
| 每家外包接多少单 | c12 LLM 调用 QPS(按 provider):rate(calls_total),钱花在哪家的实时账,被刷/活动时最先动的线 |
| 约了没来的比例 | c13 错误率 = rate(失败)/clamp_min(rate(总数),1):<5% 绿 / 5–10% 黄 / >10% 红 |
| 占线中的电话 | c14 当前在途调用数(Gauge,按 provider):持续高位 = 大家在排队等模型回话 |
| 等多久能看上病 | c15 延迟 P50/P95/P99(按 model,单位秒):gpt-5 长输出 60s+ 属正常 |
| 外包临时停诊 | >10% 错误率多半是 429 限流 / key 失效——切流量或换 key,不是修 bug |
| 医生变慢 → 候诊室爆炸 | 铁三角:延迟翻倍 → 并发翻倍 → 排队更狠——LLM 版雪崩链条 |
# 按 provider 拆 = 每家一条线(c12 口径) sum(rate(siliang_llm_calls_total[5m])) by (provider)
# Counter 必配 rate(c12 口径)
sum(rate(siliang_llm_calls_total[5m])) by (provider)# c13 口径:clamp_min 把分母兜底到 1 rate(siliang_llm_calls_total{outcome="error"}[5m]) / clamp_min(rate(siliang_llm_calls_total[5m]), 1)
_bucket,le 标签);分位数从桶里插值算出。# le 必须进 by,否则所有桶搅成一锅(c15 口径)
sum(rate(siliang_llm_call_duration_seconds_bucket[5m])) by (le, model)# 三个分位数换参数即可(c15 口径)
histogram_quantile(0.50, sum(rate(..._bucket[5m])) by (le, model))# Gauge 直接读,按 provider 拆(c14 口径) sum(siliang_llm_active_calls{job="siliang_backend_worker"}) by (provider)
# 指标名自带单位:_seconds_bucket # gpt-5 长输出 60s+ 属正常(主图谱口径卡原文)
# 延迟降一半 = 同样 QPS 下并发少一半 # 处置三选一:扩并发额度 / 换更快的模型 / 上队列
它是什么:每家大模型(gpt-5 / xAI / MiniMax…)每秒被我们 call 几次——调用总数是 Counter,配 rate 按 provider 拆开,就是"钱花在哪家的实时账"。它也是流量异动的第一传感器:被刷、活动放量,都是这条线先动。
回答什么问题:调用量分布与突增——占比合不合理?有没有无缘无故的暴涨?
✅ 各家占比随业务节奏稳定波动,无发布无活动时量级平稳
🚨 无发布无活动却突增 = 被刷/爬虫或调用方失控。动作:先对照 HTTP 侧(c16 按 handler)找是谁在大量发起,再决定限流。
# 面板 PromQL(照抄主图谱口径) sum(rate(siliang_llm_calls_total[5m])) by (provider)
联动:主图谱 c12 ↗ · 相关课程:rate():算速度 · HTTP QPS(上游是谁)
它是什么:每家供应商的"失约率"= 失败次数 ÷ 总次数。分子分母都是计数器求速率再相除,分母用 clamp_min 兜底防止低流量时爆表。这是判断"供应商是不是出事了"的核心图。
回答什么问题:哪家在失约?是持续恶化还是偶发抖动?
✅ <5% 绿 / 5–10% 黄
🚨 >10% 红:多半是 provider 限流(429)或 key 失效——切流量或换 key,不是我们的 bug。动作:先止血(切流量)再观察,别急着翻自己代码。
# 面板 PromQL(照抄主图谱口径) rate(siliang_llm_calls_total{outcome="error"}[5m]) / clamp_min(rate(siliang_llm_calls_total[5m]), 1)
联动:主图谱 c13 ↗ · 相关课程:rate():算速度 · 5xx vs 4xx:谁的错
它是什么:此刻每家供应商有几通"电话正在通话中"(在途调用)。它是 Gauge,按 provider 拆开直接读数。它回答的是"LLM 是不是成了瓶颈"——每个在途调用都占着一份协程与内存,等模型慢慢回话。
回答什么问题:LLM 是不是成了瓶颈?持续高位 = 大家在排队等模型回话。
✅ 随业务节奏低位波动,峰值后快速回落
🚨 持续高位且用户等得久 → 扩并发额度 / 换更快的模型 / 上队列。动作:结合 c15 延迟判断是"量太大"还是"太慢",铁三角(并发 ≈ QPS × 延迟)帮你定位。
# 面板 PromQL(照抄主图谱口径) sum(siliang_llm_active_calls{job="siliang_backend_worker"}) by (provider)
联动:主图谱 c14 ↗ · 相关课程:QPS·并发·延迟铁三角 · 进程资源四件套(协程占用)
它是什么:用户等模型回话要多久——延迟存在一叠"水桶"(_seconds_bucket)里,用 histogram_quantile 按 model 插值算出 P50/P95/P99 三条线。典型体验看 P50,最惨的 5%/1% 看 P95/P99。单位是秒。
回答什么问题:用户的"等得久"是普遍现象还是长尾个案?哪家模型变慢了?
✅ gpt-5 长输出跑到 60s+ 属正常,别当成故障(单位:秒)
🚨 P50 突然整体抬升 = provider 变慢/限流。动作:对照 c13 错误率互证——P50 抬升+错误率同时走高 = 限流实锤,切流量;P99 尖而 P50 平 = 长尾个案,查单笔慢调用。
# 面板 PromQL(照抄主图谱口径,0.50/0.95/0.99 三个分位) histogram_quantile(0.50, sum(rate(siliang_llm_call_duration_seconds_bucket[5m])) by (le, model))
联动:主图谱 c15 ↗ · 相关课程:P50/P95/P99:排队 · 直方图水桶
阈值只来自核心盘口径卡(<5% 绿 / 5–10% 黄 / >10% 红),持续 10 分钟才报,避开偶发抖动。
# 告警规则:LLM 错误率(阈值来自核心盘 c13 口径卡) groups: - name: siliang-llm rules: - alert: LLMErrorRateHigh expr: | rate(siliang_llm_calls_total{outcome="error"}[5m]) / clamp_min(rate(siliang_llm_calls_total[5m]), 1) > 0.10 for: 10m labels: severity: critical # >10% 红:多半 provider 限流(429)/key 失效 annotations: summary: "provider {{ $labels.provider }} 错误率超 10% —— 切流量/换 key,不是修 bug"
判读收尾:告警恢复后回头看 c12,确认流量是否已切到健康供应商。
供应商是别人家的服务,处置顺序是"确认→评估→切→验证",全程不动自己的代码。
# 1) 确认是谁失约:错误率按 provider 拆(c13 口径) rate(siliang_llm_calls_total{outcome="error"}[5m]) by (provider) / clamp_min(rate(siliang_llm_calls_total[5m]) by (provider), 1) # 2) 评估影响面:该家占多少流量(c12 口径) sum(rate(siliang_llm_calls_total[5m])) by (provider) # 3) 切流量/换 key(配置层操作,非代码修复) # 4) 验证:被切家的错误率归零,承接家 QPS 抬升且错误率保持绿区
先分清"普遍变慢"还是"长尾个案"——处置路径完全不同。
# 典型体验(P50,按 model 拆,c15 口径) histogram_quantile(0.50, sum(rate(siliang_llm_call_duration_seconds_bucket[5m])) by (le, model)) # 最惨的 1%(P99) histogram_quantile(0.99, sum(rate(siliang_llm_call_duration_seconds_bucket[5m])) by (le, model)) # 互证口径: # P50 抬升 + c13 错误率走高 = provider 变慢/限流 → 切流量 # P99 尖而 P50 平 = 长尾个案 → 查单笔慢调用(大参数/长输出属正常) # gpt-5 长输出 60s+ 属正常,别当故障(口径卡原文)
LLM 每一通电话都是钱。QPS 突增时先找到"谁在发起",再决定限谁。
# 1) 谁家在暴涨(c12 口径) sum(rate(siliang_llm_calls_total[5m])) by (provider) # 2) 上游是哪个接口在大量发起(c16 口径) sum(rate(siliang_http_requests_total[5m])) by (handler) # 3) 对照时段:有无发布/活动(无发布无活动却突增 = 被刷/爬虫) # 判读:handler 明确 → 限该 handler;全站齐涨 → 查入口与账号体系
占线电话下不来时,用并发 ≈ QPS × 延迟判断该扩额度、换模型还是上队列。
# 在途通话(c14 口径) sum(siliang_llm_active_calls{job="siliang_backend_worker"}) by (provider) # 拆解:并发高 + QPS 高 = 量大 → 扩并发额度 / 上队列削峰 # 并发高 + 延迟高 = 太慢 → 换更快的模型(c15 看 P50 抬升) # 铁三角:并发 ≈ QPS × 延迟 —— 模型快一倍,同样流量并发少一半
切换流量不是发完就完:接下来一小时用四张图做"术后监护"。
# 1) 流量到位了吗(c12):新 provider 的线应抬起来 sum(rate(siliang_llm_calls_total[5m])) by (provider) # 2) 没有跟着失约吧(c13):绿区 <5%,冲高立刻回滚 rate(siliang_llm_calls_total{outcome="error"}[5m]) by (provider) / clamp_min(rate(siliang_llm_calls_total[5m]) by (provider), 1) # 3) 占线正常吗(c14):与旧供应商同期水位对照 sum(siliang_llm_active_calls{job="siliang_backend_worker"}) by (provider) # 4) 体感没变差吧(c15):P50 与旧家同量级;gpt-5 长输出 60s+ 属正常 histogram_quantile(0.50, sum(rate(siliang_llm_call_duration_seconds_bucket[5m])) by (le, model)) # 判读:四线齐平 = 切换成功;错误率冒头 = 回滚,别恋战
# 错: P99 > 60s → 报 critical # 对: P50 平稳 + 错误率 < 5% → 长输出正常,不报警
# 错: rate(err[5m]) / rate(total[5m]) # → 凌晨除小数爆表 # 对: rate(err[5m]) / clamp_min(rate(total[5m]), 1)
# 错: sum(rate(..._duration_seconds_bucket[5m])) # 对: sum(rate(..._duration_seconds_bucket[5m])) by (le, model)
# 错: 错误率 >10% → 立刻改代码发版 # 对: 错误率 >10% → 切流量/换 key,同时对照 c12 看承接家水位
# 错: 只画 histogram_quantile(0.50, …) # 对: 0.50 / 0.95 / 0.99 三条线一起看(c15 面板口径)
_seconds_bucket,单位是秒。正解:从指标名认单位(_seconds),gpt-5 的 60s+ 才说得通。# 错: 把 c15 的 3 读成 3ms # 对: siliang_llm_call_duration_seconds_bucket → 单位是秒
# 错: c14 冲高 → 报警 # 对: c14 持续高位 + c15 P50 抬升 → 才是瓶颈信号
# 错: sum(siliang_llm_active_calls) by (provider) # 对: sum(siliang_llm_active_calls{job="siliang_backend_worker"}) by (provider)
参考答案:c12 是每家供应商每秒被 call 几次(钱花在哪家的实时账);c13 是每家的失约率(失败÷总数,<5% 绿 >10% 红);c14 是此刻有几通电话占线(在途并发,持续高位=在排队);c15 是等回话要多久(P50 典型体验、P99 最惨的 1%,单位秒)。
参考答案:先别翻自己的代码。>10% 红多半是 provider 限流(429)或 key 失效——不是我们的 bug。处置:按 provider 确认谁在失约(c13)→ 评估它占多少流量(c12)→ 切流量或换 key 止血 → 验证承接家 QPS 抬升且错误率回到绿区。
参考答案:凌晨没人调用时总次数速率趋近 0,除以接近 0 的数会让错误率爆表(例如 0.5/0.001=500%),制造假警报。clamp_min 把分母兜底到 1,低流量时段错误率就是分子本身,曲线不再爆炸。
参考答案:并发 ≈ QPS × 延迟。并发高 + QPS 高 = 来的量真大——扩并发额度或上队列削峰;并发高 + P50 抬升(c15)= 模型变慢——换更快的模型,因为延迟降一半,同样的 QPS 并发就少一半。只看 c14 一张图分不清这两种情况,所以三张图必须连着看。