首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >一条 P0 告警被晾了 8 分钟,就因为 AI 分类错了

一条 P0 告警被晾了 8 分钟,就因为 AI 分类错了

作者头像
行者全栈架构师
发布2026-07-13 15:35:30
发布2026-07-13 15:35:30
4120
举报

AI 把 MySQL 主库宕机的 P0 告警归类成了"基础设施",发到了一个只有 1 人值班的群。那个人正在处理另一个故障,P0 告警被晾了 8 分 12 秒。 这本该是一条 5 分钟内必须响应的紧急故障。

📖 本文导读

如果你只读 30 秒,记住这三个数据就够了:

指标

手动分派

AI 自动分类

改善

告警响应时间

15 分钟 😩

2 分钟 🚀

⬇️ 87%

误分派率

28% 🤦

3% ✅

⬇️ 89%

值班人员告警量

300 条/天

30 条/天

⬇️ 90%

01 被 300 条告警轰炸的运维日常 🔥

时间:上个月某天下午 地点:运维值班室

我所在的运维团队 7 个人,每天收到 300+ 条告警。真实故障混在大量噪声里,根本分不清哪个该马上看。

痛点

具体表现

告警风暴

一个数据库故障触发 80+ 条关联告警,手机震个不停

分派混乱

告警群发所有人,3 个组同时响应又互相推诿

升级滞后

P0 告警和 P3 告警混在一起,P0 反而被忽略

响应疲劳

每天 300 条轰炸,开始选择性忽略

去年有一次 P0 故障,告警发出后 12 分钟 才有人响应——因为大家已经对告警麻木了。

02 那个让我背 P0 故障的深夜 AI 误分类 👊

时间:2026 年 5 月某个晚上 场景:AI 告警分类系统刚上线第三天

一条 MySQL 主库宕机 的 P0 告警进来了。AI 分类器看了一眼,判断为"基础设施",路由到了基础设施值班群。

问题在于:基础设施群当晚只有 1 人值班,而且他正在处理另一个故障。这条 P0 告警被晾了 8 分 12 秒

问题还原

代码语言:javascript
复制
# 原始告警
告警名称: MySQLDown | 服务: order-db | 环境: production
摘要: MySQL 主库连接失败,已持续 2 分钟

# AI 分类结果(错误)
分类: infrastructure  ← ❌ 应该分类到 database
置信度: 0.52         ← ⚠️ 阈值设得太低

# 路由结果(错误)
路由到: 基础设施值班群 ← ❌ 应该路由到 DBA 组
响应时间: 8 分 12 秒  ← ❌ P0 要求 < 5 分钟

根因排查

代码语言:javascript
复制
# 检查分类器对 MySQLDown 的匹配
alert_text = "告警名称: MySQLDown 服务: order-db 摘要: MySQL 主库连接失败"

for cat, prototypes in CATEGORY_PROTOTYPES.items():
    proto_embs = category_embeddings[cat]
    a_emb = model.encode([alert_text])
    sim = np.max(np.dot(proto_embs, a_emb.T))
print(f"{cat}: 最高相似度 {sim:.4f}")

# 输出:
# database:  0.48  ← 低,因为没包含"MySQL主库"原型
# network:   0.31
# application: 0.35
# infrastructure: 0.52  ← 最高!因为"连接失败"像网络问题

三个根因叠加

  1. 分类原型不完整——database 类缺"MySQLDown""主库宕机"等变体,导致匹配到了 infrastructure
  2. 置信度阈值设太低(0.45)——0.52 的低置信度居然通过了,应该转人工兜底
  3. P0 告警没做路由复核——P0 即使分类到次要类别,也应该广播通知所有相关群

修复方案

代码语言:javascript
复制
# 修复 1:增强数据库分类原型
CATEGORY_PROTOTYPES["database"] = [
"MySQL 连接失败", "MySQLDown", "主库宕机",
"数据库不可用", "connection refused",
"replication lag", "主从延迟",
]

# 修复 2:置信度拒绝策略
defclassify_with_fallback(alert_text, threshold=0.65):
    results = classify_alert(alert_text, top_k=2)

if results[0]["max_similarity"] < threshold:
return {"category": "pending_review", "fallback": True}

# P0/P1 告警即使分类低也通知所有相关群
if alert.severity in ("P0", "P1") and results[0]["max_similarity"] < 0.75:
return {"category": results[0]["category"], "notify_all": True}

return results[0]

# 修复 3:P0 告警广播通知
P0_BROADCAST_GROUPS = ["database", "infrastructure", "application"]

修复效果

代码语言:javascript
复制
修复后 MySQLDown 分类:
- database 相似度: 0.81 ✅
- 分类准确率: 从 72% → 94%
- P0 误路由率: 从 8% → 0.5%

经验教训

  1. 分类原型必须覆盖告警名称变体——光有"MySQL 连接失败"不够,还要有"MySQLDown"
  2. 置信度阈值宁可高也不要低——低置信度转人工审核,比误分类好
  3. P0 告警走广播路由——紧急告警宁多勿少

03 智能告警分发的核心架构 ⚙️

四层架构设计

Mermaid Chart
Mermaid Chart

告警语义分类的核心逻辑

核心思路:用 text2vec-base-chinese 把告警文本转为向量,与每个类别的原型向量做余弦相似度匹配。

代码语言:javascript
复制
from sentence_transformers import SentenceTransformer
import numpy as np

model = SentenceTransformer("shibing624/text2vec-base-chinese")

# 定义分类原型
CATEGORY_PROTOTYPES = {
"database": [
"MySQL 数据库连接失败", "Redis 缓存命中率过低",
"MongoDB 副本集主从切换", "数据库慢查询数量超过阈值",
    ],
"network": [
"CDN 节点不可达", "负载均衡后端健康检查失败",
"防火墙规则导致连接被拒", "DNS 解析超时",
    ],
"application": [
"Spring Boot 服务不可用", "HTTP 接口错误率超过阈值",
"应用 OOM 内存溢出", "接口响应时间超时",
    ],
"infrastructure": [
"Kubernetes Pod CrashLoopBackOff", "服务器 CPU 使用率过高",
"磁盘 IO 等待时间过长", "节点 NotReady 状态",
    ],
}

# 预计算原型向量
category_embeddings = {}
for cat, descriptions in CATEGORY_PROTOTYPES.items():
    category_embeddings[cat] = model.encode(descriptions)

defclassify_alert(alert_text: str) -> dict:
"""对告警进行语义分类"""
    query_embedding = model.encode([alert_text])

    best_cat, best_score = None, 0
for cat, proto_embeddings in category_embeddings.items():
        similarities = np.dot(proto_embeddings, query_embedding.T).flatten()
        max_sim = float(np.max(similarities))
if max_sim > best_score:
            best_score = max_sim
            best_cat = cat

return {"category": best_cat, "confidence": round(best_score, 4)}

# 测试
test_alert = "告警名称: MySQLDown | 服务: order-db | 环境: production | 摘要: MySQL 主库连接失败"
result = classify_alert(test_alert)
print(f"分类: {result['category']} (置信度: {result['confidence']})")

💡 为什么不用规则匹配? 运维告警的措辞千变万化,同一个故障可能有 20 种不同的表述。规则匹配永远追不上词穷的速度。语义分类用向量相似度兜底,没见过的告警也能"猜"到它该去哪个组。

04 踩坑三连,帮你省 3 天排查时间 🕳️

坑 1:分类原型覆盖不全,新告警全归到"基础设施"

现象:新上线的 Kafka 集群告警被分类为"基础设施"而非"中间件",发给了错误的组。

原因:分类原型中没有"middleware"类别,Kafka 描述与"基础设施"原型相似度偏高。

解决:增加分类原型 + 低置信度归"未分类"兜底:

代码语言:javascript
复制
# 新增中间件分类
CATEGORY_PROTOTYPES["middleware"] = [
"Kafka 消息积压", "RabbitMQ 队列堆积",
"RocketMQ 消费延迟", "ZooKeeper 会话过期",
]

# 低于阈值归为未分类
defclassify_with_threshold(alert_text: str, threshold: float = 0.45):
    results = classify_alert(alert_text)
if results["confidence"] < threshold:
return {"category": "uncategorized"}
return results

🔑 记住:分类体系要跟着业务走,每季度 review 一次分类覆盖率。

坑 2:P0 升级电话打到了已离职员工的手机

现象:凌晨 P0 告警升级,电话打给了三个月前离职的同事。

原因:升级联系人列表写死在配置文件中,人员变动没有同步更新。

解决:从企业通讯录 API 动态获取值班人员:

代码语言:javascript
复制
defget_oncall_person(team: str) -> dict:
"""从企业通讯录获取当前值班人员"""
    resp = requests.get(
f"http://oncall-api.internal/api/v1/schedules",
        params={"team": team, "current": "true"}
    )
    data = resp.json()
return {
"name": data["oncall"]["name"],
"phone": data["oncall"]["phone"],
"dingtalk_id": data["oncall"]["dingtalk_id"],
    }

🔑 记住:凡是涉及人员信息的,一律从动态源获取,写死的配置迟早过期。

坑 3:告警聚合窗口太短,关联告警被拆发送

现象:数据库故障引发 20 条关联告警,被分成 5 次独立通知发送,5 个人各自响应。

原因group_wait 设为 10 秒太短,后续产生的关联告警来不及聚合。

解决:对同分类告警加宽聚合窗口,P0 设 30 秒,P1+ 设 2 分钟。通知中标注"此事件关联 N 条告警"。

🔑 记住:聚合窗口宁可稍长也不要太短——运维宁可晚 30 秒收一条完整通知,也不愿被轰炸 20 次。

05 路由与升级策略:P0 告警必须 5 分钟有人回应 ⏰

升级时间线

超时条件

升级动作

通知方式

P0 告警 5 分钟未认领

升级到组长

钉钉 + 电话

P0 告警 15 分钟未恢复

升级到运维总监

钉钉 + 电话 + 短信

P0 告警 30 分钟未恢复

升级到技术 VP

电话 + 短信

P1 告警 15 分钟未认领

升级到组长

钉钉

P2/P3 告警

不自动升级

仅记录

路由配置(简化版)

代码语言:javascript
复制
routes:
-match:
ai_category:database
ai_severity:P0
receiver:db-team-urgent
group_wait:10s
repeat_interval:5m

-match:
ai_category:database
receiver:db-team-normal
group_wait:30s
repeat_interval:30m

-match:
ai_category:application
ai_severity:P0
receiver:app-team-urgent
group_wait:10s
repeat_interval:5m

最终效果总结 📊

指标

实施前(手动)

实施后(AI 自动)

改善

告警响应时间

15 分钟

2 分钟 🚀

⬇️ 87%

误分派率

28%

3% ✅

⬇️ 89%

P0 告警认领率

65%(常漏看)

98%

⬆️ 51%

值班人员告警疲劳度

300 条/天

30 条/天

⬇️ 90%

适用场景:告警量 > 100 条/天、多团队协作、7x24 运维保障要求高的团队 不适用场景:告警量 < 20 条/天(手动分派可控)、单团队无需路由

🔔 下一篇预告:5 分钟搞定:用 Hermes Agent 接入钉钉实现运维告警自动推送。让告警不再止步于微信群,直接触达责任人。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-10,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 📖 本文导读
  • 01 被 300 条告警轰炸的运维日常 🔥
  • 02 那个让我背 P0 故障的深夜 AI 误分类 👊
    • 问题还原
    • 根因排查
    • 修复方案
    • 修复效果
  • 03 智能告警分发的核心架构 ⚙️
    • 四层架构设计
    • 告警语义分类的核心逻辑
  • 04 踩坑三连,帮你省 3 天排查时间 🕳️
    • 坑 1:分类原型覆盖不全,新告警全归到"基础设施"
    • 坑 2:P0 升级电话打到了已离职员工的手机
    • 坑 3:告警聚合窗口太短,关联告警被拆发送
  • 05 路由与升级策略:P0 告警必须 5 分钟有人回应 ⏰
    • 升级时间线
    • 路由配置(简化版)
  • 最终效果总结 📊
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档