1. AIOps 告警归因为什么需要提示工程
1.1 告警归因的老办法卡在哪
做运维的兄弟应该都有这种体会:告警系统天天在响,但真正让人头大的不是告警本身,而是"这个告警到底意味着什么"。常见的场景是,凌晨三点,支付接口超时率突然往上飙,监控大屏上同时挂着MySQL连接数告警、Redis延迟告警、Nginx 5xx告警,值班同学被淹没在几十条通知里,不知道该先处理哪一个。
传统办法里面,最常用的就是规则引擎。比如"如果MySQL连接数大于阈值并且接口P99大于500ms,则判定为数据库瓶颈"。规则在简单场景下很好用,但一旦系统规模上来,规则之间互相打架、维护成本高得吓人。一个中型微服务系统,拓扑关系几百上千条,变更平均每天几十次,靠人工写规则根本跟不上节奏。
还有一种思路是基于时序数据和拓扑关系的算法归因。比如把告警时间线对齐,找因果关系,或者做告警聚类。这类方法在故障特征明显、依赖链路清晰的时候效果不错,但对那种跨多个组件的隐性故障,比如慢查询引发的连接池耗尽、缓存穿透导致数据库打满,算法很难给出一个让值班同学看得懂、敢相信的解释。因为它输出的是一堆相关性分数,而不是一段"为什么发生、影响范围多大、下一步怎么查"的完整判断。
到这里就能理解为什么很多人开始尝试大模型了。LLM擅长从大量文本里抽因果、归纳判断、生成可读的解释,刚好能补上传统归因链条里"从指标异常到根因判断"这一段。
1.2 大模型加提示词解决什么问题
把大模型用到告警归因里,本质上做的事情是:把告警事件、指标数据、拓扑关系、变更记录、历史案例这些信息,组织成大模型能理解的语言,让它给出一份结构化的归因结论。
这里说的"能理解的语言",就是提示工程要解决的核心问题。同一个告警,你用"请分析一下这个告警"发过去,和大模型拿到"当前故障现象、最近变更、相关拓扑、历史相似案例"之后输出一个带置信度的JSON,两者产出的质量完全不在一个量级。
提示工程在这个场景里的价值,不只是把Prompt写得漂亮,而是要把诊断知识、上下文组织方式、输出约束、异常兜底都固化下来,让大模型这个"能力很强但不太守规矩"的引擎,变成一个稳定可用的生产组件。
参考《智能运维:从0搭建大规模分布式AIOps系统》里对告警平台能力的划分,告警归因处于从"告警聚合"到"根因定位"的关键一环。如果这一环靠人力,SRE团队的瓶颈就卡在这里;如果这一环能通过提示工程把大模型用起来,值班效率的提升是肉眼可见的。
不过这里我要先泼一盆冷水:网上的Demo和真实生产之间,隔着四个阶梯。我下面把这四个阶梯完整拆开讲,每一个阶梯要解决什么问题、怎么落地、会踩什么坑,都会说清楚。
2. 第一、二阶梯:从Demo到结构化模板
2.1 阶梯一:能跑通就够了吗
我见过不少团队在POC阶段拿一个通用Prompt直接问大模型"请分析这个告警的原因",效果看起来还不错,于是马上就想上生产。实际上这还处于最原始的第一阶梯——能用。
这个阶段的特点是:Prompt是写死的几行字,入参是告警原文,输出是一段不受约束的自然语言。比如这样一个Prompt:
你是一名SRE专家,请分析以下告警的根因: 告警内容:支付系统成功率下降,MySQL活跃连接数超过阈值,Redis延迟升高。大模型会回给你一段像模像样的分析,什么"可能是MySQL出现慢查询导致连接数上升,Redis延迟升高可能由网络抖动引起"。听起来很专业,但你仔细想想,它既不知道你的系统拓扑,也不知道最近的变更,更不知道这个故障历史上是怎么处理的,它的分析其实是基于通用常识的推测,用一个术语叫"幻觉式泛化"。
更麻烦的是,输出格式不稳定。今天返回的是Markdown列表,明天是普通文本,后天可能夹着JSON。排序和置信度这些关键信息有没有,完全看运气。
所以阶梯一能做一件事:验证这个方案方向可行、大模型确实能理解告警语义。如果这一步都过不了,后面也不用继续。但过了这一步之后,一定要清醒:Demo能跑通和生产能用之间,差着结构化、可复用、可控、可评估四大工程问题。
我做了一个简单的验证脚本,把线上告警示例喂给模型跑了一周,结果发现:能看出大致原因的比例大概有七成,但能稳定输出可用结论的比例不到三成。原因是模型经常"知道得太多",把一些不相干的猜测也写进去,导致结论没法自动化处理。
2.2 阶梯二:提示词工程化与结构化输出
到了阶梯二,核心关键词是"约束"。
第一件事是入参标准化。告警原文不要直接塞给大模型,先把告警字段解析成统一结构,比如事件类型、指标名、指标值、阈值、时间窗口、涉及服务、实例IP、最近变更。这些字段来自你现有监控平台的告警回调或Webhook,需要写一个适配层把它们规整成JSON。
标准化的意义在于:大模型非常吃上下文的一致性。同样的信息,用干净的结构化字段表达,比夹杂在一大段告警描述里,理解准确性高很多。我实测下来,结构化之后归因准确率一般能提升十几个百分点。
第二件事是提示词模板化。把固定套路和可变信息拆开,形成一套模板系统。模板里最关键的是三个区块:系统指令(设定角色和规则)、上下文数据(故障现象和关联信息)、输出约束(要求JSON结构并给出字段说明)。
下面这个模板是我在项目中沉淀下来的一个基础版本,可以当作起点。需要注意的是,模板里的"案例库"区块在阶梯二可以先不放,把它留到阶梯三的RAG体系里再做,但模板结构建议提前预留位置。
你是大规模分布式系统SRE专家,负责根据提供的告警上下文判断根因。 只根据下列信息分析,不要猜测未提供的因素。 【当前故障】 - 业务影响:支付接口成功率下降至92%,P99延迟从200ms升至1200ms - 告警列表: 1. mysql_active_connections=180,阈值=100 2. mysql_threads_running=45,阈值=20 3. redis_avg_latency=35ms,阈值=5ms 4. user-service_p99=1200ms,阈值=400ms - 最近变更(48小时):订单服务v1.8.0发布,新增订单明细查询接口 - 相关拓扑:user-service → order-service → MySQL(orders库);user-service → Redis(cluster-a) 【历史案例库】略(阶梯三启用) 【任务】 1. 判断根因类别,从以下枚举中选取一个:db_slow_query / db_connection_pool_exhausted / cache_penetration / cache_avalanche / dependency_timeout / network_latency / instance_down / unknown 2. 给出判断依据,按证据强弱降序排列 3. 给出下一步排查建议,最多3条 【输出要求】 严格输出JSON,格式如下,不要输出任何其他内容: { "root_cause_category": "枚举值", "confidence": 0到1之间的小数, "evidence": ["证据1", "证据2"], "suggestion": ["建议1", "建议2", "建议3"] }第三件事是输出解析和容错。业界普遍的做法是让模型输出纯JSON,然后程序解析。但实际调用中总会出现JSON里夹着多余说明、字段名被改写、引号不匹配的情况,所以解析层必须做容错。
我踩过一个典型的坑:某次Prompt里要求输出root_cause_category,结果模型在几次调用中把字段名写成了rootCauseCategory或root_cause。后来我的解析层不再死等完整JSON合法,而是先尝试json.loads,失败则用正则抽取大括号片段,再对必须字段做兜底赋值,比如默认填unknown,confidence默认0.3。宁可让结论偏保守,也不要让解析报错把整个告警处理链路中断。
到了阶梯二,系统的形态已经接近一个可用的原型:入参标准化、模板管理、输出解析、字段校验。但离生产还差两个关键能力:为特定业务场景动态补充知识,以及保底兜底的确定性规则。这正是阶梯三要解决的问题。
3. 第三阶梯:可控归因——知识注入与规则兜底
3.1 RAG用起来:历史案例与拓扑生效
阶梯二的问题在于,Prompt里只有当前故障的现场信息,没有这个系统过去积累的经验。模型不知道你的支付系统历史上出过"缓存穿透打垮MySQL"的故障,也不了解你的核心链路上哪些组件是脆弱的。这种企业私有知识不在模型参数里,必须通过检索注入到上下文里。
RAG(检索增强生成)是解决这个问题的标准方案。实现上需要四块:
- 知识库:把历史故障复盘文档、告警处置记录、变更影响分析、服务拓扑关系、容量规划文档全部清洗成可检索的文本块。
- 索引:用向量化模型把文本块转成向量,存到向量数据库。规模不大的场景直接用开源的Chroma或Milvus就行。
- 检索:根据当前告警的标准化字段(服务名、告警类型、故障关键词)召回Top-K相关案例。
- 注入:把召回结果拼到Prompt的"历史案例库"区块。
我在项目里遇到过检索质量差导致效果反而变差的情况。原因是知识库里有大量过时的架构文档,检索系统把旧拓扑当最新拓扑召回,模型基于过时信息推理出错误结论。解决方式是在知识块上打"时间戳"和"有效性标记",比如文档里维护的依赖关系以最近30天变更记录为准,过期的块直接排除。检索召回的排序权重里,时间衰减因子占了很大比例,这个设计让归因准确率又回升了一截。
下面是一个简化版的检索注入代码片段,方便你直接用起来。核心点在于把告警字段拼成检索query,同时按时间去重和截断,控制注入的上下文长度:
from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OpenAIEmbeddings embedding = OpenAIEmbeddings(model="text-embedding-3-small") vector_store = Chroma( collection_name="aiops_case_library", embedding_function=embedding, persist_directory="./vector_db" ) def build_search_query(alert: dict) -> str: parts = [ alert.get("service", ""), alert.get("alert_type", ""), alert.get("metric", ""), alert.get("summary", ""), ] return " ".join(p for p in parts if p) def recall_cases(alert: dict, top_k: int = 3): q = build_search_query(alert) docs = vector_store.similarity_search_with_score(q, k=top_k * 3) # 过滤过时记录 + 去重,最终留top_k条 valid = [] seen_keys = set() for doc, score in docs: meta = doc.metadata if not meta.get("is_valid", True): continue key = meta.get("case_id", doc.page_content[:20]) if key in seen_keys: continue seen_keys.add(key) valid.append((doc, score)) if len(valid) >= top_k: break return [doc.page_content for doc, _ in valid]这里每个历史案例块的metadata里需要维护case_id、occurred_at、is_valid这几个字段。is_valid可以由人工定期检查,也可以设计成自动规则:比如某个服务的拓扑变更超过90天没有更新,就自动标记为无效。
3.2 引擎侧的知识注入与运行时检测
知识注入不只是RAG召回那一层。真正生产可用的方案,还需要把静态知识直接固化到系统指令里。例如,你的系统是典型的"网关-订单服务-库存服务-支付服务"分层结构,核心依赖是订单服务依赖MySQL的orders库和Redis的cluster-a,那么可以在系统指令里加一段"服务依赖说明",让模型在推理时遵循这个拓扑框架,而不是自己去猜测。
这个做法和RAG的区别是:RAG处理的是"相似案例"这种经验知识,指令里的拓扑信息处理的是"当前系统的结构约束"。两者配合,效果一般好于任何单一路径。在我实践的支付系统场景里,我在系统指令里定义了"核心链路优先级"和"已知脆弱点"两段,比如明确写"orders库历史上有慢查询引发的连接池耗尽记录,当前连接数过高时优先怀疑慢查询与连接池"。这一步看似简单,实际收益很大。
3.3 规则前置与置信度评分
阶梯三的另一个关键点是"规则兜底"。大模型的输出再完善,也不能完全替代确定性逻辑,尤其是涉及高危操作或严重业务影响的时候。
我在生产系统里设计了一个"两段式校验":
第一段是前置规则。在调用大模型之前,先用一条高置信度的规则判断是否有明确的已知故障。比如某台实例的指标直接为0,或者进程在宿主机上不存在,这类强信号根本不需要模型介入,直接输出"instance_down"。前置规则可以拦截掉大概两到三成的最明显故障,既省了模型调用,又保证最基础场景的确定性。
第二段是后置校验。模型输出JSON之后,不直接作为最终结论,而是做几项检查:根因类型是否在允许枚举内、置信度是否落在合理区间、证据中的指标是否与入参一致。如果发现模型编造了入参中不存在的指标,比如模型说"order-service CPU使用率达到90%",而你的入参里根本没有这个数据,就直接把confidence打折,降权处理。如果不一致程度严重,比如两个关键指标都对不上,就把结论降级为"无法判定"。
置信度评分为什么是可上生产的重要前提?因为AIOps的输出会被下游的自动处置或值班流程消费,一个"假阳性结论"可能让值班同学朝着错误方向排查十分钟。经过我改造后的置信度规则是:模型原始置信度乘以结构校验系数乘以证据一致性系数,最终归一化在0到1之间。低于0.6的结论要求在界面上明确标注"仅供参考,建议人工核实"。
这一步做完,归因能力已经基本具备生产雏形,但还不能直接全量放量。原因很简单:你还没有一套体系衡量它好不好、什么时候会变差、出了问题怎么回滚。
4. 第四阶梯:可上生产——评测、灰度与监控
4.1 离线评测集怎么建
我见过太多项目在"效果看起来不错"之后直接上线,结果一个月后模型因为线上数据分布变化而持续输出错误归因,团队只能悄悄下掉功能。要避免这种情况,必须建评测集。
评测集的核心是"标注样本"。从历史告警里挑选有明确结论的事件,由SRE专家手工标注根因类别、影响范围、判断依据。样本量不需要特别大,关键在于覆盖度和质量。我个人的经验是:起步至少200到300条已经确认完毕的故障记录,覆盖Top 20的服务和常见告警类型,每条包含告警上下文、变更信息、最终根因、处理措施。
评测不是一次性工作,我会把评测集分成两层:回归集和新鲜集。回归集固定不变,每次Prompt模板或检索策略有改动,先跑一遍,保证历史能力不退化;新鲜集每周从最新告警中抽样补充,模拟线上数据的动态变化。回归集控制在100条以内,跑一轮成本可控,新鲜集动态扩充。
评测指标我推荐以这五个为主:
| 指标 | 计算方式 | 说明 |
|---|---|---|
| 根因准确率 | 模型根因结论与标注一致的比例 | 最重要,线上效果的直接体现 |
| 覆盖率 | 能输出有效结论的样本比例 | 不输出结论也计入分母 |
| 专家修正率 | 值班同学需要修改结论的比例 | 越低越好,反映结论的可信度 |
| 平均处理时间 | 从告警到生成归因结论的耗时 | 直接影响值班体验 |
| 置信度校准度 | 高置信区间内准确率是否接近置信度 | 防止模型"自信地犯错" |
评测结果要用版本化的方式管理。每次改Prompt、加知识库、调参数,都要记录对应的评测分数。我在团队里约定了一个硬性门槛:根因准确率低于75%的版本不允许进入灰度;低于85%的版本不允许全量放量。
4.2 灰度发布与线上效果监控
评测集只是离线的保障,线上环境和离线数据总会有差距。所以生产上线必须走灰度,而且要设计好灰度策略。
我的推荐做法是"按服务比例灰度"。先把归因服务接入占总流量5%的告警事件,运行一周看效果;确认稳定后提升到30%,再观察一周;最后全量。每个阶段都要盯线上效果指标,而不是只看系统报不报错。
线上监控要建立两个维度:一个是大模型自身的质量维度,另一个是下游消费方的反馈维度。
质量维度包括:输出JSON的解析成功率、根因类别的分布、置信度分布、生成耗时、单条成本。重点盯两个指标:解析失败率和根因类别分布是否异常。解析失败率超过2%说明Prompt或模型有问题;根因类别长期集中在一个类别(比如80%都是dependency_timeout)说明上下文可能过于单一,模型在偷懒。
反馈维度包括:值班同学是否标记了"结论有帮助",是否对归因结果点了"正确"或"错误",以及归因服务是否被下游的自动处置流程成功消费。建议在值班页面上加一个轻量的反馈按钮,不需要复杂操作,点一下就行。这个反馈数据是持续优化Prompt和知识库的最重要素材。
灰度期间还要准备好回滚方案。大模型服务比较特殊,没法像传统服务一样只回滚一个版本。我的方案是:在灰度切流时保留一个"规则模式"开关,一旦归因服务的准确率指标跌破阈值,自动切换回规则引擎模式。规则引擎虽然能力有限,但它是确定性的,不会瞎猜。先用它保底,再排查问题,比硬撑着大模型服务更稳妥。
5. 常见问题与排查技巧实录
5.1 幻觉与上下文污染
幻觉是大模型在告警归因里最让人头疼的问题。模型会一本正经地编造一个不存在的指标,或者把两个故障的原因合并成一个看起来很合理的解释。我在实践中总结了三个有效抑制幻觉的方法:
第一,约束输入。不给模型无法获取的信息任何自由发挥的空间。比如Prompt里明确写"只分析下列数据中出现的内容,禁止提及未出现的数据"。这句约束看似简单,实测能把编造指标的情况减少一半。
第二,增强证据链要求。在输出要求里加上"每个证据必须能在告警列表中找到对应指标",并在后置解析时校验这一点。如果证据里有模型自己编出来的指标名,就扣置信度。
第三,控制上下文长度。上下文里塞满几十条无关告警,模型容易被带偏。我的实践是:同一链路相关的告警保留最多十条,其他告警只保留摘要信息;检索出来的历史案例最多三条。上下文越精准,输出越稳定。
上下文污染还有一个隐蔽来源:历史案例里夹杂着过时结论。比如某个案例说"该故障由网络抖动引起",但一周后定位是"配置变更导致",复盘文档更新了,向量库里的旧版本没删掉。这个问题需要知识库有健全的生命周期管理,定期清理、去重、标注有效性,不能只加不删。
5.2 成本、延迟与稳定性问题速查
生产上最大的现实约束往往是成本和延迟,这也是我建议在架构设计阶段就预留优化空间的原因。
| 问题 | 排查思路 | 推荐方案 |
|---|---|---|
| 单次调用成本过高 | 检查上下文长度和模型选择 | 中小模型(如gpt-4o-mini或同级别)先试,不行再升级;压缩历史案例为摘要 |
| 生成延迟超过5秒 | 检查网络链路、上下文量级、重试设置 | 开启流式输出;设置超时3秒熔断;加缓存(相同告警指纹读缓存) |
| 输出JSON解析失败率升高 | 抓最近输出样本,看模型故障模式 | 改用更小的输出schema;增加约束词;必要时用JSON Mode |
| 告警洪峰时服务被打爆 | 查QPS和并发请求,查限流配置 | 加队列削峰;按告警聚合后归因(相似告警只分析一次) |
| 结论突然变差且找不到原因 | 先查上下文数据源是否有变动 | 检查最近变更数据、拓扑数据、知识库是否被错误更新;优先回滚到上一版本知识库 |
延迟问题还有一个容易被忽略的点:告警事件本身是突发的,晚高峰故障一来就是几十条告警同时触发。如果归因服务每一条都实时调模型,先不说成本,并发也扛不住。我的做法是做"故障聚合窗口":同一服务30秒内的相关告警合并成一个故障事件,只做一次归因分析。整体告警量可能下降一半以上,归因的成本和延迟压力明显变小。
稳定性问题再补充一个:大模型厂商的API不是百分百可用,生产环境必须有降级和容错链路。我在归因服务里做了一级缓存、二级降级:先查Redis里有没有同一故障指纹的历史归因结果,有就直接返回;没有才调模型;模型调用失败或超时,就走规则引擎给出基础结论,并标记"由规则引擎给出"。这样即便模型服务抖动,值班同学也不会看到告警处理链路中断。
5.3 总结一段能直接用的话
如果你准备在团队里落地这套体系,我建议按这样推进:第一周把告警适配层和Prompt v1写好,跑通一条链路;第二周把结构化输出和解析容错做完;第三周接上RAG和知识库,建好评测集的冷启动版本;第四周开始灰度上线观察。每一步都不是大工程,但每一步都需要和SRE团队保持紧密沟通,尤其是评测集的标注质量和知识库的时效性,这两块直接决定最终效果。
我在实际落地过程中最大的体会是:提示工程在AI运维里的价值,不是把模型调得"聪明一点",而是把运维同学的隐性经验用工程方式沉淀下来、用提示词结构表达出来、用评测机制守住底限。做到这三点,AIOps告警归因才能从实验玩具变成值班人真正依赖的工具。
最后再分享一个小技巧:每次上线新Prompt之前,坚持跑一遍回归评测集,哪怕只改了措辞级别的小变动。很多你觉得"应该没问题"的小改动,可能让结构化输出率从98%掉到85%,而如果你不跑评测,这个问题会在线上被值班同事发现。评测这件事很枯燥,但它是整个AI运维链路里最值得投入的环节。