案例背景
我们公司是一个面向 C 端的电商 + 履约平台,整体架构是典型的微服务形态:42 个核心服务(用户、商品、订单、支付、履约、库存、营销、风控、搜索、推荐、消息、网关等),跑在自建 K8s 集群上,日均订单量 220 万、峰值 QPS 8.4 万。监控体系基于 Prometheus + Grafana + Alertmanager 三件套,再加上 ELK 做日志聚合、Jaeger 做链路追踪。
这套监控体系从 2023 年 Q3 开始搭建,到 2026 年已经迭代了 3 年。最初只有 80 条告警规则,覆盖 CPU、内存、磁盘、接口延迟、错误率这些基础维度;随着业务复杂度上升,告警规则一路膨胀到 412 条,覆盖了业务指标(支付成功率、订单履约延迟、库存扣减失败率)、依赖指标(MySQL 慢查询、Redis 命中率、Kafka 消费滞后)、基础设施指标(节点 NotReady、PV 余量、证书过期)等。
告警现状的几个硬数据
我们从 Alertmanager 和值班系统拉了 2026 年 6 月整月 的告警数据做基线统计,结果触目惊心:
| 维度 | 数据 | 备注 |
|---|---|---|
| 日均告警条数 | 2968 条 | 工作日均值,周末略低(约 2400 条) |
| 峰值日告警数 | 4831 条 | 6 月 18 日大促当天 |
| 告警规则数 | 412 条 | 分布在 42 个服务 |
| 值班 SRE 人数 | 8 人轮班 | 2 人一组,7×24 轮班 |
| 平均响应时间(MTTA) | 25 分钟 | 从告警触发到值班人开始处理 |
| 误报 / 低优先级占比 | 70.4% | 告警噪音的主要来源 |
| 真正需要人工介入的比例 | 18.7% | 其余 10.9% 是自愈或重复告警 |
| 平均处理时间(MTTR) | 47 分钟 | 从开始处理到关闭告警 |
| 告警风暴单次最大量 | 412 条/分钟 | 一个核心服务挂掉引发雪崩 |
把 2968 条告警按"是否需要人工处理"拆开看,分布是这样的:
- P0 致命(服务中断 / 支付失败率 > 5%):日均 12 条(0.4%)——必须立即处理,但值班人经常被海量低优告警淹没,看到 P0 的时候已经过去了 8-15 分钟
- P1 严重(核心接口 5xx > 1% / 慢查询激增):日均 87 条(2.9%)——需要 30 分钟内介入
- P2 一般(非核心服务异常 / 资源水位告警):日均 524 条(17.7%)——可延后处理
- P3 低优 / 误报(磁盘瞬时抖动、探针失败、阈值边缘抖动):日均 2345 条(79.0%)——绝大多数不需要处理
痛点的三句话总结
- 值班人被噪音淹没,看不到真火。2968 条告警里 79% 是噪音,值班同学为了不漏掉 P0,只能每条都扫一眼。但人眼扫 3000 条告警的注意力曲线在第 200 条之后断崖式下跌,真出事的时候反而容易漏。
- 响应慢在"关联",不在"看到"。25 分钟的 MTTA 里,18 分钟花在"判断这条告警是不是真的 + 找相关日志 + 找关联服务 + 翻代码",真正动手处理只占 7 分钟。一个
order-service 5xx 飙升的告警,值班人要先去 ELK 搜日志、去 Jaeger 查链路、去 GitHub 翻最近 commit、去 Grafana 看关联面板——这一套下来 15 分钟就没了。 - 70% 的告警是"已知噪音",但没人敢关。比如某个 Pod 的 CPU 偶尔飙到 85% 又立刻回落、某个 Redis 实例的连接数在阈值边缘抖动——这些告警大家都心知肚明是噪音,但关掉又怕"万一哪天真有事漏了"。告警规则越加越多,删告警规则的 PR 没人敢合。
为什么选 AI 分诊
2026 年 7 月初的一次事故彻底推动了这件事。7 月 3 日凌晨 2:17,支付服务的 P99 延迟从 320ms 飙到 4.8s,告警在 2:17:03 触发。但同一分钟内,由于支付服务调用链上的下游(风控、库存、营销)也跟着抖动,Alertmanager 在 2:17 到 2:22 这 5 分钟内一共触发了 187 条告警。值班同学从 2:17 翻到 2:34 才在告警列表的第 4 页找到那条支付 P99 的 P0 告警,MTTA 17 分钟。事后复盘,支付服务实际在 2:31 就已经因为上游限流而自愈,但值班同学在这 17 分钟里处理了 12 条无关告警、打了 3 个电话、建了 2 个 Incident 通道——全是在做无用功。
这次事故后,SRE 团队定了 KPI:3 个月内把 MTTA 从 25 分钟降到 5 分钟以内,同时把值班人的告警处理量降低 60%。评估了三种方案:
| 方案 | 思路 | 评估结论 |
|---|---|---|
| 告警规则收敛 | 人工梳理 412 条规则,合并同类、调高阈值、关掉已知噪音 | 工作量 3 周 / 2 人,治标不治本,新规则还是会膨胀 |
| 传统告警平台(PagerDuty / Oncall) | 接入成熟商业方案,靠规则引擎做收敛和路由 | 规则引擎是"规则匹配"不是"语义理解",对"相似告警合并"和"根因推断"无能为力,且按人头收费贵 |
| AI 分诊流水线 | Claude 做语义分类 + MCP 做日志/代码/Issue 自动化 | 能理解告警语义、能跨数据源关联、能自动出根因假设,且可积累训练数据持续优化 |
最终选了第三条路。原因是告警分诊本质上是语义理解 + 多源数据关联的问题,规则引擎做不了"这条 payment-service latency p99=4.8s 和那条 risk-control timeout 是不是同一件事"这种判断,但 LLM 天然擅长。另外我们的告警里 70% 是噪音,过滤噪音的本质也是语义判断——"CPU 飙到 85% 又 2 分钟回落"是不是噪音,规则判断不了,但 LLM 能结合历史模式判断。
工具组合与分工
整个分诊流水线由 4 个工具协同完成,每个工具负责一段能力:
| 工具 | 分工 | 为什么是这个工具 |
|---|---|---|
| Claude | 告警分类、优先级判定、根因推断、通知路由决策 | 200K 上下文能吞下整个告警上下文(当前告警 + 关联告警 + 历史相似告警 + 服务依赖拓扑);语义理解能力能把"5xx 飙升 + 慢查询激增 + 上游调用失败"这三条独立告警关联成"同一根因" |
| 日志分析 MCP | 自动从 ELK 拉相关日志、做错误聚类和关键词提取 | Claude 直接读 ELK API 要写一堆查询 DSL,且单次拉取量大;MCP 封装了"按服务 + 时间窗 + 错误级别拉日志"和"自动做错误堆栈聚类"两个高频操作 |
| GitHub MCP | 自动创建 Issue、关联最近 commit、查 PR 变更 | 告警分诊完成后要落地成 Issue 才能跟踪;GitHub MCP 能直接在告警发生时查"最近 24 小时该服务有哪些 PR 合并",辅助根因推断 |
| Slack MCP | 告警通知路由、@相关人、建 Incident 通道 | 不同优先级告警走不同 Slack 频道(P0 走 #incident-now 并 @值班 + @服务负责人,P2 走 #alerts-low 不 @人);Slack MCP 能根据 Claude 的路由决策直接发消息 |
关键设计原则:Claude 出"决策",MCP 出"动作",人做"兜底"。具体来说:
- Claude 负责所有需要语义判断的环节:分类、优先级、根因假设、通知路由。这些是规则做不来的,必须交给 LLM。
- MCP 负责所有需要和数据源交互的动作:拉日志、建 Issue、发消息。这些是确定性的 IO 操作,封装成 MCP 让 Claude 不用写 DSL,直接调用。
- 人只负责低置信度告警的二次确认和P0 告警的最终拍板。Claude 对自己的判断输出置信度,低于 0.7 的告警走人工兜底通道,人工处理结果回流成训练数据。
整个流水线的拓扑长这样:
Prometheus → Alertmanager → Webhook → 标准化层(JSON) → Claude 分类
↓
┌───────────┴───────────┐
↓ ↓
日志分析 MCP GitHub MCP
(拉日志 + 聚类) (查最近 commit)
↓ ↓
└───────────┬───────────┘
↓
Claude 根因推断
↓
┌───────────────┴───────────────┐
↓ ↓
GitHub MCP 建 Issue Slack MCP 路由通知
↓ ↓
(置信度 < 0.7 走人工) (P0 → #incident-now)
↓ ↓
人工兜底通道 值班人收到告警
↓
反馈回流训练集
流水线架构设计决策
在动手之前,我们讨论了 4 个架构层面的决策,这些决策直接影响了后续实现的稳定性和可维护性,值得单独说明。
决策 1:同步处理 vs 异步队列——选异步
告警从 Alertmanager webhook 进来后,是同步调 Claude 等结果返回,还是先入队再异步消费?
选异步,原因有三:
- Claude 单次推理 3-8 秒,Alertmanager webhook 默认 5 秒超时,同步处理必然超时重试,导致同一告警被处理多次。
- 告警风暴时并发不可控,一次大促可能 50 条告警同时到,同步会瞬间打爆 Claude API 限流。
- 失败重试更干净,异步队列(Kafka)天然支持重试、死信队列、回放,同步处理失败后只能丢。
所以标准化层把告警写入 Kafka topic alerts.standardized,下游有 4 个 consumer group 分别消费:分类 consumer、根因推断 consumer、通知路由 consumer、反馈回流 consumer。每个 consumer 独立消费、独立失败、独立重试,互不影响。
Kafka topic 配置:
topic: alerts.standardized
partitions: 12 # 12 个分区支持并行消费
replication: 3 # 3 副本保证不丢消息
retention: 7d # 保留 7 天,方便回放和排错
cleanup.policy: delete # 不做 compact,告警是一次性事件
消费者配置(分类 consumer 示例):
consumer_group: ai-triage-classifier
concurrency: 8 # 8 个并发 worker,每个 worker 一次处理 1 条
max_poll_records: 16 # 一次 poll 16 条,批量拉取
session_timeout: 60s # 会话超时 60 秒(Claude 推理可能慢)
max_retry: 3 # 失败重试 3 次
retry_backoff: 30s # 重试退避 30 秒
dead_letter_topic: alerts.classifier.dlq # 死信队列
死信队列的处理:连续 3 次处理失败的告警进 DLQ,DLQ 的 consumer 是个降级逻辑——不调 Claude,直接按简单规则路由到 #alerts-failed 频道 @值班人,附"AI 分诊失败,请人工查看"提示。保证即使 AI 挂了,告警也不会丢。
决策 2:MCP 是常驻服务 vs 按 need 启动——选常驻
MCP server 可以做成 Claude 每次需要时启动的短命进程,也可以做成常驻服务。我们选常驻服务,原因:
- 日志分析 MCP 内部有缓存:最近 5 分钟的日志查询结果缓存命中率 40%,常驻进程能复用缓存,短命进程每次都要重新查 ELK。
- 启动开销:MCP server 启动要加载配置、建立连接池(ELK/GitHub/Slack 各一个),耗时 2-3 秒。如果每次告警都启动一次,这 2-3 秒 × 3000 条/天 = 每天 2.5 小时纯启动开销。
- 连接复用:常驻进程的 ELK/GitHub 连接池可以预热,避免冷启动时的连接握手延迟。
常驻 MCP 部署在同一个 K8s 集群,和 Claude API consumer 同 namespace,内网调用延迟 < 5ms。每个 MCP 是独立 Deployment,2 副本,HPA 按 CPU 50% 扩容。
决策 3:Claude 的并发限流——Token 预算 + QPS 双控
Claude API 有两个限制:RPM(每分钟请求数)和 TPM(每分钟 token 数)。告警高峰期如果不做限流,会打爆 API 配额导致后续告警全部失败。
我们做了两级限流:
- QPS 限流:分类 consumer 的 8 个 worker 共享一个 token bucket,限流 20 QPS(留出余量,Claude API 配额是 60 RPM)。
- Token 预算:每个 worker 消费前先估一下这条告警会消耗多少 token(按历史平均值 + 告警 JSON 长度估算),如果本分钟累计 token 超过 80% 预算,后续告警降级到"只分类不根因推断"的轻量模式。
# Token 预算控制器(伪代码)
class TokenBudget:
def __init__(self, monthly_budget):
self.minute_budget = monthly_budget // (30 * 24 * 60) # 月预算摊到分钟
self.current_usage = 0
self.degraded = False
def can_process(self, estimated_tokens):
if self.current_usage + estimated_tokens > self.minute_budget * 0.8:
self.degraded = True
return False
return True
def record_usage(self, actual_tokens):
self.current_usage += actual_tokens
# 每分钟重置
降级模式:当 token 预算告急,后续告警只走分类(Prompt 短,约 2K token),不走根因推断(Prompt 长,约 15K token)。分类结果降级路由到 #alerts-degraded 频道,值班人看到这个频道的告警知道"这次没有根因推断,需要自己查"。
决策 4:反馈数据存哪——选 PostgreSQL 而非文件
人工反馈数据一开始想存 JSON 文件,后来改成 PostgreSQL,原因:
- 需要按维度查询:"本周所有 classification 错例" / "payment-service 的所有反馈" 这种查询,文件做不到,DB 一句 SQL。
- 去重和冲突处理:同一条告警可能被两个人反馈,需要
ON CONFLICT (alert_id, feedback_type) DO UPDATE处理,DB 天然支持。 - 和训练样本生成联动:周末批处理跑 few-shot 样本抽取,直接查 DB 比读文件快。
表结构:
CREATE TABLE alert_feedback (
id BIGSERIAL PRIMARY KEY,
alert_id VARCHAR(128) NOT NULL,
alert_json JSONB NOT NULL,
ai_classification VARCHAR(32),
ai_priority VARCHAR(8),
ai_false_positive BOOLEAN,
ai_confidence FLOAT,
ai_root_cause JSONB,
human_classification VARCHAR(32),
human_priority VARCHAR(8),
human_false_positive BOOLEAN,
human_reason TEXT,
corrected_at TIMESTAMPTZ,
corrected_by VARCHAR(64),
is_used_for_training BOOLEAN DEFAULT FALSE,
training_batch_id VARCHAR(64),
UNIQUE (alert_id, corrected_by)
);
CREATE INDEX idx_feedback_not_used ON alert_feedback (is_used_for_training)
WHERE is_used_for_training = FALSE;
CREATE INDEX idx_feedback_service ON alert_feedback ((alert_json->>'service'));
is_used_for_training 字段标记这条反馈是否已被纳入 few-shot 样本,避免重复使用。每周批处理把它设成 TRUE,并把样本加入 Prompt。
实施步骤
整个流水线分 5 步,第 0 步是数据预处理,第 1-3 步是 AI 分诊主流程,第 4 步是人工兜底和反馈闭环。下面每一步都给出可复制的 Prompt 模板和操作说明。
第 0 步:告警数据采集和预处理(Prometheus webhook → JSON 标准化)
这一步的目标是把 Alertmanager 发出来的告警标准化成 Claude 能稳定理解的 JSON 结构。Alertmanager 原生的 webhook payload 长这样:
{
"version": "4",
"groupKey": "alertname=HighCpuUsage,service=order-service",
"status": "firing",
"receiver": "ai-triage-webhook",
"groupLabels": {
"alertname": "HighCpuUsage",
"service": "order-service"
},
"commonLabels": {
"alertname": "HighCpuUsage",
"severity": "warning",
"service": "order-service",
"env": "prod"
},
"commonAnnotations": {
"summary": "order-service CPU usage > 80%",
"description": "order-service pod-xxx CPU usage 87%,持续 5 分钟"
},
"externalURL": "http://alertmanager:9093",
"alerts": [
{
"status": "firing",
"labels": {
"alertname": "HighCpuUsage",
"severity": "warning",
"service": "order-service",
"instance": "10.0.12.45:8080",
"pod": "order-service-7d4f-x9k2"
},
"annotations": {
"summary": "order-service CPU usage > 80%",
"description": "order-service pod-xxx CPU usage 87%,持续 5 分钟",
"runbook_url": "https://wiki.internal/runbooks/high-cpu"
},
"startsAt": "2026-09-01T02:17:03.123Z",
"endsAt": "0001-01-01T00:00:00Z",
"generatorURL": "http://prometheus:9090/graph?g0.expr=..."
}
]
}
这个结构有几个问题:字段嵌套深(labels 和 annotations 都在 alerts 数组里)、告警可以批量打包(一次 webhook 可能带 50 条 alerts)、字段语义不统一(severity 有 warning / critical / info 三种,但有的规则用 P1 / P2)。直接喂给 Claude 会导致它把精力花在解析结构上,而不是做语义判断。
所以我们写了一个标准化中间层(一个 200 行的 Go 服务),把 Alertmanager 的 webhook 转成扁平化的标准化 JSON:
{
"alert_id": "alert-2026-09-01-021703- HighCpuUsage-order-service-x9k2",
"received_at": "2026-09-01T02:17:03.456Z",
"alert_name": "HighCpuUsage",
"severity": "warning",
"service": "order-service",
"instance": "10.0.12.45:8080",
"pod": "order-service-7d4f-x9k2",
"env": "prod",
"summary": "order-service CPU usage > 80%",
"description": "order-service pod-xxx CPU usage 87%,持续 5 分钟",
"runbook_url": "https://wiki.internal/runbooks/high-cpu",
"starts_at": "2026-09-01T02:17:03.123Z",
"metric_value": 87,
"metric_threshold": 80,
"metric_unit": "percent",
"duration_minutes": 5,
"prometheus_url": "http://prometheus:9090/graph?g0.expr=...",
"alertmanager_url": "http://alertmanager:9093"
}
标准化层做的几件事:
- 扁平化:把
alerts[0].labels.service提到顶层service,让 Claude 不用解析嵌套 - 统一 severity:把
warning/P1/critical等不同命名统一成info/warning/critical/fatal四档 - 补 metric 字段:从 Prometheus expression 里抽取出
metric_value/metric_threshold/metric_unit,让 Claude 直接拿到数值而不是去解析 URL - 生成 alert_id:用
时间 + alertname + service + pod生成稳定 ID,方便后续去重和关联 - 拆包:如果一次 webhook 带 50 条 alerts,标准化层拆成 50 个独立 JSON 投递到 Kafka,下游 Claude 消费者并行处理
标准化层还做了告警去重和风暴抑制:同一个 alert_id 在 60 秒内重复出现的只保留最新一条;同一 service 在 10 秒内触发超过 20 条告警的,触发"告警风暴"模式,这批告警打包成一个聚合 JSON 一次性喂给 Claude(避免 Claude 把 20 条同源告警当 20 个独立事件处理)。
标准化层本身不用 Prompt,它就是个确定性的转换服务。但它产出的 JSON 是后续所有 Prompt 的输入基础,格式稳定后整个流水线的准确率才能稳。
第 1 步:告警分类和优先级判定(Claude 分类 Prompt)
标准化 JSON 进来后,第一件事是让 Claude 给这条告警打三个标签:分类(基础设施 / 应用 / 业务 / 依赖 / 安全)、优先级(P0-P3)、是否疑似误报(true / false + 理由)。
这一步的 Prompt 设计踩了好几个坑才稳定下来,关键经验是:Prompt 必须给 Claude 一个稳定的输出 schema,不能让它自由发挥。最初版本的 Prompt 让 Claude "用自然语言描述分类和优先级",结果它的输出五花八门——有时输出一段话、有时输出表格、有时输出 JSON 字段名还乱变。下游解析根本没法稳定处理。
最终稳定的 Prompt 模板长这样(完整可复制版本见后面"关键 Prompt 模板"小节,这里给精简版说明设计思路):
你是值班 SRE 团队的告警分诊助手。给你一条标准化告警 JSON,你需要输出:
1. classification:告警分类,必须是这 5 类之一:
- infrastructure(基础设施:CPU/内存/磁盘/网络/节点)
- application(应用层:接口 5xx/延迟/慢查询/线程池)
- business(业务层:支付失败率/订单履约延迟/库存扣减失败)
- dependency(依赖层:MySQL/Redis/Kafka/ES 异常)
- security(安全:鉴权失败/异常登录/证书过期)
2. priority:优先级,必须是 P0/P1/P2/P3 之一,判定标准:
- P0:核心服务中断、支付/订单主链路失败率 > 5%、数据损坏风险
- P1:核心接口 5xx > 1%、P99 延迟 > 阈值 2 倍、核心依赖异常
- P2:非核心服务异常、资源水位告警、非主链路慢查询
- P3:瞬时抖动、探针失败、阈值边缘抖动、已知噪音模式
3. suspected_false_positive:是否疑似误报(true/false)+ 理由
判定误报的信号:
- 指标在告警触发后 2 分钟内自动回落
- 同类告警历史出现频次 > 5 次/天且从未需要人工处理
- metric_value 仅略高于 threshold(< 10%)
- pod 重启后立即恢复
4. confidence:你对这次分类的置信度(0.0-1.0)
输出必须是严格 JSON,schema 如下:
{
"classification": "...",
"priority": "...",
"suspected_false_positive": true/false,
"false_positive_reason": "...",
"confidence": 0.0
}
告警 JSON:
{{这里插入第 0 步产出的标准化 JSON}}
这个 Prompt 之所以稳定,关键在三点:
- 限定输出 schema:明确告诉 Claude 必须输出哪些字段、字段类型、取值范围。Claude 的输出一旦 schema 化,下游就能稳定用
json.loads解析。 - 给判定标准:不是让 Claude "凭感觉"分优先级,而是明确给"P0 = 核心服务中断 / 支付失败率 > 5%"这种可执行的标准。这把分类准确率从 78% 拉到 91%。
- 给误报信号:把"什么算误报"的判断依据显式列出来,避免 Claude 把所有 P3 都标成误报(有些 P3 是真问题,只是优先级低)。
分类完成后,根据优先级走不同分支:
- P3 + suspected_false_positive=true:直接进静默队列,不发通知,只记录到"误报归档"供后续训练
- P3 + suspected_false_positive=false:进低优通知队列,走 Slack
#alerts-low,不 @人 - P0/P1/P2:进入第 2 步做日志关联和根因推断
实际产出的一个分类示例(输入是上面第 0 步那条 HighCpuUsage 告警):
{
"classification": "infrastructure",
"priority": "P2",
"suspected_false_positive": true,
"false_positive_reason": "CPU 87% 仅高于阈值 80% 的 8.75%,且历史模式显示该 pod 在大促预热期 CPU 普遍在 85-90% 抖动,2 分钟内回落概率高",
"confidence": 0.82
}
这个分类结果会带着原告警 JSON 一起传给第 2 步。
第 2 步:自动日志关联和根因推断(日志分析 MCP 调用)
第 1 步判定 P0/P1/P2 的告警进入这一步。目标是自动拉取相关日志 + 关联最近代码变更 + 输出根因假设,把值班人原来要花 15 分钟做的"搜日志 + 翻 commit + 查链路"压缩到 30 秒。
这一步的核心是让 Claude 调度两个 MCP 协同:日志分析 MCP 拉日志做错误聚类,GitHub MCP 查最近 commit 看有没有可疑变更。Claude 作为"调度大脑"决定调哪个 MCP、传什么参数、怎么整合结果。
为什么不让 Claude 直接调 ELK API:我们试过让 Claude 直接写 Elasticsearch DSL 查询日志,结果它写的 query 经常语法错误(嵌套 bool 写错、时间范围用相对时间而非绝对 ISO 时间),而且单次返回数据量大、token 消耗惊人(一条慢查询的完整堆栈可能 8000 token,一次拉 20 条就 16 万 token,直接打爆上下文)。封装成 MCP 后,MCP 在内部做了日志裁剪(只保留错误堆栈前 3 行 + 去重)、时间窗对齐(自动按告警 starts_at 前后 5 分钟拉日志),Claude 只需要调 get_error_logs(service, time_window) 就行。
日志分析 MCP 提供三个工具:
| 工具 | 入参 | 出参 | 用途 |
|---|---|---|---|
get_error_logs | service, start_time, end_time, level | 错误日志列表(去重后的堆栈) | 拉告警服务本身的错误日志 |
get_related_logs | service_list, time_window | 跨服务日志关联 | 拉上下游服务的日志,找关联异常 |
cluster_errors | logs | 错误聚类结果(按堆栈相似度分组) | 把 200 条错误日志聚成 3-5 类,避免 Claude 处理重复 |
GitHub MCP 这里只用到两个工具:list_commits(查某服务最近 N 小时的 commit)和 get_pr_diff(拿某个 PR 的变更内容,辅助判断"是不是这次发布引入的")。
这一步的 Prompt 模板(精简版,完整版见后文):
你刚对一条告警完成分类,现在需要做根因推断。已有信息:
【告警 JSON】
{{第 0 步的标准化 JSON}}
【分类结果】
{{第 1 步的 classification + priority}}
请按以下顺序操作:
1. 调用日志分析 MCP 的 get_error_logs,参数:
- service: {{告警里的 service 字段}}
- start_time: {{starts_at - 5 分钟}}
- end_time: {{starts_at + 2 分钟}}
- level: ["error", "fatal"]
2. 调用日志分析 MCP 的 get_related_logs,参数:
- service_list: {{根据服务名推断的上下游,例如 order-service 的上下游是 payment/inventory/marketing/gateway}}
- time_window: {{starts_at 前后 5 分钟}}
3. 调用 GitHub MCP 的 list_commits,参数:
- repo: {{service 对应的 repo}}
- since: {{starts_at - 24 小时}}
- path: {{service 的核心代码目录}}
4. 基于上述三组数据,输出根因假设:
- 必须给出 1-3 个假设,按可能性排序
- 每个假设包含:root_cause(一句话)、evidence(支持证据,引用具体日志行或 commit)、suggested_action(建议处置)
- 如果证据不足以给假设,明确输出 "insufficient_evidence" 并说明缺什么数据
实际产出的一个根因推断示例(输入是 payment-service P99 延迟飙升的告警):
{
"hypotheses": [
{
"rank": 1,
"root_cause": "risk-control 服务调用超时导致 payment-service 同步等待,上游限流未生效",
"evidence": [
"payment-service 错误日志:'call risk-control timeout after 3000ms'(出现 47 次,告警前后 5 分钟内)",
"risk-control 服务在同一时间段有 'circuit breaker open' 日志(出现 12 次)",
"risk-control 最近一次发布在 2 小时前(commit: feat: add new risk rule for promo orders),引入了新的规则匹配逻辑,疑似慢查询"
],
"suggested_action": "1. 立即让 risk-control 回滚到上一版本(commit a1b2c3d);2. 检查新规则 SQL 是否走索引;3. 短期降级 payment→risk 调用为异步",
"confidence": 0.78
},
{
"rank": 2,
"root_cause": "MySQL 主库慢查询导致 payment-service 数据库连接池耗尽",
"evidence": [
"payment-service 日志:'connection pool exhausted, waiting 8s for connection'(出现 8 次)",
"payment DB 在同一时间段慢查询数从 2 条/分钟涨到 34 条/分钟"
],
"suggested_action": "1. 检查 payment DB 慢查询日志;2. 临时扩容连接池上限;3. 排查是否有未走索引的查询",
"confidence": 0.41
}
],
"data_gaps": [
"未拿到 risk-control 新规则的 SQL 执行计划,需要 DBA 协助",
"payment-service 的连接池配置当前值未知"
]
}
这个根因推断结果会作为 Issue 的内容(第 3 步)和 Slack 通知的正文(第 3 步)。
第 3 步:自动创建 Issue 和通知路由(GitHub MCP + Slack MCP)
根因推断完成后,需要把这件事落地成可跟踪的 Issue + 发到正确的 Slack 频道。这一步的核心是根据优先级和根因类型做差异化路由,而不是所有告警都发到同一个地方。
路由规则表(Claude 在 Prompt 里被告知这套规则,由它判断该走哪条):
| 优先级 + 场景 | GitHub Issue | Slack 频道 | @ 人 | Incident 通道 |
|---|---|---|---|---|
| P0 + 核心链路中断 | 自动建 Issue(label: incident/P0) | #incident-now | @值班 + @服务负责人 + @CTO | 自动建 |
| P0 + 数据损坏风险 | 自动建 Issue(label: incident/data) | #incident-now + #data-safety | @值班 + @DBA + @CTO | 自动建 |
| P1 + 核心接口异常 | 自动建 Issue(label: bug/P1) | #alerts-p1 | @值班 + @服务负责人 | 否 |
| P1 + 依赖异常 | 自动建 Issue(label: dependency) | #alerts-p1 | @值班 + @依赖负责人 | 否 |
| P2 | 自动建 Issue(label: alert/P2) | #alerts-low | 不 @人 | 否 |
| P3 + 非误报 | 不建 Issue,只记日志 | #alerts-noise | 不 @人 | 否 |
| 任何优先级 + confidence < 0.7 | 建 Issue 标记 needs-human-review | #alerts-review | @值班 | 否 |
这一步的 Prompt 模板(精简版):
你已完成根因推断,现在需要做通知路由和 Issue 创建。已有信息:
【告警 JSON】
{{标准化 JSON}}
【分类结果】
{{classification + priority + suspected_false_positive}}
【根因推断】
{{hypotheses}}
请按以下规则操作:
1. 判断是否需要建 GitHub Issue:
- P0/P1:必须建 Issue
- P2:建 Issue
- P3 非误报:不建 Issue,只记日志
- confidence < 0.7:建 Issue 并加 label `needs-human-review`
2. 判断 Slack 路由(参照路由规则表):
- 根据优先级和根因类型选频道
- 决定是否 @人、@谁
3. 调用 GitHub MCP 的 create_issue,参数:
- repo: {{service 对应的 repo}}
- title: "[{{priority}}] {{alert_name}} - {{service}} - {{summary}}"
- body: 用模板渲染(见下方 Issue body 模板)
- labels: 根据 priority 和根因选
4. 调用 Slack MCP 的 send_message,参数:
- channel: {{路由决策结果}}
- text: 用模板渲染(见下方 Slack text 模板)
- mention_users: {{路由决策结果}}
Issue body 模板:
## 告警信息
- 告警名称:{{alert_name}}
- 服务:{{service}}
- 优先级:{{priority}}
- 触发时间:{{starts_at}}
- 指标值:{{metric_value}} {{metric_unit}}(阈值 {{metric_threshold}})
## AI 分类
- 分类:{{classification}}
- 疑似误报:{{suspected_false_positive}}(理由:{{false_positive_reason}})
- 置信度:{{confidence}}
## 根因推断
{{hypotheses 渲染成 Markdown}}
## 建议处置
{{suggested_action}}
## 相关链接
- Prometheus: {{prometheus_url}}
- Alertmanager: {{alertmanager_url}}
- Runbook: {{runbook_url}}
Slack text 模板:
🚨 [{{priority}}] {{alert_name}}
服务:{{service}} | 触发:{{starts_at}}
指标:{{metric_value}} {{metric_unit}}(阈值 {{metric_threshold}})
🔍 AI 根因假设:
{{top 1 hypothesis 的 root_cause}}
🛠 建议处置:
{{top 1 hypothesis 的 suggested_action}}
📎 Issue: {{issue_url}}
📊 Prometheus: {{prometheus_url}}
实际产出的一个 Slack 消息(P0 payment 告警):
🚨 [P0] PaymentLatencyP99High
服务:payment-service | 触发:2026-09-01 02:17:03
指标:4800ms(阈值 1000ms)
🔍 AI 根因假设:
risk-control 服务调用超时导致 payment 同步等待,2 小时前的新规则疑似慢查询
🛠 建议处置:
1. 立即回滚 risk-control 到 commit a1b2c3d
2. 检查新规则 SQL 索引
3. 短期降级 payment→risk 为异步
📎 Issue: https://github.com/myteam/payment-service/issues/2847
📊 Prometheus: http://prometheus:9090/graph?...
👥 @oncall-team @risk-service-owner @cto
值班同学看到这条消息,3 分钟内就能开始执行回滚操作,而传统流程下他光是在 ELK 搜日志、在 GitHub 翻 commit、在 Jaeger 查链路就要 15 分钟。
第 4 步:人工兜底和反馈闭环(低置信度告警人工确认 → 训练数据积累)
整个流水线的最后一公里是对低置信度告警的人工兜底 + 把人工判断结果回流成训练数据。这一步不做,流水线就是个黑盒,永远无法自我优化。
触发人工兜底的条件有两个:
- Claude 的 confidence < 0.7:Claude 自己都不确定,不能让它的判断直接落地
- 告警模式在历史中从未出现:标准化层会标注
is_new_pattern: true,这类告警即使 confidence 高也走人工确认(防止 Claude 对没见过的模式过度自信)
人工兜底通道长这样:
- 这类告警走
#alerts-reviewSlack 频道,@值班人 - 消息里附带 Claude 的判断结果 + 原始数据 + "请确认 Claude 判断是否正确"的按钮
- 值班人点"确认"或"纠正",反馈进入
feedback表
反馈表的结构:
{
"alert_id": "alert-2026-09-01-021703-HighCpuUsage-order-service-x9k2",
"ai_classification": "infrastructure",
"ai_priority": "P2",
"ai_false_positive": true,
"ai_confidence": 0.62,
"human_classification": "application",
"human_priority": "P1",
"human_false_positive": false,
"human_reason": "CPU 飙升不是基础设施问题,是 order-service 新版本引入了死循环,CPU 高是表象不是根因",
"corrected_at": "2026-09-01T02:25:00Z",
"corrected_by": "sre-zhangsan"
}
这些反馈数据每周做一次Prompt 微调和 few-shot 样本更新:
- 周末跑一个批处理,把本周所有
ai_* != human_*的样本挑出来 - 把这些样本做成 few-shot 例子,加进分类 Prompt 的"历史纠错案例"小节
- 对于反复出错的告警类型(比如 Claude 总是把"死循环导致的 CPU 高"判成 infrastructure 误报),写专门的处理规则加进 Prompt
反馈闭环的实际效果:
- 第 1 周:分类准确率 78%,误报过滤准确率 71%
- 第 2 周(加入 240 条反馈样本后):分类准确率 86%,误报过滤准确率 83%
- 第 4 周(累计 980 条反馈样本):分类准确率 91%,误报过滤准确率 89%
- 第 8 周(累计 2100 条反馈样本):分类准确率 94%,误报过滤准确率 94%
这是整个流水线最关键的设计:没有反馈闭环,Claude 的判断准确率会停滞在 78% 左右(这是 zero-shot 的水平),永远到不了 94%。反馈闭环让流水线成了一个能持续学习的系统,而不是一个固定逻辑的规则引擎。
端到端完整案例走查
为了把整个流水线串起来,这里走查一条真实告警从 Alertmanager 触发到值班人收到通知的完整过程。这是一条 2026 年 8 月 19 日 14:32 的 OrderCreateFailureRate 告警,属于 P0 级别,完整走完 4 步耗时 11 秒。
起点:Alertmanager webhook 触发
Prometheus 检测到 order-service 的订单创建失败率从 0.3% 飙到 6.2%,触发告警。Alertmanager 在 14:32:07 发出 webhook:
{
"status": "firing",
"alerts": [{
"labels": {
"alertname": "OrderCreateFailureRate",
"severity": "critical",
"service": "order-service",
"env": "prod"
},
"annotations": {
"summary": "order-service 订单创建失败率 6.2%",
"description": "rate(order_create_failed_total[5m]) / rate(order_create_total[5m]) = 0.062 > 0.05"
},
"startsAt": "2026-08-19T06:32:03.123Z"
}]
}
第 0 步:标准化层处理(耗时 0.3 秒)
标准化层收到 webhook,做四件事:
- 拆包(这条只有 1 个 alert,无需拆)
- 扁平化 + 字段补全:从 Prometheus expression 抽取
metric_value=6.2、metric_threshold=5.0、metric_unit=percent - 查历史统计:
history_stats字段查alert_feedback表,发现该告警历史出现 3 次,其中 1 次人工介入(human_action_count=1),2 次自愈。标注is_new_pattern=false。 - 风暴检测:过去 10 秒内 order-service 只有这 1 条告警,不触发风暴聚合。
标准化后的 JSON 写入 Kafka:
{
"alert_id": "alert-2026-08-19-063203-OrderCreateFailureRate-order-service-prod",
"received_at": "2026-08-19T06:32:07.200Z",
"alert_name": "OrderCreateFailureRate",
"severity": "critical",
"service": "order-service",
"env": "prod",
"summary": "order-service 订单创建失败率 6.2%",
"metric_value": 6.2,
"metric_threshold": 5.0,
"metric_unit": "percent",
"starts_at": "2026-08-19T06:32:03.123Z",
"is_new_pattern": false,
"history_stats": {
"show_count_30d": 3,
"human_action_count": 1,
"auto_recovered_ratio": 0.67
},
"service_profile": "long-running"
}
第 1 步:Claude 分类(耗时 4.1 秒)
分类 consumer 消费到这条告警,调 Claude 分类 Prompt。Claude 的推理过程(从日志还原):
分析:
- alert_name = OrderCreateFailureRate,含"failure rate"关键词 → business 分类
- service = order-service,是核心服务(支付/订单主链路)
- metric_value = 6.2% > 5% 阈值,且 order 是核心链路 → P0
- 误报判定:
- metric_value 高于 threshold 24%(6.2 vs 5.0),不满足"< 10%"
- history_stats.human_action_count = 1,历史上有人工介入过
- 不满足误报信号 → suspected_false_positive = false
- confidence = 0.91(模式清晰、历史数据充分)
输出:
{
"classification": "business",
"priority": "P0",
"suspected_false_positive": false,
"false_positive_reason": "",
"confidence": 0.91,
"reasoning": "订单创建失败率 6.2% 超过 5% 阈值,order-service 是核心链路,历史有人工介入记录,判定 P0"
}
P0 告警,进入第 2 步根因推断。
第 2 步:Claude 根因推断(耗时 5.4 秒)
Claude 作为调度大脑,依次调用三个 MCP:
调用 1:日志分析 MCP get_error_logs(耗时 1.2 秒)
参数:service=order-service, start_time=06:27:03, end_time=06:34:03, level=[error,fatal]
MCP 返回 47 条错误日志,自动聚类后 3 类:
聚类 1(28 条):"create order failed: inventory-service timeout after 2000ms"
聚类 2(14 条):"create order failed: deduct stock failed, retry exhausted"
聚类 3(5 条):"panic: nil pointer in orderCreateHandler line 287"
调用 2:GitHub MCP list_commits(耗时 0.8 秒)
参数:repo=myteam/order, since=2026-08-18T06:32:03Z, path=internal/handler
返回 2 个 commit:
commit a3b4c5d (8 小时前): feat: add bulk order create API
commit e6f7g8h (2 小时前): fix: handle nil pointer in orderCreateHandler
调用 3(Claude 自主决策,基于 commit e6f7g8h):GitHub MCP get_pr_diff(耗时 0.9 秒)
参数:pr=2841 (对应 commit e6f7g8h)
返回 diff,Claude 看到:修复了 line 287 的 nil pointer,但修复逻辑里引入了新的 inventory 调用路径(bulk create 时批量调 inventory)。
Claude 综合推理 + 输出根因假设:
{
"hypotheses": [
{
"rank": 1,
"root_cause": "2 小时前的 commit e6f7g8h 引入的 bulk order create 路径,对 inventory-service 发起批量调用导致后者超时,进而订单创建失败",
"evidence": [
"错误日志聚类1(28条):inventory-service timeout after 2000ms",
"commit e6f7g8h (2小时前) 的 diff 显示新增了 bulk 调用 inventory 的逻辑",
"inventory-service 在同一时间段(告警前后5分钟)应该有压力告警,建议确认"
],
"suggested_action": [
"1. 立即回滚 order-service 到 commit a3b4c5d(bulk API 之前版本)",
"2. 检查 inventory-service 是否需要扩容以应对批量调用",
"3. bulk order create 路径改为异步或分批"
],
"confidence": 0.84
},
{
"rank": 2,
"root_cause": "inventory-service 自身异常导致所有调用超时,与 order 代码变更无关",
"evidence": [
"错误日志聚类2(14条):deduct stock failed, retry exhausted",
"stock 扣减失败可能是 inventory 侧问题"
],
"suggested_action": [
"1. 检查 inventory-service 健康状态和资源水位",
"2. 查看 inventory DB 是否有锁等待"
],
"confidence": 0.42
}
],
"data_gaps": [
"未确认 inventory-service 在 06:32 前后是否有自身告警",
"未拿到 inventory DB 的慢查询数据"
]
}
第 3 步:通知路由和 Issue 创建(耗时 1.8 秒)
Claude 基于分类 + 根因做路由决策:
路由推理:
- priority = P0
- service = order-service(核心链路)
- 根因涉及代码回滚 → 需要服务负责人介入
- 路由规则:P0 + 核心链路 → #incident-now,@oncall + @order-service-owner + @cto
- 建 Issue,label: [incident, P0, business]
- 建 Incident 通道(P0 必建)
调用 GitHub MCP create_issue(耗时 0.9 秒):
创建 Issue #2849,body 按模板渲染,包含告警信息、AI 分类、根因假设、建议处置、相关链接。
调用 Slack MCP(耗时 0.9 秒):
- 先调
create_incident_channel,建#inc-0819-order-fail通道 - 调
send_message发到#incident-now,Block Kit 格式:
{
"blocks": [
{
"type": "header",
"text": {"type": "plain_text", "text": "🚨 [P0] OrderCreateFailureRate"}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": "*服务*:order-service"},
{"type": "mrkdwn", "text": "*触发*:2026-08-19 14:32"},
{"type": "mrkdwn", "text": "*指标*:失败率 6.2%(阈值 5%)"}
]
},
{
"type": "section",
"text": {"type": "mrkdwn", "text": "🔍 *AI 根因*\n2 小时前 commit e6f7g8h 引入 bulk order 路径,批量调用 inventory 导致超时"}
},
{
"type": "section",
"text": {"type": "mrkdwn", "text": "🛠 *建议*\n1. 回滚到 commit a3b4c5d\n2. 检查 inventory 扩容需求"}
},
{
"type": "actions",
"elements": [
{"type": "button", "text": {"type": "plain_text", "text": "📎 Issue #2849"}, "url": "https://github.com/..."},
{"type": "button", "text": {"type": "plain_text", "text": "📊 Prometheus"}, "url": "http://prometheus/..."}
]
}
],
"text": "🚨 [P0] OrderCreateFailureRate - order-service 失败率 6.2%"
}
消息 @ 了 @oncall-current(值班人 zhangsan)、@order-service-owner(负责人 lisi)、@cto。
终点:值班人收到通知
14:32:19(告警触发后 16 秒,AI 处理 11 秒 + 网络延迟 5 秒),值班人 zhangsan 的手机收到 Slack 推送。他看到:
- 这是一条 P0 告警(🚨 标记)
- 根因是 2 小时前的某次 commit 引入的 bulk 路径
- 建议是回滚到上一个 commit
zhangsan 3 分钟内在 #inc-0819-order-fail 通道确认了根因,执行回滚。14:38 订单失败率回落到 0.4%。
对比基线:这条告警如果在人工流程下,zhangsan 要先在 Alertmanager 看到告警(1 分钟),去 ELK 搜日志找到 timeout 关键词(5 分钟),去 GitHub 翻最近 commit 发现 e6f7g8h 可疑(4 分钟),看 diff 确认引入了 bulk 调用(3 分钟),最终决定回滚。MTTA 约 13 分钟,MTTR 约 20 分钟。AI 流水线把 MTTA 压到 16 秒、MTTR 压到 6 分钟。
这就是整条流水线的设计目标:把值班人从"找线索"的体力活里解放出来,让他直接从"已知根因 + 已知处置"开始行动。
关键 Prompt 模板
这一节给出四个核心 Prompt 的完整可复制版本。前三个是流水线主流程的关键环节(分类、根因、路由),第四个是告警风暴场景的专用 Prompt。这些 Prompt 直接决定了分诊的准确率。
Prompt 模板 1:告警分类和优先级判定
你是值班 SRE 团队的告警分诊助手。你的任务是对一条标准化告警做分类、优先级判定、误报识别。
## 输入
你会收到一个告警 JSON,字段包括:alert_id, alert_name, severity, service, instance, pod, env, summary, description, metric_value, metric_threshold, metric_unit, duration_minutes, starts_at, is_new_pattern, history_stats(历史同类告警统计)。
## 任务
输出以下 4 个字段:
### 1. classification(告警分类)
必须是以下 5 类之一:
- infrastructure:基础设施层告警(CPU/内存/磁盘/网络/节点 NotReady/PV 余量)
- application:应用层告警(接口 5xx/延迟/慢查询/线程池/连接池/GC)
- business:业务层告警(支付失败率/订单履约延迟/库存扣减失败/转化率下跌)
- dependency:依赖层告警(MySQL/Redis/Kafka/ES/RocketMQ 异常)
- security:安全告警(鉴权失败/异常登录/证书过期/敏感操作)
判定依据:
- 看 alert_name 和 summary 里的关键词:含 CPU/memory/disk/node → infrastructure;含 5xx/latency/p99/timeout → application;含 payment_success/order_fulfill → business;含 mysql/redis/kafka → dependency;含 auth/cert/login → security
- 如果模糊,看 metric_unit:percent 且 service 含 -node- 或 -host- → infrastructure;ms 且含 p99/latency → application
### 2. priority(优先级)
必须是 P0/P1/P2/P3 之一,判定标准:
P0(致命,需 1 分钟内响应):
- 核心服务(payment/order/inventory/fulfill/gateway)完全不可用
- 支付成功率 < 95% 或订单创建失败率 > 5%
- 数据损坏风险(DB 主从断开、数据不一致告警)
- 任何 service 的 5xx 错误率 > 10%
P1(严重,需 15 分钟内响应):
- 核心接口 5xx 错误率 1%-10%
- 核心接口 P99 延迟 > 阈值 2 倍
- 核心依赖(MySQL/Redis 主库)异常
- 非核心服务完全不可用
P2(一般,需 1 小时内响应):
- 非核心服务 5xx 错误率 1%-10%
- 资源水位告警(CPU > 80%、内存 > 85%、磁盘 > 85%)
- 非主链路慢查询
- Kafka 消费滞后但未影响业务
P3(低优,可延后处理):
- 瞬时抖动(指标超阈值 < 2 分钟即回落)
- 探针失败(健康检查偶发失败)
- 阈值边缘抖动(metric_value 仅高于 threshold 10% 以内)
- 已知噪音模式(history_stats.show_count > 5/天 且 human_action_count = 0)
### 3. suspected_false_positive(是否疑似误报)
true 或 false,并给出理由。
判定为误报(true)的信号(满足任意 2 条即可):
- metric_value 仅高于 threshold 10% 以内,且 history_stats.auto_recovered_ratio > 0.8
- 同类告警 history_stats.show_count > 5 次/天 且 history_stats.human_action_count = 0
- duration_minutes < 2 且当前时间距 starts_at 已过 3 分钟(说明已自动恢复)
- is_new_pattern = false 且历史同类告警 100% 被标记为误报
判定为非误报(false)的信号:
- metric_value 高于 threshold 50% 以上
- history_stats.human_action_count > 0(历史上有过人工处理)
- 伴随其他服务同时间段异常(需要结合 history_stats.related_alerts)
### 4. confidence(置信度,0.0-1.0)
- 1.0:告警模式清晰、历史数据充分、判定标准明确
- 0.8-0.9:判定有依据但有一点点模糊
- 0.6-0.7:判定依据不足,建议人工复核
- < 0.6:强烈建议人工复核,不要直接落地
## 输出格式
必须严格输出以下 JSON,不要输出任何其他内容(不要 markdown 代码块标记):
{
"classification": "infrastructure|application|business|dependency|security",
"priority": "P0|P1|P2|P3",
"suspected_false_positive": true|false,
"false_positive_reason": "如果 true,给出理由;如果 false,输出空字符串",
"confidence": 0.0,
"reasoning": "一句话说明你的判定依据"
}
## 告警 JSON
{{alert_json}}
Prompt 模板 2:根因推断
你是值班 SRE 团队的根因推断助手。你的任务是基于告警 + 日志 + 代码变更,输出 1-3 个根因假设。
## 输入
1. 告警 JSON(标准化格式,含 alert_name/service/summary/metric_value/starts_at 等)
2. 分类结果(classification/priority/suspected_false_positive/confidence)
3. 你需要主动调用以下 MCP 工具获取数据:
- 日志分析 MCP:get_error_logs(拉告警服务错误日志)
- 日志分析 MCP:get_related_logs(拉上下游服务日志,找关联异常)
- 日志分析 MCP:cluster_errors(对错误日志做堆栈聚类)
- GitHub MCP:list_commits(查告警服务最近 24 小时 commit)
- GitHub MCP:get_pr_diff(对可疑 commit 拿 PR diff)
## 操作步骤
### 步骤 1:拉告警服务自身错误日志
调用 get_error_logs,参数:
- service: {{告警 JSON.service}}
- start_time: {{starts_at - 5 分钟,ISO8601 格式}}
- end_time: {{starts_at + 2 分钟,ISO8601 格式}}
- level: ["error", "fatal"]
拉到日志后,立即调用 cluster_errors 做堆栈聚类,避免处理大量重复日志。
### 步骤 2:拉上下游服务日志
调用 get_related_logs,参数:
- service_list: 根据告警服务推断的上下游(参考下方服务依赖表)
- time_window: {{starts_at 前后 5 分钟}}
服务依赖表(用于推断上下游):
- payment-service 的上下游:order-service, risk-control, inventory-service, marketing-service, gateway
- order-service 的上下游:payment-service, inventory-service, fulfill-service, user-service, gateway
- inventory-service 的上下游:order-service, fulfill-service, warehouse-service
- fulfill-service 的上下游:order-service, inventory-service, logistics-service
- gateway 的上下游:所有下游服务
如果告警服务不在上表,用告警 JSON.service 做关键词模糊匹配(含 -service 后缀的取前缀做推断)。
### 步骤 3:查最近代码变更
调用 GitHub MCP 的 list_commits,参数:
- repo: {{service 对应的 GitHub repo,约定为 myteam/{{service 去掉 -service 后缀}}}}
- since: {{starts_at - 24 小时,ISO8601}}
- until: {{starts_at,ISO8601}}
如果有 commit,对每个 commit 调用 get_pr_diff 拿变更内容,重点看:
- 是否引入了新的 SQL 查询(看 .sql 文件或 ORM 查询代码)
- 是否修改了限流/熔断/重试配置
- 是否新增了外部调用(HTTP/RPC client)
- 是否改了并发相关代码(goroutine/thread/pool)
### 步骤 4:输出根因假设
基于上述三组数据,输出 1-3 个假设,按可能性从高到低排序。
每个假设必须包含:
- root_cause:一句话描述根因(不要笼统说"服务异常",要具体到"X 服务 Y 操作导致 Z")
- evidence:支持证据列表,每条证据必须引用具体数据(日志行、commit hash、指标值)
- suggested_action:建议处置步骤(按可执行性排序,立即能做的放前面)
- confidence:该假设的置信度(0.0-1.0)
如果证据不足以给出任何假设,明确输出:
{
"status": "insufficient_evidence",
"missing_data": ["缺什么数据"],
"suggested_next_step": "建议人工查什么"
}
## 输出格式
严格输出以下 JSON:
{
"hypotheses": [
{
"rank": 1,
"root_cause": "...",
"evidence": ["证据1", "证据2"],
"suggested_action": ["步骤1", "步骤2"],
"confidence": 0.0
}
],
"data_gaps": ["缺失的数据1"]
}
## 告警 JSON
{{alert_json}}
## 分类结果
{{classification_result}}
Prompt 模板 3:通知路由和 Issue 创建
你是值班 SRE 团队的通知路由助手。你的任务是基于告警 + 分类 + 根因推断,决定 Slack 路由和 GitHub Issue 创建。
## 输入
1. 告警 JSON
2. 分类结果(classification/priority/suspected_false_positive/confidence)
3. 根因推断结果(hypotheses)
## 任务
### 1. 判断是否需要建 GitHub Issue
规则:
- priority = P0 或 P1:必须建 Issue
- priority = P2:建 Issue
- priority = P3 且 suspected_false_positive = false:不建 Issue,只记日志
- priority = P3 且 suspected_false_positive = true:不建 Issue,进静默队列
- confidence < 0.7:建 Issue 并加 label `needs-human-review`(无论 priority)
### 2. 判断 Slack 路由
路由规则表:
| 优先级 + 场景 | 频道 | @ 人 |
|---|---|---|
| P0 + 核心链路中断(payment/order/inventory/fulfill/gateway 不可用) | #incident-now | @oncall-current + @service-owner + @cto |
| P0 + 数据损坏风险(DB 异常/数据不一致) | #incident-now + #data-safety | @oncall-current + @dba + @cto |
| P0 + 其他 | #incident-now | @oncall-current + @service-owner |
| P1 + 核心接口异常(5xx/延迟) | #alerts-p1 | @oncall-current + @service-owner |
| P1 + 依赖异常(mysql/redis/kafka) | #alerts-p1 | @oncall-current + @dependency-owner |
| P2 | #alerts-low | 不 @ 人 |
| P3 非误报 | #alerts-noise | 不 @ 人 |
| confidence < 0.7(任何 priority) | #alerts-review | @oncall-current |
其中:
- service-owner:根据告警 JSON.service 查服务负责人映射表
- dependency-owner:根据根因推断里提到的依赖服务查负责人
- oncall-current:当前值班人,从轮班表查
### 3. 判断是否建 Incident 通道
规则:
- P0 必须建 Incident 通道(Slack MCP 的 create_incident_channel)
- P1 以下不建
### 4. 执行
调用 GitHub MCP 的 create_issue,参数:
- repo: myteam/{{service 去掉 -service 后缀}}
- title: "[{{priority}}] {{alert_name}} - {{service}} - {{summary 前 50 字}}"
- body: 按 Issue body 模板渲染
- labels: 根据 priority 和 classification 选(见 label 映射表)
label 映射表:
- P0 → ["incident", "P0", "{{classification}}"]
- P1 → ["bug", "P1", "{{classification}}"]
- P2 → ["alert", "P2", "{{classification}}"]
- confidence < 0.7 → 追加 "needs-human-review"
调用 Slack MCP 的 send_message(如果需要建 Incident 通道,先调 create_incident_channel 再 send_message)。
Issue body 模板:
"""
## 告警信息
- 告警名称:{{alert_name}}
- 服务:{{service}}
- 优先级:{{priority}}
- 触发时间:{{starts_at}}
- 指标值:{{metric_value}} {{metric_unit}}(阈值 {{metric_threshold}})
- 持续时长:{{duration_minutes}} 分钟
## AI 分类
- 分类:{{classification}}
- 疑似误报:{{suspected_false_positive}}
{{if true: 理由:{{false_positive_reason}}}}
- 分类置信度:{{confidence}}
## AI 根因推断
{{for each hypothesis in hypotheses}}
### 假设 {{rank}}(置信度 {{confidence}})
**根因**:{{root_cause}}
**证据**:
{{for each evidence}}- {{evidence}}{{end for}}
**建议处置**:
{{for each action}}1. {{action}}{{end for}}
{{end for}}
## 数据缺口
{{for each gap in data_gaps}}- {{gap}}{{end for}}
## 相关链接
- Prometheus: {{prometheus_url}}
- Alertmanager: {{alertmanager_url}}
- Runbook: {{runbook_url}}
## 处置记录
<!-- 值班人处理后在这里更新 -->
"""
Slack text 模板:
"""
{{priority_emoji}} [{{priority}}] {{alert_name}}
服务:{{service}} | 触发:{{starts_at}}
指标:{{metric_value}} {{metric_unit}}(阈值 {{metric_threshold}})
🔍 AI 根因假设(Top 1):
{{hypotheses[0].root_cause}}
🛠 建议处置:
{{for each action in hypotheses[0].suggested_action}}{{index}}. {{action}}
{{end for}}
📎 Issue: {{issue_url}}
📊 Prometheus: {{prometheus_url}}
👥 {{mention_users}}
"""
priority_emoji 映射:
- P0: 🚨
- P1: ⚠️
- P2: 📋
- P3: ℹ️
## 输出格式
严格输出以下 JSON,记录你的所有动作:
{
"issue_created": true|false,
"issue_url": "https://github.com/...",
"slack_channel": "#incident-now",
"slack_message_sent": true|false,
"incident_channel_created": true|false,
"mentioned_users": ["@user1", "@user2"],
"routing_reason": "一句话说明为什么这么路由"
}
## 告警 JSON
{{alert_json}}
## 分类结果
{{classification_result}}
## 根因推断
{{root_cause_result}}
Prompt 模板 4:告警风暴聚合处理
当标准化层检测到同一服务在 10 秒内触发超过 10 条告警,会聚合成一个 alert_storm JSON 走专用 Prompt,而不是逐条处理。这个 Prompt 的核心任务是找共同根因 + 决定是否需要为个别告警单独处理。
你是值班 SRE 团队的告警风暴分析助手。你收到的是一个"告警风暴"聚合包,包含同一服务在短时间内的多条告警。你的任务是找出共同根因,避免逐条重复处理。
## 输入
你会收到一个 alert_storm JSON,字段:
- storm_id:风暴唯一 ID
- service:风暴所在服务
- storm_started_at:风暴开始时间
- alerts:告警数组,每条含 alert_name/summary/metric_value/starts_at/severity
- storm_root_signal:标准化层标记的"风暴根信号"(通常是第一个触发的告警)
- affected_downstream:受影响的下游服务列表
## 任务
### 步骤 1:分析告警聚类
看 alerts 数组,把告警按 alert_name 聚类,统计每类出现次数和触发时间分布。判断:
- 是否存在一个"源告警"(storm_root_signal 标记的那条),其他告警都是它的"衍生告警"(比如源是 DB 慢查询,衍生的是各接口 5xx、延迟、连接池耗尽)
- 还是存在多个独立根因(比如同时有 DB 异常和 Redis 异常,是巧合同时发生)
### 步骤 2:拉根因证据
只拉一次日志,不要为每条告警都拉(避免重复):
- 调日志分析 MCP 的 get_error_logs,参数用 storm_started_at 前后 5 分钟
- 调 GitHub MCP 的 list_commits,查 service 最近 24 小时变更
- 不要调 get_related_logs 逐条拉下游(风暴模式下下游 affected_downstream 已经给了)
### 步骤 3:输出风暴报告
输出一个"风暴报告"JSON,包含:
{
"storm_type": "single_root_cause | multiple_independent",
"root_cause": "一句话描述共同根因(如果是 multiple_independent,列出多个)",
"evidence": ["证据1", "证据2"],
"affected_services": ["服务1", "服务2"],
"alerts_summary": [
{"alert_name": "...", "count": N, "is_derived": true|false}
],
"suggested_action": ["步骤1", "步骤2"],
"confidence": 0.0,
"priority": "P0|P1|P2",
"needs_individual_handling": [
{"alert_name": "...", "reason": "为什么这条需要单独处理"}
]
}
### 步骤 4:路由决策
风暴模式下的路由规则和单条告警不同:
- 只发一条聚合 Slack 消息到 #incident-now(即使有 50 条告警也只发一条)
- 消息列出受影响服务列表 + 共同根因 + 建议处置
- @oncall-current + @service-owner
- 建一个 Issue(不要建 50 个),label 加 `alert-storm`
- needs_individual_handling 里标出的告警,单独发到 #alerts-p1 附带说明
## 风暴 Slack 消息模板
"""
🚨🚨 [告警风暴] {{service}} 触发 {{alerts_count}} 条告警
风暴时间:{{storm_started_at}}
受影响服务:{{affected_services}}
🔍 共同根因:
{{root_cause}}
🛠 建议处置:
{{suggested_action}}
📊 告警构成:
{{for each in alerts_summary}}- {{alert_name}} × {{count}}{{if is_derived}} (衍生){{end if}}
{{end for}}
📎 Issue: {{issue_url}}
👥 @oncall-current @service-owner
"""
## 输入:alert_storm JSON
{{storm_json}}
为什么风暴模式必须独立 Prompt:用单条告警 Prompt 处理 50 条风暴告警,会产生 50 个根因假设,其中 45 个是重复的"上游超时导致下游 5xx"变体。值班人收到 50 条 Slack 消息根本没法看。风暴 Prompt 强制 Claude 先做聚类和共同根因识别,只输出 1 个风暴报告,把 50 条消息压成 1 条可执行的。
效果数据对比
流水线上线后跑了 8 周(2026 年 7 月 8 日 - 9 月 1 日),和基线期(6 月整月)做对比。数据来自 Alertmanager、值班系统、GitHub Issue、Slack 消息记录的全量统计。
核心指标对比
| 指标 | 基线期(6 月,人工) | AI 期(7-8 月) | 变化 |
|---|---|---|---|
| 日均告警条数 | 2968 条 | 3012 条 | +1.5%(告警量本身在涨) |
| 值班人日均处理告警数 | 2968 条(全部需人工扫) | 947 条(AI 过滤后) | -68.1% |
| 平均响应时间 MTTA | 25 分钟 | 3.2 分钟 | -87.2% |
| P0 告警 MTTA | 17 分钟(被噪音淹没) | 1.8 分钟(P0 直达 #incident-now) | -89.4% |
| 平均处理时间 MTTR | 47 分钟 | 22 分钟 | -53.2% |
| 误报过滤准确率 | N/A(人工识别) | 94.3% | — |
| 根因推断 Top-1 准确率 | N/A | 61.7% | — |
| 根因推断 Top-3 准确率 | N/A | 89.2% | — |
| 值班人周工时(告警相关) | 42 小时 | 13.5 小时 | -67.9% |
| 告警风暴平均持续时间 | 23 分钟 | 4.5 分钟 | -80.4% |
| 自动建 Issue 数 | 0(全人工) | 日均 38 个 | — |
| 人工兜底触发率 | N/A | 11.2%(confidence < 0.7) | — |
分类准确率细分
按告警分类拆开看准确率(人工抽检 500 条标注真值):
| 分类 | 样本数 | 分类准确率 | 优先级准确率 | 误报识别准确率 |
|---|---|---|---|---|
| infrastructure | 142 | 96.5% | 91.2% | 92.3% |
| application | 168 | 93.5% | 88.7% | 90.1% |
| business | 89 | 91.0% | 85.4% | 87.6% |
| dependency | 71 | 89.6% | 83.1% | 85.9% |
| security | 30 | 86.7% | 80.0% | 83.3% |
规律:越偏基础设施的告警,Claude 分类越准(关键词明确、模式固定);越偏业务和安全的告警,准确率越低(语义复杂、需要业务背景知识)。security 类准确率最低,主要错在"鉴权失败"和"异常登录"的区分上——前者是技术问题,后者可能涉及攻击,Claude 经常分不清。
根因推断准确率随时间变化
| 周次 | Top-1 准确率 | Top-3 准确率 | 累计反馈样本 |
|---|---|---|---|
| 第 1 周 | 38.2% | 67.1% | 0 |
| 第 2 周 | 45.6% | 74.3% | 240 |
| 第 3 周 | 51.3% | 79.8% | 510 |
| 第 4 周 | 55.8% | 83.2% | 980 |
| 第 6 周 | 58.9% | 86.5% | 1560 |
| 第 8 周 | 61.7% | 89.2% | 2100 |
关键观察:根因推断准确率随反馈样本积累持续提升,但提升速度在递减(第 1-2 周涨 7.4pp,第 7-8 周涨 2.8pp)。说明 few-shot 样本到 2000 条左右开始进入收益递减区,后续要继续提升得靠模型本身的升级或更结构化的推理(比如引入 ReAct 模式让 Claude 显式做"假设-验证-修正"循环)。
值班人主观满意度
对 8 个值班 SRE 做了问卷调查(1-5 分):
| 维度 | 基线期评分 | AI 期评分 |
|---|---|---|
| 告警疲劳度(1=极度疲劳,5=轻松) | 1.4 | 4.1 |
| P0 告警响应信心(1=经常漏,5=从不漏) | 2.1 | 4.3 |
| 根因定位效率(1=慢,5=快) | 1.8 | 4.0 |
| 误报干扰程度(1=严重干扰,5=无干扰) | 1.2 | 3.8 |
| 整体工作满意度 | 2.3 | 4.2 |
最显著的提升在"告警疲劳度"——值班同学不用再每条告警都扫一眼,AI 已经把 68% 的噪音过滤掉了,他们只需要处理真正需要人工判断的告警。
踩坑经验
流水线开发和迭代过程中踩了 5 个主要的坑,每个都影响了准确率或可用性,这里详细复盘。
坑 1:告警语义歧义——同一 alert_name 不同含义
现象:早期分类准确率只有 78%,抽检错例发现大量错误集中在"同名告警不同语义"上。最典型的例子是 HighMemoryUsage 这条告警——它在不同服务上语义完全不同:
- 在
gateway服务上:是网关缓存了大请求体,需要扩容 - 在
data-pipeline服务上:是 Spark 任务的正常行为,任务结束后自动回落 - 在
order-service上:可能是内存泄漏,需要重启 + 排查代码
Claude 早期用同一套逻辑判断 HighMemoryUsage,结果把 data-pipeline 的正常内存上涨标成 P1 严重告警,把 order-service 的内存泄漏标成 P3 误报。
根因:告警 JSON 里只有 alert_name 和 summary,缺乏服务上下文。Claude 不知道同一个告警名在不同服务上语义不同。
解法:
- 在 Prompt 里加"服务上下文"小节,明确告诉 Claude "判断
HighMemoryUsage时必须结合 service 字段:data-pipeline 类服务内存上涨是常态,order-service 类长驻服务内存上涨需警惕" - 在标准化层补
service_profile字段,标注每个服务是"长驻服务"还是"批处理任务",让 Claude 有判据 - 对历史错例做 few-shot 样本,把"这条 HighMemoryUsage 在 data-pipeline 上是误报,在 order-service 上是 P1"这种对比案例加进 Prompt
效果:同名告警的分类准确率从 71% 提到 89%。
坑 2:多服务关联链路过长——上下文打爆
现象:第 2 步根因推断需要拉上下游服务日志,但有些场景调用链特别长。一次 order-service 5xx 飙升 的告警,实际根因在 logistics-service 的一个慢查询,调用链是 order → fulfill → inventory → warehouse → logistics,5 跳。Claude 拉了 5 个服务的日志,每个服务 200 条错误日志,加上 commit diff,单次上下文打到 180K token,接近 200K 上限,后面的推理质量明显下降(开始漏证据、给出的假设变泛泛)。
根因:调用链深度未知,Claude 一次性拉所有上下游日志,token 爆炸。
解法:
- 分阶段拉日志:先拉告警服务本身 + 直接上下游(1 跳),做第一轮推断。如果第一轮推断的 confidence < 0.6 且 evidence 里有"疑似上游异常"信号,再拉下一跳日志。这样把 180K token 压到 60K 一轮、做 3 轮。
- 日志裁剪增强:MCP 的
cluster_errors工具强化,不只按堆栈聚类,还做"只保留每个聚类 Top 3 代表性日志",把 200 条压到 15 条。 - 服务依赖图预计算:离线把所有服务的依赖拓扑算好存成图,Claude 查图而不是自己推断上下游,避免它推断错。
效果:单次上下文从峰值 180K 压到 60K 以内,长调用链场景的根因推断准确率从 41% 提到 73%。
坑 3:误报过滤的 false negative——把真告警当噪音静默了
现象:这是最危险的坑。误报过滤准确率 94% 听起来很高,但剩下 6% 的错误里,false negative(把真告警当误报静默)比 false positive(把误报当真告警)严重 10 倍。8 周里发生过 3 次 false negative:
- 一次是
inventory-service的StockDeductFailed告警,历史模式显示该告警经常抖动后自愈,Claude 标成误报静默了。结果这次是真问题——库存扣减失败率从 0.3% 涨到 2.1%,静默了 18 分钟才被业务方反馈发现。 - 一次是
payment-service的DBConnectionPoolLow,Claude 看历史都是自动恢复,标误报。实际是 DB 主库 OOM 导致连接断开,20 分钟后才有人处理。
根因:误报判定的"历史模式"信号权重过高,Claude 倾向于"历史上是误报 = 这次也是误报"。
解法:
- 加"业务关键告警白名单":对支付、订单、库存这三个核心业务的告警,即使历史模式显示是误报,也不允许直接静默,必须降级到 P2 走人工确认通道。宁可多处理几条,不能漏真火。
- 调高 false negative 的代价:在 Prompt 里明确告诉 Claude "误报判定的 false negative 代价是 false positive 的 10 倍,不确定时宁可标 false(非误报)"。
- 静默告警的"熔断机制":如果一个服务在过去 1 小时内有 > 3 条告警被静默,触发"静默熔断"——该服务后续告警一律不静默,全部走人工。
效果:false negative 率从 2.1% 降到 0.4%(8 周后未再发生核心业务告警被误静默)。
坑 4:MCP 超时——告警风暴时日志分析 MCP 拉日志要 30 秒
现象:平时日志分析 MCP 拉一次日志 2-3 秒,但告警风暴时(一次 50+ 条告警同时进来),50 个并发请求打 ELK,ELK 自己也慢,MCP 调用超时(设的 10 秒),Claude 拿不到日志,根因推断只能靠告警 JSON 本身,准确率断崖式下跌。
根因:ELK 在高负载下查询慢 + MCP 没做并发控制和降级。
解法:
- MCP 加并发限制:日志分析 MCP 内部用 semaphore 限制最多 5 个并发查询,超过的排队。避免一次性把 ELK 打爆。
- 加超时降级:MCP 调用超时后,Claude 走降级路径——不拉日志,只基于告警 JSON + 最近 commit 做根因推断,并在输出里标注
data_quality: degraded,通知路由降级到#alerts-degraded频道,提示值班人"这次 AI 判断数据不全,请人工复核"。 - 告警风暴模式:标准化层检测到告警风暴(同一服务 10 秒内 > 20 条告警),不再逐条处理,而是打包成一个聚合告警交给 Claude 一次性分析。Claude 对聚合告警做"风暴根因"推断(找出这批告警的共同上游根因),而不是逐条推断。
效果:告警风暴场景的 MCP 超时率从 47% 降到 3%,降级路径保证了即使超时也不会完全失能。
坑 5:告警风暴——Claude 把同一根因的 50 条告警当 50 个独立事件
现象:这个坑和上一个相关但不同。告警风暴时,即使 MCP 不超时,Claude 也容易把 50 条同源告警当 50 个独立事件处理,每条都给一个根因假设,结果 50 个假设里 45 个是重复的,值班人被 50 条 Slack 消息轰炸。
根因:标准化层一开始没做风暴聚合,50 条告警是 50 个独立 JSON 进来的。
解法:
- 标准化层加风暴聚合:同一 service 在 10 秒内触发 > 10 条告警,聚合成一个
alert_stormJSON,包含alerts数组 +storm_root_signal(风暴根信号,通常是第一个触发的告警)。 - Claude 对聚合告警走专用 Prompt:风暴模式 Prompt 让 Claude 先找"共同根因",再判断是否需要为个别告警单独处理。输出是一个"风暴报告"而不是 50 条独立报告。
- Slack 只发一条聚合消息:风暴模式下只发一条消息到
#incident-now,列出受影响的服务列表 + 共同根因 + 建议处置,而不是 50 条独立消息。
效果:告警风暴场景的 Slack 消息数从平均 187 条/风暴降到 1 条/风暴,值班人体验从"被轰炸"变成"收到一份风暴报告"。
坑 6:服务依赖拓扑过期——Claude 推断的上下游是错的
现象:根因推断的准确率在调用链长的场景一直上不去,抽检发现一个高频错误模式——Claude 拉的"上下游日志"里,该查的服务没查到、不该查的查了一堆无用日志。原因是我们在 Prompt 里写死了一份"服务依赖表"(payment 的上下游是 order/risk/inventory...),但这张表是基于 2023 年的架构画的,两年里加了 7 个新服务、改了 12 条调用关系,表早就过期了。Claude 按过期表拉日志,要么漏掉真正的根因服务,要么拉一堆无关日志浪费上下文。
根因:服务依赖拓扑靠人工维护,一定会过期。微服务架构里调用关系天天变(新增下游、拆服务、改调用方式),人工维护的拓扑图永远滞后于真实状态。
解法:
- 从 Jaeger 自动生成拓扑:写了个离线 job,每天凌晨从 Jaeger 拉过去 24 小时的全部 trace,解析出
serviceA → serviceB的调用关系,更新到一张service_topology表。这样拓扑图每天自动刷新,不再靠人工。 - Prompt 改成查表:根因推断 Prompt 里不再是写死的依赖表,而是告诉 Claude "调用
get_service_dependencies(service)工具查当前服务的上下游",MCP 从service_topology表查实时拓扑。 - 加调用频次权重:拓扑表不只存"谁调用谁",还存"调用频次"。Claude 拉日志时优先拉高频依赖的日志,低频依赖(一天调几次的)排在后面。
-- 服务依赖拓扑表(Jaeger 离线解析写入)
CREATE TABLE service_topology (
caller VARCHAR(64),
callee VARCHAR(64),
call_count_24h INT, -- 过去 24 小时调用次数
avg_latency_ms FLOAT, -- 平均延迟
error_rate FLOAT, -- 错误率
last_seen TIMESTAMPTZ, -- 最近一次调用时间
updated_at TIMESTAMPTZ, -- 拓扑更新时间
PRIMARY KEY (caller, callee)
);
效果:根因推断的"日志关联"步骤准确率从 67% 提到 88%(不再拉错服务),调用链超过 3 跳的长链路场景准确率提升最明显。
坑 7:Slack 消息格式在不同终端的渲染差异
现象:这是一个看起来很小但很烦的坑。Claude 生成的 Slack 消息用了大量 emoji 和多行文本,在 Slack 桌面端渲染正常,但值班人手机端收到推送时,emoji 变成方框、多行被压缩、@人 链接失效。凌晨被 P0 告警叫醒的值班人,看着手机上一堆方框和乱掉的格式,体验极差,有两次直接漏看了关键信息。
根因:Slack 的 markdown 渲染在不同客户端有差异,尤其手机端对 block kit 和复杂格式的支持不完整。
解法:
- 消息格式极简化:去掉非必要的 emoji(只保留 🚨⚠️📋ℹ️ 四个优先级标记),去掉多级嵌套,关键信息放第一屏(根因 + 建议 + Issue 链接),证据详情折叠到 Issue 里看。
- 用 Slack Block Kit 而非纯 markdown:Block Kit 在各端渲染一致性更好,但写起来复杂。我们做了个消息模板渲染器,Claude 输出 JSON,渲染器转成 Block Kit JSON 发送。
- @人 用 user_id 不用 @name:
@name在某些客户端不解析成 mention,必须用 Slack user_id(<@U12345>)才能保证全端生效。我们在服务负责人映射表里存的是 user_id。
// Block Kit 消息示例(P0 告警)
{
"blocks": [
{
"type": "header",
"text": {"type": "plain_text", "text": "🚨 [P0] PaymentLatencyP99High"}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": "*服务*:payment-service"},
{"type": "mrkdwn", "text": "*触发*:2026-09-01 02:17"},
{"type": "mrkdwn", "text": "*指标*:4800ms(阈值 1000ms)"}
]
},
{
"type": "section",
"text": {"type": "mrkdwn", "text": "🔍 *AI 根因*\nrisk-control 调用超时,2 小时前新规则疑似慢查询"}
},
{
"type": "actions",
"elements": [
{"type": "button", "text": {"type": "plain_text", "text": "📎 Issue"}, "url": "{{issue_url}}"},
{"type": "button", "text": {"type": "plain_text", "text": "📊 Prometheus"}, "url": "{{prometheus_url}}"}
]
}
]
}
效果:手机端消息可读性显著提升,值班人反馈"凌晨看一眼就知道发生了什么、该怎么处理"。
部署和运维
流水线跑起来后的日常运维也很关键,这里记录几个运维要点。
监控 AI 分诊流水线自身
"谁来看着看告警的 AI"是个绕不开的问题。我们给流水线自身建了一套监控,指标包括:
| 指标 | 阈值 | 告警动作 |
|---|---|---|
| 分类 consumer 消费延迟(lag) | > 100 条 | 告警到 #ai-triage-ops,说明 Claude 处理不过来 |
| 根因推断 consumer 消费延迟 | > 50 条 | 同上,可能需要扩容 consumer |
| Claude API 调用失败率 | > 5% | 立即告警,可能 API 配额用完或服务异常 |
| 日志分析 MCP 调用超时率 | > 20% | 告警,ELK 可能慢了 |
| 死信队列消息数 | > 10 条/小时 | 告警,说明大量告警处理失败,走降级 |
| 人工兜底触发率 | > 20% | 告警,说明 Claude 准确率下降,需要补样本 |
| 误报过滤率突变 | 24h 内变化 > 15pp | 告警,可能是规则变更导致告警模式变了 |
这套"监控的监控"确保流水线出问题时能第一时间发现,而不是等值班人抱怨"AI 怎么不发告警了"才发现。
Prompt 版本管理
Prompt 是流水线的"逻辑代码",必须有版本管理。我们的做法:
- Prompt 存在独立的 Git 仓库
ai-triage-prompts,每个 Prompt 一个.md文件 - 修改 Prompt 走 PR 流程,PR 描述必须说明"改了什么、为什么改、预期影响"
- Prompt 上线前在影子环境跑 100 条历史告警做回归测试,对比修改前后的准确率,准确率下降超过 2pp 不允许合并
- 线上用的 Prompt 版本号记录在每条告警的处理日志里,出问题可以回滚到上一版
成本核算
8 周运行的成本数据,供参考:
| 成本项 | 月均花费 | 备注 |
|---|---|---|
| Claude API 调用 | ¥4,800 | 日均 3000 条告警 × 平均 6K token × 单价 |
| ELK 额外查询负载 | ¥600 | MCP 查询增加的 ELK 资源开销 |
| Kafka 集群 | ¥0 | 复用现有集群,新增一个 topic |
| PostgreSQL | ¥0 | 复用现有实例,新增一张表 |
| K8s 资源(MCP + consumer) | ¥800 | 4 个 MCP × 2 副本 + 8 个 consumer pod |
| 合计 | ¥6,200/月 | — |
对比收益:
- 值班人告警相关工时从 42 小时/周降到 13.5 小时/周,节省 28.5 小时/周 × 4 周 = 114 小时/月
- 按 SRE 工时成本 ¥200/小时算,节省 ¥22,800/月
- 净收益 ¥16,600/月,ROI 约 3.7x
还不算 MTTR 降低带来的业务损失减少(P0 告警少拖 14 分钟,按支付业务每分钟流水 ¥8 万算,单次 P0 就省 ¥112 万潜在损失)。
经验总结
8 周的实践,3 条最关键的经验:
经验 1:AI 分诊的本质是"语义理解 + 多源关联",不是"规则匹配"
我们一开始评估过用 PagerDuty 的规则引擎做告警收敛,结论是规则引擎能做"同一 alertname + 同一 service 在 5 分钟内的告警合并",但做不了"payment 延迟飙升 + risk-control 熔断 + 最近 commit 引入新规则,这三件事是不是同一根因"这种跨源关联和语义判断。LLM 的价值就在这里——它能理解"上游服务超时导致下游同步等待"这种因果链,规则引擎永远做不了。
但反过来,LLM 也做不了规则引擎能稳定做的事——比如"同一 alertname 5 分钟内去重"这种确定性逻辑,让 Claude 做反而不如写死在标准化层。所以我们的设计是"标准化层做确定性逻辑(去重、风暴检测、字段标准化),Claude 做语义逻辑(分类、根因、路由)",各司其职。
经验 2:反馈闭环是流水线从"能用"到"好用"的唯一路径
第 1 周的准确率(分类 78%、根因 Top-1 38%)只能叫"勉强能用",真正让它变成"好用"的是 8 周积累的 2100 条反馈样本。没有反馈闭环,LLM 的判断永远停留在 zero-shot 水平;有了反馈闭环,它才成了一个能持续学习的系统。
反馈闭环的关键设计是"低置信度走人工 + 人工结果回流"。这既保证了线上安全(低置信度不直接落地),又保证了训练数据质量(人工标注的都是 Claude 拿不准的难例,价值最高)。
经验 3:MCP 的价值不在"连接",在"裁剪"
一开始我们觉得 MCP 就是个 API 适配层,把 ELK API 包一层给 Claude 调。后来发现真正的价值在 MCP 内部做的数据裁剪:把 200 条错误日志聚成 5 类只返回 Top 3、把 16 万 token 的原始日志压成 8 千 token 的摘要、把 5 跳调用链的日志按"告警时间窗对齐"只拉相关片段。
如果不做这些裁剪,Claude 的上下文会被原始数据打爆,推理质量断崖式下跌。MCP 的设计哲学应该是"给 Claude 刚好够用的数据,不多不少"——少了 Claude 推断不准,多了上下文爆掉,MCP 的活就是找到这个"刚好够用"的平衡点。
下一步
流水线跑稳后,下一步的两个方向:
- 从 few-shot 到微调:现在准确率提升靠 few-shot 样本累加,收益已经递减。下一步打算用积累的 2100 条反馈数据做一次 Claude 的微调(fine-tuning),看能不能把根因推断 Top-1 准确率从 61% 推到 75%。
- 从告警分诊扩展到故障自愈:现在流水线的终点是"建 Issue + 发通知 + 给处置建议",人还是要去执行。下一步想接 K8s MCP 和 Argo Rollout MCP,让 Claude 对"回滚到上一版本"这种确定性处置直接执行(带审批),把 MTTR 从 22 分钟再往下压。
告警分诊只是 SRE 智能化的第一站,但它证明了"LLM + MCP"的组合在运维场景是可行的——只要设计好分工、做好反馈闭环、控制好上下文,AI 能把值班同学从噪音里解放出来,真正去做只有人能做的判断和决策。