很多团队做 AIOps 时,第一步就是把所有监控系统的原始告警一股脑丢给大模型,让它“总结归纳”或者“自动定位根因”。我参与过好几个项目都是这么启动的,结局也惊人地一致——上线一两周后,模型不是在定位根因,而是在给值班同学重复转述告警内容;稍微遇到一次小规模故障,上下文就被几千条告警塞满;更麻烦的是,模型还会根据不完整的文本脑补出一些根本不存在的“根因”。
问题不在大模型本身,而在你喂进去的东西。原始告警是监控系统写给人类值班员看的流水账,不是写给模型做决策用的结构化信息。两者之间缺的,就是所谓的“告警工程”。
1. 为什么不让 AI 直接吃原始告警?三个绕不过去的现实问题
1.1 告警不是信息,是噪声
一条原始告警长什么样?拿常见的监控系统来说,大概是这样的:
[2025-06-01 01:23:45] CRITICAL - CPU Load High on server 10.10.12.33, current 95.2%, warning threshold 80%, please check the server and take actions as soon as possible.看起来信息挺全?有主机 IP、有指标值、有阈值,甚至还有一句“please check”。但这条告警对 AI 来说,信息密度其实非常低。
首先,它没有告诉你这台服务器属于哪个业务,是订单服务还是支付服务。它也没有告诉你这台服务器上跑了多少个实例,是不是所有实例都在同一时间 CPU 飙升。它更没有告诉你,昨天凌晨是不是刚好有人在这个集群做过配置变更。换句话说,这条告警只是把“某个资源超了阈值”这个孤立事实推给你,而 AI 做根因判断最需要的背景信息,恰恰全部缺失。
更常见的情况是,一次小故障会同时触发几十条甚至上百条这样的告警。底层主机告警、容器重启告警、应用接口错误率告警、日志组件报错告警,全部混在一起。从 AI 的视角看,它面对的不是一条条逻辑清晰的故障信号,而是一堆互相重叠、互相嵌套、甚至自相矛盾的文本。
所以原始告警的本质不是信息,是噪声。把噪声直接喂给模型,再好的模型也只会变得更困惑。
1.2 大模型的上下文窗口和 token 成本,扛不住告警风暴
直接吃原始告警的第二个问题,是成本和窗口。
稍微算一笔账:一条原始告警文本,短的 200 字左右,长的可能超过 500 字。换算成 token,一条告警大概 100 到 250 token。一次普通的小故障,5 分钟内触发三五百条告警很常见;遇到依赖抖动、网络分区这种场景,几千条告警瞬间打进来也是常态。
如果是 2000 条告警,单次喂给模型的输入就是 20 万到 40 万 token。就算模型支持超长上下文,推理时长也会拉到几十秒甚至更久;如果模型不支持超长上下文,那不好意思,后面的告警根本进不来。
这还只是单次调用的成本。告警是流式数据,每 5 分钟轮询一次,每触发一次就调用一次模型,一天下来调用量非常可观。而且,当模型输入里堆满了重复和低价值的告警内容时,真正起作用的只有一小部分,等于你花了大价钱,却让模型看了一堆无关紧要的东西。
所以从工程角度讲,不让 AI 直接吃原始告警,不只是为了效果,更是为了钱。做好告警治理之后,同样的故障可能只需要给模型喂 1000 token 左右的高质量事件摘要,成本和效果完全不是一个量级。
1.3 大模型不是解析器,它需要的是“决策上下文”
很多人对大模型的定位有个误解,以为它是全能解析器,什么脏活累活都能干。实际上,大模型擅长的是语义理解和生成,但在精确解析、去重、字段对齐这些确定性任务上很容易翻车。你让模型从一条告警文本里提取出“主机名”“指标名”“阈值”这三个字段,它能做,但偶尔会漏字、会脑补、会把格式搞乱。而这类工作在规则引擎里就是几条正则的事。
更关键的是,大模型做告警分析时需要的是一份“决策上下文”,也就是完整的背景知识包。它包括:
- 这个告警属于哪个服务、哪个集群、哪个环境;
- 这个服务最近有没有变更,最近的发布记录是什么;
- 这个告警和当前窗口内的其他告警是什么关系,是同一个根因还是多个根因;
- 历史上是否出现过类似告警,当时的处置方案是什么。
你把这些上下文补全给模型,它才能给出可靠的分析结论。你只给它一句“CPU 95.2%”,然后指望它做根因判断,它就只能猜测,而猜测在故障场景里是最危险的事情。
2. 告警工程的整体思路:先治理,再喂给 AI
既然不能直接吃原始告警,那正确的思路是什么?其实很简单:在告警进入大模型之前,先把它加工成一个结构清晰、自带上下文的事件对象。
2.1 从“原始告警流”到“结构化事件”
我习惯把这条链路分成两个阶段:前段是数据治理阶段,后段才是模型决策阶段。
数据治理阶段的核心工作,是把各种监控系统吐出来的告警流,变成一条条规范化的“事件”。所谓事件,不是说一段文本,而是一个包含固定字段的结构化对象。大概长这样:
{ "event_id": "evt_20250601012345_001", "event_time": 1753938225000, "source": "zabbix", "category": "resource.cpu", "level": 1, "entity": { "ip": "10.10.12.33", "hostname": "node-prod-a-01", "service": "order-api", "cluster": "prod-a" }, "metric": { "cpu.usage": 95.2, "threshold": 80 }, "related_alerts": ["evt_002", "evt_003"], "context": { "owner": "order-infra", "recent_change": "config_update at 01:20" } }这个结构化对象里,包含了原始告警里的所有关键信息,同时补上了业务归属、关联告警、变更记录等背景信息。大模型拿到它之后,不需要再从文本里抠字段,也不需要对缺失的信息瞎猜,它可以基于一个相对完整的“事实底座”去做推理。
这就是告警工程的核心目标:不让模型面对一堆原始告警文本,而是让它面对一组经过整理的事实。
2.2 先理清要解决的三类问题
落地告警工程的时候,我建议先把问题拆成三类,因为每一类的处理手段完全不同。
第一类是降噪问题。告警里大量重复告警、关联告警、维护窗口内的误报,这些是不该传给模型的低价值内容。降噪的手段主要是规则,包括去重、维护窗口静默、同源合并、以及抑制因一个根因而引发的上下游告警。
第二类是归一问题。不同监控系统的告警格式完全不同,有的用 UTC 时间,有的用北京时间;有的把重要级别叫 P1,有的叫 Critical;有的字段叫 host,有的叫 hostname。这些差异如果不做处理,模型会把这些当成不同实体。归一化就是把所有告警统一成同一套 Schema。
第三类是富化问题。原始告警里缺失的业务上下文、变更信息、依赖关系,需要从外部系统补进来。富化的手段包括查 CMDB、关联发布平台、拉取最近指标数据、检索历史告警记录。
这三类问题的核心手段其实都不依赖大模型。降噪和归一用规则引擎就够了,富化用的是一些确定性查询逻辑。所以整个告警工程的架构,并不是“AI 方案”,而是一套“规则+大数据+AI”的混合架构。
2.3 一条可复用的五层处理管道
在我实际落地的项目里,告警处理是这样一个五层管道:
| 层级 | 位置 | 核心工作 | 常用手段 |
|---|---|---|---|
| 接入层 | 管道最前端 | 从各监控系统拉取原始告警,保留完整字段,解析文本内容 | Webhook、消息队列、日志采集 |
| 归一层 | 接入层之后 | 字段对齐、时间统一、级别映射、实体归一 | 规则引擎、正则、字段映射表 |
| 聚合层 | 归一层之后 | 去重、收敛、事件窗口切分、同根因合并 | 指纹计算、滑动窗口、相似度算法 |
| 富化层 | 聚合层之后 | 补充业务归属、变更记录、拓扑关系、历史处置记录 | CMDB 接口、发布平台对接、时序数据库 |
| 决策层 | 管道末端 | 将富化后的事件组装成 Prompt,调用大模型输出根因和处置建议 | 大模型、Prompt 模板、知识库检索 |
前端四层都是确定性的逻辑处理,只有最后一层用到了大模型。这是我特别想强调的一点:把大模型放在决策链路的末端,而不是信号链路的开端。这样既能省成本,也能降低模型的幻觉概率。
打个比方,AI 像是值班室里经验丰富的大师傅,他需要看的是已经整理好的菜单摘要,而不是后厨所有灶台上烟气腾腾的原始信号。你非要把所有信号都堆到他面前,他反而会看花眼。
3. 实操:把一条告警变成 AI 能看懂的事件
下面按我这边落地的方式,把每一步具体拆开讲。
3.1 接入层与字段归一化:从“无法解析”到“结构统一”
接入层要解决的第一件事,是让所有监控系统的告警都进入同一个管道。我们当时接入了资源监控、容器平台、日志中心、APM 和拨测系统,每家的数据格式都不一样,有的直接推送 JSON,有的只能说是一段文本。
我的建议是:在接入层不要急着清洗,先把原始内容完整留存。原始告警里往往有一些字段在后续排查时还有用,比如告警自带的跳转链接、原始触发条件等。清洗得太早,后面想查反而查不到。
接着是归一化。这一步主要做四件事:
时间统一。不同系统可能输出不同时区的时间,有的还是带时区偏移的字符串。最好的方式是在接入层就把所有时间都转成统一的毫秒时间戳,同时保留原始字符串。注意,告警里往往会存在两个时间,一个是监控系统产生告警的时间,一个是告警事件传入平台的时间。后面做窗口聚合时,应该统一用监控系统产生告警的时间,否则会因为网络延迟导致告警被分到错误的窗口里。
字段对齐。建立一套公共告警 Schema,至少包含事件 ID、事件时间、告警来源、告警类别、严重级别、实体、指标、描述。不同系统的字段通过映射规则转到公共字段上。比如有的系统字段叫instance,有的叫host,有的叫target,都统一映射到entity。
级别映射。制定一套统一级别,比如 P0 到 P3,然后把不同系统的级别映射过来。这个映射规则必须人工确认,因为不同系统对级别的定义不完全一致,直接按名称对号入座会出错。
实体归一。这是归一化里最容易被忽视但又最重要的一件事。同一个资源,在系统 A 里叫 IP,在系统 B 里叫主机名,在系统 C 里叫容器名。需要建一张“资源别名表”,把 IP、主机名、容器 ID、所属服务等关联到同一个实体 ID 上。否则,后面做聚合时,本该聚到一起的告警会因为实体名不一致而分成两堆。
3.2 聚合层的指纹设计与收敛窗口
归一化之后就可以做去重和收敛了。这里的核心是“指纹”的概念。
指纹(fingerprint)是对一组字段做哈希后生成的 ID,用来判断两条告警是否属于同一类。我常用的聚合键是“告警类别 + 实体 + 严重级别 + 指标名”。在同一个指纹下,一个时间窗口内只保留第一条作为代表事件,后面的都记为重复次数。
举个例子,一台主机的 CPU 使用率超过阈值,监控系统每 5 分钟触发一次,一小时内在同一个实体上会触发 12 条原始告警。用指纹聚合后,会得到一个事件,附带一个属性repeat_count: 12。这样 AI 看到的不是 12 条重复文本,而是一个“这个实体已经连续告警 12 次”的事实。连续触发次数本身就是很有价值的信号,它能告诉模型这个故障是持续性的还是瞬时性的。
聚合窗口的设置需要根据业务场景来调。我们这边的参考值是 15 分钟。窗口太短,收敛效果不明显;窗口太长,会把两个独立故障合并成一个,反而误导判断。遇到大促或重点保障期间,可以临时把窗口缩短到 5 分钟。
除了基于字段的指纹聚合,还可以做相似度聚合。比如两条告警的文本非常相似,只是 IP 段和部分参数不同,可以借助文本相似度算法(如 Jaccard 相似度)把它们归到同一个事件组。这种聚合方式对“同一次发布引发多个实例批量告警”的场景特别有效。
3.3 富化层:补上大模型最缺的“背景知识”
聚合完成后的每个事件,已经比原始告警干净很多了。但这时候还不足以喂给大模型,因为模型仍然不知道这个告警意味着什么业务后果。富化层就是把外部背景信息补进来。
这里我按优先级说一下我们做过哪些富化:
首先是业务归属。通过 CMDB 查询实体对应的服务、集群、环境、负责人。告警里带上服务名,模型的判断就会很不一样。比如同样是“CPU 使用率 95%”,如果服务是“订单下单接口”,模型会倾向于判断为“高影响故障”;如果服务是一个离线任务集群,模型则可能判断为“对用户无影响”。
其次是变更记录。拉取该服务最近 1 小时内的发布记录、配置变更、扩缩容操作。大量故障的根本原因其实都跟变更有关。不查变更就去推理根因,模型只能猜。
然后是历史处置记录。检索最近 30 天同类事件的记录,看当时是怎么处理的,结论是什么。这些历史信息可以直接作为上下文片段放在 Prompt 里,让模型参考过往经验。
最后是指标上下文。如果可能,把该实体最近 15 分钟的指标采样数据(比如 CPU、内存、网络包量)一起带进去。有时候故障发生的瞬间指标已经回落,只看当前值会误判,加上趋势信息会让判断更准确。
富化的信息要控制大小,不要把 CMDB 里所有字段倒进来。我们一般是选择 10 到 20 个最有用的字段,如果富化完事件体仍然过大,就再做一层截断,优先保留业务归属和变更记录。
3.4 决策层的 Prompt 组装:让模型读摘要而不是读流水账
富化之后,事件终于可以交给模型了。但 Prompt 怎么写,同样有讲究。
我见过很多团队把告警 JSON 直接丢给模型,然后在后面加一句“请分析”。这样效果很有限,因为模型不知道该按什么维度分析,也不知道该输出什么格式。Prompt 组装要解决的核心问题是:让模型基于我们给的事实做判断,而不是自由发挥。
我的 Prompt 模板大概包含这几块:
- 当前事件的结构化信息(级别、实体、指标、服务归属、变更记录、历史处置记录);
- 当前事件聚合后的摘要描述,包括涉及的告警条数和第一触发时间;
- 分析任务说明,明确要求模型区分“根因假设”和“影响范围”;
- 输出格式约束,要求按固定 JSON 结构输出;
- 安全约束,明确要求模型只能给建议,不能擅自给出执行指令,并且明确指出缺失字段时要说明缺失,不要推测。
这里我踩过的坑是:模型很喜欢把告警摘要里的数据原样抄一遍,然后说“已分析”,看似内容很多,其实没有增量信息。后来加了“必须基于字段之间的关联给出推断,并说明推断依据”的约束,情况才改善。
另外,Prompt 里的摘要不要超过一段话。聚合之后一个事件的信息已经压缩了很多,但如果是大促期间的大规模故障,一个事件里可能关联了几百个实体,摘要也会很长。按我们的经验,超过 500 字的事件内容,模型就容易开始“抓不住重点”。这种情况下建议先做二级摘要,把事件里的关键实体、关键指标、共同特征提炼出来,再进入决策层。
4. 常见问题与排查技巧实录
告警工程的坑不少,很多问题不是出现在大模型环节,而是出现在前端的治理环节。下面是我实际记录过的一些典型问题。
4.1 问题速查表
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 同一台机器的告警在聚合后分成多个事件 | 不同来源的实体名称不一致,映射表没配全 | 完善资源别名表,把 IP、主机名、容器名统一映射到实体 ID |
| 事件窗口与故障实际时间对不上 | 窗口计算用了接入时间而不是告警产生时间 | 统一以监控系统产生告警的时间作为事件时间 |
| 模型给出的根因比较发散、东一个西一个 | Prompt 里的上下文信息不够,或历史处置记录缺失 | 重点补齐变更记录和历史相似事件信息 |
| 同一事件反复触发,模型反复输出相同建议 | 只做了文本去重,没做时间窗口内的去重 | 加指纹聚合,对重复告警合并记录为 repeat_count |
| 告警风暴时事件摘要还是太大 | 聚合层次不够,缺少二级摘要 | 增加摘要压缩层,超过阈值就做二次摘要 |
| 模型把“10.10.12.33”和“server-12-33”当成两个实体 | 实体归一化没做,或映射表未覆盖该环境 | 完善实体归一,在 Prompt 前已经统一实体 ID |
| 模型按照文本猜测缺失字段,给出误导结论 | Prompt 缺少缺失字段约束 | 强制要求模型对缺失字段显式说明“缺失”,不得推测 |
4.2 遇见告警风暴时,先别急着调 Prompt
有一次做演练,模拟的是核心数据库抖动场景。结果短时间内,资源监控、日志组件、APM、拨测系统全部报警,5 分钟内的原始告警超过了 2000 条。当时我们的处理管道已经做了聚合,但一个事件组里还是塞进了 600 多个独立实体,摘要写了 3000 多字,模型调用直接超时。
后来排查发现,聚合层只按“告警类别 + 实体 + 级别”做了分组,没有把“同一个时间窗口+同一类实体集合”的更上层关联做起来。也就是说,数据库抖动引发的几百个上游实例的告警,被分到了几百个独立的事件里,并没有收敛成“数据库抖动导致上游批量出现连接失败”这个更宏观的事件。
解决方式是增加一层“根因事件聚合”:基于拓扑关系,把同一时间窗口内指向同一依赖实体的所有告警归并到同一个事件组。这样做之后,2000 条原始告警最终收敛成了 3 个事件组,每个组提供一份摘要,模型的输入一下子降到了几百 token。
这个经验值得单独说一遍:告警收敛不能只做字段级别的重复收敛,还要做基于故障域的语义收敛。故障域可以是几个核心服务,可以是某个机房,也可以是某个数据库实例。有拓扑关系的时候,优先按拓扑的上下游关系做聚合。
4.3 一个关于幻觉的具体教训
还有一次,模型给了一个看起来非常专业的根因结论,说“判断为网络层丢包导致的跨区域调用超时”。结果我们按这个结论排查了半天,发现网络根本没有问题,根因其实是接入层的一个上游服务在做无延迟返回的限流。
为什么模型会给出这么武断的结论?回看 Prompt 我们发现,事件对象里确实有一个字段叫net.package_loss,但这个字段是富化的时候从指标库里取出来的历史最大值,不是当前故障时刻的值。模型看不到字段的时间标签,就把它当成当前故障的指标来使用了。
从那以后,我们做富化时就给所有指标字段加上了采样时间,并且在 Prompt 里明确标注“以下指标数据仅展示故障前 15 分钟趋势,不要直接等同于当前状态”。这类问题在很多项目里都容易踩,数据本身没有错,错的是模型不知道数据的时效性,从而产生误判。
5. 一些边界与实用建议
5.1 别把所有流量都交给 AI,分级有多重要
做告警工程还有一个很容易忽略的地方:不是所有告警都需要进入 AI 分析链路。
以我们的实践为例,P0 和 P1 级别的告警几乎都会经过完整链路,因为这类告警直接影响业务可用性;P2 级别的告警会做降噪和聚合,只有形成事件的才进入模型;P3 级别、低频率的辅助类告警,一般只做存储和报表,不会触发大模型调用。这样分级之后,模型的调用量和成本能控制在一个稳定范围内,而且高优先级告警的分析质量会更好,因为不会被大量低优先级任务稀释。
另外,维护窗口、变更窗口、业务低峰期这种场景,要配置静默规则。比如重启应用期间,容器崩溃和接口错误告警都会被触发,但这些告警属于已知操作产生的结果,不应该进入 AI 分析。不加静默,这些低价值内容会抢占模型资源,还会把模型的结论带偏。
5.2 我最后想说的话
做告警工程这几年,我最大的感受是:一个好的 AI 告警系统,百分之七十的功夫在模型外面的数据流水线上,而不是在大模型的选型或 Prompt 调优上。
很多人上来就想用大模型解决所有问题,结果反而把简单的链路搞复杂了。先把接入、归一、去重、聚合、富化这些确定性的脏活累活做扎实,再把大模型放到最值得用它的语义分析和根因推断环节,整个项目的成功概率会高得多。
前端治理越干净,模型的表现越稳定。这是我在实际项目里体会最深的一点。尝试过几次之后你就会发现,当 AI 分析的输入不再是原始告警时,它的输出质量会有多么明显的提升。