国庆长假线上值班机器人:GLM 5.3 接入企业微信与报警短信智能降噪

封面信息图

国庆长假的这几天,刚好轮到我排班线上值班。对于维护着两百多个微服务、上千个容器节点的架构团队来说,长假值班往往是一场对神经耐受力的严峻考验。

平时在工位上,多块大屏常驻 Grafana看板,大家各司其职,告警响了有人顺手点开排查。但到了放假,所有告警都集中轰炸到值班人员的手机和企业微信上。仅仅是网络骨干网一次 50ms 的轻微抖动,Prometheus、Cat 和微服务健康检查组件就会在半分钟内连环触发 80 多条报警短信与群推送。

大部分告警都是瞬时恢复的“狼来了”,但你又绝不敢把群消息静音,因为你不知道哪一条夹杂在几十条抖动信息里的“订单库活跃连接数打满”是真的致命 P1 故障。长期处于这种告警疲劳(Alert Fatigue)状态,人不仅无法好好休息,更容易在真正出事时麻痹大意。

为了根治这个问题,在假期前一周,我们基于智谱 GLM 5.3 大模型与企业微信群机器人,搭了一套报警智能降噪与根因聚类服务。这几天线上稳定跑下来,手机从每天上百次震动缩减为每天 4 次高可信摘要,排查效率反而翻倍。


告警洪峰的症结与聚类思路

监控系统之所以会产生海量噪声,根本原因在于单点探测无法感知拓扑因果。

例如,底层的公共 Redis 集群由于某台宿主机网卡重置中断了 3 秒钟:

  1. 依赖该缓存的用户服务抛出 JedisConnectionException;
  2. 订单服务调用用户服务失败,抛出 DubboTimeoutException;
  3. 网关感知到下游响应过慢,抛出 Gateway 504 熔断告警;
  4. 消息队列消费者反压,积压告警被激活。

监控系统会机械地将上述 4 层的每台机器指标逐条发送给值班人员。但从架构师的视角来看,这起事件有且仅有一个事实:“底层某台 Redis 发生网络短暂抖动,导致下游链路出现连锁超时,3 秒后已自愈”。

我们设计的降噪架构逻辑非常纯粹:

  • 第一步:时间窗口聚合。Alertmanager 的 Webhook 不直接外发,而是先灌入应用内部的 90 秒缓冲池。
  • 第二步:拓扑与日志富化。从缓冲池捞出一批告警,提取报错堆栈摘要、受影响的应用名。
  • 第三步:GLM 5.3 根因聚类。将多条原始事件打包给大模型,让其结合系统依赖关系进行归因,判断影响范围与修复建议。
  • 第四步:企业微信卡片推送。同一类根因在指定静音窗口内合并更新,禁止刷屏。

核心实现:从接收聚合到大模型归因

1. Alertmanager Webhook 聚合器

首先在 Spring Boot 中暴露一个接收端点,将告警灌入基于并发安全队列的时间窗口收集器:

package com.yali.ops.alert;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

@RestController
@RequestMapping("/internal/alert")
public class AlertWebhookController {

    private final AlertAggregationEngine engine;

    public AlertWebhookController(AlertAggregationEngine engine) {
        this.engine = engine;
    }

    @PostMapping("/receive")
    public String receiveAlerts(@RequestBody AlertPayload payload) {
        if (payload != null && payload.getAlerts() != null) {
            engine.enqueue(payload.getAlerts());
        }
        return "SUCCESS";
    }
}

收集器内部采用定时调度器,每隔 90 秒清空一次暂存区,打包进入聚类流程:

package com.yali.ops.alert;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;

@Component
public class AlertAggregationEngine {

    private static final Logger log = LoggerFactory.getLogger(AlertAggregationEngine.class);
    private final ConcurrentLinkedQueue<AlertItem> queue = new ConcurrentLinkedQueue<>();
    private final GlmAnalysisService glmAnalysisService;
    private final WeComNotificationService weComService;

    public AlertAggregationEngine(GlmAnalysisService glmAnalysisService, WeComNotificationService weComService) {
        this.glmAnalysisService = glmAnalysisService;
        this.weComService = weComService;
    }

    public void enqueue(List<AlertItem> items) {
        queue.addAll(items);
    }

    @Scheduled(fixedDelay = 90_000)
    public void processBatch() {
        if (queue.isEmpty()) {
            return;
        }

        List<AlertItem> batch = new ArrayList<>();
        AlertItem item;
        while ((item = queue.poll()) != null) {
            batch.add(item);
        }

        log.info("提取到报警批次,共 {} 条,开始请求 GLM 5.3 聚类分析...", batch.size());
        try {
            AlertSummary summary = glmAnalysisService.analyze(batch);
            if (summary.isNeedNotify()) {
                weComService.sendMarkdownCard(summary);
            }
        } catch (Exception e) {
            log.error("报警分析或通知异常", e);
        }
    }
}

2. 构建 GLM 5.3 专家 Prompt 与结构化提取

大模型最擅长阅读凌乱但具备语义关联的文本。我们在 Prompt 中对 GLM 5.3 赋予“资深 SRE 架构师”角色,并约束其输出严格的 JSON 结构。

package com.yali.ops.alert;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class GlmAnalysisService {

    private final ChatClient chatClient;
    private final ObjectMapper objectMapper = new ObjectMapper();

    public GlmAnalysisService(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    public AlertSummary analyze(List<AlertItem> alerts) {
        String alertJson;
        try {
            alertJson = objectMapper.writeValueAsString(alerts);
        } catch (Exception e) {
            alertJson = alerts.toString();
        }

        String systemPrompt = """
                你是一位资深分布式系统 SRE 专家。
                以下是在过去 90 秒内系统捕获的一批监控告警日志。
                请分析这些告警之间的因果关联,完成以下任务:
                1. 识别核心根因(Root Cause),过滤掉因上游超时引发的连锁下游告警;
                2. 评估当前系统整体健康风险等级(P0/P1/P2/INFO);
                3. 判断是否需要值班人员立即登录机器介入(如果已自动恢复或纯属网络瞬断且无业务损伤,标记 needNotify=false);
                4. 给出一键排查命令建议(如检查某端口连接数、JVM 堆栈等)。
                
                必须严格返回如下 JSON 格式,禁止任何额外前导词:
                {
                  "severity": "P1",
                  "rootCause": "简明扼要的一句话根因",
                  "impactScope": "受影响的核心业务模块",
                  "status": "已自动恢复 / 持续恶化中",
                  "needNotify": true,
                  "troubleshootingTip": "推荐执行的排查命令或操作路径"
                }
                """;

        String response = chatClient.prompt()
                .system(systemPrompt)
                .user(alertJson)
                .call()
                .content();

        try {
            // 清理可能包含的 markdown 标签
            String cleanJson = response.replaceAll("```json", "").replaceAll("```", "").trim();
            return objectMapper.readValue(cleanJson, AlertSummary.class);
        } catch (Exception e) {
            // 解析失败时的降级兜底:直接组装简单通知
            AlertSummary fallback = new AlertSummary();
            fallback.setSeverity("P2");
            fallback.setRootCause("解析聚合异常,原始报警共 " + alerts.size() + " 条");
            fallback.setNeedNotify(true);
            return fallback;
        }
    }
}

3. 企业微信群卡片渲染与静音防轰炸

将降噪后的结果格式化为美观的 Markdown 消息发送到企业微信群。对于持续发生的同一根因事件,利用本地缓存进行哈希锁定,30 分钟内只更新不重报:

package com.yali.ops.alert;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class WeComNotificationService {

    private final RestClient restClient;
    private final String webhookUrl;
    private final ConcurrentHashMap<String, Long> silenceCache = new ConcurrentHashMap<>();

    public WeComNotificationService(@Value("${ops.wecom.webhook}") String webhookUrl) {
        this.webhookUrl = webhookUrl;
        this.restClient = RestClient.create();
    }

    public void sendMarkdownCard(AlertSummary summary) {
        String silenceKey = summary.getSeverity() + ":" + summary.getRootCause();
        Long lastTime = silenceCache.get(silenceKey);
        long now = System.currentTimeMillis();

        // 同一类告警在 30 分钟内静音抑制
        if (lastTime != null && (now - lastTime) < 30 * 60 * 1000L) {
            return;
        }
        silenceCache.put(silenceKey, now);

        String markdown = String.format("""
                ### 🚨 智能值班哨兵诊断报告 [%s]
                > **根因归纳**:%s
                > **影响范围**:%s
                > **当前状态**:%s
                > **建议操作**:`%s`
                
                *系统已自动将 90 秒内 50+ 条级联告警降噪压缩,请按需介入。*
                """,
                summary.getSeverity(),
                summary.getRootCause(),
                summary.getImpactScope(),
                summary.getStatus(),
                summary.getTroubleshootingTip()
        );

        Map<String, Object> body = Map.of(
                "msgtype", "markdown",
                "markdown", Map.of("content", markdown)
        );

        restClient.post()
                .uri(webhookUrl)
                .contentType(MediaType.APPLICATION_JSON)
                .body(body)
                .retrieve()
                .toBodilessEntity();
    }
}

假期线上实战复盘

在国庆假期第四天上午,这套系统打了一场极其漂亮的仗。

当天上午 10:14,机房骨干网交换机出现短时丢包。如果在过去,几十个业务微信群会同时刷出上百条 Dubbo 超时和连接断开告警,值班人员得挨个点进去看堆栈确认哪儿坏了。

而这一次,企业微信哨兵机器人在 10:16 准时推送了一条聚合卡片:

  • 等级:P2(黄色预警)
  • 根因:IDC 跨机房专线网络在 10:14:02 发生约 4 秒丢包,导致订单微服务调用营销中心出现 12 次超时重试。
  • 状态:下游重试均已成功,链路当前已完全恢复,无数据积压。
  • 建议:无需人工登录介入,保持监控观察。

看完这条卡片,我只需要扫一眼核心指标大盘确认 QPS 和错误率恢复平缓,连笔记本电脑都无需打开。

把大语言模型用在运维场景中,不一定非要去搞全自动自愈或者高大上的端到端自治。先从最折磨工程师的“告警降噪”切入,把散乱的指标重组为人看得懂的因果逻辑,就能释放巨大的生产力。技术不仅要为业务兜底,也要真正善待每一个在节假日坚守岗位的人。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐