前言:
日常需要维护 Linux 服务器集群。之前每天都要人工登录主机或者查看 Prometheus 指标,逐个核对 CPU、内存、磁盘,手动汇总巡检报告发到企业微信。
人工巡检重复枯燥,节点多的时候很容易漏看告警,汇总耗时很长。于是基于 Python 开发了一套轻量运维 Agent,实现Prometheus 指标采集 → LLM 结构化汇总 → 企微机器人推送的全链路自动化巡检日报系统。

本项目特点:

  • 基于 OpenAI 兼容接口,无重型 AIOps 平台依赖,单脚本即可部署;
  • 面向服务器巡检场景,强约束 Prompt,严格限制 LLM 幻觉,只基于采集到的数据输出;
  • 模块解耦,采集、LLM 调用、推送相互独立,方便替换监控源、大模型服务商;
  • 自带降级逻辑,LLM 超时 / 接口异常时不会直接崩溃,推送降级提示通知运维。

一、整体方案架构

整个Agent采用三段解耦流水线设计,各模块可独立替换
1、采集层: 调用Prometheus API 来取监控指标、包含内存使用率、全分区磁盘使用率、CPU15分钟负载;自动过滤tmpfs、overlay等虚拟文件系统,识别磁盘挂载点/,/data,/log等(这个可以在根据实际情况可修改)
2、LLM分析层: 将结构化监控文本送入DeepSeek接口,通过Prompt约束,自动区分异常告警项,正常指标分组汇总,禁止编造数据;
3、推送层: LLM生成完成巡检日报后,通过企业微信Webhook机器人推送;
4、调度层: Linux crontab定时调度脚本,实现每日定点自动巡检。
这样架构的优势:

  • 轻量:单Python脚本,兼容Python3.6,适配老旧服务器环境;
  • 可扩展:后续可对接Zabbix,SkyWalking等其他监控源;
  • 高可控:任何场景服务器巡检场景优先保证准确性,杜绝AI幻觉编造告警;
  • 容错:LLM调用失败自动降级,推送异常通知,保障运维感知;

二、项目踩坑实录

坑 1:OpenAI 兼容协议 ≠ 模型名称通用

最开始开发时直接沿用 OpenAI 写法,模型参数写gpt-3.5-turbo,接口直接返回 400
排查:

  1. 先使用 curl 最小用例验证 API 密钥、网络连通正常;
  2. 在用脚本打印完整请求 header、payload,对比报文;
  3. 查看返回报错:DeepSeek 不识别gpt-3.5-turbo,仅支持deepseek-flash、deepseek-v4-pro等自有模型标识。
    ✅ 得出的结论:兼容只是 HTTP 协议、请求体格式对齐,模型名称是各大厂商独立管控,不能直接复用 OpenAI 模型标识。

坑 2:Prompt 模板占位符忘记替换,LLM 拿不到监控数据

写好 Prompt 模板后,直接把模板字符串发给 LLM,{metrics}占位符没有替换成采集到的监控文本,大模型返回提示监控数据为空,无法生成日报。
✅ 解决:使用字符串format方法,采集完成拿到结构化指标文本之后,填充进 Prompt 模板,再发起 LLM 请求。

坑 3:原始指标无字段说明,LLM 产生幻觉,报告可读性差

早期版本采集输出只有IP + 数值,没有标注指标类型,大模型不知道数字代表 CPU、内存还是磁盘,容易自行猜测指标含义,输出报告其他人看不懂。
✅ 优化:采集层统一结构化输出格式:
节点IP | 指标名称 | 挂载点(磁盘专属) | 数值

  • CPU、内存无挂载点的指标,挂载点填充-;
  • 磁盘指标携带mountpoint挂载点,区分多个业务分区;
  • Prompt 增加强约束:禁止猜测指标含义,仅基于输入数据输出。

坑 4:磁盘只采集根分区,巡检覆盖不全

一开始 PromQL 只采集根分区/使用率,忽略/data、/log等业务分区,日志盘、数据盘打满风险无法感知。

✅ 优化 PromQL,过滤虚拟文件系统,抓取全部真实挂载分区:

100 - (node_filesystem_avail_bytes{mountpoint!~"/sys|/proc|/dev|/run",fstype!~"tmpfs|devtmpfs|overlay"} / node_filesystem_size_b

坑 5:新增指标后输入文本变长,出现 Read timed out

扩容指标(增加全分区磁盘、CPU 负载)之后,送入 LLM 的文本 token 上涨,大模型生成耗时变长,requests 默认超时时间短,抛出读取超时。
✅ 解决:给 post 请求显式设置读写超时 timeout=(10,30),10s 连接超时,30s 读取超时;
预留优化方向:节点数量过多时,做指标裁剪,只保留高风险指标,减少输入 token,避免频繁超时。

三、核心代码(Python3.6 兼容)

import requests
import logging

# ========== 配置区,请自行修改 ==========
PROM_URL = "http://127.0.0.1:9090/api/v1/query"
DEEPSEEK_KEY = "sk-xxxx"
DEEPSEEK_URL = "https://api.deepseek.com/v1/chat/completions"
WECOM_WEBHOOK = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxx"
# =======================================

# 日志开关,方便调试排错
logging.basicConfig(level=logging.INFO)

def collect_all_metrics():
    """采集Prometheus指标:内存、全分区磁盘、15分钟负载、CPU核数"""
    query_map = {
        "mem": '100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100)',
        "disk": '100 - (node_filesystem_avail_bytes{mountpoint!~"/sys|/proc|/dev|/run",fstype!~"tmpfs|devtmpfs|overlay"} / node_filesystem_size_bytes{mountpoint!~"/sys|/proc|/dev|/run",fstype!~"tmpfs|devtmpfs|overlay"} * 100)',
        "load15": "node_load15",
        "cpu_core": "count by (instance) (node_cpu_seconds_total{mode='idle'})"
    }
    ip_core_map = {}
    line_list = []

    # 第一步:先获取所有节点CPU核数,构建映射关系
    resp_core = requests.get(PROM_URL, params={"query": query_map["cpu_core"]}, timeout=5)
    for item in resp_core.json()["data"]["result"]:
        ip = item["metric"]["instance"]
        core = float(item["value"][1])
        ip_core_map[ip] = core

    # 采集内存使用率
    resp_mem = requests.get(PROM_URL, params={"query": query_map["mem"]}, timeout=5)
    for item in resp_mem.json()["data"]["result"]:
        ip = item["metric"]["instance"]
        val = round(float(item["value"][1]), 2)
        line_list.append(f"节点IP:{ip} | 指标:内存使用率 | 挂载点:- | 数值:{val}")
        if val > 85:
            line_list.append(f"⚠️ 告警:节点IP:{ip} | 指标:内存使用率 | 阈值85%")

    # 采集全分区磁盘使用率
    resp_disk = requests.get(PROM_URL, params={"query": query_map["disk"]}, timeout=5)
    for item in resp_disk.json()["data"]["result"]:
        ip = item["metric"]["instance"]
        mp = item["metric"]["mountpoint"]
        val = round(float(item["value"][1]), 2)
        line_list.append(f"节点IP:{ip} | 指标:磁盘使用率 | 挂载点:{mp} | 数值:{val}")
        if val > 85:
            line_list.append(f"⚠️ 告警:节点IP:{ip} | 指标:磁盘使用率 | 挂载点:{mp} | 阈值85%")

    # 采集CPU 15分钟负载
    resp_load = requests.get(PROM_URL, params={"query": query_map["load15"]}, timeout=5)
    for item in resp_load.json()["data"]["result"]:
        ip = item["metric"]["instance"]
        val = round(float(item["value"][1]), 2)
        core = ip_core_map.get(ip, 4)
        line_list.append(f"节点IP:{ip} | 指标:CPU15分钟负载 | 挂载点:- | 数值:{val}")
        if val > core:
            line_list.append(f"⚠️ 告警:节点IP:{ip} | 指标:CPU15分钟负载 | 阈值本机CPU核数{core}")
    return "\n".join(line_list)

def call_llm(metrics_text):
    prompt_template = """
你是金融运维工程师,负责服务器巡检日报整理。
数据格式:节点IP | 指标类型 | 挂载点(磁盘专属) | 数值(%)
⚠️ 告警标记 = 指标超出预设阈值
阈值规则:
- 内存使用率:>85%告警
- 单分区磁盘使用率:>85%告警
- CPU 15分钟负载:高于节点CPU核数告警

要求:
1. 严禁编造数据、禁止猜测指标含义,只基于输入内容生成;
2. 输出结构固定:标题 → 统计概览 →【异常告警项】→【正常指标汇总】
3. 异常告警项优先,每条写明:IP、指标、挂载点(磁盘)、当前值、阈值;
4. 正常指标汇总:按【CPU负载、内存使用率、各磁盘分区】分组展示,同IP的指标合并,不要无意义逐条刷屏;
5. 输出适配企业微信,只用换行、序号,不要markdown加粗;
6. 正常指标只展示关键范围,列出高值节点,方便运维快速感知整体状态。

监控数据:
{metrics}
"""
    final_prompt = prompt_template.format(metrics=metrics_text)
    headers = {
        "Authorization": f"Bearer {DEEPSEEK_KEY}",
        "Content-Type": "application/json"
    }
    payload = {
        "model": "deepseek-flash",
        "messages": [{"role": "user", "content": final_prompt}],
        "temperature": 0.2
    }
    try:
        resp = requests.post(DEEPSEEK_URL, headers=headers, json=payload, timeout=(10, 30))
        resp.raise_for_status()
        return resp.json()["choices"][0]["message"]["content"]
    except Exception as e:
        logging.error(f"LLM调用失败,触发降级。err:{e}")
        return "【巡检降级通知】LLM接口调用异常,请人工查看Prometheus监控"

def send_wecom(content):
    """企业微信机器人推送"""
    data = {
        "msgtype": "text",
        "text": {"content": content}
    }
    requests.post(WECOM_WEBHOOK, json=data, timeout=10)

if __name__ == "__main__":
    metrics = collect_all_metrics()
    report = call_llm(metrics)
    send_wecom(report)

四、定时调度配置

使用 Linux crontab 实现每日自动巡检,同时输出日志方便排查:

# 每天早上8点执行巡检脚本
0 8 * * * /usr/bin/python3.6 /opt/aihmt/agent_demo.py >> /opt/aihmt/agent.log 2>&1

五、推送效果样例(企微机器人)

【巡检告警日报】
金融运维巡检日报
数据合计56条,告警2条。

【异常告警项】
1. IP:10.13.51.21;指标:内存使用率;当前值:88.39%;阈值85%
2. IP:10.13.51.69;指标:内存使用率;当前值:97.73%;阈值85%

【正常指标汇总】
CPU15分钟负载:
10.13.51.21:2.7;10.13.51.69:1.8;其余节点负载均低于本机CPU核数,无负载异常
内存使用率(正常节点):
范围:0.84% ~ 84.64%,全部低于85%阈值
磁盘分区使用率:
10.13.51.21:/ 72.1%、/data 61.5%、/log 44.2%
10.13.51.69:/ 53.2%、/data 78.6%
其余节点各分区使用率均低于85%阈值

六、后续优化方向

  1. 增加 token 长度判断,节点过多时自动采样聚合,避免超长上下文、超时;
  2. 增加 LLM 重试机制,失败重试 1-2 次后再执行降级;
  3. 告警分级,区分 P1/P2 级告警;
  4. 巡检记录落库,支持历史趋势查看;
  5. 支持多环境分组巡检,区分生产、测试环境;
  6. 密钥加密,增加权限控制,适配金融环境安全规范。

七、总结

这个运维 Agent 不是简单的 Demo,是在金融交付运维场景落地可用的自动化工具。
核心思路不是让大模型做复杂推理,而是把 LLM 当成结构化文本汇总工具,依靠强约束 Prompt 规避幻觉,贴合金融行业对准确性、稳定性的硬性要求。
整个项目从 0 到 1 踩了协议兼容、Prompt 拼接、指标采集不全、接口超时等一系列坑,最终实现每日自动巡检,解放运维人员重复的汇总工作。

Logo

更多推荐