AIOps方案:OpenCode+Skills+Nightingale MCP实现高效监控分析
本文介绍了一套基于OpenCode+Skills+夜莺MCP的AIOps高效监控分析方案。该方案通过整合现有技术资源,构建轻量化、高效化的运维监控体系。核心思路是:利用Skills封装Prometheus查询逻辑,通过MCP获取夜莺监控数据,形成"AI→Skills→MCP"的简洁交互链路。
文章目录
AIOps方案:OpenCode+Skills+Nightingale MCP实现高效监控分析
PS :这篇文章你将获得什么:
- 一套能跑起来的 AI 运维交互环境
- 一套可控、低 token 的 Skills 编排方法
- 一套夜莺 MCP 接入与排障经验
适用读者:运维/SRE、平台工程、AIOps 探索者
最终产出:2 份 SKILL.md + 1 份 opencode.json + 1 个演示用全流程案例
一、方案概述:为何选择OpenCode+Skills+夜莺MCP?
1.1 方案灵感来源–运维拥抱AI
方案的灵感来源于运维场景的实际需求与AI技术的快速发展,OpenCode虽然本质上是一款类似Claude Code、Cursor的代码开发工具,最初并非为运维场景设计;而MCP虽功能强大,但过多的定制化开发会,对于中小企业与单人运维的公司来说会严重分散运维人员的精力,难以聚焦核心运维目标,调用MCP出现的超长上下文交互会造成大量token消耗。

Skills与OpenClaw(俗称“龙虾”)的爆火,为运维领域拥抱AI提供了新的思路。不过实际体验后发现,OpenClaw虽热度高、功能强劲,但权限过高,不符合公司生产环境的安全要求,难以落地应用。
所以有了想法就得去实现,一个人花了大概1周的时间,做出了一套还算不错的方案;结合夜莺近期推出的官方MCP,搭配细颗粒度的Skills能力,个人认为可以作为AIOps高效运维的探索方向。方案的本质我认为是:“整合思想”。
不去花过多的时间去创造,而是利用已经出现的资源将其整合,实现高效利用。
1.2 核心组件解析:MCP与Skills核心作用
先来看一下简单的对比:

MCP(Management Control Plane,管理控制平面):运维体系的统一管控枢纽,是AI工具与底层运维资源沟通的核心桥梁。简单来说,它承接AI工具的指令,汇聚各类运维数据,让AI能够“读懂”运维场景、操控运维资源,无需AI直接对接复杂的底层组件。
Skills:AI与运维场景的适配媒介,是简化交互、传递指令的关键载体。它将复杂的运维操作逻辑封装成简单模块,让AI工具无需掌握复杂运维接口,就能通过调用Skills,快速实现与MCP等组件的交互,高效完成运维操作。
二者联动核心:AI工具通过Skills传递需求指令,MCP执行指令并反馈结果,形成“AI→Skills→MCP”的简洁交互链路,高效衔接AI能力与运维场景。
1.3 方案核心定位与价值
本方案核心定位是打造轻量化、高效化的AIOps监控分析体系,核心价值在于对传统监控架构的“瘦身”与AI能力的深度融合。方案中,Prometheus专注于Metrics数据存储,夜莺平台负责监控告警的可视化处理,彻底摒弃传统“Prometheus+Alertmanager+PrometheusAlert”的复杂架构,简化监控链路,降低运维成本。

- 接入AI的核心逻辑也比较容易清晰的理解,
- 给Prometheus编写专属Skills
- 由Skills告知AI如何操作监控数据
- AI自动生成调用命令并在宿主机执行
- 执行后返还数据并完成智能分析;
- 夜莺MCP则负责获取平台内的关键信息,包括已配置的监控告警规则、近期告警记录等,为AI分析提供全面支撑
最终实现“数据存储-可视化监控-AI分析-信息获取”的高效闭环。
二、OpenCode环境搭建
本方案以Ubuntu22.04 操作系统为实操环境,OpenCode 同时支持 macOS/Windows/Linux 多平台,前置环境需满足:
- 系统权限:具备 root / 管理员权限,可执行服务配置、目录创建等操作;
- 基础依赖:Linux 需预装
curl,macOS 需安装brew,Windows 需安装choco/scoop包管理工具; - 端口预留:提前预留 4096 端口(可自定义),确保端口未被占用且防火墙放行;
- 网络要求:能正常访问 OpenCode 官网及大模型供应商接口。
2.1 OpenCode安装步骤
官网下载地址:https://opencode.ai/zh/download
OpenCode 支持 macOS / Windows / Linux 多平台安装。
通用一键安装脚本这是最简单的方法:
curl -fsSL https://opencode.ai/install | bash
安装完成后,你应该能通过命令行运行:
opencode --version
如果输出类似 1.2.14 这种的版本号信息表示安装成功。
macOS / Linux
brew install opencode
或者:
npm install -g opencode-ai
Windows
choco install opencode
或者:
scoop bucket add extras
scoop install extras/opencode
2.2 Web控制台启动与访问校验
为了降低CLI的使用难度,我们选择开启Web模式进行使用,
opencode web --hostname 0.0.0.0 --port 8000

使用这样的方式可以启动web控制台,但是问题是它是前台启动,关闭和重启不是很方便,所以我们写一个opencode-web.service,然后用systemd进行管理,这样的话我们后续更新skills和mcp直接可以使用systemd管理就很方便。
vim /etc/systemd/system/opencode-web.service
[Unit]
Description=OpenCode Web Console(仅启动Web工作台)
After=network.target
[Service]
Type=simple
User=root
# 配置密码:
Environment="OPENCODE_SERVER_PASSWORD=password"
ExecStart=/root/.opencode/bin/opencode web --hostname 0.0.0.0 --port 4096
StandardOutput=append:/var/log/opencode/opencode-web.log
StandardError=append:/var/log/opencode/opencode-web-error.log
Restart=on-failure
RestartSec=5s
UMask=0022
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl start opencode-web.service
这样我们就可以访问 http://ip:4096 端口进行访问服务了(和在windows启动.exe服务是一样的)

2.3 大模型配置
OpenCode 支持对接第三方大模型或使用自带免费模型,配置步骤如下:
- 进入 Web 控制台,点击左下角「设置」→「供应商」;
- 选择大模型供应商(本方案演示使用 OpenCode 自带免费大模型,也可选择硅基流动等第三方平台);
- 若对接第三方大模型,在对应供应商模块填写 API 密钥,保存后即可完成配置。
点击左下角设置,点击供应商,配置大模型,我选择接入硅基流动,毕竟白嫖了一点现金券,也可以使用opencode自带的免费大模型,也可以完成相关操作,本方案也会用免费的大模型为大家演示:

填写API密钥保存即可,在模型里,可以自己选择相关大模型进行使用。

2.4 环境初始化与校验
创建用于opencode初始化的目录:
mkdir /opt/opencode
# 用于存放opencode初始化数据
mkdir /opt/opencode/.opencode
在opencode上打开目录:

打开后,在Build模式下,执行
/init
进行初始化,虽然我们不用什么Agents,但是我们需要这个目录作为我们存放我们的skills。执行完成后,在Linux端重启opencode-web。


右侧就会出现相关的文件和目录,代表我们初始化完成。
三、Prometheus Skills开发
3.1 Skills核心逻辑
这套 Prometheus Skills 的核心就四个部分:
- 先画像:先判断当前指标体系是
node_exporter / categraf / mixed,并确定优先聚合标签(优先ident,否则用instance)。 - 先估算:任何可能“高基数/大范围”的查询,先估算返回规模(series 数、点位数),避免一上来就
query_range拉爆。 - 默认摘要:默认只返回统计摘要(min/max/avg/p95/last/trend + top series),不返回全量点位。
- 按需下钻:用户明确需要时才返回 raw,而且 raw 也要截断+采样(只给 head/tail 或均匀采样的一小段)。
一句话总结:profile → estimate → summary → analyze → raw(按需)。
所以为什么这么节省token已经很明显了:
- 默认不回全量 points(最大头的 token 消耗被砍掉)
- 先估算再查询(避免一次错误查询把对话“炸穿”)
- 按需下钻(只在需要的时候付费)
3.2 Skills具体编写
这里先展示出skills文档,因为我们既然使用了夜莺,夜莺的生态也很完善,夜莺的Categraf采集器功能也非常强大,所以我们的skills做了exporter采集器与Categraf采集器的适配,支持两种数据的查询功能。
---
name: prometheus
description: "Prometheus 细颗粒度查询与诊断技能(原生 Prom + 适配夜莺 Categraf 指标),用「先估算、再查询、默认摘要、按需取 raw」的方式完成监控排障,避免一次性拉取高基数/大时间范围数据导致 token 爆炸。"
license: MIT
compatibility: opencode
metadata:
audience: 运维工程师
workflow: 监控
side_effect: read_only
token_policy: summary_first
version: 1.0.0
---
# Prometheus Skill(细颗粒度 + Categraf 适配)
## 你应该如何使用本 Skill(给 AI 的路由规则)
**默认流程(强制优先级,从上到下):**
1) **先探测指标画像**:`prom_detect_profile`(判断 node_exporter / categraf / mixed,以及优先聚合标签 ident/instance,如果用户已经明确说“这是 Categraf 指标/这是 node_exporter”,跳过 profile 探测进入下一阶段)
2) **先估算返回规模**:`prom_estimate_cardinality`(避免高基数/点位爆炸)
3) **再做查询**:优先 `prom_range_query`(`result_mode=summary`)或 `prom_instant_query`(当前值)
4) **需要解释/诊断**:把 summary 结果交给 `analyze_trend` / `promql_optimize` / `generate_promql`
5) **只有在明确需要**才用 `result_mode=raw`,且 raw 也会被截断与采样
> 重要:任何时候都不要“直接 query_range 返回全量矩阵”。先 profile → estimate → summary。
---
## 适配说明:原生 Prom + 夜莺 Categraf
你的 Prometheus 是原生 Prom,但数据里可能同时存在:
- **node_exporter 风格指标**:`node_cpu_seconds_total`、`node_memory_*`、`node_filesystem_*`、`node_network_*` 等
- **Categraf/Telegraf 风格指标**(夜莺生态常见):`cpu_usage_idle`、`mem_used_percent`、`system_load_norm_5`、`net_drop_in` 等
本 Skill 通过 `prom_detect_profile` 自动选择 PromQL 模板:
- 若检测到 `cpu_usage_idle` / `mem_used_percent` / `system_load_norm_5` 等 ⇒ `profile=categraf_system`
- 若检测到 `node_cpu_seconds_total` / `node_memory_*` 等 ⇒ `profile=node_exporter`
- 两者都存在 ⇒ `profile=mixed`(按对象/label 决策)
**聚合标签优先级:**
- 如果 series labels 里存在 `ident` ⇒ 优先用 `ident` 聚合/筛选(更贴近夜莺对象模型)
- 否则使用 `instance`
- 若同时存在:默认 `ident`,但允许显式选择
**建议(可选但强烈推荐):**
如果你能在 Categraf 或写入链路上增加来源标签(例如 `metrics_from="categraf"` 或 `agent="categraf"`),PromQL 会更精准、更省 token(避免扫全库)。
---
## 设计目标(减少 token 与费用)
- **Summary First**:默认只返回统计摘要(min/max/avg/p95/last/trend),不返回全量 points
- **强限制**:max_range / max_series / max_points,超过自动拒绝或降采样
- **先估算**:任何可能高基数的查询先 `prom_estimate_cardinality`
- **raw 必须“截断 + 采样”**:只允许 head/tail + 每条序列最多返回有限点位
---
## 必要配置
- `prometheus_base_url`:如 `http://localhost:9090`
- `auth`(可选):
- `bearer_token`
- `basic_user` / `basic_pass`
- `timeout_seconds`:默认 10
---
## 全局默认保护参数(可覆盖)
- `max_range_seconds`: **21600**(6小时)
- `max_series`: **50**
- `max_points_per_series`: **600**
- `max_raw_points_returned_per_series`: **120**(raw 时每条最多返回点位)
- `default_result_mode`: **summary**
- `truncate_strategy`: **head_tail**
- `auto_step_policy`:
- range ≤ 15m → step=15s
- 15m~2h → step=30~60s
- 2h~6h → step=120~300s
- >6h → 拒绝(必须缩短范围或显式增大 step 并再次估算)
---
# 能力模块(Actions)
> 所有 Actions 均为 **read_only**:只调用 Prometheus HTTP API,不做任何写操作。
---
## A. 画像探测(Profile)
### 1) prom_detect_profile
**用途**:探测当前 Prom 里主机指标来自 node_exporter / categraf / mixed,并确定优先 label(ident/instance)
**输入**
- `hint_target`(可选): `{ ident?: string, instance?: string, job?: string }`
- `time_window_seconds`(可选):默认 3600(1h)
- `limit`(可选):默认 50
**行为**
- 轻量探测:只做 “指标名存在性 + 少量 series labels” 的发现,不拉 points
- 输出 profile + label_strategy + 推荐 PromQL 模板集名称
**输出**
```json
{
"profile": "node_exporter | categraf_system | mixed | unknown",
"label_strategy": { "primary": "ident | instance", "secondary": "instance | ident" },
"signals": {
"found_metrics": ["cpu_usage_idle", "mem_used_percent", "node_cpu_seconds_total"],
"found_labels": ["ident", "instance", "job"]
},
"next_step": "prom_estimate_cardinality"
}
````
---
## B. 规模估算(Cardinality / Risk)
### 2) prom_estimate_cardinality
**用途**:在真正查询前估算“会返回多少序列/点位”,给出风险等级与降基数建议
**输入**
* `query`(必填):PromQL
* `start`/`end`(可选):若提供则估算点位(否则只估算序列规模)
* `step_seconds`(可选):`auto` 或整数
* `max_series`(可选):默认 50
* `max_points_per_series`(可选):默认 600
**输出**
```json
{
"risk_level": "low | medium | high",
"estimated_series_upper_bound": 120,
"estimated_points_per_series": 360,
"suggested_step_seconds": 60,
"suggestions": [
"为 query 增加 label 过滤(job/instance/ident/namespace/pod)",
"用 topk() 或 sum by() 先聚合",
"缩短时间范围或增大 step"
]
}
```
---
## C. 取数(Query:instant / range)
### 3) prom_instant_query
**用途**:单点查询当前值/最近值;验证 PromQL 是否可用
**输入**
* `query`(必填)
* `time`(可选,默认 now)
* `timeout_seconds`(可选,默认 10)
* `result_mode`(可选:`summary|raw`,默认 summary)
* `max_series`(可选,默认 50)
**输出(summary)**
```json
{
"executed_at": "2026-02-26T12:00:00Z",
"series_count": 18,
"top_series": [
{ "labels": { "instance": "10.0.0.1:9100" }, "value": 0.92 }
],
"truncated": false,
"notes": ["如 series_count 偏大,建议加 label 过滤或 topk()"]
}
```
**输出(raw)**
* 仍会截断:最多返回 `min(max_series, 50)` 条序列;超出仅返回 head/tail + 统计
---
### 4) prom_range_query
**用途**:区间趋势查询(排障主力),默认只返回统计摘要
**输入**
* `query`(必填)
* `start`/`end`(必填)
* `step_seconds`(可选:`auto|int`,默认 auto)
* `timeout_seconds`(可选,默认 10)
* `result_mode`(可选:`summary|raw`,默认 summary)
* `max_series`(可选,默认 50)
* `max_points_per_series`(可选,默认 600)
**输出(summary)**
```json
{
"range_seconds": 3600,
"step_seconds": 60,
"points_per_series": 60,
"series_count": 12,
"top_series": [
{
"labels": { "ident": "host-a" },
"stats": { "min": 12.1, "max": 88.4, "avg": 43.2, "p50": 41.8, "p95": 81.0, "last": 55.6, "trend": "up|down|flat" }
}
],
"anomalies": [
{ "labels": { "ident": "host-a" }, "type": "spike", "at": "2026-02-26T11:33:00Z", "hint": "短时尖刺" }
],
"notes": ["需要更细点位时再改为 result_mode=raw(会采样+截断)"]
}
```
**输出(raw)**
* 每条序列最多返回 `max_raw_points_returned_per_series` 点(默认 120)
* 超出范围将按 head/tail + 均匀采样裁剪
## D. 发现(Discovery:指标/标签/series)
### 5) prom_find_metrics_by_regex
**用途**:根据正则查指标名(避免全量枚举)
**输入**
* `name_regex`(必填):如 `^node_.+_total$`
* `limit`(可选,默认 200)
**输出**
```json
{ "metric_names": ["node_cpu_seconds_total", "node_network_receive_bytes_total"], "truncated": false }
```
### 6) prom_list_label_names
**用途**:列出 label 名(限制数量)
**输入**
* `limit`(可选,默认 200)
**输出**
```json
{ "label_names": ["job","instance","ident","namespace"], "truncated": false }
```
### 7) prom_list_label_values
**用途**:列出某 label 的取值(支持 matchers 缩小范围)
**输入**
* `label_name`(必填)
* `matchers`(可选):如 `["up{job=\"node\"}"]`
* `limit`(可选,默认 200)
* `cursor`(可选:分页游标)
**输出**
```json
{ "values": ["host-a","host-b"], "next_cursor": null, "truncated": false }
```
### 8) prom_series_lookup
**用途**:查 matcher 命中的 series(只返回 labels,不拉 points)
**输入**
* `matchers`(必填):如 `["cpu_usage_idle{cpu=\"cpu-total\"}"]`
* `start`/`end`(可选,默认最近 1h)
* `limit`(可选,默认 200)
**输出**
```json
{ "series_labels": [ { "ident":"host-a","cpu":"cpu-total" } ], "truncated": false }
```
---
## E. 状态(Targets / Rules / Alerts / Metadata)
### 9) prom_targets_summary
**用途**:抓取目标摘要;默认只返回异常 targets
**输入**
* `state`(可选:`active|dropped|any`,默认 active)
* `only_problematic`(可选,默认 true)
* `filter_job`(可选)
* `limit`(可选,默认 50)
**输出**
```json
{
"total_targets": 120,
"up_targets": 118,
"down_targets": 2,
"down_list": [
{ "job":"node", "instance":"10.0.0.9:9100", "lastError":"context deadline exceeded", "lastScrape":"..." }
]
}
```
### 10) prom_alerts_summary
**用途**:当前告警;默认只返回 firing
**输入**
* `state`(可选:`firing|pending|any`,默认 firing)
* `limit`(可选,默认 50)
**输出**
```json
{ "alerts": [ { "name":"HostHighCPU", "labels": { "ident":"host-a" }, "activeAt":"..." } ] }
```
### 11) prom_rules_summary
**用途**:规则摘要;只回问题规则(firing/pending/错误)
**输入**
* `rule_type`(可选:`alerting|recording|any`,默认 any)
* `only_problematic`(可选,默认 true)
* `limit`(可选,默认 50)
**输出**
```json
{ "problem_rules": [ { "name":"HostHighCPU", "state":"firing" } ] }
```
### 12) prom_metric_metadata
**用途**:解释指标含义/type/help/unit(可用于生成面板/告警)
**输入**
* `metric`(必填)
**输出**
```json
{ "metadata": { "type":"gauge", "help":"...", "unit":"percent" } }
```
---
## F. 轻量分析(只吃 summary / 采样)
### 13) analyze_trend
**用途**:对 `prom_range_query.summary` 做趋势/异常/下一步建议(不接受全量矩阵)
**输入**
* `range_summary`(必填):来自 `prom_range_query` 的 summary
* `baseline_summary`(可选):对比基线
**输出**
```json
{
"findings": ["host-a CPU p95 在过去1小时上升明显"],
"suspected_causes": ["突发流量/线程飙升/某进程异常"],
"next_queries": [
"topk(5, 100 - cpu_usage_idle{cpu=\"cpu-total\", ident=\"host-a\"})",
"rate(node_cpu_seconds_total{mode!=\"idle\", instance=\"...\"}[5m])"
]
}
```
### 14) generate_promql
**用途**:把“问题描述”转成 1~3 条 PromQL,并给出降基数守则
**输入**
* `question`(必填)
* `context`(可选):`{ job, ident, instance, namespace, pod, service }`
* `profile`(可选):来自 `prom_detect_profile`
**输出**
```json
{
"candidates": [
{ "promql": "...", "purpose": "查看主机CPU使用率", "expected_shape": "vector|matrix" }
],
"guardrails": ["必须限制对象(ident/instance)或先聚合再细化"]
}
```
### 15) promql_optimize
**用途**:优化 PromQL(降基数、降点位、提速)
**输入**
* `promql`(必填)
* `profile`(可选)
**输出**
```json
{
"optimized_promql": ["...", "..."],
"why": ["减少 series 数量", "减少昂贵的 label join"],
"risk": ["聚合后会丢失细分维度"]
}
```
---
# Categraf 适配 PromQL 模板库(常用主机排障)
> 使用前先 `prom_detect_profile`,再按 profile 选模板;必要时补充 `ident/instance/job` 过滤。
## 1) CPU 使用率
* **categraf_system**
* `100 - cpu_usage_idle{cpu="cpu-total"}`
* (如有来源标签)`100 - cpu_usage_idle{cpu="cpu-total", metrics_from="categraf"}`
* **node_exporter**
* `100 - avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100`
## 2) Load(归一化 5min)
* **categraf_system**
* `system_load_norm_5`
* **node_exporter(示例)**
* `node_load5 / count by(instance)(node_cpu_seconds_total{mode="idle"})`
## 3) 内存使用率
* **categraf_system**
* `mem_used_percent`
* **node_exporter**
* `(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100`
## 4) 磁盘 inode 使用率
* **categraf_system(示例写法)**
* `disk_inodes_used / disk_inodes_total * 100`
* **node_exporter**
* `(1 - node_filesystem_files_free / node_filesystem_files) * 100`
## 5) 网络丢包(1m 增量)
* **categraf_system**
* `increase(net_drop_in[1m])`
* `increase(net_drop_out[1m])`
* **node_exporter**
* `increase(node_network_receive_drop_total[1m])`
* `increase(node_network_transmit_drop_total[1m])`
## 6) TCP TIME_WAIT
* **categraf_system**
* `netstat_tcp_time_wait`
* **node_exporter**
* `node_sockstat_TCP_tw`
---
# PromQL 降基数守则(必须遵守)
1. **先限制对象**:优先加 `ident="xxx"` 或 `instance="x:port"` 或 `job="xxx"`
2. **先聚合再下钻**:`sum by(ident)` / `avg by(instance)` → 再细分 label
3. **先 topk 再展开**:`topk(10, <expr>)`
4. **对高频 counter 用 rate**:`rate(x_total[5m])` / `increase(x_total[1m])`
5. **区间查询必须控制 step**:step 不要过小;默认 auto
---
# 失败与拒绝策略(省 token + 防事故)
* 若 `end-start > max_range_seconds`:拒绝并要求缩短范围/增大 step,再 `prom_estimate_cardinality`
* 若估算 `risk_level=high`:拒绝直接查询,先返回 `suggestions`(加过滤/聚合/topk)
* 若 `series_count > max_series`:summary 仅返回 top_series,并提示如何缩小
* raw 模式:永远截断与采样,不返回全量矩阵
---
# 示例工作流(推荐)
## 示例 1:排查 host-a CPU 是否异常(混合环境)
1. `prom_detect_profile(hint_target={ident:"host-a"})`
2. `generate_promql(question="host-a CPU 使用率", context={ident:"host-a"}, profile=...)`
3. `prom_estimate_cardinality(query=候选PromQL, start=now-1h, end=now)`
4. `prom_range_query(query=..., start=..., end=..., result_mode="summary")`
5. `analyze_trend(range_summary=...)`
## 示例 2:告警 firing,先看 Prom 自身 targets 是否有问题
1. `prom_alerts_summary(state="firing")`
2. `prom_targets_summary(only_problematic=true, filter_job="node")`
3. 再回到 query/analysis 流程定位根因
---
# Prometheus API 参考(实现提示)
本 Skill 可能用到的只读端点:
* `/api/v1/query`
* `/api/v1/query_range`
* `/api/v1/series`
* `/api/v1/labels`
* `/api/v1/label/<name>/values`
* `/api/v1/targets`
* `/api/v1/rules`
* `/api/v1/alerts`
* `/api/v1/metadata`
---
## 输出约定(统一)
所有 actions 输出必须为结构化 JSON,且尽量短:
* 默认返回 `summary`
* 若返回 raw,必须包含 `truncated=true/false` 与裁剪说明
* 永远避免把全量 points 直接拼到自然语言里
```
如果你愿意,我还可以基于你们实际环境再“贴合一下”:你随便给我两条样例(复制 Prom 查询结果里的 labels 就行):
- 一条 Categraf 指标(比如 `cpu_usage_idle` 任意一条 series labels)
- 一条 node_exporter 指标(比如 `node_cpu_seconds_total` 任意一条 series labels)
我就能把 `prom_detect_profile` 的“指标画像判定”和“聚合标签优先级”做得更准(比如你们是不是用 `ident`、还是 `host`/`hostname` 之类)。
```
3.3 Skills测试验证
上面给出了skills,那么这个文件在哪里编写呢?
pwd
/opt/opencode
# 创建skills目录与存放Prometheus的skills的目录
mkdir -p .opencode/skills/prometheus
# 编写Prometheus的skills
vim .opencode/skills/prometheus/SKILL.md
# 将3.2skills文件粘入
重启opencode:
systemctl restart opencode-web.service
这个时候在web上询问AI,我有哪些skills可用:

我们来测试一下:
Prometheus地址是192.168.16.2:9090,查询一下服务器内存剩余量。

证明我们的skills是可用的状态,该阶段任务完成。
四、夜莺MCP与Skills对接
夜莺的部署:https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v8/install/binary/
4.1 Skills核心逻辑
这套 Nightingale Skills 的定位很明确:
夜莺负责“平台对象”(规则/告警/目标/屏蔽/通知/订阅/流水线/用户组),Prometheus 负责“指标取数与趋势分析”。
所以 Nightingale Skills 的核心逻辑可以总结成四句话:
- 先意图路由:用户问什么,就只调用对应 toolset(通常 1~2 次 MCP 调用解决)。
- 默认摘要输出:list 只返回 topN + 字段白名单,不输出完整 JSON、不翻到底。
- 按需下钻:只有用户点名某条 event_id/rule_id/mute_id 才 get 详情。
- 需要验证表达式才联动 Prometheus:比如“为什么触发/命中哪些机器/过去 1h 趋势”,才把 promql 交给 Prometheus Skills(estimate → summary → analyze)。
一句话总结:
Nightingale:查对象、查关联;Prometheus:查数据、做趋势。
4.2 Skills具体编写
---
name: nightingale
description: "Nightingale(夜莺)MCP 细颗粒度平台技能:基于官方 toolsets(alerts/targets/datasource/mutes/notify_rules/alert_subscribes/event_pipelines/users/busi_groups)。默认 summary_first + 字段白名单 + 分页,尽量少调用、少返回,避免 token 爆炸;当需要验证告警表达式/趋势分析时按需联动 Prometheus skills(estimate→summary→raw按需)。"
license: MIT
compatibility: opencode
metadata:
audience: 运维工程师
workflow: 告警管理/监控目标排障/事件响应/团队协作
side_effect: read_only_by_default
token_policy: summary_first
version: 1.0.0
---
# Nightingale Skill(MCP 细颗粒度 + 联动 Prometheus)
## 1. 总体设计目标(省 token / 省调用 / 可联动)
- **Summary First**:默认只输出“摘要 + 关键证据”,不输出大段 JSON、不贴长文本字段。
- **最少工具调用**:先根据用户意图选择最小动作集(通常 1~2 个 MCP 调用)。
- **字段白名单**:所有 list/get 的输出做字段裁剪,避免 MCP 返回体过大。
- **分页与 topN**:list 默认只返回前 N 条,并提供 next/继续方式(不自动翻到底)。
- **必要时联动 Prometheus**:当用户提出“验证表达式/看趋势/为什么触发/是否命中”等,才把 PromQL 丢给 Prometheus skills(强制 estimate→summary→raw按需)。
---
## 2. 路由规则(AI 如何决定调用哪些 MCP 工具)
### 2.1 强制优先级(从上到下)
1) **用户明确问告警规则**(“有哪些规则/规则详情/某条规则怎么写的/阈值是多少/规则里通知到哪”)
- 优先:`alerts.list_alert_rules` 或 `alerts.get_alert_rule`
2) **用户明确问活跃告警/历史告警**(“当前哪些在 firing/过去24h有哪些/某事件id详情”)
- 活跃:`alerts.list_active_alerts` / `alerts.get_active_alert`
- 历史:`alerts.list_history_alerts` / `alerts.get_history_alert`
3) **用户明确问监控目标/主机**(“业务组有哪些主机/离线目标/某实例状态”)
- `targets.list_targets`
4) **用户明确问数据源**(“有哪些 datasource/某数据源 id 是啥”)
- `datasource.list_datasources`
5) **用户明确要做事件响应**(“创建/更新屏蔽/查看屏蔽/看通知规则/看流水线执行记录”)
- 屏蔽查询:`mutes.list_mutes` / `mutes.get_mute`
- 屏蔽写入(有副作用,默认禁用,见 2.3 门禁):`mutes.create_mute` / `mutes.update_mute`
- 通知:`notify_rules.list_notify_rules` / `notify_rules.get_notify_rule`
- 流水线:`event_pipelines.*`
6) **用户明确问订阅/团队协作**
- 订阅:`alert_subscribes.*`
- 用户/团队:`users.*`
7) **用户只说“夜莺里看看/帮我排查”但没有明确对象**
- 默认只做:`alerts.list_alert_rules`(或先 `busi_groups.list_busi_groups` 确定 gid 后 list)
- 不自动拉 targets/notify/mutes/pipelines(除非用户明确要)
### 2.2 业务组 gid 决策
- 用户给了 gid:直接用
- 用户给业务组名但没 gid:`busi_groups.list_busi_groups` 找匹配
- 用户没给任何业务组信息:
- 先 `busi_groups.list_busi_groups`(只返回 topN id/name)
- 再提示用户选择(或默认选 “Default Busi Group/第一个可访问业务组”)
> **缓存策略**:同一轮会话里,业务组列表/数据源列表获取一次后应复用,不要重复 list。
### 2.3 写操作门禁(默认只读)
本技能默认 **read_only**。只有满足以下条件才允许触发写操作:
- 用户明确包含:**动作词 + 对象词**
- 动作词:创建/新增/更新/修改/变更/调整
- 对象词:屏蔽规则(mute)
- 且用户提供必要参数(起止时间/匹配条件/业务组 gid 等)
允许写入的仅限:
- `mutes.create_mute`
- `mutes.update_mute`
> 注意:**告警规则的创建/修改/删除**不在当前 MCP 工具范围(无对应 tool),不得“脑补”写操作。
---
## 3. 全局保护参数(默认值,可按需覆盖)
- `default_limit`: 50(任何 list 默认最多 50)
- `hard_max_limit`: 200(用户明确要求更多时上限 200)
- `max_text_len`: 256(名称/描述/备注等长字段截断)
- `truncate_strategy`: topN + 截断 + 字段白名单
- `default_time_range`:
- 活跃告警:默认最近 1h(或不指定让后端默认)
- 历史告警:默认最近 24h(用户没给时间范围时)
- `output_mode`:
- 默认只输出摘要表(id/name/state/time/关键标签/简短原因)
- 仅当用户点名某 id 才 get 详情
---
## 4. 官方工具集(Actions)与“省 token 使用法”
> 所有 Actions 均映射你贴的官方工具,下面是“何时调用 + 最短输出形态”。
### A) busi_groups
#### A1. `busi_groups.list_busi_groups`
**用途**:列出当前用户可访问业务组(只输出 id + name)
**调用时机**:用户没提供 gid 或业务组名需要匹配时
**输出(summary)**:
- `[{id,name}]` + `truncated`
---
### B) alerts(告警/规则)
#### B1. `alerts.list_active_alerts`
**用途**:列出当前活跃告警(支持过滤)
**输出(summary)字段白名单建议**:
- `event_id, rule_id, rule_name, severity, state, first_trigger_time, last_eval_time, tags(top5), brief_text`
**策略**:只返回 topN;如需某条详情再 `get_active_alert`
#### B2. `alerts.get_active_alert`
**用途**:按事件 ID 获取活跃告警详情
**策略**:只在用户点名 event_id 或需要证据时调用;输出裁剪(不贴长原文)
#### B3. `alerts.list_history_alerts`
**用途**:列出历史告警(默认最近 24h)
**输出字段**:`event_id, rule_id, rule_name, severity, fired_at, recovered_at, duration, tags(top5)`
#### B4. `alerts.get_history_alert`
**用途**:按事件 ID 获取历史告警详情(同 get_active_alert 策略)
#### B5. `alerts.list_alert_rules`
**用途**:列出业务组告警规则
**输出(summary)字段白名单建议**:
- `rule_id, name, enabled, severity(if any), datasource_hint(if any), update_at, brief_expr(截断)`
**策略**:默认只列 topN;用户要“某条规则详情/表达式/通知关联”才 `get_alert_rule`
#### B6. `alerts.get_alert_rule`
**用途**:获取告警规则详情(用于:查看表达式/评估周期/for/标签/通知关联等)
**输出(details)字段白名单建议**:
- `id, name, enabled, expr/promql, for, every/eval_interval, datasource_hint, notify_rule_ids(if any), tags/labels(top10), update_at`
---
### C) targets(监控目标)
#### C1. `targets.list_targets`
**用途**:列出被监控主机/目标(支持过滤,如离线超过 5 分钟)
**输出字段建议**:
- `ident/instance, job(if any), state(up/down), last_scrape, last_error(截断), labels(top8)`
**策略**:默认只回 “down/问题目标”;用户明确要全量再扩大过滤。
---
### D) datasource
#### D1. `datasource.list_datasources`
**用途**:列出所有可用数据源(用于把规则 expr 交给 Prometheus skills 时选择 base_url)
**输出字段**:`id,name,type,url(可选/脱敏), is_default`
**策略**:只有当用户明确要“数据源列表/规则验证需要确定数据源”时才调用。
---
### E) mutes(屏蔽规则)
#### E1. `mutes.list_mutes`
**用途**:列出业务组的告警屏蔽规则
**输出字段**:`id,name,enabled,start,end,matchers(top5),creator,update_at`
#### E2. `mutes.get_mute`
**用途**:屏蔽规则详情(只在点名 id 时)
#### E3. `mutes.create_mute`(写)
**门禁**:仅当用户明确“创建屏蔽规则”且提供关键参数时才调用
**输出**:`created_id + 摘要(时间窗/匹配条件/业务组)`(不贴完整 payload)
#### E4. `mutes.update_mute`(写)
**门禁**:仅当用户明确“更新屏蔽规则”且提供 id 与变更内容
**输出**:`id + diff_summary`
---
### F) notify_rules(通知规则)
#### F1. `notify_rules.list_notify_rules`
**用途**:列出所有通知规则(用于“这条规则通知到哪/有哪些通知规则”)
**输出字段**:`id,name,channels/receivers(摘要),enabled,update_at`
#### F2. `notify_rules.get_notify_rule`
**用途**:通知规则详情(仅点名 id 时)
---
### G) alert_subscribes(订阅)
#### G1. `alert_subscribes.list_alert_subscribes`
#### G2. `alert_subscribes.list_alert_subscribes_by_gids`
#### G3. `alert_subscribes.get_alert_subscribe`
**策略**:同上:list 先摘要,点名再 get。
---
### H) event_pipelines(事件流水线)
#### H1. `event_pipelines.list_event_pipelines`
#### H2. `event_pipelines.get_event_pipeline`
#### H3. `event_pipelines.list_event_pipeline_executions`
#### H4. `event_pipelines.list_all_event_pipeline_executions`
#### H5. `event_pipelines.get_event_pipeline_execution`
**策略**:默认只输出 “pipeline id/name/最近执行状态/失败摘要”;排查某次执行再 get_execution。
---
### I) users(用户/团队)
#### I1. `users.list_users`
#### I2. `users.get_user`
#### I3. `users.list_user_groups`
#### I4. `users.get_user_group`
**策略**:只返回必要字段(id/name/email(可选脱敏)/members_count);点名再取成员详情。
---
## 5. 联动 Prometheus skills(何时联动 + 怎么联动)
### 5.1 触发联动的典型语句
- “这条规则为什么触发?”
- “这个告警表达式命中了哪些机器?”
- “帮我验证一下表达式在过去 1 小时趋势”
- “阈值是不是太低/是否波动?”
### 5.2 联动流程(强制)
1) `alerts.get_alert_rule` 获取 `expr/promql` + (如果有)`datasource_hint`
2) (可选)`datasource.list_datasources` 仅在需要把 hint 映射到 Prom base_url 时调用
3) 调 Prometheus skills(必须遵守它的 guardrails):
- `prom_estimate_cardinality(query=expr, start/end, step=auto)`
- `prom_range_query(result_mode=summary)`
- `analyze_trend(range_summary=...)`
4) 返回最终结论:只用 summary 统计(min/max/p95/last/trend/top_series),不贴全量点位
> 约束:Prometheus raw 模式必须显式请求,并且截断+采样。
---
## 6. 输出模板(固定短格式)
### 6.1 规则列表(默认)
- `gid/name`
- `top_rules`: `id | name | enabled | datasource | brief_expr | updated_at`
- `truncated/next_hint`
### 6.2 活跃告警(默认)
- `count`
- `top_alerts`: `event_id | rule_name | severity | since | tags(top5) | brief`
- `next_hint`(过滤条件建议:severity/gid/关键标签)
### 6.3 目标状态(默认)
- `down_count/up_count`
- `down_list`: `ident/instance | last_scrape | last_error(截断)`
---
## 7. 失败与降级策略(防 token 爆炸 + 防误操作)
- list 返回过多:只给 topN + 提示过滤(关键词/时间范围/gid/状态)
- get 返回字段过大:字段白名单 + 截断长字段
- toolsets 未启用(例如你只启用了 alerts,busi_groups,却调用 targets):提示用户在 MCP 启动参数里启用对应 toolsets(例如追加 `--toolsets targets`)
- 写操作:若 read_only=true 或用户意图不明确 → 拒绝并给“如何触发”的一句示例
---
## 8. 推荐工作流示例(最少调用)
### 示例 1:Default Busi Group 有哪些告警规则?
1) `busi_groups.list_busi_groups`(仅首次)
2) `alerts.list_alert_rules(gid=...)`
### 示例 2:某条告警为什么触发?验证表达式命中情况(联动 Prom)
1) `alerts.get_alert_rule(id=...)`
2) Prometheus:`prom_estimate_cardinality` → `prom_range_query(summary)` → `analyze_trend`
### 示例 3:创建 2 小时屏蔽(写操作门禁)
1) 判断用户明确“创建屏蔽规则”且给出:gid + matchers + start/end
2) `mutes.create_mute`
3) 返回 created_id + 摘要
---
4.3 OpenCode接入夜莺MCP
首先要准备夜莺平台的token,点击个人信息,进入token管理,创建token并保存。

夜莺官方给出的夜莺mcp接入opencode的方式我尝试了一下,但是在opencode上并不支持:

所以,我重新查找了一下资料,opencode接入mcp是在**.opencode/opencode.json** 里进行添加,所以我们就用这种方式进行添加:
pwd
/opt/opencode
root@baidu:/opt/opencode# vim .opencode/opencode.json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"nightingale_read": {
"type": "local",
"command": [
"npx", "-y", "@n9e/n9e-mcp-server", "stdio",
"--toolsets", "alerts",
"--toolsets", "targets",
"--toolsets", "datasource",
"--toolsets", "mutes",
"--toolsets", "notify_rules",
"--toolsets", "alert_subscribes",
"--toolsets", "event_pipelines",
"--toolsets", "users",
"--toolsets", "busi_groups"
],
"enabled": true,
"timeout": 20000,
"environment": {
"N9E_BASE_URL": "http://ip:17000",
"N9E_TOKEN": "token",
"N9E_READ_ONLY": "true"
}
}
}
}
- “N9E_BASE_URL”: “http://ip:17000”,夜莺的访问IP地址
- “N9E_TOKEN”: “token”,刚刚申请的token。
- “N9E_READ_ONLY”: “true”,是否为只读模式。(是否开启看个人,关闭只读后续可让AI自动创建告警规则)
重启opencode-web后,在控制台上右上角状态信息可以查看接入的mcp:

4.4 夜莺Skills与MCP联动测试
与Prometheus同样的道理:
pwd
/opt/opencode
# 创建skills目录与存放夜莺的skills的目录
mkdir -p .opencode/skills/nightingale
# 编写夜莺的skills
vim .opencode/skills/nightingale/SKILL.md
# 将4.2 skills文件粘入
重启opencode:
systemctl restart opencode-web.service
这个时候在web上询问AI,“我有哪些skills可用” :

我们也可以看到AI查询的过程,显示查询skills下所有的目录,看这些目录下是否存在SKILL.md,若果有就去读取,并变成可以skills。
测试mcp,我们问一下夜莺上目前都配置了哪些告警规则,告警阈值都是多少。

测试成功,与夜莺上配置的内容相符。

夜莺的mcp功能如下:(由夜莺官方提供)
| 模块 | 工具名称 | 工具说明 |
|---|---|---|
| alerts | list_active_alerts | 列出当前活跃告警,支持过滤条件 |
| alerts | get_active_alert | 根据事件 ID 获取活跃告警详情 |
| alerts | list_history_alerts | 列出历史告警,支持过滤条件 |
| alerts | get_history_alert | 获取历史告警详情 |
| alerts | list_alert_rules | 列出业务组的告警规则 |
| alerts | get_alert_rule | 获取告警规则详情 |
| targets | list_targets | 列出被监控主机 / 目标,支持过滤条件 |
| datasource | list_datasources | 列出所有可用数据源 |
| mutes | list_mutes | 列出业务组的告警屏蔽规则 |
| mutes | get_mute | 获取告警屏蔽规则详情 |
| mutes | create_mute | 创建告警屏蔽规则 |
| mutes | update_mute | 更新告警屏蔽规则 |
| notify_rules | list_notify_rules | 列出所有通知规则 |
| notify_rules | get_notify_rule | 获取通知规则详情 |
| alert_subscribes | list_alert_subscribes | 列出业务组的告警订阅 |
| alert_subscribes | list_alert_subscribes_by_gids | 列出多个业务组的订阅 |
| alert_subscribes | get_alert_subscribe | 获取订阅详情 |
| event_pipelines | list_event_pipelines | 列出所有事件流水线 |
| event_pipelines | get_event_pipeline | 获取事件流水线详情 |
| event_pipelines | list_event_pipeline_executions | 列出指定流水线的执行记录 |
| event_pipelines | list_all_event_pipeline_executions | 列出所有流水线的执行记录 |
| event_pipelines | get_event_pipeline_execution | 获取执行记录详情 |
| users | list_users | 列出用户,支持过滤条件 |
| users | get_user | 获取用户详情 |
| users | list_user_groups | 列出用户组 / 团队 |
| users | get_user_group | 获取用户组详情(包含成员) |
| busi_groups | list_busi_groups | 列出当前用户可访问的业务组 |
五、全流程实战展示-Prometheus与夜莺联动查询
查询指令:
Prometheus地址为192.168.16.2:9090,查询一下服务器内存剩余状态,是否会触发夜莺上的内存阈值告警。

可以查看到,整个过程如下:
- 查看了Prometheus的skills,进行了数据调取
- 调用了夜莺的mcp查看夜莺上的告警规则
- 分析了二者获取到的数据进行分析
- 最终输出了结果
可以看到,我们想让Prometheus与夜莺进行联动的功能已经完美实现了。
我们再让AI深度分析一下,这次我们拿GLM5大模型进行分析:
Prometheus地址为192.168.16.2,分析Prometheus里的服务器状态,与夜莺里的告警规则,帮我分析一下我还缺少哪些告警规则。
下面均为AI分析结果

分析结果
当前夜莺告警规则(共2条)
| 规则名称 | 表达式 | 问题 |
|---|---|---|
| 系统5分钟内负载较高 | node_load5 > 4 |
✅ 可用,但建议按CPU核数归一化 |
| 系统内存不足1G | node_memory_MemAvailable_bytes < 1 |
⚠️ 表达式错误:单位是字节,应该是 < 1073741824(1GB) |
Prometheus可用的node_exporter指标分类
| 类别 | 可监控项 | 当前告警 |
|---|---|---|
| CPU | 使用率、负载、上下文切换 | ❌ 无使用率告警 |
| 内存 | 可用内存、使用率、Swap | ✅ 有(但表达式错误) |
| 磁盘空间 | 磁盘使用率、inode使用率 | ❌ 缺失 |
| 磁盘IO | 读写延迟、IO等待 | ❌ 缺失 |
| 网络 | 丢包、错误、流量 | ❌ 缺失 |
| TCP | 连接数、TIME_WAIT、重传 | ❌ 缺失 |
| 文件描述符 | fd使用率 | ❌ 缺失 |
| 系统压力 | PSI压力、OOM | ❌ 缺失 |
| 连接跟踪 | conntrack表使用率 | ❌ 缺失 |
| 时钟 | 时钟偏移 | ❌ 缺失 |
建议新增的告警规则
高优先级(必须)
# 1. CPU使用率过高
- name: CPU使用率过高
prom_ql: 100 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100 > 80
for: 300s
# 2. 磁盘空间不足
- name: 磁盘空间不足
prom_ql: (1 - node_filesystem_avail_bytes / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) * 100 > 85
for: 300s
# 3. 磁盘inode不足
- name: 磁盘inode不足
prom_ql: (1 - node_filesystem_files_free / node_filesystem_files) * 100 > 85
for: 300s
中优先级(建议)
# 4. 网络丢包
- name: 网络接收丢包
prom_ql: rate(node_network_receive_drop_total[5m]) > 10
# 5. TCP TIME_WAIT过多
- name: TCP TIME_WAIT过多
prom_ql: node_sockstat_TCP_tw > 5000
# 6. 文件描述符不足
- name: 文件描述符使用率过高
prom_ql: node_filefd_allocated / node_filefd_maximum * 100 > 80
# 7. 磁盘IO等待过高
- name: 磁盘IO等待过高
prom_ql: rate(node_disk_io_time_seconds_total[5m]) > 0.8
低优先级(可选)
# 8. OOM发生
- name: 系统发生OOM
prom_ql: increase(node_vmstat_oom_kill[5m]) > 0
# 9. 连接跟踪表接近满
- name: conntrack表使用率过高
prom_ql: node_nf_conntrack_entries / node_nf_conntrack_entries_limit * 100 > 80
# 10. 时钟偏移过大
- name: 时钟偏移过大
prom_ql: abs(node_timex_offset_seconds) > 0.05
总结:当前只有2条规则,覆盖率约10%。建议至少补充CPU使用率、磁盘空间、网络丢包、TCP连接监控。
六、方案总结
6.1 方案总结
其实这就是一个个人探索过程,运维行业感觉会和现在的开发一样,都要拥抱AI,学习AI,适应AI,用AI提高运维效率这是发展的必然趋势了,就看谁能把AI用的更好,用的更高效就是下一阶段需要我们共同探索的方向了。
6.2 下一阶段目标:构建微服务全维度AIOps闭环
现在方案仅覆盖 Metrics 维度,下一阶段将整合Trace(链路)、Log(日志)、Metric三大可观测性支柱,搭建现代微服务体系的全套 AIOps,核心方向:
- 数据层统一:基于 OpenTelemetry 实现 Trace/Log/Metric 的标准化采集,通过统一标签(service/instance/traceID 等)打通多维度数据关联;
- 技能层扩展:为 Trace(链路摘要 / 异常定位)、Log(关键词摘要 / 异常采样)开发轻量化 Skills,延续 “summary first” 原则控制 token 成本;
- AI 闭环落地:联动多维度 Skills,实现 “指标异常→链路卡点定位→日志根因分析” 的端到端 AI 运维,保持轻量化、低定制成本的核心特性。
那么再见,喜欢的可以点点赞哦,我们评论区见。
更多推荐



所有评论(0)