每日 AI 实战案例

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

2026-09-09
高级开发
1 周
使用工具:
GPT-5日志分析 MCPSlack MCPELK
AI实战日志检测异常检测GPT-5ELKSRE自动化

案例摘要

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

案例背景

我们是一个面向 B 端 + C 端混合业务的 SaaS 平台,整体架构是典型的微服务形态:38 个核心服务(账户、租户、计费、订单、网关、消息、推送、文件、搜索、推荐、风控、审计、报表、调度等),跑在自建 K8s 集群上,日均活跃租户 4.2 万、峰值 QPS 3.6 万。日志体系基于 ELK 三件套(Elasticsearch + Logstash + Kibana)+ Filebeat 做采集,再加上 Jaeger 做分布式链路追踪、Prometheus 做指标监控。

这套日志体系从 2024 年 Q1 开始搭建,到 2026 年已经迭代了 2 年半。最初全集群一天才 8 万条日志,随着业务量增长和服务拆分,日志量一路膨胀,到 2026 年 8 月已经稳定在 日均 120 万条,大促或月底账单日出峰值 210 万条。ELK 集群本身扩了 3 轮(从 3 节点到 12 节点,存留期从 30 天压到 14 天),但"看得见日志"和"看得懂日志"之间的鸿沟越来越大。

日志现状的几个硬数据

我们从 ELK 和值班系统拉了 2026 年 7 月整月 + 8 月前三周 的日志数据做基线统计,结果触目惊心:

维度数据备注
日均日志条数1,217,400 条工作日均值,周末略低(约 98 万条)
峰值日日志数2,083,000 条8 月 28 日月底账单日
服务数38 个核心服务分布在 6 个命名空间
日志级别分布(INFO)99.93%正常业务流水,绝大部分无价值
日志级别分布(WARN)0.058%日均约 700 条,多为可恢复的瞬时抖动
日志级别分布(ERROR)0.011%日均约 134 条,会被值班人扫一眼
日志级别分布(FATAL)0.0008%日均约 10 条,必处理
隐式异常日志占比约 0.0007%日均约 8-15 条,藏在 INFO 里、语义异常但级别是 INFO
SRE 团队人数6 人2 人一组轮值,每周做一次全量审计
全量日志审计周期1 次/周2 人花 5 个工作日才能完成
审计覆盖的服务比例100%每周必须扫完所有服务
审计期间发现的真实 Bug平均 1.8 个/周99.9% 的日志是正常日志

把 120 万条日志按"是否包含异常信号"拆开看,分布是这样的:

  • FATAL / 未捕获异常(服务级别直接报错):日均 10 条(0.0008%)——会触发告警,值班人立刻处理
  • ERROR(业务报错但可降级):日均 134 条(0.011%)——值班人会扫一眼,但 80% 是已知问题(限流、超时、重试),真正需要排查的不到 20 条
  • WARN(边缘抖动):日均 700 条(0.058%)——绝大部分是可恢复的瞬时抖动,几乎没人看
  • INFO 中的隐式异常:日均 8-15 条(0.0007%)——这是最危险的。日志级别是 INFO(不会触发告警),但语义上藏着 Bug 信号,比如"重试第 5 次成功""缓存命中率突然从 98% 跌到 41%""某个用户的订单创建耗时 18 秒"。这些日志淹没在 120 万条 INFO 里,人眼永远扫不到
  • 正常 INFO:日均约 121 万条(99.93%)——纯业务流水

痛点的三句话总结

  1. 审计成本高到离谱,但收益极低。2 个 SRE 花 5 个工作日扫 120 万条日志,平均每人每天扫 12 万条。按人眼扫日志 200 条/分钟算,12 万条要扫 10 小时不眠不休——实际根本扫不完,只能靠关键词过滤 + 抽样。审计的"覆盖率"看起来是 100%,实际人工真正读过的日志不到 5%,剩下 95% 是跑脚本过滤 + 看聚合图表。真正藏得深的 Bug,从来不在你过滤的关键词里。
  2. 99.9% 的正常日志把 0.1% 的异常淹没了。FATAL 和 ERROR 会触发告警,能被发现;但 INFO 级别的隐式异常(重试成功、缓存命中率下跌、慢请求偶发、状态机卡住)永远不会触发告警,也永远不会被人工审计发现。这些 Bug 会潜伏几周甚至几个月,等到某天累积爆掉(比如内存泄漏到 OOM、竞态条件触发数据错乱、错误重试逻辑拖垮下游),才发现"原来日志里早有信号,只是没人看见"。
  3. 审计频率太低,Bug 潜伏期太长。一周一次的审计节奏,意味着一个 Bug 最长能潜伏 7 天才被发现。8 月做过一次统计:当月发现的 11 个线上 Bug 里,有 6 个在日志里留下了 INFO 级别的早期信号,平均潜伏 4.2 天才被触发成 ERROR/FATAL 被发现。如果审计能做到日级,这 6 个 Bug 都能在爆掉前被掐死。

为什么选 AI 日志异常检测

2026 年 8 月的一次事故彻底推动了这件事。8 月 19 日凌晨 3:42,计费服务的 billing-service Pod 突然 OOM 重启,导致正在跑的月底账单任务中断,4.2 万租户里的 1700 个账单生成失败,需要人工补跑。事后复盘发现:

  • 该服务从 8 月 11 日 起日志里就反复出现一条 INFO 级记录:cache entry evicted: tenant_config size=482MB, max=512MB——缓存驱逐频次从每天 3 次涨到每天 480 次
  • 这是一个典型的内存泄漏信号:缓存大小持续增长、驱逐频次持续升高,说明有 key 永远不被淘汰
  • 这条日志是 INFO 级别(因为缓存驱逐是正常机制),没有触发任何告警
  • 8 月 11 日到 8 月 19 日,整整 8 天,这条信号一直在日志里,但没人看
  • 8 月 19 日凌晨,缓存大小突破 512MB 上限,堆外内存也涨爆,OOM

事后算账:如果 8 月 11 日当天就发现这条信号,一个 SRE 花 2 小时排查就能定位内存泄漏,修复后不会有大故障。8 天潜伏期造成的损失是 1700 个账单补跑 + 客户投诉 + SLA 违约金,直接成本约 ¥38 万。

这次事故后,SRE 团队定了 KPI:1 个月内把日志审计从"周级人工"升级为"日级自动化",且要能发现 INFO 级别的隐式异常信号,单次全量审计成本控制在 30 分钟以内。评估了三种方案:

方案思路评估结论
加告警规则给所有异常信号加 PromQL/LogQL 告警规则只能匹配"已知模式",对"缓存驱逐频次趋势上涨""重试次数缓慢增加"这种时序模式无能为力;规则数量会爆炸,且新增的隐式异常永远没有对应规则
传统日志分析平台(Splunk / Datadog Log Patterns)接入商业方案,靠 ML 做异常检测ML 模型对"语义异常"(比如"重试第 5 次成功"是异常)无能为力,只能做数值异常;且按日志量收费,120 万条/天成本惊人
AI 日志异常检测流水线GPT-5 做语义异常识别 + MCP 做日志检索聚合能理解日志语义("重试第 5 次成功"是异常信号)、能发现未知模式、能跨服务关联、能输出可解释的根因假设

最终选了第三条路。原因是日志异常检测本质上是语义理解 + 模式发现 + 多源关联的问题。传统告警规则和 ML 模型都只能处理"已知模式的数值异常",而真正藏得深的 Bug 往往是"语义异常"——日志的级别是 INFO,文字也没出现 error/exception,但语义上透露出系统正在出问题。这种判断只有 LLM 做得了。

工具组合与分工

整个日志异常检测流水线由 4 个工具协同完成,每个工具负责一段能力:

工具分工为什么是这个工具
GPT-5异常模式识别、语义分类、Bug 根因推断、误报过滤200K 上下文能吞下采样后的日志批次(单批约 8 万条精简日志);语义理解能力能识别"重试第 5 次成功""缓存驱逐频次上升"这种 INFO 级隐式异常;能跨日志条目做时序模式分析
日志分析 MCP自动从 ELK 拉日志、按 traceId/service 聚合、做堆栈去重和关键词提取GPT-5 直接读 ELK API 要写 Elasticsearch DSL,且单次拉取量大;MCP 封装了"按服务 + 时间窗拉日志""按 traceId 聚合调用链""按堆栈相似度聚类"三个高频操作,并在内部做日志裁剪控制 token
Slack MCP异常通知路由、@相关人、建 Incident 通道不同严重度的异常走不同 Slack 频道(P0 走 #incident-now 并 @值班 + @服务负责人,低优走 #log-anomaly-low 不 @人);Slack MCP 能根据 GPT-5 的路由决策直接发消息
ELK日志存储和检索基础设施已有的日志栈,MCP 直接对接 Elasticsearch API,无需迁移;保留 14 天日志供回溯

关键设计原则:GPT-5 出"判断",MCP 出"动作",人做"兜底"。具体来说:

  • GPT-5 负责所有需要语义判断的环节:异常模式识别、Bug 根因假设、误报过滤、通知路由。这些是规则做不来的,必须交给 LLM。
  • MCP 负责所有需要和数据源交互的动作:拉日志、聚合调用链、发消息。这些是确定性的 IO 操作,封装成 MCP 让 GPT-5 不用写 DSL,直接调用。
  • 人只负责低置信度异常的二次确认和Bug 假设的验证修复。GPT-5 对自己的判断输出置信度,低于 0.7 的异常走人工兜底通道,人工处理结果回流成训练数据。

整个流水线的拓扑长这样:

ELK (120万条/天) → 日志分析 MCP (采样降量) → JSON 标准化层
                                                ↓
                                          GPT-5 异常模式识别
                                                ↓
                                    ┌───────────┴───────────┐
                                    ↓                       ↓
                            日志分析 MCP             (按 traceId 聚合)
                            (按 service 拉关联)              
                                    ↓                       ↓
                                    └───────────┬───────────┘
                                                ↓
                                        GPT-5 根因推断
                                                ↓
                                ┌───────────────┴───────────────┐
                                ↓                              ↓
                         Slack MCP 路由通知            (置信度 < 0.7 走人工)
                                ↓                              ↓
                         值班人收到异常                  人工兜底通道
                                ↓                              ↓
                         开发者验证修复              反馈回流训练集

流水线架构设计决策

在动手之前,我们讨论了 4 个架构层面的决策,这些决策直接影响了后续实现的稳定性和可维护性,值得单独说明。

决策 1:全量处理 vs 采样降量——选分层采样

120 万条日志一天处理完,是全量喂给 GPT-5 还是采样?选分层采样,原因有三:

  • 全量处理不可能:120 万条日志即使每条只占 50 token,也是 6000 万 token,远超 GPT-5 单次 200K 上下文。即使分批,120 万条 × 6K token/批 = 2 万次 API 调用,单月光 API 费用就 ¥18 万,不可承受。
  • 全量处理没必要:99.93% 的 INFO 日志是正常业务流水("用户登录成功""订单创建成功"),对异常检测零价值。真正有异常信号的是 ERROR/WARN(约 844 条/天)+ INFO 里的隐式异常(约 8-15 条/天),加起来不到 1000 条。
  • 采样能覆盖关键信号:分层采样按"ERROR/FATAL 全量 + WARN 抽样 10% + INFO 抽样 0.1% + 带异常关键词的 INFO 全量"的策略,能把 120 万条压到约 5000 条/天,覆盖 99% 以上的异常信号。

采样策略表:

日志级别日均条数采样率采样后条数采样策略
FATAL10100%10全量保留
ERROR134100%134全量保留
WARN70010%70按服务分层抽样,每服务至少 3 条
INFO(含异常关键词)约 2000100%2000全量保留,关键词表见下方
INFO(其他)121 万0.05%约 600按服务 + 按小时分层抽样
合计120 万—约 2800—

异常关键词表(用于从 INFO 里挑出可能含异常信号的):

anomaly_keywords:
  - "retry"           # 重试相关
  - "fallback"        # 降级相关
  - "circuit"         # 熔断相关
  - "evict"           # 缓存驱逐
  - "timeout"         # 超时(即使 INFO 级也可能是异常)
  - "slow"            # 慢请求
  - "degrad"          # 降级
  - "recover"         # 恢复(可能意味着之前异常)
  - "reconnect"       # 重连
  - "queue backlog"   # 队列堆积
  - "connection pool" # 连接池
  - "gc "             # GC 相关
  - "oom"             # OOM 前兆
  - "memory"          # 内存相关
  - "race"            # 竞态
  - "stale"           # 过期/陈旧
  - "inconsistent"    # 不一致
  - "duplicate"       # 重复(可能数据错乱)
  - "missing"         # 缺失
  - "unexpected"      # 非预期

决策 2:实时流处理 vs 批处理——选日级批处理

日志异常检测是实时流处理(每条日志进来立刻分析)还是批处理(攒一天一起分析)?选日级批处理,原因:

  • 实时处理成本爆炸:120 万条/天 = 平均 14 条/秒,峰值 40 条/秒。每条都调 GPT-5 是 ¥18 万/月光 API 费用就拦不住,且 GPT-5 推理延迟 3-8 秒,实时处理根本跟不上日志产生速度。
  • 批处理能做时序模式分析:很多隐式异常是时序模式——"缓存驱逐频次从 3 次/天涨到 480 次/天"这种趋势,必须看一天的数据才能发现,单条日志看不出异常。批处理天然支持时序聚合。
  • 批处理能做跨日志关联:竞态条件的信号是"两个请求的日志交织出现",必须把一段时间的日志放一起看才能发现。批处理让 GPT-5 能看到全貌。

执行节奏:每天凌晨 2:00(业务低峰)跑一次批处理,处理前一天 0:00-24:00 的全部采样日志,约 30 分钟内完成分析并输出异常报告。值班人早上 8:30 上班时,异常报告已经在 Slack 里等着了。

schedule:
  cron: "0 2 * * *"           # 每天凌晨 2:00
  timezone: "Asia/Shanghai"
  processing_window: "前一天 0:00-24:00"
  sla: "30 分钟内完成"
  output: "Slack 异常报告 + GitHub Issue(P0/P1)"

决策 3:GPT-5 的上下文管理——分批 + 摘要 + 递进

采样后约 2800 条日志,每条平均 80 token(裁剪后),总共约 22 万 token,单次能塞进 GPT-5 的 200K 上下文。但这样会有两个问题:

  1. 单批太大,GPT-5 容易漏异常:200K 上下文里塞 2800 条日志,GPT-5 的注意力会被高频日志吸引,低频隐式异常容易被忽略。
  2. 无法做时序聚合:单批处理看不出"驱逐频次趋势"这种时序模式。

所以我们做了三段式递进处理:

  • 第 1 段:按服务分批——38 个服务,每个服务的日志单独一批喂给 GPT-5 做异常模式识别。单批约 70 条日志(2800/38),token 约 5600,GPT-5 注意力集中。
  • 第 2 段:跨服务关联——第 1 段识别出的异常,按 traceId 跨服务聚合,看是不是同一调用链上的关联异常。
  • 第 3 段:全局根因推断——把第 1、2 段的异常汇总,让 GPT-5 做全局根因推断,输出 Bug 假设。
# 三段式处理流程(伪代码)
def run_daily_analysis():
    # 第 1 段:按服务分批异常识别
    service_anomalies = {}
    for service in services:  # 38 个服务
        logs = log_mcp.get_sampled_logs(service, window="yesterday")
        anomalies = gpt5.detect_anomalies(logs, service=service)
        service_anomalies[service] = anomalies

    # 第 2 段:跨服务关联聚合
    all_anomalies = flatten(service_anomalies.values())
    correlated = log_mcp.aggregate_by_trace(all_anomalies)
    correlated_clusters = gpt5.cluster_related_anomalies(correlated)

    # 第 3 段:全局根因推断
    bug_hypotheses = gpt5.infer_root_causes(
        anomalies=all_anomalies,
        correlated_clusters=correlated_clusters,
        service_anomalies=service_anomalies
    )

    # 路由通知
    for hypothesis in bug_hypotheses:
        route_and_notify(hypothesis)

决策 4:GPT-5 的调用是同步 vs 异步——选异步

日志分析是同步调 GPT-5 等结果,还是先入队再异步消费?选异步,原因有三:

  • GPT-5 单次推理 3-8 秒,38 个服务如果串行处理要 4 分钟,并行 8 worker 也要 1 分钟。同步处理会让定时任务总耗时不可控。
  • 失败重试更干净:异步队列(Kafka)天然支持重试、死信队列、回放。某个服务这批日志处理失败,不影响其他服务。
  • 便于降级:当 GPT-5 API 限流或故障时,异步队列可以缓存待处理批次,等 API 恢复后继续,不会丢数据。

所以采样后的日志写入 Kafka topic logs.sampled,下游有 3 个 consumer group 分别消费:异常识别 consumer、关联分析 consumer、根因推断 consumer。每个 consumer 独立消费、独立失败、独立重试,互不影响。consumer 配置:

consumer_group: ai-log-detector
concurrency: 8           # 8 个并发 worker
max_poll_records: 16
session_timeout: 120s     # GPT-5 推理可能慢
max_retry: 3
retry_backoff: 30s
dead_letter_topic: logs.detector.dlq

死信队列的处理:连续 3 次处理失败的批次进 DLQ,DLQ 的 consumer 是降级逻辑——不调 GPT-5,直接按简单规则(关键词匹配)做异常识别,结果降级路由到 #log-anomaly-degraded 频道提示"AI 分析降级,请人工复核"。保证即使 GPT-5 挂了,日志审计也不会完全失能。

决策 5:反馈数据存哪——选 PostgreSQL

人工反馈数据(开发者对 GPT-5 Bug 假设的确认/否定)存哪?选 PostgreSQL,原因:

  • 需要按维度查询:"本周所有 false positive 的异常""billing-service 的所有历史 Bug"这种查询,文件做不到。
  • 去重和冲突处理:同一条异常可能被两个开发者反馈,需要 ON CONFLICT 处理。
  • 和训练样本生成联动:周末批处理跑 few-shot 样本抽取,直接查 DB。

表结构:

CREATE TABLE log_anomaly_feedback (
    id BIGSERIAL PRIMARY KEY,
    anomaly_id VARCHAR(128) NOT NULL,
    anomaly_json JSONB NOT NULL,
    ai_pattern_type VARCHAR(32),         -- GPT-5 识别的异常类型
    ai_bug_hypothesis JSONB,             -- GPT-5 的 Bug 假设
    ai_confidence FLOAT,                 -- GPT-5 的置信度
    human_confirmed BOOLEAN,             -- 人工是否确认为真 Bug
    human_false_positive BOOLEAN,        -- 人工是否标为误报
    human_bug_category VARCHAR(32),      -- 人工标注的 Bug 类型
    human_reason TEXT,
    fixed_commit VARCHAR(40),           -- 修复的 commit
    confirmed_at TIMESTAMPTZ,
    confirmed_by VARCHAR(64),
    is_used_for_training BOOLEAN DEFAULT FALSE,
    training_batch_id VARCHAR(64),
    UNIQUE (anomaly_id, confirmed_by)
);

CREATE INDEX idx_feedback_not_used ON log_anomaly_feedback (is_used_for_training)
    WHERE is_used_for_training = FALSE;
CREATE INDEX idx_feedback_service ON log_anomaly_feedback ((anomaly_json->>'service'));

实施步骤

整个流水线分 5 步,第 0 步是数据预处理,第 1-3 步是 AI 分析主流程,第 4 步是人工验证和反馈闭环。下面每一步都给出可复制的 Prompt 模板和操作说明。

第 0 步:日志采集和预处理(ELK query → JSON 标准化 → 采样降量)

这一步的目标是把 ELK 里原始的日志标准化成 GPT-5 能稳定理解的 JSON 结构,并通过采样把 120 万条压到约 2800 条。

ELK 里原始的日志(通过 Filebeat 采集)长这样:

{
  "_index": "filebeat-2026.09.08",
  "_source": {
    "@timestamp": "2026-09-08T14:23:01.234Z",
    "log": {
      "level": "INFO",
      "logger": "com.company.billing.cache.ConfigCache",
      "file": {"path": "/var/log/billing/billing.log"},
      "offset": 48237123
    },
    "service": {"name": "billing-service", "version": "2.14.3"},
    "kubernetes": {
      "pod": "billing-service-7d4f-x9k2",
      "namespace": "prod-billing",
      "node": "node-pool-a-12"
    },
    "message": "cache entry evicted: tenant_config size=482MB, max=512MB",
    "trace_id": "a1b2c3d4e5f6",
    "span_id": "1a2b3c4d",
    "tenant_id": "T_48217",
    "host": "10.0.12.45"
  }
}

这个结构有几个问题:字段嵌套深(log.level / kubernetes.pod)、字段语义不统一(有的服务日志用 levelname 有的用 log.level)、message 是纯文本(GPT-5 要解析"cache entry evicted"这种自然语言)。直接喂给 GPT-5 会导致它把精力花在解析结构上。

所以我们写了一个标准化中间层(一个 250 行的 Python 服务),把 ELK 的日志转成扁平化的标准化 JSON:

{
  "log_id": "log-2026-09-08-142301-billing-x9k2-48217123",
  "timestamp": "2026-09-08T14:23:01.234Z",
  "level": "INFO",
  "service": "billing-service",
  "service_version": "2.14.3",
  "pod": "billing-service-7d4f-x9k2",
  "namespace": "prod-billing",
  "node": "node-pool-a-12",
  "logger": "com.company.billing.cache.ConfigCache",
  "message": "cache entry evicted: tenant_config size=482MB, max=512MB",
  "trace_id": "a1b2c3d4e5f6",
  "span_id": "1a2b3c4d",
  "tenant_id": "T_48217",
  "host": "10.0.12.45",
  "is_sampled": true,
  "sample_reason": "anomaly_keyword:evict"
}

标准化层做的几件事:

  1. 扁平化:把 log.level 提到顶层 level,把 kubernetes.pod 提到顶层 pod
  2. 统一 level:把 INFO/info/Information/I 等不同命名统一成 DEBUG/INFO/WARN/ERROR/FATAL 五档
  3. 生成 log_id:用 时间 + service + pod + offset 生成稳定 ID
  4. 采样打标:根据采样策略给每条日志标 is_sampled 和 sample_reason,方便后续追溯
  5. 异常关键词预筛:对 INFO 日志做关键词匹配,命中的标 has_anomaly_keyword: true 并全量保留

标准化层还做了日志去重和堆栈折叠:完全相同的日志(同 service + 同 message + 1 秒内)只保留一条并标 repeat_count;含堆栈的 ERROR 日志,堆栈折叠成"第一行 + 末尾 3 行 + 总行数",避免一条日志占 8000 token。

采样后的日志写入 Kafka topic logs.sampled,下游 GPT-5 消费者消费。Kafka 配置:

topic: logs.sampled
partitions: 12
replication: 3
retention: 7d
cleanup.policy: delete

这一步不用 Prompt,它是确定性的转换服务。但它产出的 JSON 是后续所有 Prompt 的输入基础,格式稳定后整个流水线的准确率才能稳。

第 1 步:异常模式识别(GPT-5 语义分析 Prompt)

采样日志进 Kafka 后,第一件事是让 GPT-5 对每个服务的日志批次做异常模式识别。目标不是找 ERROR(ERROR 已经有告警了),而是找 INFO/WARN 级别里藏着的隐式异常信号。

这一步是整个流水线的核心,也是最难的。关键挑战是:怎么让 GPT-5 理解"什么样的日志算异常"。我们试过让 GPT-5 "找出异常日志",结果它把所有 WARN 都标成异常,把所有"含 timeout 关键词的 INFO"都标成异常——这些大部分是已知且可恢复的,不是真异常。

最终稳定的 Prompt 设计思路是:给 GPT-5 一套明确的"异常模式"分类标准,让它按标准判断,而不是凭感觉。异常模式分 6 类:

异常模式含义示例日志
trend_shift指标趋势性变化"cache eviction count: today=480, avg=3"
retry_anomaly重试行为异常"retry attempt 5 succeeded"(正常不该重试 5 次)
latency_outlier延迟离群"request completed in 18342ms"(正常 200ms)
state_inconsistency状态不一致"order status: PAID but inventory not deducted"
resource_pressure资源压力信号"connection pool: active=48/50"
unusual_pattern罕见模式该服务从未出现过的日志模式

这一步的 Prompt 模板(精简版,完整版见后文"关键 Prompt 模板"小节):

你是 SRE 团队的日志异常检测助手。给你一个服务一天的采样日志(约 70 条),
请识别其中隐藏的异常信号。

重点:不要只看 ERROR/FATAL(那些已经被告警系统覆盖了)。
你要找的是 INFO/WARN 级别里藏着的、不会触发告警但语义上异常的信号。

异常模式分 6 类:
1. trend_shift:某个指标在日志里呈现趋势性变化
   (比如缓存驱逐频次从 3 次/天涨到 480 次/天)
2. retry_anomaly:重试行为异常
   (比如重试第 5 次才成功,正常不该重试这么多次)
3. latency_outlier:延迟离群
   (某次请求耗时远超正常水平,即使最终成功)
4. state_inconsistency:状态不一致
   (日志显示业务状态机卡住或矛盾)
5. resource_pressure:资源压力信号
   (连接池/线程池/缓存接近上限)
6. unusual_pattern:该服务从未出现过的罕见日志模式

对每条疑似异常日志,输出:
- pattern_type:上述 6 类之一
- log_id:日志 ID
- anomaly_signal:一句话说明为什么异常
- severity:high / medium / low
- confidence:0.0-1.0

日志批次:
{{logs_json}}

这个 Prompt 之所以稳定,关键在三点:

  1. 明确告诉 GPT-5 "不要只看 ERROR":否则它会偷懒只标 ERROR,那和告警系统没区别。
  2. 给 6 类异常模式的明确分类:让 GPT-5 按标准判断,而不是凭感觉。这把异常识别准确率从 62% 拉到 91%。
  3. 每类给具体示例:示例锚定了 GPT-5 的判断尺度,避免它把"重试 1 次"也标成异常。

实际产出的一个异常识别示例(输入是 billing-service 的采样日志):

{
  "anomalies": [
    {
      "pattern_type": "trend_shift",
      "log_id": "log-2026-09-08-142301-billing-x9k2-48217123",
      "anomaly_signal": "缓存驱逐频次从历史均值 3 次/天升至今天的 480 次/天,且驱逐的 cache size 持续增长(从 280MB 涨到 482MB),疑似内存泄漏或缓存策略失效",
      "severity": "high",
      "confidence": 0.87
    },
    {
      "pattern_type": "retry_anomaly",
      "log_id": "log-2026-09-08-091522-billing-x9k2-48217110",
      "anomaly_signal": "billing 调用 payment-service 的请求重试第 5 次才成功,正常重试上限是 3 次,疑似下游服务间歇性不可用",
      "severity": "medium",
      "confidence": 0.74
    }
  ],
  "no_anomaly_logs_count": 68,
  "scan_summary": "扫描 70 条日志,识别 2 条疑似异常,其余 68 条正常"
}

这个异常识别结果会带着原日志一起传给第 2 步做跨服务关联。

第 2 步:日志聚合和关联分析(日志分析 MCP 按 traceId/service 聚合)

第 1 步识别出的异常是单服务视角的,但很多 Bug 的信号分散在多个服务的日志里——比如竞态条件的信号是"两个服务的日志交织出现",内存泄漏的信号是"缓存服务驱逐 + 调用方服务重试"。这一步的目标是用日志分析 MCP 把分散的异常按 traceId 和调用链聚合起来,还原跨服务的关联。

为什么不让 GPT-5 直接调 ELK API 做聚合:我们试过让 GPT-5 直接写 Elasticsearch DSL 做 traceId 聚合,结果它写的 query 经常语法错误(trace_id 字段在不同索引里命名不一致、时间范围用相对时间而非绝对 ISO 时间),而且单次返回数据量大、token 消耗惊人(一条调用链的完整日志可能 1.2 万 token,一次拉 20 条链路就 24 万 token,直接打爆上下文)。封装成 MCP 后,MCP 在内部做了调用链裁剪(只保留每个 span 的关键日志)、traceId 补全(跨服务 traceId 断链时做模糊匹配),GPT-5 只需要调 aggregate_by_trace(trace_ids) 就行。

日志分析 MCP 提供三个工具:

工具入参出参用途
aggregate_by_tracetrace_id 列表按调用链聚合的日志(每 span 关键日志)还原跨服务调用链,找关联异常
get_related_service_logsservice, time_window上下游服务日志拉上下游服务同时间窗日志,找关联
cluster_similar_anomaliesanomaly 列表按语义相似度聚类的异常组把 30 条异常聚成 3-5 组,避免 GPT-5 处理重复

这一步的 Prompt 模板(精简版):

你已完成单服务异常识别,现在需要做跨服务关联分析。
已有信息:

【各服务异常列表】
{{service_anomalies}}

请按以下顺序操作:

1. 从异常列表里提取所有 trace_id,调用日志分析 MCP 的 aggregate_by_trace,
   参数:trace_ids = {{提取的 trace_id 列表}}

2. 对没有 trace_id 但疑似跨服务的异常,调用 get_related_service_logs,
   参数:service = {{异常所在服务}}, time_window = {{异常时间前后 5 分钟}}

3. 调用 cluster_similar_anomalies,把所有异常按语义相似度聚类,
   找出可能是同一根因的异常组

4. 输出关联分析结果:
   - 每个异常组:包含哪些异常、它们如何关联、共同根因假设
   - 孤立异常:无法关联到其他的异常,单独标注

实际产出的一个关联分析示例(输入是 billing-service 的缓存驱逐异常 + payment-service 的重试异常):

{
  "correlated_clusters": [
    {
      "cluster_id": "cluster-1",
      "anomalies": [
        "billing-service: cache eviction trend_shift (480次/天)",
        "payment-service: retry_anomaly (重试第5次成功)",
        "gateway: latency_outlier (billing 请求 18秒)"
      ],
      "correlation": "billing-service 缓存驱逐频次飙升 → 缓存命中率下跌 → payment-service 调用 billing 时缓存未命中导致慢 → gateway 观察到 billing 请求延迟离群。三者通过 trace_id a1b2c3d4e5f6 关联,是同一调用链上的连锁异常",
      "common_root_hypothesis": "billing-service 缓存策略异常导致连锁慢请求",
      "confidence": 0.81
    }
  ],
  "isolated_anomalies": [
    "user-service: unusual_pattern (新出现的鉴权日志模式,无关联)"
  ]
}

这个关联结果会传给第 3 步做 Bug 根因推断。

第 3 步:Bug 根因推断(GPT-5 综合分析 → 输出 Bug 假设、证据链、置信度)

第 2 步的关联分析把异常聚成了"可能属于同一根因"的组,但还没说根因到底是什么。这一步让 GPT-5 做全局根因推断,输出具体的 Bug 假设——不是"billing-service 缓存异常"这种笼统的话,而是"billing-service 的 ConfigCache 有内存泄漏,某个 key 永远不被淘汰,导致缓存驱逐频次持续上涨,最终 OOM"这种可执行的根因。

这一步的 Prompt 模板(精简版):

你是 SRE 团队的 Bug 根因推断助手。基于异常识别 + 跨服务关联分析,
输出具体的 Bug 假设。

已有信息:
【各服务异常列表】
{{service_anomalies}}

【跨服务关联分析】
{{correlated_clusters}}

请输出 1-3 个 Bug 假设,按可能性排序。每个假设包含:
- bug_hypothesis:一句话描述 Bug(要具体到代码层,比如"X 服务的 Y 模块有 Z 问题")
- evidence_chain:证据链,每条引用具体日志行
- affected_services:受影响的服务
- expected_symptom:如果这个 Bug 成立,应该还能观察到什么现象
- suggested_investigation:建议开发者怎么验证(查哪段代码、看哪个指标)
- confidence:0.0-1.0

如果证据不足,输出 insufficient_evidence 并说明缺什么数据。

实际产出的一个根因推断示例:

{
  "bug_hypotheses": [
    {
      "rank": 1,
      "bug_hypothesis": "billing-service 的 ConfigCache 存在内存泄漏:某个 tenant_config 的 key 永远不被 LRU 淘汰,导致缓存大小持续增长、驱逐频次飙升,最终 OOM",
      "evidence_chain": [
        "billing-service 日志:cache entry evicted: tenant_config size=482MB, max=512MB(8月11日起出现,频次从3次/天涨到480次/天)",
        "billing-service 日志:缓存驱逐的总是非 tenant_config 的 key(驱逐日志显示 evicted key=feature_flag_xxx,而非 tenant_config)",
        "billing-service 日志:tenant_config 的 size 持续增长(280MB→482MB,8天内)",
        "关联:gateway 观察到 billing 请求延迟离群(18秒),疑似缓存满导致查询阻塞"
      ],
      "affected_services": ["billing-service", "gateway"],
      "expected_symptom": "如果假设成立,billing-service 的堆内存使用应持续上涨、GC 频次升高、tenant_config 缓存的 key 数量持续增长",
      "suggested_investigation": [
        "1. 查 ConfigCache 的 LRU 实现代码,看 tenant_config 的 key 是否被特殊保留(比如被外部引用导致 LRU 无法淘汰)",
        "2. 看 billing-service 的 JVM 堆内存监控,确认是否持续上涨",
        "3. dump 堆内存,看 tenant_config 缓存的 key 数量"
      ],
      "confidence": 0.84
    }
  ],
  "data_gaps": [
    "未拿到 ConfigCache 的源码,无法确认 LRU 实现是否有 bug"
  ]
}

这个根因推断结果会作为 Slack 通知的正文和 GitHub Issue 的内容(第 4 步)。

第 4 步:人工验证和修复(开发者确认 → 创建 Issue → 修复 → 回归验证)

根因推断完成后,需要让开发者验证 GPT-5 的假设是不是对的。这一步的核心是:GPT-5 的输出是"假设"不是"结论",必须人工验证后才能动手改代码。

路由规则表(根据严重度和置信度差异化路由):

严重度 + 置信度GitHub IssueSlack 频道@ 人人工兜底
high severity + confidence > 0.8自动建 Issue(label: bug/P0)#log-anomaly-high@值班 + @服务负责人否
high severity + confidence < 0.8建 Issue 标 needs-review#log-anomaly-review@值班是
medium severity建 Issue(label: bug/P1)#log-anomaly-medium@服务负责人否
low severity不建 Issue,只记日志#log-anomaly-low不 @人否
任何 + confidence < 0.6建 Issue 标 needs-review#log-anomaly-review@值班是

这一步的 Prompt 模板(精简版,负责路由决策):

你已完成 Bug 根因推断,现在需要做通知路由和 Issue 创建。

已有信息:
【Bug 假设】{{bug_hypotheses}}

请按以下规则操作:
1. 判断严重度(high/medium/low)和置信度
2. 根据路由规则表决定 Slack 频道和 @人
3. 调用 GitHub MCP 的 create_issue
4. 调用 Slack MCP 的 send_message

Issue body 模板:
## AI 识别的异常
{{anomalies 渲染}}

## Bug 假设
{{hypotheses 渲染}}

## 建议验证步骤
{{suggested_investigation}}

## 相关日志
{{log_ids}}

开发者验证流程:

  1. 服务负责人收到 Slack 通知,看到 Bug 假设 + 证据链 + 建议验证步骤
  2. 开发者按 suggested_investigation 查代码、看指标,确认或否定假设
  3. 如果确认:开发者在 Issue 里回复"假设确认,根因是 XXX",开始修复
  4. 如果否定:开发者回复"假设不成立,实际是 YYY",这个否定会回流到反馈表,用于训练 GPT-5
  5. 修复后:开发者在 Issue 里贴 commit hash,标记 resolved
  6. 回归验证:修复上线后,下一次日批处理会重新扫该服务的日志,确认异常信号消失

反馈闭环的实际效果:

  • 第 1 周:异常识别准确率 62%,误报率 18%
  • 第 2 周(加入 180 条反馈样本后):准确率 78%,误报率 11%
  • 第 4 周(累计 720 条反馈样本):准确率 86%,误报率 6%
  • 第 8 周(累计 1640 条反馈样本):准确率 91%,误报率 4%

这是整个流水线最关键的设计:没有反馈闭环,GPT-5 的判断准确率会停滞在 62% 左右(zero-shot 水平),永远到不了 91%。反馈闭环让流水线成了能持续学习的系统。

端到端完整案例走查

为了把整个流水线串起来,这里走查一次真实的日批处理过程。这是 2026 年 9 月 8 日凌晨 2:00 触发的一次批处理,处理 9 月 7 日 0:00-24:00 的全部采样日志,最终在 2:27 输出异常报告,耗时 27 分钟,发现了 3 个隐藏 Bug。

起点:采样层拉取日志(2:00-2:03,耗时 3 分钟)

凌晨 2:00,定时任务触发。标准化层从 ELK 拉取 9 月 7 日的全量日志,做采样:

拉取时间窗:2026-09-07T00:00:00Z ~ 2026-09-07T23:59:59Z
总日志条数:1,184,327 条
采样后条数:2,743 条
采样率:0.23%
按服务分布:
  billing-service: 78 条(含 12 条 anomaly_keyword 命中)
  payment-service: 65 条(含 8 条 anomaly_keyword)
  order-service: 82 条(含 15 条 anomaly_keyword)
  ...(共 38 个服务)

采样后的日志写入 Kafka topic logs.sampled。

第 1 步:按服务分批异常识别(2:03-2:14,耗时 11 分钟)

38 个服务的日志分 38 批喂给 GPT-5。每批约 70 条日志,token 约 5600,GPT-5 单次推理 3-5 秒。38 批并行 8 个 worker,总耗时 11 分钟。

以 billing-service 这一批为例,GPT-5 的推理过程(从日志还原):

扫描 78 条日志:
- 12 条含 anomaly_keyword(evict × 8, retry × 3, slow × 1)
- 66 条正常 INFO

分析 evict 日志:
- 8 条都是 "cache entry evicted: tenant_config size=XXX, max=512MB"
- size 值序列:280MB, 312MB, 358MB, 401MB, 442MB, 467MB, 481MB, 482MB
- 趋势:8 天内从 280MB 涨到 482MB,接近 512MB 上限
- 历史基线:该服务缓存驱逐频次正常 3 次/天,今天 480 次/天
- 判定:trend_shift,severity=high,confidence=0.87

分析 retry 日志:
- 3 条 "retry attempt N succeeded",N=3,4,5
- 重试第 5 次才成功,正常重试上限 3 次
- 判定:retry_anomaly,severity=medium,confidence=0.74

输出 2 条异常,76 条正常

38 个服务扫描完,共识别出 11 条疑似异常(分布在 7 个服务)。

第 2 步:跨服务关联分析(2:14-2:19,耗时 5 分钟)

GPT-5 调用日志分析 MCP 把 11 条异常按 traceId 聚合:

调用 1:aggregate_by_trace(耗时 1.8 秒)

参数:trace_ids = [a1b2c3d4e5f6, b2c3d4e5f6a1, ...](从 11 条异常里提取)

MCP 返回 3 条调用链的聚合日志,GPT-5 看到其中一条链路 a1b2c3d4e5f6 串联了 billing + payment + gateway 三个服务的异常。

调用 2:cluster_similar_anomalies(耗时 1.2 秒)

参数:anomalies = 11 条异常

MCP 返回聚类结果:11 条异常聚成 4 组,其中 1 组含 3 条异常(billing 缓存驱逐 + payment 重试 + gateway 延迟离群),其余 3 组各 1-2 条。

GPT-5 综合输出:

{
  "correlated_clusters": [
    {
      "cluster_id": "c1",
      "anomalies": ["billing: cache eviction trend", "payment: retry anomaly", "gateway: latency outlier"],
      "correlation": "同一 trace_id a1b2c3d4e5f6 串联,billing 缓存慢 → payment 重试 → gateway 延迟",
      "confidence": 0.81
    },
    {
      "cluster_id": "c2",
      "anomalies": ["order: state_inconsistency"],
      "correlation": "孤立异常,无关联",
      "confidence": 0.42
    }
  ]
}

第 3 步:Bug 根因推断(2:19-2:24,耗时 5 分钟)

GPT-5 基于关联分析输出 3 个 Bug 假设:

{
  "bug_hypotheses": [
    {
      "rank": 1,
      "bug_hypothesis": "billing-service ConfigCache 内存泄漏:tenant_config 的 key 永远不被 LRU 淘汰",
      "confidence": 0.84,
      "severity": "high"
    },
    {
      "rank": 2,
      "bug_hypothesis": "order-service 状态机竞态条件:并发更新订单状态时未加锁,导致 PAID 但未扣库存",
      "confidence": 0.71,
      "severity": "high"
    },
    {
      "rank": 3,
      "bug_hypothesis": "payment-service 重试逻辑错误:对 5xx 重试但对 4xx 也重试,导致下游压力倍增",
      "confidence": 0.68,
      "severity": "medium"
    }
  ]
}

第 4 步:通知路由(2:24-2:27,耗时 3 分钟)

GPT-5 按路由规则发 Slack + 建 Issue:

  • 假设 1(high, conf=0.84):建 Issue #2847,发 #log-anomaly-high,@值班 + @billing-owner
  • 假设 2(high, conf=0.71):建 Issue #2848 标 needs-review,发 #log-anomaly-review,@值班
  • 假设 3(medium, conf=0.68):建 Issue #2849 标 needs-review,发 #log-anomaly-review,@值班

值班人早上 8:30 上班,看到 3 条异常报告。假设 1 高置信度直接验证,假设 2、3 走人工兜底。

三个假设后续验证结果:假设 1 确认(内存泄漏),假设 2 确认(竞态条件),假设 3 部分确认(重试逻辑有问题但描述不完全准确)。准确率 2.5/3 = 83%(这次批处理的准确率,8 周平均是 91%)。

关键 Prompt 模板

这一节给出三个核心 Prompt 的完整可复制版本。这三个 Prompt 直接决定了异常检测的准确率。

Prompt 模板 1:异常模式识别

你是 SRE 团队的日志异常检测助手。你的任务是扫描一个服务一天的采样日志,
识别其中隐藏的异常信号。

## 重要原则
1. 不要只看 ERROR/FATAL——那些已经被告警系统覆盖了。你的核心价值是发现
   INFO/WARN 级别里藏着的、不会触发告警但语义上异常的信号。
2. 不要把所有 WARN 都标异常——大部分 WARN 是可恢复的瞬时抖动,只有
   表现出趋势性、频次异常、或语义矛盾的才是真异常。
3. 不要凭感觉——必须按下方 6 类异常模式的标准判断,每条异常要说明
   命中了哪类模式、为什么。

## 异常模式分类标准

### 1. trend_shift(趋势性变化)
某个指标在日志里呈现趋势性变化(相比历史基线)。
判定信号:
- 某个数值(计数、大小、频次)在一天内持续上涨或下跌
- 相比历史均值变化超过 3 倍
- 即使最终值仍在"正常范围",但趋势本身异常
示例:
- "cache eviction count: today=480, avg=3"(驱逐频次涨 160 倍)
- "connection pool active: 8:00=12, 12:00=38, 18:00=45(max=50)"(持续上涨接近上限)

### 2. retry_anomaly(重试行为异常)
重试行为不符合预期。
判定信号:
- 重试次数超过正常上限(默认 3 次,服务可自定义)
- 重试成功但重试次数异常高(说明下游间歇性不可用)
- 不该重试的错误码被重试(比如对 4xx 重试)
示例:
- "retry attempt 5 succeeded"(正常上限 3 次)
- "retrying 404 Not Found, attempt 2"(4xx 不该重试)

### 3. latency_outlier(延迟离群)
某次请求耗时远超正常水平,即使最终成功。
判定信号:
- 单次请求耗时 > 服务 P99 的 3 倍
- 批量请求中有个别耗时离群
示例:
- "request completed in 18342ms"(正常 P99=200ms)
- "batch processed: 9999 succeeded in 50ms, 1 succeeded in 8s"

### 4. state_inconsistency(状态不一致)
日志显示业务状态机卡住或矛盾。
判定信号:
- 状态转换不符合预期(比如 PAID 后未扣库存)
- 同一实体的状态在不同服务间不一致
- 状态机卡在某个中间态超时
示例:
- "order status: PAID but inventory not deducted"
- "task stuck in PENDING for 4 hours"

### 5. resource_pressure(资源压力信号)
资源接近上限,即使还没报错。
判定信号:
- 连接池 active > 80% of max
- 线程池 queue size > 50% of max
- 缓存 hit rate 跌破历史 P5
- 队列 backlog 持续上涨
示例:
- "connection pool: active=48/50"
- "cache hit rate: 41% (baseline 98%)"

### 6. unusual_pattern(罕见模式)
该服务从未出现过的日志模式(需要和该服务历史日志对比)。
判定信号:
- 日志 message 是该服务历史上从未出现过的内容
- 日志的触发频次远超历史(比如某日志历史上 1 次/周,今天 20 次)
- 日志的出现时间异常(比如只在凌晨出现的日志白天出现了)
示例:
- 该服务首次出现 "deadlock detected" 日志
- 某鉴权日志历史上 0 次,今天出现 12 次

## 输入
你会收到一个 JSON 数组,每个元素是一条采样日志,字段:
log_id, timestamp, level, service, pod, logger, message, trace_id, sample_reason

## 输出格式
严格输出以下 JSON,不要输出任何其他内容:

{
  "anomalies": [
    {
      "pattern_type": "trend_shift|retry_anomaly|latency_outlier|state_inconsistency|resource_pressure|unusual_pattern",
      "log_id": "...",
      "anomaly_signal": "一句话说明为什么异常(必须引用具体数值或日志内容)",
      "severity": "high|medium|low",
      "confidence": 0.0,
      "related_metrics": ["如果能看到趋势,列出关键数值序列"]
    }
  ],
  "scan_summary": {
    "total_logs": N,
    "anomaly_count": M,
    "no_anomaly_logs": N-M
  }
}

severity 判定标准:
- high:疑似会导致服务故障(内存泄漏、数据错乱、死锁)
- medium:影响性能或可靠性,但未到故障(重试异常、延迟离群)
- low:值得关注但暂无影响(资源水位偏高、罕见模式)

## 日志批次
{{logs_json}}

Prompt 模板 2:日志聚合和关联分析

你是 SRE 团队的日志关联分析助手。你的任务是把各服务识别出的异常按
traceId 和调用链聚合,找出属于同一根因的异常组。

## 输入
1. 各服务异常列表(JSON 数组,每条含 pattern_type/log_id/service/message/trace_id/timestamp)
2. 你需要主动调用日志分析 MCP 获取关联数据

## 操作步骤

### 步骤 1:按 trace_id 聚合调用链
从异常列表里提取所有 trace_id(去重),调用日志分析 MCP 的 aggregate_by_trace:
- 参数:trace_ids = {{提取的 trace_id 列表}}
- MCP 返回每条调用链的跨服务日志聚合(每 span 关键日志)

拿到调用链后,分析:
- 哪些异常在同一条调用链上(说明是连锁异常)
- 调用链上的异常传播方向(上游 → 下游)

### 步骤 2:对无 trace_id 的异常做时间窗关联
有些异常没有 trace_id(比如缓存驱逐日志通常不带 trace_id),按时间窗关联:
调用日志分析 MCP 的 get_related_service_logs:
- 参数:service = {{异常所在服务}}, time_window = {{异常时间前后 5 分钟}}
- 看同时间窗内其他服务有没有相关异常

### 步骤 3:异常聚类
调用日志分析 MCP 的 cluster_similar_anomalies:
- 参数:anomalies = {{所有异常列表}}
- MCP 按语义相似度聚类,返回异常组

### 步骤 4:输出关联分析结果
对每个异常组输出:
- cluster_id
- 包含的异常列表
- correlation:它们如何关联(同 trace_id / 时间窗 / 语义相似)
- common_root_hypothesis:共同根因假设(一句话)
- confidence

无法关联到任何组的异常,标为 isolated_anomalies。

## 输出格式
严格输出以下 JSON:

{
  "correlated_clusters": [
    {
      "cluster_id": "c1",
      "anomalies": ["log_id1", "log_id2"],
      "correlation": "...",
      "common_root_hypothesis": "...",
      "confidence": 0.0
    }
  ],
  "isolated_anomalies": ["log_id3"]
}

## 各服务异常列表
{{service_anomalies_json}}

Prompt 模板 3:Bug 根因推断

你是 SRE 团队的 Bug 根因推断助手。你的任务是基于异常识别 + 关联分析,
输出具体的 Bug 假设。假设必须具体到代码层,不能笼统说"服务异常"。

## 输入
1. 各服务异常列表(含 pattern_type/anomaly_signal/log_id)
2. 跨服务关联分析(correlated_clusters + isolated_anomalies)

## 任务

### 步骤 1:对每个异常组(或孤立的 high severity 异常)推断根因
基于异常的 pattern_type 和 anomaly_signal,推断最可能的 Bug。
推断时要结合:
- 异常模式(trend_shift 多半是资源泄漏或配置问题;retry_anomaly 多半是
  下游间歇性故障或重试逻辑错误;state_inconsistency 多半是并发/竞态)
- 日志内容(具体数值、错误信息)
- 跨服务关联(哪些服务受影响、传播方向)

### 步骤 2:为每个假设构建证据链
每个假设必须有 3-5 条证据,每条证据引用具体日志行(log_id + message 片段)。
证据要形成逻辑链:现象 → 中间推断 → 根因。

### 步骤 3:给出验证建议
告诉开发者怎么验证这个假设:
- 查哪段代码(具体到模块/函数名,如果能从 logger 名推断)
- 看哪个监控指标(JVM 堆、连接池、缓存命中率)
- 做什么操作能复现或排除

### 步骤 4:标注数据缺口
如果证据不足以确信,说明缺什么数据(比如"需要源码确认""需要某指标的
历史曲线"),这些缺口会引导值班人补数据。

## 输出格式
严格输出以下 JSON:

{
  "bug_hypotheses": [
    {
      "rank": 1,
      "bug_hypothesis": "一句话描述 Bug(具体到 X 服务的 Y 模块有 Z 问题)",
      "pattern_type": "trend_shift|...",
      "evidence_chain": [
        "证据1(引用 log_id + 日志内容)",
        "证据2",
        "证据3"
      ],
      "affected_services": ["service1", "service2"],
      "expected_symptom": "如果假设成立,应该还能观察到什么",
      "suggested_investigation": [
        "1. 查 XXX 代码",
        "2. 看 XXX 指标",
        "3. 做 XXX 操作"
      ],
      "confidence": 0.0,
      "severity": "high|medium|low"
    }
  ],
  "data_gaps": ["缺什么数据1", "缺什么数据2"]
}

confidence 标准:
- 0.8-1.0:证据链完整、模式清晰、有历史类似案例
- 0.6-0.79:证据较充分但有一两个缺口
- 0.4-0.59:有方向但证据不足,需人工补数据
- < 0.4:弱假设,仅作参考

severity 标准:
- high:疑似会导致服务故障(OOM、数据错乱、死锁、安全漏洞)
- medium:影响性能或可靠性,但未到故障
- low:值得关注但暂无影响

## 各服务异常列表
{{service_anomalies_json}}

## 跨服务关联分析
{{correlation_result_json}}

效果数据对比

流水线上线后跑了 8 周(2026 年 7 月 14 日 - 9 月 8 日),和基线期(6 月整月 + 7 月前两周的人工审计)做对比。数据来自 ELK、值班系统、GitHub Issue、Slack 消息记录的全量统计。

核心指标对比

指标基线期(人工审计)AI 期(自动化)变化
日均日志条数1,217,400 条1,224,800 条+0.6%(日志量本身在涨)
审计覆盖率(真正读过的日志比例)约 5%(脚本过滤+抽样)100%(采样后全扫)+95pp
单次全量审计耗时5 个工作日(2 人)27 分钟(凌晨自动跑)-99.9%
审计频率1 次/周1 次/天7x
隐式异常检出率0%(人眼看不过来)91.3%—
Bug 平均潜伏期4.2 天0.5 天-88.1%
异常识别准确率N/A(人工识别)91.3%—
误报率N/A4.1%—
根因推断 Top-1 准确率N/A67.8%—
根因推断 Top-3 准确率N/A88.4%—
SRE 日均审计工时8 小时(2 人×4 小时)0.5 小时(看报告)-93.8%
自动建 Issue 数0(全人工)日均 2.3 个—
人工兜底触发率N/A12.4%(confidence < 0.7)—

异常识别准确率细分

按异常模式拆开看准确率(人工抽检 320 条标注真值):

异常模式样本数识别准确率误报率备注
trend_shift6895.6%2.9%趋势明显,GPT-5 最擅长
retry_anomaly5492.6%5.6%个别正常重试被误标
latency_outlier4789.4%6.4%偶发延迟和持续延迟难分
state_inconsistency3987.2%7.7%业务语义复杂,需业务知识
resource_pressure5190.2%5.9%阈值判断较明确
unusual_pattern6188.5%8.2%"从未出现"判断依赖历史数据完整性

规律:trend_shift 准确率最高(数值趋势明显),unusual_pattern 和 state_inconsistency 准确率最低(前者依赖历史数据完整性,后者需要业务背景知识)。

准确率随反馈样本积累的变化

周次异常识别准确率误报率根因 Top-1累计反馈样本
第 1 周62.4%18.2%41.3%0
第 2 周78.1%11.4%53.7%180
第 4 周86.2%6.1%61.2%720
第 6 周89.1%4.8%65.4%1180
第 8 周91.3%4.1%67.8%1640

关键观察:准确率随反馈样本积累持续提升,但提升速度在递减(第 1-2 周涨 15.7pp,第 7-8 周涨 2.2pp)。说明 few-shot 样本到 1500 条左右进入收益递减区,后续要继续提升得靠模型升级或更结构化的推理。

SRE 主观满意度

对 6 个 SRE 做了问卷调查(1-5 分):

维度基线期评分AI 期评分
审计疲劳度(1=极度疲劳,5=轻松)1.24.3
隐式异常发现信心(1=经常漏,5=从不漏)1.04.1
根因定位效率(1=慢,5=快)1.63.9
误报干扰程度(1=严重干扰,5=无干扰)N/A3.7
整体工作满意度2.14.4

最显著的提升在"隐式异常发现信心"——基线期人工审计根本发现不了 INFO 级隐式异常(评分 1.0),AI 流水线让这件事从"不可能"变成"每天自动做"。

3 个发现的 Bug 详情

这里详细复盘流水线发现的 3 个隐藏 Bug,每个含日志片段、AI 分析过程、人工确认结果。

Bug 1:billing-service 内存泄漏(ConfigCache 的 key 永不淘汰)

日志片段(INFO 级,无告警):

[2026-09-07 08:14:32] INFO  com.company.billing.cache.ConfigCache - 
  cache entry evicted: key=feature_flag_promo_2026, 
  tenant_config size=482MB, max=512MB, eviction_count_today=478
[2026-09-07 08:14:35] INFO  com.company.billing.cache.ConfigCache - 
  cache entry evicted: key=feature_flag_search_v2, 
  tenant_config size=482MB, max=512MB, eviction_count_today=479
[2026-09-07 08:14:38] INFO  com.company.billing.cache.ConfigCache - 
  cache entry evicted: key=feature_flag_xxx, 
  tenant_config size=482MB, max=512MB, eviction_count_today=480

AI 分析过程:

  1. 第 1 步异常识别:GPT-5 识别为 trend_shift,证据是 eviction_count_today 从历史均值 3 次/天涨到 480 次/天,且 tenant_config size 从 8 月 11 日的 280MB 涨到 482MB,接近 512MB 上限。confidence=0.87。

  2. 第 2 步关联分析:GPT-5 调用 aggregate_by_trace,发现同一时间段 payment-service 有 retry_anomaly(重试 billing 调用)、gateway 有 latency_outlier(billing 请求 18 秒)。三者通过 trace_id 串联,判定是连锁异常。

  3. 第 3 步根因推断:GPT-5 输出假设——"ConfigCache 的 tenant_config key 永远不被 LRU 淘汰,导致非 tenant_config 的 key 被反复驱逐,缓存大小持续增长,最终 OOM"。证据是驱逐的总是 feature_flag_* 而非 tenant_config_*。confidence=0.84。

人工确认结果:

billing-service 负责人按 suggested_investigation 查 ConfigCache 源码,发现:

// ConfigCache.java(问题代码)
public class ConfigCache {
    private static final Cache<String, Config> CACHE = Caffeine.newBuilder()
        .maximumSize(10000)
        .build();
    
    // Bug:tenant_config 的 key 被外部 static 引用持有,
    // 导致 Caffeine 的弱引用 key 无法被 GC,永远不淘汰
    private static final Map<String, Config> TENANT_CONFIG_REF = new HashMap<>();
    
    public Config get(String key) {
        Config config = CACHE.get(key, k -> loadFromDb(k));
        if (key.startsWith("tenant_config_")) {
            TENANT_CONFIG_REF.put(key, config);  // ← 这里持有强引用
        }
        return config;
    }
}

根因确认:TENANT_CONFIG_REF 这个 static Map 持有了所有 tenant_config 的强引用,导致 Caffeine 的弱引用 key 无法被 GC 回收,tenant_config 永远不被淘汰,缓存大小持续增长。

修复:删除 TENANT_CONFIG_REF(它是个调试时遗留的代码,本不该存在),改用 Caffeine 的 recordStats() 做监控。修复 commit:a1b2c3d。

回归验证:修复上线后,下一次日批处理确认 eviction_count 回落到 3 次/天,tenant_config size 稳定在 280MB。Bug 消除。

Bug 2:order-service 竞态条件(并发更新订单状态未加锁)

日志片段(INFO 级,无告警):

[2026-09-07 14:23:01.234] INFO  OrderService - 
  order status update: order_id=ORD_48217, 
  from=CREATED, to=PAID, thread=pool-1-thread-7
[2026-09-07 14:23:01.235] INFO  OrderService - 
  order status update: order_id=ORD_48217, 
  from=CREATED, to=PAID, thread=pool-3-thread-2
[2026-09-07 14:23:01.237] INFO  InventoryService - 
  inventory deducted: order_id=ORD_48217, 
  amount=1, thread=pool-1-thread-7
[2026-09-07 14:23:01.238] INFO  InventoryService - 
  inventory deducted: order_id=ORD_48217, 
  amount=1, thread=pool-3-thread-2

AI 分析过程:

  1. 第 1 步异常识别:GPT-5 识别为 state_inconsistency,证据是同一 order_id 在 1 毫秒内被两个线程从 CREATED 更新到 PAID,且库存被扣了 2 次(每次扣 1,共扣 2,但订单只应扣 1)。confidence=0.71(这个模式需要理解业务语义)。

  2. 第 2 步关联分析:这条异常没有跨服务关联(就是 order-service 内部的竞态),但 GPT-5 调用 get_related_service_logs 发现 InventoryService 同时间窗有"库存扣减"日志,确认是 order 的并发更新导致 inventory 被重复调用。

  3. 第 3 步根因推断:GPT-5 输出假设——"OrderService 的状态更新没有对 order_id 加锁,支付回调并发到达时两个线程同时把 CREATED→PAID,各自触发库存扣减,导致库存被扣 2 次"。confidence=0.71。

人工确认结果:

order-service 负责人查源码:

// OrderService.java(问题代码)
public void handlePaymentCallback(String orderId) {
    Order order = orderRepo.findById(orderId);
    if (order.getStatus() == OrderStatus.CREATED) {
        order.setStatus(OrderStatus.PAID);
        orderRepo.save(order);              // ← 无锁,并发时会重复执行
        inventoryService.deduct(orderId, 1); // ← 被调用 2 次
    }
}

根因确认:handlePaymentCallback 没有对 orderId 加分布式锁,支付平台重试回调时两个请求并发到达,都判断 status==CREATED,都执行状态更新和库存扣减。

修复:加 Redis 分布式锁:

public void handlePaymentCallback(String orderId) {
    String lockKey = "order:callback:" + orderId;
    if (redisLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) {
        try {
            Order order = orderRepo.findById(orderId);
            if (order.getStatus() == OrderStatus.CREATED) {
                order.setStatus(OrderStatus.PAID);
                orderRepo.save(order);
                inventoryService.deduct(orderId, 1);
            }
        } finally {
            redisLock.unlock(lockKey);
        }
    }
}

修复 commit:e5f6g7h。同时修复了已重复扣库存的 17 个订单(人工补偿)。

回归验证:修复上线后,并发回调场景下库存只扣 1 次。Bug 消除。

Bug 3:payment-service 错误的重试逻辑(对 4xx 也重试)

日志片段(INFO 级,无告警):

[2026-09-07 09:15:22.100] INFO  PaymentClient - 
  call billing-service failed, http_status=429, 
  retrying (attempt 2/5)
[2026-09-07 09:15:23.345] INFO  PaymentClient - 
  call billing-service failed, http_status=429, 
  retrying (attempt 3/5)
[2026-09-07 09:15:24.612] INFO  PaymentClient - 
  call billing-service failed, http_status=429, 
  retrying (attempt 4/5)
[2026-09-07 09:15:25.890] INFO  PaymentClient - 
  call billing-service failed, http_status=429, 
  retrying (attempt 5/5)
[2026-09-07 09:15:27.123] INFO  PaymentClient - 
  call billing-service failed, http_status=429, 
  retries exhausted, giving up

AI 分析过程:

  1. 第 1 步异常识别:GPT-5 识别为 retry_anomaly,证据是对 HTTP 429(Too Many Requests)重试了 5 次,而 429 是限流信号,重试只会加剧限流。confidence=0.74。

  2. 第 2 步关联分析:GPT-5 关联到 billing-service 同时间有 resource_pressure(连接池 active=48/50),说明 billing 被打满。payment 的重试是雪上加霜。

  3. 第 3 步根因推断:GPT-5 输出假设——"PaymentClient 的重试逻辑对所有非 2xx 状态码都重试,包括 4xx(429/400/404),4xx 是客户端错误不该重试,对 429 重试会导致限流恶化"。confidence=0.68(部分确认——GPT-5 描述的方向对,但具体机制不完全准确)。

人工确认结果:

payment-service 负责人查源码:

// PaymentClient.java(问题代码)
public Response callBilling(Request req) {
    for (int i = 0; i < MAX_RETRY; i++) {
        Response resp = httpClient.execute(req);
        if (resp.getStatus() >= 200 && resp.getStatus() < 300) {
            return resp;
        }
        // Bug:所有非 2xx 都重试,包括 4xx
        log.info("call billing-service failed, http_status={}, retrying", resp.getStatus());
    }
    throw new RuntimeException("retries exhausted");
}

根因确认:重试逻辑只判断"非 2xx 就重试",没有区分 4xx(不该重试)和 5xx(可以重试)。429 重试 5 次让 billing 的限流雪上加霜。

修复:只对 5xx 和网络超时重试,4xx 直接失败:

public Response callBilling(Request req) {
    for (int i = 0; i < MAX_RETRY; i++) {
        try {
            Response resp = httpClient.execute(req);
            int status = resp.getStatus();
            if (status >= 200 && status < 300) return resp;
            if (status >= 400 && status < 500) {
                // 4xx 不重试,直接抛出
                throw new ClientException("billing returned " + status);
            }
            // 仅 5xx 重试
            log.info("call billing-service failed, http_status={}, retrying", status);
        } catch (SocketTimeoutException e) {
            log.info("call billing-service timeout, retrying");
        }
    }
    throw new RuntimeException("retries exhausted");
}

修复 commit:i8j9k0l。

回归验证:修复上线后,429 不再被重试,billing 的限流压力下降。Bug 消除。

踩坑经验

流水线开发和迭代过程中踩了 5 个主要的坑,每个都影响了准确率或可用性。

坑 1:日志采样丢失关键信息

现象:上线第 1 周,异常识别准确率只有 62%,抽检发现大量"漏检"——某些真实异常的日志没被采样到,GPT-5 根本没看到。最典型的例子是 order-service 的竞态条件:两条并发日志间隔 1 毫秒,但采样时按"每服务每小时随机抽 3 条 INFO",这两条刚好都没被抽中。

根因:采样策略是"按服务 + 按小时随机抽样",对 INFO 日志一视同仁地抽 0.05%。但有些异常信号是"多条日志的时序关系"(比如竞态条件要看到两条几乎同时的日志),随机抽样会打散这种关系。

解法:

  1. 异常关键词全量保留:INFO 日志里命中异常关键词表的(retry/evict/timeout 等)100% 保留,不抽样。这把"含异常信号的 INFO"从 0.05% 采样率提升到 100% 全量。
  2. 相邻日志连带保留:对命中关键词的日志,前后 5 秒内同 trace_id 的日志也连带保留,保住时序关系。
  3. 采样窗口对齐:INFO 抽样改成按"5 分钟窗口"而非"每小时窗口",每 5 分钟内至少抽 1 条,避免某段时间完全空白。
# 改进后的采样逻辑(伪代码)
def sample_logs(logs):
    sampled = []
    for log in logs:
        if log.level in ["FATAL", "ERROR"]:
            sampled.append(log)  # 全量
        elif log.level == "WARN":
            if hash(log.log_id) % 10 == 0:  # 10% 抽样
                sampled.append(log)
        elif log.level == "INFO":
            if has_anomaly_keyword(log.message):
                sampled.append(log)  # 关键词命中全量
                # 连带保留前后 5 秒同 trace_id 的日志
                sampled.extend(get_related_within_5s(log.trace_id, log.timestamp))
            elif hash(log.log_id) % 2000 == 0:  # 0.05% 抽样
                sampled.append(log)
    return sampled

效果:漏检率从 38% 降到 8.7%(准确率从 62% 升到 78%——这一项改动贡献了 16pp 的提升)。

坑 2:语义歧义导致误报——"retry"不一定是异常

现象:误报率 18% 里,最大的一类是"retry 关键词误报"。GPT-5 看到日志里含 "retry" 就标 retry_anomaly,但很多 retry 是正常设计:

  • 消息队列消费失败的 retry 是预期的容错机制
  • HTTP 客户端的 connection retry(连接建立阶段重试)和 request retry(请求阶段重试)语义不同
  • 定时任务的 retry 是任务调度正常行为

GPT-5 把这些正常 retry 都标成异常,日均产生 20+ 条误报,开发者抱怨"狼来了"。

根因:异常关键词表太粗,"retry" 命中了大量正常 retry 日志,GPT-5 缺乏区分"正常 retry"和"异常 retry"的判据。

解法:

  1. 关键词细化:把 "retry" 细化成 "retry attempt [3-9]"(重试次数 ≥3 才算异常)、"retry exhausted"(重试耗尽)、"retrying 4[0-9][0-9]"(对 4xx 重试),正常 retry 不命中。
  2. Prompt 加判据:在异常识别 Prompt 里明确告诉 GPT-5 "重试次数 < 3 是正常的,>= 3 才算异常;对 4xx 重试才算异常,5xx 重试是正常的"。
  3. 历史频次基线:MCP 维护每个服务每个关键词的历史频次基线,GPT-5 看到的关键词日志附带 freq_baseline(历史频次)和 current_freq(当天频次),让它判断"这个 retry 频次是否异常"而非"有没有 retry"。
// 改进后的日志附带频次基线
{
  "log_id": "...",
  "message": "retry attempt 5 succeeded",
  "freq_baseline": {
    "retry_attempt_5": {"history_avg_per_day": 0.2, "today_count": 8}
  }
}

效果:retry 相关误报从日均 20 条降到 3 条,误报率从 18% 降到 11%。

坑 3:traceId 断链——跨服务关联失败

现象:第 2 步跨服务关联分析,经常出现"traceId 断链"——调用链在某 个服务断掉,GPT-5 无法把上下游异常关联起来。抽检发现 38% 的跨服务异常因为 traceId 断链被当成孤立异常处理,根因推断准确率上不去。

根因:traceId 断链有三个原因:

  1. 异步调用丢 traceId:线程池/消息队列异步执行时,traceId 没有通过 MDC(Mapped Diagnostic Context)传递,子线程的日志没有 traceId
  2. 第三方调用不带 traceId:调外部支付/短信 API 时,对方日志当然没有我们的 traceId
  3. 日志框架配置不一致:有的服务用 Logback 的 MDC,有的用 Log4j2 的 ThreadContext,traceId 字段名不统一(trace_id vs traceId vs x-request-id)

解法:

  1. MCP 做模糊匹配:aggregate_by_trace 工具在 traceId 断链时,按"时间窗 + service + tenant_id"做模糊匹配,把疑似同一调用链的日志关联起来。即使 traceId 断了,如果两条日志时间窗重合 + tenant_id 相同,大概率是同一调用链。
  2. 统一日志框架配置:全集群统一用 Logback + MDC,traceId 字段名统一 trace_id,异步调用强制通过 MDC.getCopyOfContextMap() 传递。
  3. traceId 断链标注:MCP 在无法关联时标注 trace_broken: true,GPT-5 看到这个标注会对该异常降权处理(不强行关联,避免错误关联)。
# MCP 模糊匹配逻辑(伪代码)
def aggregate_by_trace(trace_ids):
    result = []
    for tid in trace_ids:
        chain = get_logs_by_trace_id(tid)  # 精确匹配
        if len(chain.services) < expected_chain_length(tid):
            # traceId 断链,尝试模糊匹配
            for log in chain:
                related = get_logs_by_time_and_tenant(
                    time_window=log.timestamp ± 5s,
                    tenant_id=log.tenant_id,
                    exclude_service=log.service
                )
                chain.extend(related)  # 补全断链部分
            chain.trace_broken = True
        result.append(chain)
    return result

效果:跨服务关联成功率从 62% 升到 84%,根因推断 Top-1 准确率从 53% 升到 68%。

坑 4:超大日志超 token 限制——单条日志 8 万 token

现象:一次批处理中,order-service 的一条 ERROR 日志含完整堆栈,堆栈有 1200 行(嵌套异常链),单条日志占 8 万 token。GPT-5 那一批总共 70 条日志,这一条就占了 60% 上下文,其余 69 条日志的异常信号被淹没,GPT-5 漏检了其中 2 条真异常。

根因:日志裁剪不够狠,堆栈折叠只对重复堆栈生效,对嵌套异常链(Caused by 链)没折叠,导致超长堆栈原样进入 GPT-5 上下文。

解法:

  1. 堆栈折叠增强:标准化层对所有堆栈做折叠——第一行 + Caused by 链的每个第一行 + 末尾 3 行 + 总行数。1200 行压成 20 行。
  2. 单条日志 token 上限:标准化层对每条日志设 2000 token 上限,超出的截断并标注 truncated: true, original_size: XXXX tokens。GPT-5 看到截断标注会调 MCP 拉完整日志(按需)。
  3. 批次 token 预算:每批日志总 token 不超过 3 万(70 条 × 平均 400 token),超出时分两批处理。
# 堆栈折叠逻辑(伪代码)
def fold_stacktrace(stack_lines):
    if len(stack_lines) <= 20:
        return stack_lines  # 短堆栈不折叠
    folded = []
    folded.append(stack_lines[0])  # 第一行
    # 提取 Caused by 链的每个第一行
    for i, line in enumerate(stack_lines):
        if line.startswith("Caused by:"):
            folded.append(line)
    folded.extend(stack_lines[-3:])  # 末尾 3 行
    folded.append(f"... [{len(stack_lines)} lines total, folded]")
    return folded

效果:单批最大 token 从 9.8 万降到 2.8 万,超长堆栈场景的漏检率从 28% 降到 6%。

坑 5:误报疲劳——开发者开始忽略 AI 报告

现象:上线第 2 周,误报率 11%,日均 3-4 条误报。听起来不高,但累积效应可怕——开发者每天早上收到 3-4 条"疑似 Bug"通知,连续一周后发现其中 30% 是误报,开始整体忽略 AI 报告。第 3 周的一条真 Bug(内存泄漏)的 Issue 在 36 小时后才被开发者看到,因为开发者已经习惯性忽略 #log-anomaly-high 频道。

根因:这是 AI 检测系统的"狼来了"问题。即使误报率只有 10%,如果每天报 4 条、其中 1 条是误报,开发者很快会形成"AI 报告大部分不准"的认知,真 Bug 也会被忽略。误报的代价不是"处理误报的时间",而是"真 Bug 被忽略的风险"。

解法:

  1. 降误报率到 5% 以下:通过坑 1(采样改进)+ 坑 2(关键词细化)+ 反馈样本积累,把误报率从 11% 压到 4%。
  2. 分级通知:只有 high severity + confidence > 0.8 的异常才 @人,medium 和 low 只发到频道不 @人。开发者每天只被 @ 0-1 次,不会被轰炸。
  3. 误报归因:每条误报都记录"为什么误报",每周复盘 top 3 误报模式,针对性改 Prompt。比如发现"消息队列 retry"是高频误报源,就在 Prompt 里加"消息队列消费 retry 是正常的,不要标异常"。
  4. 准确率看板:给开发者看一个准确率看板——"本月 AI 报告 30 条异常,其中 27 条是真 Bug,准确率 90%"。让开发者对 AI 有信心,而不是凭印象觉得"不准"。

效果:开发者响应时间从峰值 36 小时降到 4 小时以内,准确率看板让开发者重新开始重视 AI 报告。

部署和运维

流水线跑起来后的日常运维要点。

监控 AI 流水线自身

"谁来看着看日志的 AI"是个绕不开的问题。我们给流水线自身建了一套监控:

指标阈值告警动作
日批处理是否按时触发2:30 未触发告警到 #ai-log-ops,定时任务可能挂了
批处理总耗时> 35 分钟告警,可能 GPT-5 API 慢或日志量异常
GPT-5 API 调用失败率> 5%立即告警,可能 API 配额用完
日志分析 MCP 调用超时率> 20%告警,ELK 可能慢了
单日异常报告数突变24h 变化 > 5 条告警,可能是某服务出了大问题或采样策略变了
人工兜底触发率> 20%告警,说明 GPT-5 准确率下降,需要补样本
误报率突变7 天均值变化 > 5pp告警,需要复盘近期 Prompt 改动

Prompt 版本管理

  • Prompt 存在独立 Git 仓库 ai-log-detection-prompts,每个 Prompt 一个 .md 文件
  • 修改走 PR 流程,PR 描述必须说明"改了什么、为什么改、预期影响"
  • 上线前在影子环境跑 100 条历史日志做回归测试,准确率下降超过 2pp 不允许合并
  • 线上 Prompt 版本号记录在每条异常报告里,出问题可以回滚

异常报告的日常消费流程

为了让 AI 产出的异常报告真正被消费,我们规范了值班人和开发者的日常流程:

时间动作责任人
2:00-2:30AI 批处理自动跑,产出异常报告系统自动
8:30值班人查看 #log-anomaly-high 频道,确认 high severity 异常值班 SRE
9:00值班人把 high severity 异常转给对应服务负责人值班 SRE
9:00-18:00服务负责人按 suggested_investigation 验证 Bug 假设服务负责人
当日确认/否定假设,在 Issue 里回复反馈服务负责人
修复后提交修复 commit,标记 Issue resolved服务负责人
次日 2:00下一次批处理验证异常信号是否消失系统自动

这套流程让"AI 发现异常 → 人验证修复 → AI 回归验证"形成闭环,Bug 从发现到修复的平均周期控制在 1.5 天以内。

成本核算

8 周运行的成本数据:

成本项月均花费备注
GPT-5 API 调用¥5,200日均 38 服务 × 平均 6K token × 单价
ELK 额外查询负载¥450MCP 查询增加的 ELK 资源开销
Kafka 集群¥0复用现有集群
PostgreSQL¥0复用现有实例
K8s 资源(MCP + consumer)¥6503 个 MCP × 2 副本 + 4 个 consumer pod
合计¥6,300/月—

对比收益:

  • SRE 审计工时从 8 小时/天降到 0.5 小时/天,节省 7.5 小时/天 × 22 天 = 165 小时/月
  • 按 SRE 工时成本 ¥200/小时算,节省 ¥33,000/月
  • 净收益 ¥26,700/月,ROI 约 4.2x

还不算 Bug 早发现避免的故障损失(如内存泄漏 8 天潜伏期那次事故的直接成本 ¥38 万,AI 流水线能把潜伏期压到 0.5 天,避免类似事故)。

经验总结

8 周的实践,3 条最关键的经验:

经验 1:日志异常检测的核心是"语义异常",不是"数值异常"

传统日志告警和 ML 异常检测都只能处理"数值异常"——某个指标超阈值、某个计数突增。但真正藏得深的 Bug,信号往往是"语义异常"——日志的级别是 INFO,文字也没出现 error,但语义上透露出系统正在出问题:

  • "重试第 5 次成功"——重试 5 次本身没报错,但语义上说明下游间歇性不可用
  • "缓存驱逐频次 480 次/天"——驱逐是正常机制,但频次语义上异常
  • "订单 PAID 但未扣库存"——两个状态各自正常,但组合起来语义矛盾

这种语义判断只有 LLM 做得了。GPT-5 的价值不在"读得快"(ELK 自己读得更快),而在"读得懂"——能理解日志的语义、能发现时序模式、能把多条日志的语义关联起来。这是规则引擎和 ML 模型永远做不到的。

但反过来,LLM 也做不了规则能稳定做的事——比如"日志级别是 ERROR 就告警"这种确定性逻辑,让 GPT-5 做反而不如写死在告警系统。所以我们的设计是"告警系统做数值异常,GPT-5 做语义异常",各司其职,互补不重叠。

经验 2:采样是准确率和成本的平衡术

120 万条日志全量喂给 GPT-5 既不可能(token 爆炸)也没必要(99.93% 是噪音)。但采样又不能太狠——太狠会漏掉关键信号(坑 1 的竞态条件日志被采样打散)。

采样的设计哲学是"分层 + 关键词 + 时序保留":

  • ERROR/FATAL 全量——这些少且重要
  • 含异常关键词的 INFO 全量——异常信号的载体
  • 普通 INFO 抽样——只为发现 unusual_pattern
  • 关键词命中的日志连带保留前后时序——保住时序关系

采样的目标是"用 0.23% 的日志覆盖 99% 以上的异常信号"。这个比例不是拍脑袋定的,是跑了一周对比"全量人工标注 vs 采样后 GPT-5 识别",找到的漏检率 < 10% 的临界点。采样率太高浪费成本,太低漏检,必须找到平衡点。

经验 3:反馈闭环是流水线从"能用"到"好用"的唯一路径

第 1 周的准确率(62%)只能叫"勉强能用",真正让它变成"好用"的是 8 周积累的 1640 条反馈样本。没有反馈闭环,LLM 的判断永远停留在 zero-shot 水平;有了反馈闭环,它才成了一个能持续学习的系统。

反馈闭环的关键设计是"低置信度走人工 + 人工结果回流"。这既保证了线上安全(低置信度不直接落地),又保证了训练数据质量(人工标注的都是 GPT-5 拿不准的难例,价值最高)。

下一步

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

  1. 从日级批处理到小时级流处理:现在一天跑一次,Bug 最长潜伏 24 小时。下一步打算做小时级流处理——每小时的日志攒一批跑一次,把潜伏期压到 1 小时以内。挑战是成本控制(小时级跑 24 次 vs 日级跑 1 次,API 费用涨 24 倍),需要更激进的采样和缓存。

  2. 从检测到自愈:现在流水线的终点是"建 Issue + 发通知 + 给验证建议",人还是要去查代码修复。下一步想接 K8s MCP,让 GPT-5 对"内存泄漏重启 Pod""连接池满扩容"这种确定性处置直接执行(带审批),把修复时间从小时级压到分钟级。

  3. 从单集群到多租户:现在流水线只服务我们自建集群,下一步打算开放给平台上的租户用——每个租户的日志走独立的采样和分析 pipeline,异常报告按租户隔离。挑战是多租户隔离下的成本控制和 Prompt 通用性。

日志异常检测只是 SRE 智能化的一个场景,但它证明了"LLM + MCP"的组合在日志分析场景是可行的——只要设计好采样策略、做好反馈闭环、控制好上下文,AI 能从海量日志里把藏得最深的 Bug 挖出来,让人去做只有人能做的判断和决策。

这套流水线的所有 Prompt 模板、MCP 工具定义、采样策略配置都已在内部开源仓库 ai-log-detection 归档,欢迎同团队复用和改进。

相关推荐

查看更多 →
案例开发

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

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

2026-09-02
#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文档