
上个月我把 AI 动态阈值告警上线,本想在大促中显显灵,结果 10 分钟 60 条告警,误报率高达 97%——比传统规则告警还差了 22 个百分点。

6 月 18 日凌晨 2 点,我的手机开始疯狂震动。
Prometheus 的 AI 动态阈值告警像机关枪一样推送——10 分钟内触发了 60+ 条告警。
我迷迷糊糊点开一看:订单服务 QPS 超限、API 延迟爆表、CPU 全线飘红……当时脑子嗡的一下——完了,大促崩了?
结果仔细一看,全是误报。
时间 | 告警数 | 指标 | 动态阈值上界 | 实际值 |
|---|---|---|---|---|
00:02 | 12 | 订单服务 QPS | 3500 | 8200 🚨 |
00:05 | 28 | API P99 延迟 | 450ms | 1200ms 🚨 |
00:08 | 45 | CPU 使用率 | 72% | 95% 🚨 |
真正让我崩溃的是这个对比:
告警类型 | 告警数 | 真实故障 | 误报 | 误报率 |
|---|---|---|---|---|
传统规则告警(固定阈值) | 8 | 2 | 6 | 75% |
AI 动态阈值告警 | 60 | 2 | 58 | 97% |
AI 告警误报率 97%——比规则告警还差!

那一刻我意识到:没有业务上下文的 AI,比没有 AI 更可怕。
排查了一整夜,锁定了三个根因:
[INFO] 订单服务 QPS 基线计算
- 训练数据: 2026-06-10 ~ 2026-06-17 (7天)
- 均值: 1850 QPS ← 全是日常流量
- 标准差: 550
- 动态阈值上限: 1850 + 3×550 = 3500 QPS
- ⚠️ 大促实际峰值 8200 QPS
动态阈值基于"过去 7 天"的日常流量算基线,但 618 大促的 QPS 是日常的 4 倍。AI 认为 3500 以上是异常,但实际正常峰值是 8200。
系统没有"业务日历"感知。AI 不知道 6 月 18 日是 618 大促,没有自动切换到宽限模式。
当告警触发速率超过 20 条/分钟时,系统没有自动静默,而是持续轰炸。

方案 1:基线排除大促数据
defget_training_data(metric: str, exclude_events: list[str] = None):
"""获取训练数据,排除异常事件影响"""
raw_data = fetch_metric(metric, days=30)
if exclude_events:
for event in exclude_events:
raw_data = raw_data[raw_data['ds'] notin event_dates]
return calc_dynamic_threshold(raw_data)
方案 2:大促模式预设宽限窗口
PROMOTION_CONFIG = {
"618": {"date": "2026-06-18", "multiplier": 3.0}, # 阈值放宽 3 倍
"双11": {"date": "2026-11-11", "multiplier": 5.0},
"黑五": {"date": "2026-11-27", "multiplier": 4.0},
}
defis_promotion_today() -> bool:
today = datetime.now().strftime("%Y-%m-%d")
for name, config in PROMOTION_CONFIG.items():
if today == config["date"]:
return name
returnNone
promotion = is_promotion_today()
threshold_multiplier = PROMOTION_CONFIG.get(promotion, {}).get("multiplier", 1.0)
upper_threshold = mean + sigma * std * threshold_multiplier
修复后效果:
修复前: 告警 60+ 条,误报 58 条,误报率 97%
修复后: 告警 5 条,误报 1 条,误报率 2%
真实故障捕获: 2/2(100% 全覆盖)
教训:AI 不是银弹。动态阈值的"智能"建立在"历史代表未来"的假设上,但大促日这个假设根本不成立。业务日历是 AIOps 的基石。
翻车之后,我重新设计了智能告警体系。下面三种模式是从踩坑中打磨出来的:
传统阈值是固定值(如 CPU > 80%),动态阈值根据历史数据自动计算正常范围。
原理很简单:
过去 7 天同时段数据 → 计算均值 + 标准差 → 动态阈值 = 均值 + 3σ
↓
实时指标与动态阈值对比
↓ ↓
超阈值→告警 正常→跳过
Python 实现:
import requests
import numpy as np
from datetime import datetime, timedelta
PROMETHEUS_URL = "http://localhost:9090"
defget_history(metric: str, hours: int = 168) -> list:
"""获取过去 N 小时的指标数据"""
end = datetime.now()
start = end - timedelta(hours=hours)
response = requests.get(
f"{PROMETHEUS_URL}/api/v1/query_range",
params={
"query": metric,
"start": start.timestamp(),
"end": end.timestamp(),
"step": "300s"
}
)
values = [float(v[1]) for v in response.json()["data"]["result"][0]["values"]]
return values
defcalc_dynamic_threshold(values: list, sigma: float = 3.0) -> dict:
"""计算动态阈值(3-Sigma)"""
arr = np.array(values)
mean = np.mean(arr)
std = np.std(arr)
upper = mean + sigma * std
lower = mean - sigma * std
return {
"mean": round(mean, 2),
"std": round(std, 2),
"upper_threshold": round(upper, 2),
"current": round(values[-1], 2),
"is_anomaly": values[-1] > upper or values[-1] < lower
}
# 示例
result = calc_dynamic_threshold(get_history('node_cpu_seconds_total{mode="idle"}'))
print(f"动态阈值上界: {result['upper_threshold']}%")
print(f"当前值: {result['current']}%")
print(f"是否异常: {'⚠️ 是'if result['is_anomaly'] else'✅ 否'}")
关键价值:凌晨 CPU 30% 也可能告警(因为该时段基线是 15%),白天 CPU 75% 反而正常。同一个阈值,不同时段自动调整。

真实故障往往涉及多个指标——数据库慢查询导致接口超时、内存泄漏导致 OOM 导致服务重启。AI 关联分析能识别"指标 A 异常导致指标 B 异常"的因果链。
因果链 | 触发指标 | 关联指标 | 告警策略 |
|---|---|---|---|
数据库慢查询 → 接口超时 | MySQL 慢查询数 | HTTP P99 延迟 | 合并为一条 |
内存泄漏 → OOM → 服务重启 | 内存使用率 | 进程重启次数 | 合并为一条 |
磁盘满 → 写入失败 → 服务异常 | 磁盘使用率 | 错误日志数 | 合并为一条 |
告警聚类实现:
"""告警聚类降噪:将关联告警合并为一条事件"""
from collections import defaultdict
CORRELATION_RULES = [
{
"name": "数据库慢查询链",
"triggers": ["MySQLSlowQueries", "HTTPP99High"],
"group": "db_performance"
},
{
"name": "内存泄漏链",
"triggers": ["MemoryHigh", "ProcessRestarted", "OOMKilled"],
"group": "memory_issue"
},
]
defcluster_alerts(active_alerts: list) -> list:
"""将活跃告警按关联规则聚类"""
groups = defaultdict(list)
ungrouped = []
for alert in active_alerts:
matched = False
for rule in CORRELATION_RULES:
if alert["name"] in rule["triggers"]:
groups[rule["group"]].append(alert)
matched = True
break
ifnot matched:
ungrouped.append(alert)
# 合并每组为一条事件
events = []
for group_name, alerts in groups.items():
events.append({
"event_type": group_name,
"alert_count": len(alerts),
"alerts": [a["name"] for a in alerts],
"severity": max(a.get("severity", "warning") for a in alerts)
})
for alert in ungrouped:
events.append({
"event_type": alert["name"],
"alert_count": 1,
"alerts": [alert["name"]],
"severity": alert.get("severity", "info")
})
return events
聚类效果:
指标 | 聚类前 | 聚类后 | 改善 |
|---|---|---|---|
告警数量 | 50 条 | 8 条 | ⬇️ 84% |
事件数量 | — | 3 个 | — |
通知条数 | 50 条 | 3 条 | ⬇️ 94% |

新服务上线没有告警规则?让 AI 根据服务特征自动生成:
hermes chat "为以下服务生成 Prometheus 告警规则:
- 服务名:order-service
- 类型:Spring Boot REST API
- SLA:99.9% 可用性,P99 < 500ms
- 依赖:MySQL、Redis、Kafka
要求:
1. 覆盖可用性、延迟、错误率、资源四大类
2. 每条规则标注严重级别(P0-P3)
3. 输出 Prometheus rule YAML 格式"
AI 生成的节选:
groups:
-name:order-service-availability
rules:
-alert:OrderServiceDown
expr:up{job="order-service"}==0
for:1m
labels:
severity:P0
annotations:
summary:"订单服务不可用"
-alert:OrderServiceHighErrorRate
expr:|
(
sum(rate(http_requests_total{job="order-service",code=~"5.."}[5m]))
/
sum(rate(http_requests_total{job="order-service"}[5m]))
) > 0.01
for:3m
labels:
severity:P1
annotations:
summary:"订单服务错误率超过 1%"
关键:AI 生成 +
promtool check rules验证,双保险才能上线。
就是开头的故事。解决:业务日历 + 宽限窗口。
两条独立故障的告警被合并成一条,漏处理了第二条。
解决:关联规则增加时间窗口约束——同 5 分钟内的告警才关联,超时不合并。宁可漏合并,不要误合并。
大模型对 PromQL 的理解不如 SQL 熟练,复杂表达式容易出错。
解决:生成后用 promtool check rules 验证,AI 生成 + 工具验证双保险。
全部切换到 AI 告警后,某些简单指标(如磁盘满)的告警反而延迟了。
解决:核心指标保留规则告警作为兜底,AI 告警作为增强层叠加。规则告警是安全网,AI 告警是放大镜。
AI 智能告警需要和传统规则协同工作。我目前的生产环境配置:
监控项 | 采集方式 | 更新间隔 | 告警阈值 | 严重级别 |
|---|---|---|---|---|
服务可用性 | Prometheus up | 15s | == 0 持续 1m | P0 |
错误率 | 指标计算 | 60s | > 1% | P1 |
P99 延迟 | 直方图 | 60s | > 500ms | P1 |
CPU 使用率 | node_exporter | 30s | > 80% | P2 |
动态阈值 - QPS | AI 3-Sigma | 60s | 均值 + 3σ | P1 |
动态阈值 - 延迟 | AI 3-Sigma | 60s | 均值 + 3σ | P2 |
动态阈值定时更新脚本(每 5 分钟执行一次):
#!/bin/bash
# =====================================================
# dynamic_threshold_cron.sh - AI 动态阈值更新
# =====================================================
PROMETHEUS="http://localhost:9090"
ALERT_DIR="/etc/prometheus/rules/dynamic"
BACKUP_DIR="/var/backup/prometheus_rules"
DATE=$(date +%Y%m%d_%H%M%S)
# 备份当前规则
mkdir -p $BACKUP_DIR
cp -r $ALERT_DIR$BACKUP_DIR/rules_$DATE
find $BACKUP_DIR -name "rules_*" -mtime +30 -execrm -rf {} \;
# 计算动态阈值
for metric in"cpu_usage""memory_usage""qps""latency_p99"; do
python3 /opt/scripts/calc_threshold.py \
--prometheus $PROMETHEUS \
--metric $metric \
--output $ALERT_DIR/${metric}_dynamic.yml
done
# 验证规则语法
promtool check rules $ALERT_DIR/*.yml
if [ $? -ne 0 ]; then
echo"规则验证失败,回滚!"
cp -r $BACKUP_DIR/rules_$(ls -t $BACKUP_DIR | head -2 | tail -1)/* $ALERT_DIR/
exit 1
fi
# 重新加载 Prometheus 规则
curl -X POST http://localhost:9090/-/reload
echo"✅ 动态阈值更新完成: $DATE"
# Crontab 配置 - 每 5 分钟更新动态阈值
*/5 * * * * /opt/scripts/dynamic_threshold_cron.sh
0 0 * * 1 /opt/scripts/prometheus_backup.sh

翻车不可怕,翻车后不知道为什么不爬起来才可怕。经历了 618 的 97% 误报率后,我总结出智能告警的三个核心原则:
效果数据(翻车修复后在双 11 大促验证):
适用场景:告警量大(50+ 条/天)、误报率高、运维人力紧张 不适用场景:告警量少(< 10 条/天)、已有成熟规则的稳定系统
🔔 下一篇预告:AI 故障预测实战——基于时序数据的容量规划与提前预警