每日 AI 实战案例

AI 监控告警智能分诊:日均 3000 条告警从 25 分钟降到 3 分钟响应

2026-09-02
高级开发
2 周
使用工具:
Claude日志分析 MCPGitHub MCPSlack MCP
AI实战告警分诊监控SREClaudePrometheus自动化

案例摘要

公司的微服务架构每天产生约 3000 条告警(Prometheus + Grafana),值班 SRE 平均响应时间 25 分钟,其中 70% 是误报或低优先级。我们用 Claude 做告警分类和根因推断 + 日志分析 MCP 做自动日志关联 + GitHub MCP 做自动创建 Issue + Slack MCP 做告警通知路由,构建了端到端的告警分诊流水线。误报过滤准确率 94%,真正需要人工处理的告警平均响应时间降到 3 分钟,值班人员工作量减少 68%。本文复盘工具分工、分诊 Prompt 模板、根因推断准确率数据和踩过的坑。

案例背景

我们公司是一个面向 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%)——绝大多数不需要处理

痛点的三句话总结

  1. 值班人被噪音淹没,看不到真火。2968 条告警里 79% 是噪音,值班同学为了不漏掉 P0,只能每条都扫一眼。但人眼扫 3000 条告警的注意力曲线在第 200 条之后断崖式下跌,真出事的时候反而容易漏。
  2. 响应慢在"关联",不在"看到"。25 分钟的 MTTA 里,18 分钟花在"判断这条告警是不是真的 + 找相关日志 + 找关联服务 + 翻代码",真正动手处理只占 7 分钟。一个 order-service 5xx 飙升 的告警,值班人要先去 ELK 搜日志、去 Jaeger 查链路、去 GitHub 翻最近 commit、去 Grafana 看关联面板——这一套下来 15 分钟就没了。
  3. 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 配额导致后续告警全部失败。

我们做了两级限流:

  1. QPS 限流:分类 consumer 的 8 个 worker 共享一个 token bucket,限流 20 QPS(留出余量,Claude API 配额是 60 RPM)。
  2. 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"
}

标准化层做的几件事:

  1. 扁平化:把 alerts[0].labels.service 提到顶层 service,让 Claude 不用解析嵌套
  2. 统一 severity:把 warning / P1 / critical 等不同命名统一成 info / warning / critical / fatal 四档
  3. 补 metric 字段:从 Prometheus expression 里抽取出 metric_value / metric_threshold / metric_unit,让 Claude 直接拿到数值而不是去解析 URL
  4. 生成 alert_id:用 时间 + alertname + service + pod 生成稳定 ID,方便后续去重和关联
  5. 拆包:如果一次 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 之所以稳定,关键在三点:

  1. 限定输出 schema:明确告诉 Claude 必须输出哪些字段、字段类型、取值范围。Claude 的输出一旦 schema 化,下游就能稳定用 json.loads 解析。
  2. 给判定标准:不是让 Claude "凭感觉"分优先级,而是明确给"P0 = 核心服务中断 / 支付失败率 > 5%"这种可执行的标准。这把分类准确率从 78% 拉到 91%。
  3. 给误报信号:把"什么算误报"的判断依据显式列出来,避免 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_logsservice, start_time, end_time, level错误日志列表(去重后的堆栈)拉告警服务本身的错误日志
get_related_logsservice_list, time_window跨服务日志关联拉上下游服务的日志,找关联异常
cluster_errorslogs错误聚类结果(按堆栈相似度分组)把 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 IssueSlack 频道@ 人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 步:人工兜底和反馈闭环(低置信度告警人工确认 → 训练数据积累)

整个流水线的最后一公里是对低置信度告警的人工兜底 + 把人工判断结果回流成训练数据。这一步不做,流水线就是个黑盒,永远无法自我优化。

触发人工兜底的条件有两个:

  1. Claude 的 confidence < 0.7:Claude 自己都不确定,不能让它的判断直接落地
  2. 告警模式在历史中从未出现:标准化层会标注 is_new_pattern: true,这类告警即使 confidence 高也走人工确认(防止 Claude 对没见过的模式过度自信)

人工兜底通道长这样:

  • 这类告警走 #alerts-review Slack 频道,@值班人
  • 消息里附带 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 样本更新:

  1. 周末跑一个批处理,把本周所有 ai_* != human_* 的样本挑出来
  2. 把这些样本做成 few-shot 例子,加进分类 Prompt 的"历史纠错案例"小节
  3. 对于反复出错的告警类型(比如 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. 拆包(这条只有 1 个 alert,无需拆)
  2. 扁平化 + 字段补全:从 Prometheus expression 抽取 metric_value=6.2、metric_threshold=5.0、metric_unit=percent
  3. 查历史统计:history_stats 字段查 alert_feedback 表,发现该告警历史出现 3 次,其中 1 次人工介入(human_action_count=1),2 次自愈。标注 is_new_pattern=false。
  4. 风暴检测:过去 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 秒):

  1. 先调 create_incident_channel,建 #inc-0819-order-fail 通道
  2. 调 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%
平均响应时间 MTTA25 分钟3.2 分钟-87.2%
P0 告警 MTTA17 分钟(被噪音淹没)1.8 分钟(P0 直达 #incident-now)-89.4%
平均处理时间 MTTR47 分钟22 分钟-53.2%
误报过滤准确率N/A(人工识别)94.3%—
根因推断 Top-1 准确率N/A61.7%—
根因推断 Top-3 准确率N/A89.2%—
值班人周工时(告警相关)42 小时13.5 小时-67.9%
告警风暴平均持续时间23 分钟4.5 分钟-80.4%
自动建 Issue 数0(全人工)日均 38 个—
人工兜底触发率N/A11.2%(confidence < 0.7)—

分类准确率细分

按告警分类拆开看准确率(人工抽检 500 条标注真值):

分类样本数分类准确率优先级准确率误报识别准确率
infrastructure14296.5%91.2%92.3%
application16893.5%88.7%90.1%
business8991.0%85.4%87.6%
dependency7189.6%83.1%85.9%
security3086.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.44.1
P0 告警响应信心(1=经常漏,5=从不漏)2.14.3
根因定位效率(1=慢,5=快)1.84.0
误报干扰程度(1=严重干扰,5=无干扰)1.23.8
整体工作满意度2.34.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 不知道同一个告警名在不同服务上语义不同。

解法:

  1. 在 Prompt 里加"服务上下文"小节,明确告诉 Claude "判断 HighMemoryUsage 时必须结合 service 字段:data-pipeline 类服务内存上涨是常态,order-service 类长驻服务内存上涨需警惕"
  2. 在标准化层补 service_profile 字段,标注每个服务是"长驻服务"还是"批处理任务",让 Claude 有判据
  3. 对历史错例做 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. 分阶段拉日志:先拉告警服务本身 + 直接上下游(1 跳),做第一轮推断。如果第一轮推断的 confidence < 0.6 且 evidence 里有"疑似上游异常"信号,再拉下一跳日志。这样把 180K token 压到 60K 一轮、做 3 轮。
  2. 日志裁剪增强:MCP 的 cluster_errors 工具强化,不只按堆栈聚类,还做"只保留每个聚类 Top 3 代表性日志",把 200 条压到 15 条。
  3. 服务依赖图预计算:离线把所有服务的依赖拓扑算好存成图,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 倾向于"历史上是误报 = 这次也是误报"。

解法:

  1. 加"业务关键告警白名单":对支付、订单、库存这三个核心业务的告警,即使历史模式显示是误报,也不允许直接静默,必须降级到 P2 走人工确认通道。宁可多处理几条,不能漏真火。
  2. 调高 false negative 的代价:在 Prompt 里明确告诉 Claude "误报判定的 false negative 代价是 false positive 的 10 倍,不确定时宁可标 false(非误报)"。
  3. 静默告警的"熔断机制":如果一个服务在过去 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 没做并发控制和降级。

解法:

  1. MCP 加并发限制:日志分析 MCP 内部用 semaphore 限制最多 5 个并发查询,超过的排队。避免一次性把 ELK 打爆。
  2. 加超时降级:MCP 调用超时后,Claude 走降级路径——不拉日志,只基于告警 JSON + 最近 commit 做根因推断,并在输出里标注 data_quality: degraded,通知路由降级到 #alerts-degraded 频道,提示值班人"这次 AI 判断数据不全,请人工复核"。
  3. 告警风暴模式:标准化层检测到告警风暴(同一服务 10 秒内 > 20 条告警),不再逐条处理,而是打包成一个聚合告警交给 Claude 一次性分析。Claude 对聚合告警做"风暴根因"推断(找出这批告警的共同上游根因),而不是逐条推断。

效果:告警风暴场景的 MCP 超时率从 47% 降到 3%,降级路径保证了即使超时也不会完全失能。

坑 5:告警风暴——Claude 把同一根因的 50 条告警当 50 个独立事件

现象:这个坑和上一个相关但不同。告警风暴时,即使 MCP 不超时,Claude 也容易把 50 条同源告警当 50 个独立事件处理,每条都给一个根因假设,结果 50 个假设里 45 个是重复的,值班人被 50 条 Slack 消息轰炸。

根因:标准化层一开始没做风暴聚合,50 条告警是 50 个独立 JSON 进来的。

解法:

  1. 标准化层加风暴聚合:同一 service 在 10 秒内触发 > 10 条告警,聚合成一个 alert_storm JSON,包含 alerts 数组 + storm_root_signal(风暴根信号,通常是第一个触发的告警)。
  2. Claude 对聚合告警走专用 Prompt:风暴模式 Prompt 让 Claude 先找"共同根因",再判断是否需要为个别告警单独处理。输出是一个"风暴报告"而不是 50 条独立报告。
  3. Slack 只发一条聚合消息:风暴模式下只发一条消息到 #incident-now,列出受影响的服务列表 + 共同根因 + 建议处置,而不是 50 条独立消息。

效果:告警风暴场景的 Slack 消息数从平均 187 条/风暴降到 1 条/风暴,值班人体验从"被轰炸"变成"收到一份风暴报告"。

坑 6:服务依赖拓扑过期——Claude 推断的上下游是错的

现象:根因推断的准确率在调用链长的场景一直上不去,抽检发现一个高频错误模式——Claude 拉的"上下游日志"里,该查的服务没查到、不该查的查了一堆无用日志。原因是我们在 Prompt 里写死了一份"服务依赖表"(payment 的上下游是 order/risk/inventory...),但这张表是基于 2023 年的架构画的,两年里加了 7 个新服务、改了 12 条调用关系,表早就过期了。Claude 按过期表拉日志,要么漏掉真正的根因服务,要么拉一堆无关日志浪费上下文。

根因:服务依赖拓扑靠人工维护,一定会过期。微服务架构里调用关系天天变(新增下游、拆服务、改调用方式),人工维护的拓扑图永远滞后于真实状态。

解法:

  1. 从 Jaeger 自动生成拓扑:写了个离线 job,每天凌晨从 Jaeger 拉过去 24 小时的全部 trace,解析出 serviceA → serviceB 的调用关系,更新到一张 service_topology 表。这样拓扑图每天自动刷新,不再靠人工。
  2. Prompt 改成查表:根因推断 Prompt 里不再是写死的依赖表,而是告诉 Claude "调用 get_service_dependencies(service) 工具查当前服务的上下游",MCP 从 service_topology 表查实时拓扑。
  3. 加调用频次权重:拓扑表不只存"谁调用谁",还存"调用频次"。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 和复杂格式的支持不完整。

解法:

  1. 消息格式极简化:去掉非必要的 emoji(只保留 🚨⚠️📋ℹ️ 四个优先级标记),去掉多级嵌套,关键信息放第一屏(根因 + 建议 + Issue 链接),证据详情折叠到 Issue 里看。
  2. 用 Slack Block Kit 而非纯 markdown:Block Kit 在各端渲染一致性更好,但写起来复杂。我们做了个消息模板渲染器,Claude 输出 JSON,渲染器转成 Block Kit JSON 发送。
  3. @人 用 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 额外查询负载¥600MCP 查询增加的 ELK 资源开销
Kafka 集群¥0复用现有集群,新增一个 topic
PostgreSQL¥0复用现有实例,新增一张表
K8s 资源(MCP + consumer)¥8004 个 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 的活就是找到这个"刚好够用"的平衡点。

下一步

流水线跑稳后,下一步的两个方向:

  1. 从 few-shot 到微调:现在准确率提升靠 few-shot 样本累加,收益已经递减。下一步打算用积累的 2100 条反馈数据做一次 Claude 的微调(fine-tuning),看能不能把根因推断 Top-1 准确率从 61% 推到 75%。
  2. 从告警分诊扩展到故障自愈:现在流水线的终点是"建 Issue + 发通知 + 给处置建议",人还是要去执行。下一步想接 K8s MCP 和 Argo Rollout MCP,让 Claude 对"回滚到上一版本"这种确定性处置直接执行(带审批),把 MTTR 从 22 分钟再往下压。

告警分诊只是 SRE 智能化的第一站,但它证明了"LLM + MCP"的组合在运维场景是可行的——只要设计好分工、做好反馈闭环、控制好上下文,AI 能把值班同学从噪音里解放出来,真正去做只有人能做的判断和决策。

相关推荐

查看更多 →
案例开发

AI 日志异常检测:从日均百万条日志中 30 分钟定位 3 个隐藏 Bug

微服务集群每天产生约 120 万条日志(ELK 栈),但 99.9% 是正常日志,真正的异常淹没在海量数据中。团队 2 名 SRE 花一周才能完成一次全量日志审计。我们用 GPT-5 做异常模式识别和语义分类 + 日志分析 MCP 做自动日志检索和聚合 + Slack MCP 做实时通知,构建了端到端的日志异常检测流水线。30 分钟内从 120 万条日志中定位了 3 个隐藏 Bug(一个内存泄漏、一个竞态条件、一个错误的重试逻辑),准确率 91%,误报率仅 4%。本文复盘工具分工、分步 Prompt 模板、检测准确率数据和踩过的坑。

2026-09-09
#AI实战#日志检测
案例开发

用 OpenAI Agents API + MCP 把 Codex Harness 改成可编排的 Agent 服务:中型 SaaS 团队实战

OpenAI 在 2026 年 9 月把内部跑 Codex 的 harness 改成了对外可用的 Agents API:原生支持 MCP server 注册、多 Agent 编排、handoff、guardrail、结构化输出。一个中型 SaaS 团队(23 名工程师)用这套 API + 自建的 6 个 MCP server,把内部 17 个零散的 AI 脚本统一成了一个可编排的 Agent 服务,每月节省 480 工时。本文复盘架构设计(Agent 拓扑、handoff 流程、guardrail 配置)、Prompt 模板(主 Agent + 4 个子 Agent)、MCP 集成(自建 6 个 MCP 的分工)、3 个真实业务场景(客户工单自动分流 / 代码评审流水线 / 数据报告自动化)、以及踩过的 11 个坑(handoff 死循环、guardrail 误杀、MCP 超时、token 计费翻倍等)。

2026-09-11
#AI实战#OpenAI
案例开发

AI 辅助数据库零停机迁移:从 MySQL 到 PostgreSQL 只用 3 天

公司核心业务从 MySQL 5.7 迁移到 PostgreSQL 16,数据库有 47 张表、230 万行数据、86 个存储过程和 34 个触发器。手动迁移评估 3 周,最终用 Claude Code + PostgreSQL MCP + 自定义 Schema 转换 MCP,3 天完成全量迁移(Schema 转换 + 数据迁移 + 应用层适配 + 回归测试),停机窗口仅 12 分钟。本文复盘工具分工、分步 Prompt 模板、SQL 方言转换踩过的 23 个坑(AUTO_INCREMENT vs SERIAL、datetime 格式、ON DUPLICATE KEY UPDATE、GROUP_CONCAT、utf8mb4 编码等)、以及零停机迁移的灰度切换策略。

2026-09-10
#AI实战#数据库迁移
案例开发

AI 自动生成 API 文档:从 8 万行无注释代码到完整 API 手册只用了 4 小时

团队的微服务项目积累了 8 万行 Go 代码,几乎没有注释和 Swagger 定义。手动写 API 文档评估要 2 周,最终用 Claude Code + GitHub MCP + 自定义代码分析 MCP,4 小时生成了完整的 API 文档,覆盖 47 个接口、120+ 参数说明、14 个完整请求/响应示例。本文复盘工具组合、分步 Prompt 模板、以及踩过的坑(接口签名推断错误、参数类型歧义、嵌套结构解析失败)。

2026-09-01
#AI实战#API文档