🟡 LLM 健康:大模型供应商门诊(核心盘 4 图)

下游依赖门诊三件套+延迟:调用量分布 / 失约率 / 在途通话 / 等待时长。gpt-5 长输出 60s+ 属正常别当故障;>10% 错误率=限流(429)/key 失效——切流量不是修 bug。覆盖核心盘 🟡 组 4 张面板(c12–c15)。

每通电话都记账 LLM 门诊:三家供应商并排,四组观测线(钱花在谁家 · 谁在失约 · 谁在占线 · 等多久) backend(我们) LLM 编排 · 4 worker 每次调用都记 outcome 外部 LLM 提供商(云边界) gpt-5 长输出 60s+ 属正常 xAI 文生图 / 生图 MiniMax 视频任务(H3) Seedance provider 生图 它们是别人家的服务:坏了修不了,只能"切流量" 错误率 >10% = 限流(429) / key 失效(主图谱口径) 四组观测线(本课 4 图) ① 调用 QPS(c12) 钱花在谁家 / 被刷先动 ② 错误率(c13) 失约率:<5% 绿 · >10% 红 ③ 在途并发(c14) 通话中几通 / 排队 ④ 延迟 P50/95/99(c15) 等回话多久(单位:秒) 健康路:错误率 <5% 绿 / 5–10% 黄 · P50 平稳 曲线低位平稳,偶发小刺 事故现场:错误率 >10% 红 = 限流(429) / key 失效 动作:切流量或换 key —— 不是我们的 bug,先止血再谈修复 互证:P50 突然整体抬升 = provider 变慢/限流;gpt-5 60s+ 别当故障 四组线的关系:QPS 突增先看(被刷/活动)→ 错误率定性(谁失约)→ 并发看排队 → 延迟看体感 铁三角 LLM 版:并发 ≈ QPS × 延迟 —— 模型慢一倍,同样流量下并发堆一倍

💡 一句话理解

我们雇了几家"外包医生"(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 版雪崩链条

🛠 动手验证(4 步,在 Grafana 里亲手做一遍)

  1. 看 c12 三条线的分布——谁家占比最高?这就是成本大头;记下平时量级,突增时才有对照。
  2. 看 c13 确认都在绿区——各 provider 错误率 <5%;顺手找到那条 5% 的参考线在心里。
  3. 看 c14 的常态水位——在途调用平时低位波动;记住这个水位,"持续高位"才有判断基准。
  4. 看 c15 的 P50 量级——确认单位是秒,gpt-5 的 P95/P99 到 60s+ 不慌;对照 c13:P50 抬升而错误率绿 = 只是变慢,两线同时炸 = 限流。

🧠 必知必会 看懂本课全部面板的地基

provider / model 标签
每次 LLM 调用都带着身份证:provider=哪家(gpt-5/xAI/MiniMax…),model=哪个型号——by 一下就是"按家拆"的账本。
# 按 provider 拆 = 每家一条线(c12 口径)
sum(rate(siliang_llm_calls_total[5m])) by (provider)
QPS = rate(调用总数)
调用总数是 Counter(_total 只增不减),配 rate 才是"每秒被 call 几次"的活账。
# Counter 必配 rate(c12 口径)
sum(rate(siliang_llm_calls_total[5m])) by (provider)
错误率 = 失败÷总数
分子分母都是速率再相除;分母用 clamp_min 兜底,防低流量时除以接近 0 的数爆表。
# c13 口径:clamp_min 把分母兜底到 1
rate(siliang_llm_calls_total{outcome="error"}[5m])
  / clamp_min(rate(siliang_llm_calls_total[5m]), 1)
延迟是 Histogram 桶
延迟不存单值,存一叠累积水桶(_bucket,le 标签);分位数从桶里插值算出。
# le 必须进 by,否则所有桶搅成一锅(c15 口径)
sum(rate(siliang_llm_call_duration_seconds_bucket[5m])) by (le, model)
P50 / P95 / P99
把所有调用从快到慢排队:P50=典型体验,P95/P99=最惨的 5%/1%。平均延迟会被快请求稀释,不看平均。
# 三个分位数换参数即可(c15 口径)
histogram_quantile(0.50, sum(rate(..._bucket[5m])) by (le, model))
在途并发是 Gauge
active_calls 是温度计:此刻几通电话占线,直接读数;持续高位=排队等模型。
# Gauge 直接读,按 provider 拆(c14 口径)
sum(siliang_llm_active_calls{job="siliang_backend_worker"}) by (provider)
单位是秒;60s+ 别慌
LLM 延迟面板单位是秒;gpt-5 长输出跑到 60s+ 属正常,别当成故障——对照错误率再下结论。
# 指标名自带单位:_seconds_bucket
# gpt-5 长输出 60s+ 属正常(主图谱口径卡原文)
铁三角 LLM 版
并发 ≈ QPS × 延迟:模型慢一倍,同样流量下并发堆一倍——c14 持续高位的数学原理。
# 延迟降一半 = 同样 QPS 下并发少一半
# 处置三选一:扩并发额度 / 换更快的模型 / 上队列

📋 逐面板精讲 4 张图一张不落

🟡 c12 · LLM 调用 QPS(按 provider)

它是什么:每家大模型(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(上游是谁)

🟡 c13 · LLM 错误率(按 provider)

它是什么:每家供应商的"失约率"= 失败次数 ÷ 总次数。分子分母都是计数器求速率再相除,分母用 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:谁的错

🟡 c14 · 当前 LLM 并发调用数

它是什么:此刻每家供应商有几通"电话正在通话中"(在途调用)。它是 Gauge,按 provider 拆开直接读数。它回答的是"LLM 是不是成了瓶颈"——每个在途调用都占着一份协程与内存,等模型慢慢回话。

回答什么问题:LLM 是不是成了瓶颈?持续高位 = 大家在排队等模型回话。

✅ 随业务节奏低位波动,峰值后快速回落

🚨 持续高位且用户等得久 → 扩并发额度 / 换更快的模型 / 上队列。动作:结合 c15 延迟判断是"量太大"还是"太慢",铁三角(并发 ≈ QPS × 延迟)帮你定位。

# 面板 PromQL(照抄主图谱口径)
sum(siliang_llm_active_calls{job="siliang_backend_worker"}) by (provider)

联动:主图谱 c14 ↗ · 相关课程:QPS·并发·延迟铁三角 · 进程资源四件套(协程占用)

🟡 c15 · LLM 调用延迟 P50/P95/P99(按 model)

它是什么:用户等模型回话要多久——延迟存在一叠"水桶"(_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:排队 · 直方图水桶

🏭 生产实战 real world

场景 1 · 错误率告警:>10% 就是供应商出事

阈值只来自核心盘口径卡(<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,确认流量是否已切到健康供应商。

场景 2 · 供应商失约处置 SOP:先止血

供应商是别人家的服务,处置顺序是"确认→评估→切→验证",全程不动自己的代码。

# 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 抬升且错误率保持绿区

场景 3 · "用户说慢":P50 抬升的两分法

先分清"普遍变慢"还是"长尾个案"——处置路径完全不同。

# 典型体验(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+ 属正常,别当故障(口径卡原文)

场景 4 · 被刷侦查:钱线突增先找上游

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;全站齐涨 → 查入口与账号体系

场景 5 · 并发持续高位:铁三角定处置

占线电话下不来时,用并发 ≈ QPS × 延迟判断该扩额度、换模型还是上队列。

# 在途通话(c14 口径)
sum(siliang_llm_active_calls{job="siliang_backend_worker"}) by (provider)
# 拆解:并发高 + QPS 高 = 量大 → 扩并发额度 / 上队列削峰
#       并发高 + 延迟高 = 太慢 → 换更快的模型(c15 看 P50 抬升)
# 铁三角:并发 ≈ QPS × 延迟 —— 模型快一倍,同样流量并发少一半

场景 6 · 新供应商上线 / 换 key 后的四线观察

切换流量不是发完就完:接下来一小时用四张图做"术后监护"。

# 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))
# 判读:四线齐平 = 切换成功;错误率冒头 = 回滚,别恋战

⚠️ 常见坑 pitfalls

坑 1 · 把 gpt-5 的 60s+ 当故障 — 症状:P95/P99 上到 60 秒就拉响警报、半夜爬起来"救火"。原因:LLM 长输出本来就慢,gpt-5 长输出 60s+ 属正常(口径卡原文)。正解:先看 P50 与错误率——P50 平稳、错误率绿区就是正常长尾。
# 错: P99 > 60s → 报 critical
# 对: P50 平稳 + 错误率 < 5% → 长输出正常,不报警
坑 2 · 错误率分母不 clamp — 症状:凌晨曲线炸成 300%,以为是灾难。原因:低流量时总次数趋近 0,除以接近 0 的数会爆炸。正解:分母用 clamp_min(rate(总数), 1) 兜底(面板就是这么写的)。
# 错: rate(err[5m]) / rate(total[5m])          # → 凌晨除小数爆表
# 对: rate(err[5m]) / clamp_min(rate(total[5m]), 1)
坑 3 · histogram 忘 by (le) — 症状:延迟曲线低得离谱且死平。原因:所有水桶被 sum 搅成一锅,分位数失去意义。正解:by 里永远带 le(再带 model/handler 等维度)。
# 错: sum(rate(..._duration_seconds_bucket[5m]))
# 对: sum(rate(..._duration_seconds_bucket[5m])) by (le, model)
坑 4 · 供应商错误当成自己的 bug 修 — 症状:>10% 错误率,团队通宵翻编排代码。原因:多半是 provider 限流(429)或 key 失效,锅不在我们。正解:先切流量或换 key 止血,事后再复盘重试策略。
# 错: 错误率 >10% → 立刻改代码发版
# 对: 错误率 >10% → 切流量/换 key,同时对照 c12 看承接家水位
坑 5 · 只看 P50 不看 P99 — 症状:P50 一路绿,用户投诉却不断。原因:平均值/中位数被快请求稀释,长尾藏在 P99 里。正解:P50 定基线、P95/P99 抓长尾,三线一起读。
# 错: 只画 histogram_quantile(0.50, …)
# 对: 0.50 / 0.95 / 0.99 三条线一起看(c15 面板口径)
坑 6 · 单位误会(秒当毫秒) — 症状:看到 P50=3 以为 3 毫秒"快得离谱"。原因:LLM 延迟指标是 _seconds_bucket,单位是秒。正解:从指标名认单位(_seconds),gpt-5 的 60s+ 才说得通。
# 错: 把 c15 的 3 读成 3ms
# 对: siliang_llm_call_duration_seconds_bucket → 单位是秒
坑 7 · 并发高就直接报警 — 症状:c14 冲高就当事故。原因:并发 = QPS × 延迟,业务高峰本身会推高并发。正解:"持续高位且用户等得久"才处置;结合 c12(量)与 c15(慢)定位是量还是慢。
# 错: c14 冲高 → 报警
# 对: c14 持续高位 + c15 P50 抬升 → 才是瓶颈信号
坑 8 · c14 忘加 job 过滤 — 症状:自建并发面板数值虚高。原因:c14 口径带 {job="siliang_backend_worker"},server job 会双计。正解:照抄面板口径的 job 过滤。
# 错: sum(siliang_llm_active_calls) by (provider)
# 对: sum(siliang_llm_active_calls{job="siliang_backend_worker"}) by (provider)

🎓 费曼自测 合上书,能讲出来才算懂

Q1 · 用一句话向老板解释 c12/c13/c14/c15 分别是什么?

参考答案:c12 是每家供应商每秒被 call 几次(钱花在哪家的实时账);c13 是每家的失约率(失败÷总数,<5% 绿 >10% 红);c14 是此刻有几通电话占线(在途并发,持续高位=在排队);c15 是等回话要多久(P50 典型体验、P99 最惨的 1%,单位秒)。

Q2 · 错误率冲到 15%,你的第一反应和处置是什么?

参考答案:先别翻自己的代码。>10% 红多半是 provider 限流(429)或 key 失效——不是我们的 bug。处置:按 provider 确认谁在失约(c13)→ 评估它占多少流量(c12)→ 切流量或换 key 止血 → 验证承接家 QPS 抬升且错误率回到绿区。

Q3 · 为什么错误率的分母要写 clamp_min(..., 1)?

参考答案:凌晨没人调用时总次数速率趋近 0,除以接近 0 的数会让错误率爆表(例如 0.5/0.001=500%),制造假警报。clamp_min 把分母兜底到 1,低流量时段错误率就是分子本身,曲线不再爆炸。

Q4 · c14 持续高位,怎么用铁三角决定"扩额度还是换模型"?

参考答案:并发 ≈ QPS × 延迟。并发高 + QPS 高 = 来的量真大——扩并发额度或上队列削峰;并发高 + P50 抬升(c15)= 模型变慢——换更快的模型,因为延迟降一半,同样的 QPS 并发就少一半。只看 c14 一张图分不清这两种情况,所以三张图必须连着看。

← 上一课:🔴 后台生成任务:内存泄漏核心哨兵(核心盘 5 图) 📚 课程目录 下一课:🟢 HTTP 健康:门面四件套 →