延迟别再看平均值了:AI 应用指标与告警实战(O03)
系列《AI 应用生产化手册》第 8 篇(共 30 篇)|配套开源项目:github.com/ChenYingbo/...
一、先看问题:trace 回答不了"整体表现"
O02 有了 trace,能看"单次请求内部发生了什么",但马上遇到第 3 个问题:
- "整体表现"没有答案------今天比昨天慢了吗?失败率多少?每请求平均花多少钱?trace 一次看一条,回答不了。
- 退化是悄悄发生的------P95 从 2s 涨到 5s,是两周才涨上去的;没有聚合指标,等用户抱怨才发现。
- 看板 = 老板问什么你答什么------"最近稳定吗?"你只能翻 trace 碰运气。
解法:把"单次事件"聚合为"指标"------延迟分位、成功率、成本,再配上看板和告警。
核心认知:指标 = 日志的聚合视图。日志回答"这次发生了什么",指标回答"整体怎么样、在变好还是变坏"。告警让机器在"变坏"时主动叫你。
二、原理
在线指标:选哪些、为什么
| 指标 | 定义 | 为什么重要 |
|---|---|---|
| 延迟分位(P50/P95/P99) | 响应时间分布 | P95 才是用户体感,P99 暴露最差体验 |
| 成功率 / 错误率 | 成功请求 / 总数 | 最基础的"活着吗"信号 |
| Token 消耗/请求 | 每次请求平均 token | 成本与质量的双重指标 |
| 成本/请求 | token × 单价 | 上线后的真金白银 |
| 幻觉率(采样) | 抽样人工/评测判分 | 质量趋势 |
设计原则:一张图回答一个问题。 看板不是堆指标。
为什么用分位不用平均值(必懂)
ini
请求延迟:1s ×9, 60s ×1
平均 = 6.9s ← 看起来还行?
P95 = 1s ← 真实体感:大部分用户 1 秒内
P99 = 60s ← 但最差的用户等了一分钟
平均值掩盖分布,分位暴露分布。生产监控只用分位。
本系列的真实数据 更说明问题:样例日志平均延迟 1.7s,但 P95 = 3000ms------看平均值你根本不知道有人在等 3 秒。
告警设计三原则
| 原则 | 做法 |
|---|---|
| 阈值 = 基线 + 容差 | 先跑基线再定,不拍脑袋 |
| 分级 | WARNING(观察)→ CRITICAL(行动),不要只有一个"严重"档 |
| 抗告警疲劳 | 告警要可行动(写清"看哪、怎么修");连续告警合并去重 |
关键问题:收到告警的人,第一反应知道"干什么"吗?不知道 = 这个告警是噪音。
指标从哪来(两条路径)
| 路径 | 数据源 | 用在哪 |
|---|---|---|
| Langfuse 内置面板 | trace | 日常看板(人看) |
| metrics.py | 结构化日志 | 自动化告警/巡检(机器跑) |
两条路径数据同源,互为补充------面板给人看,脚本给机器跑。
三、动手:算指标 + 验证告警
bash
git clone https://github.com/ChenYingbo/ai-prod-demo.git && cd ai-prod-demo
source .venv/bin/activate
# 1. 用样例日志算指标(免 Key)
python scripts/metrics.py --log tests/fixtures/sample_structured.log \
--input-cost-1m 2 --output-cost-1m 8
真实输出:
yaml
请求数 10
成功 / 失败 9 / 1
错误率 0.1
总延迟 P50/P95/P99(ms) 1200.0 / 3000.0 / 3000.0
LLM 延迟 P50/P95/P99(ms) 1100.0 / 2900.0 / 2900.0
平均 token (入/出) 136.0 / 23.6
估算成本 0.0046
bash
# 2. 告警验证:P95 超过阈值应报警(退出码 1,可接 cron/CI)
python scripts/metrics.py --log tests/fixtures/sample_structured.log --alert-p95-ms 2500
echo "exit=$?" # 1(ALERT 触发)
bash
# 3. 真实日志(有 API Key 后):跑 10 个问题产生日志 → 算真实指标
python scripts/ask.py -f eval/e01_questions.txt
python scripts/metrics.py --log /tmp/app.log --input-cost-1m 2 --output-cost-1m 8
设定你的告警基线
- 连续跑 3 次评测问题集,记录 P95 和错误率
- 按"基线 + 20% 容差"定阈值
- 写进定时巡检(cron):
bash
0 10 * * * cd /path/to/ai-prod-demo && .venv/bin/python scripts/metrics.py \
--log /tmp/app.log --alert-p95-ms 5000 --alert-error-rate 0.05
四、真实踩坑
- 只看平均值:平均 1.7s 掩盖 P95=3s------监控只用分位
- 告警阈值拍脑袋:先跑基线再定;阈值写进配置而不是散在脚本里
- 告警疲劳:阈值太紧天天响,最后没人看------宁可少而准
- 成本算错:单价没配(默认 0)或输入输出价搞混------按实际模型配置
- 口径不一:总延迟(含失败)vs LLM 延迟(纯模型)混为一谈------分两列输出,别合并
- (实测)口径差异会骗人 :同题压测(缓存命中)P50=44ms、QPS 7.07;变题压测(真实模型)才是 P95=4.5s------报指标必须说清口径,否则"缓存性能"会被当成"模型性能"
- 指标和日志脱节:指标异常了没法下钻到具体请求------窗口期日志要留档,配合排障 SOP
五、小结
- 分位不是平均数:P50 常态 / P95 体感 / P99 兜底
- 告警三原则:基线+容差 / 分级 / 抗疲劳
- 两条路径同源:面板(人看) + 脚本(机器跑)
明天(O04):《线上问题定位 SOP》------用 trace + 日志 + 指标三层配合,从"用户报错"到根因。 收藏 + 关注,每天一篇,30 天把 AI 应用送上生产。