RCACopilot - Automatic Root Cause Analysis via Large Language Models for Cloud Incidents
- RCA: Root Cause Analysis, 根因分析, 找到事故的真正起因;
- OCE: On-Call Engineer, 轮值工程师, 收到告警的那个人;
- Handler: 一段事先写好的诊断流程, 由 OCE 编写, 系统按 alert 类型匹配执行;
- Diagnostic Information: handler 收集到的所有运行时证据 (logs / metrics / traces / configs 等);
- CoT: Chain-of-Thought, 让 LLM 先一步步推理再下结论的 prompt 技巧;
- In-context Learning / Few-shot: 在 prompt 里给 LLM 几条相似历史例子作为参考;
前置: cloud incident RCA 是个什么问题
云服务一旦出事 (latency 飙高、error rate 飙升、磁盘炸了、配置错了), 监控系统会在毫秒到秒级吐出一条 incident 警报, 然后人接手:
- OCE 起夜接到 page;
- 翻 dashboard, 看 metrics 异常时间段;
- 拉日志 (
kusto query/ KQL / Splunk /grep), 找 error pattern; - 看 trace, 定位到具体哪个 service 哪段调用挂了;
- 形成假设, 在系统里验证;
- 找到根因, 写报告, 决定 mitigation;
这套流程慢、费眼睛、依赖经验; OCE 资深与否直接决定恢复时间; 而每个团队、每种 alert 又都有自己的诊断套路, 这种隐性知识很难规模化复用 — RCACopilot 想解决的就是这个;
前置: 把 RCA 自动化为什么不容易
直觉上你会想: 把日志和 metrics 都扔给 GPT, 让它推根因不就好了; 实际上不行, 主要三个原因:
- LLM 不知道你的 telemetry 系统怎么查: 不同公司用 Kusto/Splunk/Prom/Loki/自研一堆, LLM 没法生成对的查询;
- 数据量太大: 一个事故关联的日志可以是 GB 级的, 直接灌 LLM token 预算炸穿;
- 诊断顺序也是知识: “先看 CPU 还是先看 GC, 看完不对再切到哪个 region” 这种 SOP 不是从日志能推出来的, 是 OCE 脑里的经验;
所以纯 prompt-engineering 解决不了这事, 必须有一层确定性的诊断流程脚手架, LLM 才能在已经收齐证据的基础上做它擅长的事 — 基于证据归因 + 自然语言解释;
论文定位
RCACopilot 是一套部署在 Microsoft 内部的 on-call 系统, 把 OCE 的诊断 SOP 编码成可执行的 handler, handler 自动跑完后用 LLM 做最后一步根因分类与解释;
设计上的关键判断: 将"取证据"和"做归因"彻底分离, 取证据由人写的 handler 做 (确定性), 做归因由 LLM 做 (概率性); 这是它跟一刀切的端到端 LLM 方案最大的不同;
论文 claim 的最强结果: 在 Microsoft 真实一年的 incident 数据上达到 0.766 根因分类准确率, 而且诊断收集模块 (Stage 1) 在 MS 已经生产部署 4+ 年;
论文解决的核心问题
具体回答的是这几个问题:
- 怎么把 OCE 的诊断 SOP 用一种可扩展、可让团队自己写的方式编码下来?
- handler 跑完拿到一堆 raw 数据, 怎么压缩成 LLM 能吃的形式?
- 怎么让 LLM 借鉴历史相似 incident 的根因经验, 而不是从零推断?
- 整套系统怎么部署到 Microsoft 真实生产并跑出工业可接受的准确率?
整体架构: 两阶段四存储
整个系统横向分两阶段, 各有自己的输入输出, 中间用结构化 diagnostic info 过渡:
1 | Collection Stage Prediction Stage |
注意系统里有两个独立的存储:
- 左边 Collection Stage 的 DB: 存所有团队写好的 handler 定义, 还存历次跑出来的诊断结果;
- 右边 Prediction Stage 的 Embedding vector DB: 存历史 incident 的 FastText 向量, 用来做 KNN 检索;
paper 没明说这两个 DB 的具体技术栈, 考虑到部署在 Microsoft, 大概率是 Cosmos DB / Kusto 这种自家服务; vector DB 因为 FastText 300 维 + incident 量级在 , 实现上可以非常轻, 哪怕是个 in-memory FAISS index 都够;
Stage 1: Diagnostic Information Collection
这一阶段的目的是: 拿到 incident alert 后, 系统化地把所有相关诊断证据按 SOP 收齐;
1.1 Incident Parsing
incident 进系统的第一步是解析关键字段:
- ID
- Title
- OwningTenant / OwningTeam (谁的服务)
- Severity (优先级)
- Active 状态
这一步纯字符串处理, 没什么 magic;
1.2 Handler Matching
按 OwningTeam + alert type 在 handler DB 里查到对应的 handler; 一个团队会预先写一组 handler, 每个 handler 对应一种典型 alert (例如 “Latency spike on service X”, “Disk full on storage tier Y”);
handler 是 OCE 手写, 不是 AI 自动生成; 这是 paper 一个很务实的判断 — 团队自己最清楚要看什么, 不要把这个负担转嫁给 LLM; 而且这样系统永远是可解释、可审计的;
1.3 Information Collection: 三种 action 原语
handler 是一段控制流, 由三类 action 拼接而成:
| Action | 干嘛 | 例子 |
|---|---|---|
| Scope Switching | 动态调整调查范围 | 单 instance 失败 → 看整个 cluster, 单 region 异常 → 看 partner region 对照 |
| Query | 调外部系统拉数据, 输出 KV 对 | KQL 查日志, Prometheus 查 metrics, 查配置库, 查 trace 系统 |
| Mitigation | 直接采取行动 | 重启服务, 切流量, 升级二线 oncall, 触发 failover |
Query action 输出的是结构化 KV 对, 不是原始日志倾倒; 这意味着OCE 写 query 的时候已经做了一次粗压缩 — KQL 里的 group by, summarize, take top N 这些操作把原始数据压成了 LLM 友好的形式; paper 没显式讲"日志怎么 summarize", 因为它把这个责任交给了写 handler 的 OCE;
1.4 三种 telemetry 信号在 handler 里的不同处理
这是 paper 没明说但能从设计推出的:
| 信号 | handler 端通常怎么处理 | 进入 Stage 2 时的形态 |
|---|---|---|
| Logs | KQL/Splunk 加 filter + group by + take top N | 半结构化 KV, 但仍可能含原始 lines |
| Metrics | Prom/Geneva 查询返回数字 (P99, count, rate) | 几乎是纯数字 KV |
| Traces | 取关键 span 或 critical path | 带结构的 latency 拆解 |
Logs 是最难处理的, 这就是为什么 Stage 2 第一步要再做一次 LLM-based summarization;
Stage 2: Root Cause Prediction
这一阶段的目的是: 拿到诊断信息后, 借助历史经验推根因, 输出可读解释;
2.1 Incident Summarization (LLM 第一次出场)
handler 跑出来的 diagnostic info 经常 > 2000 tokens, 不仅 token 预算吃紧, 而且人读起来都费劲; 所以这里有一道显式的 LLM-based summarization:
- 输入: handler 输出的全量诊断信息;
- 输出: 约 120-140 词的 readable note;
- 模型: 同一个 LLM (跟 prediction 阶段共用);
这个 summary 后面会被两次使用:
- 拿去做 embedding, 用于 KNN 检索;
- 拼进 prediction prompt, 让 LLM 看到"这次的简明病情";
[!此处的小 tradeoff]
同一个 LLM 既做 summarize 又做 predict, 第一次的偏差会传染给第二次; paper 没特别 mitigate 这一点, 是后续工作的口子;
2.2 Embedding + Vector DB + Neighbor Search
这是整个 RAG (retrieval-augmented generation) 流水的核心:
- Embedding model: FastText, 输出约 300 维向量;
- Distance: 欧几里得距离;
- 存储: Embedding vector DB (paper 没说具体产品);
- 检索: 找当前 incident 的 top-K 最近邻历史 incident;
为什么用 FastText 而不是 GPT embedding 或 sentence-transformers:
| 维度 | FastText | GPT embedding |
|---|---|---|
| 速度 | 极快, 离线训完直接跑 | 每次要调 API 或推理 |
| 成本 | 几乎为 0 | 按 token 付费 |
| 维度 | ~300d, 内存友好 | ~1500d 起 |
| 准确率 | 一般 | 高 |
paper 选 FastText 是个工程优先级判断: 这是一个频繁触发的内嵌系统, 不能每来一次 incident 就调一次 OpenAI; 准确率的损失通过后续 LLM 弥补;
[!为什么不上重型 vector DB]
Microsoft 一年的 incident 量虽然不小, 但单团队 / 单类 alert 下的历史 incident 通常在 条; 加上 FastText 300 维, 一次 KNN 暴力算就 ms 级; 论文用了 vector DB 抽象, 但实现上完全不需要 Pinecone 这种托管服务, 一个 in-memory FAISS index 就够了;
2.3 Chain-of-Thought Prediction (LLM 第二次出场)
最后一步把所有线索捏成 prompt 喂给 LLM:
1 | Prompt 结构 (示意): |
这是标准的 few-shot in-context learning + Chain-of-Thought, 要点:
- few-shot: top-K 历史相似 incident 当例子, 教 LLM “这种症状对应这种根因”;
- CoT: 提示词里写 “step by step”, 让 LLM 先推导再下结论, 减少瞎猜;
- Unseen 退路: 如果检索回来的 K 个都不相似, 显式允许 LLM 说 “我没见过这种, 兜底交给 OCE”;
LLM 输出两块内容:
- Root Cause Category: 一个离散标签, 从已有根因类别里选;
- Explanatory Narrative: 一段自然语言解释, 给 OCE 看为什么是这个原因, 含 CoT 中间过程;
关键设计决策的 tradeoff
把这套架构里几个值得拎出来的判断列一下:
| 决策 | 取舍 | paper 的回答 |
|---|---|---|
| handler 谁写 | OCE 手写 vs LLM 自动生成 | OCE 手写, 牺牲 generality 换可解释性和正确性 |
| 数据怎么压缩 | 规则 vs LLM-based summarization | handler-level 隐式压缩 + LLM-level 显式总结, 两层夹击 |
| Embedding model | FastText vs GPT embedding | FastText, 牺牲一点准确率换速度和成本 |
| Vector DB | 自研 vs 现成 | paper 没说, 多半是 MS 内部存储 |
| Prediction model | 微调 vs 提示工程 | 纯 in-context learning, 不微调, 换模型零成本 |
| 处理 Unseen | 强行猜 vs 显式 reject | 显式 reject 选项, 放给 OCE 兜底 |
这一套组合拳的共同主题是: 能用确定性手段做的就不交给 LLM, LLM 只在最后一步做归因; 这是 LLM-for-Ops 这一类系统设计的一个示范模板;
评测亮点
paper 的评测部分主要打三件事:
- 准确率: 在一年真实 MS incident 上达到 0.766 根因分类准确率, 比纯 LLM baseline 高出 ~26 个百分点;
- 消融: 去掉 in-context learning (即不检索历史) 准确率明显下降, 验证 RAG 的必要性;
- 生产部署: Stage 1 (诊断收集) 已在 MS 跑了 4+ 年, 处理过大量真实事故 — 这是工业系统 paper 最硬的背书;
一些值得讨论的局限
检索匹配的是 summary, 不是原始诊断信息
这是 paper 一个隐藏但关键的设计选择, 也是它最大的实验空白; 完整 retrieval 链路其实是:
1 | raw logs/metrics/traces ──► handler query ──► KV 诊断信息 (>2000 tokens) |
注意:
- embedding 作用对象是 summary, 不是 KV 诊断信息, 也不是原始 log;
- 历史 incident 在 vector DB 里也是以 summary 形态存的, 这是 summary-vs-summary 的相似度匹配;
- raw log/metrics 在 Stage 1 末尾就被丢弃了, 后面 retrieval 完全看不到它们;
paper 没认真讨论这个压缩带来的损失
这是这套设计最值得追问的地方:
- 没有 ablation 研究 “summary 长度 vs 检索准确率”: 60 词 / 240 词 / 500 词分别效果如何, paper 没做;
- 没有对比 “summary embedding vs 直接拿 KV 诊断信息 embedding”: 跳过 summarization 这一步反而是好是坏, 没数据;
- 没有讨论 LLM context window 对系统设计的影响: 选 120-140 词应该是经验值, 没解释为什么不是 200 或 80; 也没说模型升到 100k context 后这个设计还能不能成立;
- 没有限制 log/metrics 量级的实验: paper 没正面回应 “多少 token 的诊断信息是上限” 这种工程问题;
这种 summary-based retrieval 至少有三类失真
| 失真类型 | 描述 |
|---|---|
| 高阶相似但根因不同 | 两个 incident summary 都说 “P99 latency 飙到 1s”, 但一个是 GC stall 一个是 DB 锁竞争; raw log 里本有差异 token (GC pause 980ms vs wait for lock), 被 LLM summarize 时抹平了 |
| 被 summary 风格盖掉 | LLM 总结有自己的语言模板, 容易把不同的原始证据用相似句式表达, 让本来不像的两次事故变得相似 |
| 数值信号被语言化 | 原始 metrics 里精确数字 (P99 = 982ms, error rate = 0.34%) 在 summary 里常变成 “high latency”, magnitude 信息丢失; FastText 是 word-level embedding, 对数字也不敏感 |
但这个损失大概率被工程兜底吸收了
retrieval 的 summary 只是检索 query key, 不是终极证据;最终 prediction 的 prompt 里, top-K 历史 incident 是带完整 diagnostic info 喂给 LLM 的; 所以即使 retrieval 阶段稍有偏差, K=5/10 个候选里通常仍有真正相似的那条; 也就是说 paper 默认假设了 retrieval 的 recall 比 precision 重要; 但这个假设没被实证, 是后续工作可以挖的口子;
其他局限
- Handler 质量上限决定整体上限: OCE 写的 KQL 没把关键信息查到, 后续 LLM 也救不回来; 系统自动化的是执行, 不是诊断思路本身;
- 历史依赖: 前所未见的全新事故只能 fall back 到 OCE; 这是 RAG 系统通病;
- 同一 LLM 做 summarize + predict: 第一次的偏差会传染给第二次, paper 没显式处理;
- 跨服务事件: 多服务联动的复杂事故单 handler 不够覆盖, paper 没讨论;
- 可复现性: 不公开 handler 模板、不公开后端、不公开 vector DB 产品, 外部要完整复刻只能照思路重写;
把 RCACopilot 的设计思路浓缩成几句, 它的主流观点可以总结为:
- 把云事故 RCA 拆成两个性质不同的阶段: 取证据 (确定性) + 做归因 (概率性), 不要让 LLM 同时干这两件事;
- 取证据这一步交给 OCE 手写 handler, 用 Scope Switching / Query / Mitigation 三类 action 拼出诊断 SOP, 不让 LLM 自动生成查询;
- handler 输出的诊断信息再用 LLM 自己做一道 summarization 压到 ~120 词, 解决 token 预算问题;
- 用 FastText embedding + vector DB 检索 top-K 相似历史 incident, 走 RAG 把"过往经验"喂给 LLM;
- 用 few-shot + Chain-of-Thought prompt 让 LLM 输出根因类别 + 解释, 同时保留 “Unseen” 退路给 OCE 兜底;
- 整个系统选型上该轻就轻 (FastText 而不是 GPT embed, 简单 KNN 而不是高级 ANN), 工程上不是炫技导向, 这是它能在 MS 跑 4+ 年的关键;
