news 2026/9/14 17:48:12

Nightingale 订阅规则 HTTP API 实战指南:面向外部 A2A Agent 与 curl 调用的完整接口手册

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Nightingale 订阅规则 HTTP API 实战指南:面向外部 A2A Agent 与 curl 调用的完整接口手册

Nightingale 订阅规则 HTTP API 实战指南:面向外部 A2A Agent 与 curl 调用的完整接口手册

【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale

本文基于aiagent/skill/embedded/builtin/alert-subscribe-copilot/http-api.md编写,并辅以仓库源码(路由注册、处理器实现、模型定义、内存缓存)进行纵深展开。

订阅规则(Alert Subscribe)是 Nightingale 告警通知阶段的关键机制:按条件筛选告警事件后,克隆一份副本并按订阅配置改写,再走一遍通知链路,从而实现跨团队抄送、告警升级(持续 N 分钟未处理后通知负责人)等典型场景。Nightingale 为这类订阅规则提供了一套完整的 HTTP REST API,专门供站外 A2A Agent 集成为用户生成可执行的 curl 命令使用。读完本文,你将掌握全部 7 个端点的请求/响应形态、Bearer Token 鉴权与两级权限模型、Tryrun 逐门校验机制,以及绕过 API 直改数据库的兜底方案。

订阅规则与这套 HTTP API 的定位

在动手调接口之前,先明确这套 API 的服务对象。仓库中aiagent/skill/embedded/builtin/alert-subscribe-copilot/SKILL.md明确写道:

You are the in-app AI assistant for n9e, running inside the n9e process and already authenticated as the current user.Operate directly via the built-in tools — do not log in, do not call HTTP APIs, and do not use http_fetch against your own endpoints(the HTTP flow inhttp-api.mdis for external A2A agents).

也就是说,Nightingale 内部 AI 助手(in-app assistant)禁止调用本套 HTTP 端点,它应使用内置 Function Calling 工具(如create_alert_subscribeupdate_alert_subscribe,见 aiagent/tools/subscribe.go)。本套 HTTP API 是为外部 A2A Agent 和手工 curl 调用而设计的公开能力。

订阅本身发生在通知阶段alert/dispatch/dispatch.go中的handleSubs):原始事件照常走它自己的通知路径,每个匹配的订阅会克隆一份事件副本、按订阅配置改写后再跑一遍通知链路。订阅是叠加式的——它不拦截、不替换原始通知。理解这一点,是正确使用增删改查接口的前提。

接口总览:七个端点一张表

所有端点均挂在/api/n9e前缀下,路由定义集中在 center/router/router.go。完整清单如下(与原始文档保持一致):

操作方法路径说明
列表(跨业务组)GET/api/n9e/busi-groups/alert-subscribes返回当前用户可见业务组下的订阅
列表(单业务组)GET/api/n9e/busi-group/:id/alert-subscribes指定业务组:id下的订阅
详情GET/api/n9e/alert-subscribe/:sid按订阅 ID 获取完整配置
创建POST/api/n9e/busi-group/:id/alert-subscribesBody 为单个AlertSubscribeJSON 对象;group_id取自 URL
更新PUT/api/n9e/busi-group/:id/alert-subscribesBody 为数组[{...}](与创建相反);按显式字段列表更新,但仍建议先 GET 详情、改完整个对象再 PUT
删除DELETE/api/n9e/busi-group/:id/alert-subscribesBody:{"ids":[1,2,3]}
TryrunPOST/api/n9e/alert-subscribe/alert-subscribes-tryrunBody:{"event_id":<历史事件ID>,"config":{...订阅草稿...}};逐门校验匹配条件,新版本(notify_version=1)还会真实发送通知规则测试——编辑后先 Tryrun 再保存

从路由源码可以看到每个端点的完整中间件链,例如:

pages.GET("/busi-groups/alert-subscribes", rt.auth(), rt.user(), rt.perm("/alert-subscribes"), rt.alertSubscribeGetsByGids) pages.POST("/busi-group/:id/alert-subscribes", rt.auth(), rt.user(), rt.perm("/alert-subscribes/add"), rt.bgrw(), rt.alertSubscribeAdd) pages.PUT("/busi-group/:id/alert-subscribes", rt.auth(), rt.user(), rt.perm("/alert-subscribes/put"), rt.bgrw(), rt.alertSubscribePut) pages.DELETE("/busi-group/:id/alert-subscribes", rt.auth(), rt.user(), rt.perm("/alert-subscribes/del"), rt.bgrw(), rt.alertSubscribeDel) pages.POST("/alert-subscribe/alert-subscribes-tryrun", rt.auth(), rt.user(), rt.perm("/alert-subscribes/add"), rt.alertSubscribeTryRun)

请求统一要求Authorization: Bearer <token>头,token 为用户令牌(用户登录后生成)。

认证与鉴权:Bearer Token 与两级权限模型

所有端点都要求Authorization: Bearer <token>。在此基础上,权限是两级叠加的:

  1. 菜单级权限rt.perm(...)):对应/alert-subscribes/alert-subscribes/add/alert-subscribes/put/alert-subscribes/del四个操作码。实现见 center/router/router_mw.go 的perm中间件,内部调用me.CheckPerm校验。其中:

    • 查询类接口只要求读权限/alert-subscribes
    • 创建要求/alert-subscribes/add
    • 更新要求/alert-subscribes/put
    • 删除要求/alert-subscribes/del
    • Tryrun 挂在/alert-subscribes/add下(与创建同级)。
  2. 业务组级权限rt.bgro()/rt.bgrw()):对 URL 中的业务组:id做数据级校验。bgro(只读)调用CanDoBusiGroup检查是否可见;bgrw(读写)调用CanDoBusiGroup(..., "rw")检查是否具备读写权限,不满足直接返回 403forbidden。实现见 center/router/router_mw.go。

因此,创建/更新/删除订阅都需要「业务组读写权限(bgrw)+ 对应/alert-subscribes/*菜单权限」双重满足;纯列表查询只需要业务组可见(bgro)+ 读菜单权限。跨业务组列表接口alertSubscribeGetsByGids对非管理员会回退到MyBusiGroupIds只列出自己所属业务组的数据(见 center/router/router_alert_subscribe.go)。

查询类接口:列表与详情

跨业务组列表

curl -H "Authorization: Bearer <token>" \ "http://<n9e-host>/api/n9e/busi-groups/alert-subscribes"
  • 可选查询参数gids(逗号分隔的业务组 ID 列表),用于精确限定范围;不带时对非管理员自动收敛到其可见业务组。
  • 返回当前用户可见业务组下的全部订阅(服务端不做分页,由前端自行搜索分页,见alertSubscribeGetsByGids的注释 "Return all, front-end search and paging")。

单业务组列表与详情

# 列出业务组 2 下的订阅 curl -H "Authorization: Bearer <token>" \ "http://<n9e-host>/api/n9e/busi-group/2/alert-subscribes" # 获取订阅 123 的完整详情 curl -H "Authorization: Bearer <token>" \ "http://<n9e-host>/api/n9e/alert-subscribe/123"

单业务组列表与详情处理器(alertSubscribeGets/alertSubscribeGet)在返回前会做一整套填充(见 center/router/router_alert_subscribe.go):

  • FillUserGroups:把user_group_ids解析为完整用户组对象;
  • FillRuleNames:把rule_ids解析为告警规则名,规则缺失时标记"Error: AlertRule not found"
  • FillDatasourceIdsDB2FE:把数据库中的 JSON 序列化串还原为前端/API 形态的数组字段。

返回结构中的序列化字段

理解响应结构的关键在于模型 models/alert_subscribe.go 的字段定义——数据库行里存的是 JSON 字符串,API 返回的是 JSON 数组DB2FE(DB→前端/API 形态)负责这层转换:

  • datasource_ids:DB 中是 JSON 字符串,API 中是[]int64
  • severities:DB 中是 JSON 字符串,API 中是[]int
  • webhooks:DB 中是 JSON 字符串,API 中是[]string
  • extra_config:DB 中是 JSON 字符串,API 中是任意 JSON 对象;
  • tags/busi_groups:本身就是ormx.JSONArr
  • rule_ids/notify_rule_ids:使用gorm:"serializer:json"直接序列化存储。

一个典型详情响应(片段)形如:

{ "id": 123, "name": "跨团队抄送-支付核心链路", "group_id": 2, "disabled": 0, "prod": "", "cate": "", "datasource_ids": [1, 2], "rule_ids": [45, 67], "severities": [1, 2, 3], "for_duration": 600, "tags": [{"key": "team", "func": "==", "value": "pay"}], "busi_groups": [{"key": "groups", "func": "==", "value": "支付中心"}], "notify_version": 1, "notify_rule_ids": [9], "note": "支付核心链路告警升级" }

创建:POST 单对象

创建接口的 Body 是单个AlertSubscribeJSON 对象(不是数组),group_id从 URL 路径取,不读 Body。对应处理器alertSubscribeAdd(center/router/router_alert_subscribe.go)的关键行为:

  1. 从 URL 取:id作为GroupId<= 0直接 400group_id invalid
  2. 从登录态注入CreateBy/UpdateBy
  3. 调用AlertSubscribe.Add落库。
curl -X POST -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ "http://<n9e-host>/api/n9e/busi-group/2/alert-subscribes" \ -d '{ "name": "跨团队抄送-支付核心链路", "disabled": 0, "prod": "", "cate": "", "datasource_ids": [1, 2], "cluster": "0", "rule_ids": [45, 67], "severities": [1, 2, 3], "for_duration": 600, "tags": [{"key": "team", "func": "==", "value": "pay"}], "busi_groups": [{"key": "groups", "func": "==", "value": "支付中心"}], "notify_version": 1, "notify_rule_ids": [9], "note": "支付核心链路告警升级" }'

落库前的校验逻辑集中在AlertSubscribe.Verify()(models/alert_subscribe.go),外部 Agent 组装请求时应遵循这些规则:

  • severities必填,新旧版本都校验,缺省报severities is required[1,2,3]表示全部严重级别;
  • 新版本notify_version=1)要求notify_rule_ids非空,否则报no notify rules selected;且该校验会清空旧版改写字段(redefine_severity/redefine_channels/webhooks/user_group_ids/new_channels等);
  • 旧版本notify_version=0,默认)要求:若指定了user_group_ids,则new_channels(新告警通知渠道)必须指定,否则可能因告警规则未配置通知渠道而导致订阅通知发不出去;同时NotifyRuleIds会被强制置空;
  • datasource_ids为空数组会自动规范化为[0](FE 表示"全部数据源"的哨兵值),Verify中的IsAllDatasource分支处理该归一化。

更新:PUT 数组与显式字段列表

更新接口与创建相反:Body 必须是数组[{...}](可批量更新多条)。对应处理器alertSubscribePut(center/router/router_alert_subscribe.go)的语义要点:

  1. 按显式字段列表更新Update只更新服务端白名单列——name, disabled, prod, cate, datasource_ids, cluster, rule_id, rule_ids, tags, redefine_severity, new_severity, redefine_channels, new_channels, user_group_ids, update_at, update_by, webhooks, for_duration, redefine_webhooks, severities, extra_config, busi_groups, note, notify_rule_ids, notify_version
  2. rule_id被强制清零:源码注释说明,"批量订阅告警规则功能上线后改用rule_ids而非rule_id;更新时置rule_id=0,防止遗留的旧字段引发错误订阅";
  3. 服务端自动覆盖UpdateBy/UpdateAt
# 先 GET 详情拿到完整对象,修改后再 PUT(推荐做法) curl -X PUT -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ "http://<n9e-host>/api/n9e/busi-group/2/alert-subscribes" \ -d '[{ "id": 123, "name": "跨团队抄送-支付核心链路", "disabled": 0, "prod": "", "cate": "", "datasource_ids": [1, 2], "cluster": "0", "rule_ids": [45, 67], "severities": [1, 2, 3], "for_duration": 1200, "tags": [{"key": "team", "func": "==", "value": "pay"}], "busi_groups": [{"key": "groups", "func": "==", "value": "支付中心"}], "notify_version": 1, "notify_rule_ids": [9] }]'

之所以强烈建议"先 GET 详情 → 改完整个对象 → 再 PUT":由于tags/busi_groups/webhooks/extra_config/notify_rule_ids等是序列化字段,且数组字段在更新时是整体替换语义,若只传部分字段,未触及的序列化字段会被空值清掉。这也是站内内置工具update_alert_subscribe采用"DB2FE 后再合并 patch、整行替换式UpdateFull"的原因(见 aiagent/tools/subscribe.go 中updateAlertSubscribe的注释:merge 底座必须是 FE 形态,否则未修改的序列化字段会被空 FE 值清掉)。

删除:ids 数组

curl -X DELETE -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ "http://<n9e-host>/api/n9e/busi-group/2/alert-subscribes" \ -d '{"ids": [123, 124, 125]}'

处理器alertSubscribeDel将 Body 绑定到idsForm{"ids":[...]})后调用AlertSubscribeDel按主键批量删除(center/router/router_alert_subscribe.go、models/alert_subscribe.go)。

Tryrun:先验证后保存

POST /api/n9e/alert-subscribe/alert-subscribes-tryrun是本套 API 中最具实战价值的端点——编辑订阅后、正式保存前,先用一个历史事件试跑,逐门校验匹配条件。请求体为:

{ "event_id": 123456, "config": { "...订阅草稿..." } }

其中event_id必须是历史告警事件 IDconfig是待验证的订阅草稿(与创建接口的单个对象同构)。处理器alertSubscribeTryRun(center/router/router_alert_subscribe.go)的执行顺序就是引擎实际的匹配顺序:

curl -X POST -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ "http://<n9e-host>/api/n9e/alert-subscribe/alert-subscribes-tryrun" \ -d '{ "event_id": 123456, "config": { "name": "草稿-支付链路升级", "datasource_ids": [1, 2], "rule_ids": [45, 67], "severities": [1, 2], "tags": [{"key": "team", "func": "==", "value": "pay"}], "busi_groups": [{"key": "groups", "func": "==", "value": "支付中心"}], "notify_version": 1, "notify_rule_ids": [9] } }'

逐门校验顺序

  1. 配置自检:先执行Verify(),配置不合法(如缺severities)直接报错;
  2. 事件存在性:按event_id查历史事件,不存在返回 404event not found
  3. 数据源门MatchCluster比对事件DatasourceIddatasource_ids,不匹配报event datasource not match
  4. 告警规则门rule_ids非空时要求事件RuleId在列表中,否则报event rule id not match
  5. 标签门MatchTags比对事件标签与tags,不匹配报event tags not match
  6. 业务组名门MatchGroupsName比对事件业务组名与busi_groups(注意这里匹配的是事件所属业务组的名称),不匹配报event group name not match
  7. 严重级别门severities含 0 表示通配,否则要求事件 Severity 命中列表,不匹配报event severity not match

任一扇门失败即返回对应错误信息,这正是"订阅没生效"排查时最有用的反馈。

新版本的真实通知测试

如果草稿是新版本notify_version=1)且notify_rule_ids非空,Tryrun 会进一步真实执行通知规则的发信测试:逐个加载notify_rule_ids指向的通知规则,对其每一个NotifyConfig调用SendNotifyChannelMessage真正发送测试通知。全部成功返回event match subscribe and notification test ok;通知规则不存在返回 404,发信失败返回notify rule send error: ...

如果草稿是旧版本notify_version=0),则走ModifyEvent逻辑:校验new_channels是否选择了渠道、user_group_ids对应的用户是否为所选渠道配置了 token(如钉钉/企业微信/飞书等,见 center/router/router_alert_subscribe.go 对非默认渠道的跳过逻辑),返回event match subscribe and notify settings ok

所以工作流应该是:编辑草稿 → Tryrun 验证(必要时结合返回的错误逐门修正)→ 确认所有门通过后再 PUT 正式保存

生效与一致性:约 9 秒缓存轮询

调用写接口落库后,订阅不会立刻生效——Nightingale 采用内存缓存 + 轮询同步机制。缓存实现在 memsto/alert_subscribe_cache.go:

  • loopSyncAlertSubscribes9000ms(9 秒)轮询一次,通过AlertSubscribeStatisticstotal/last_updated判断数据是否变化,变了才全量重载(见StatChangedSet的配合);
  • 重载时Disabled == 1的订阅被直接过滤(不进入内存表),因此置disabled:1等于立刻失能,置回disabled:0后最多 9 秒恢复;
  • 缓存按rule_ids建索引:rule_ids为空时归入 key0(表示订阅所有规则的事件),CompatibleWithOldRuleId保证老数据rule_id字段也能兼容(见 memsto/alert_subscribe_cache.go)。

这意味着:任何通过 API 或直接改库的变更,最迟约 9 秒后生效,无需重启服务

兜底方案:直接修改数据库

当 API 无法覆盖(例如批量导入、脚本修复、极端场景下的紧急处置)时,可以直改数据库。约束如下(与原始文档一致):

  • 数据表名为alert_subscribe(模型TableName()见 models/alert_subscribe.go);
  • 以下列为JSON / 序列化字段,修改时必须按 JSON 格式写入,不能写裸字符串:
    • tagsormx.JSONArr,标签过滤数组)
    • busi_groupsormx.JSONArr,业务组名过滤数组)
    • webhooks(回调 URL 的 JSON 字符串)
    • extra_config(扩展配置 JSON 字符串)
    • notify_rule_ids/rule_idsserializer:json
    • datasource_ids/severities(DB 中为 JSON 字符串,API 中为数组,二者由FE2DB/DB2FE互转,见 models/alert_subscribe.go);
  • 直改数据库后无需重启:内存缓存约 9 秒后自动重载(轮询周期见上节);
  • 修改前务必备份数据——序列化字段格式错误会导致该条订阅在缓存同步时被跳过(syncAlertSubscribesParse/DB2FE失败仅记 warning 并continue,见 memsto/alert_subscribe_cache.go),从而静默失效。

边界说明:站内助手与外部 Agent 的分工

最后重申这套 API 的使用边界,避免误用:

场景正确做法
Nightingale 站内 AI 助手(in-app assistant)使用内置 FC 工具(create_alert_subscribe/update_alert_subscribe/list_alert_subscribes/get_alert_subscribe_detail),不调用 HTTP API、不登录、不对自身端点发起 http_fetch
外部 A2A Agent / 自动化脚本使用本文的 HTTP API,携带Authorization: Bearer <token>
给用户提供可复制执行的命令按本文 curl 模板提供,仅在用户明确要求 curl 时输出,不要替用户执行

外部 Agent 若要完整操作订阅,建议的调用序列是:GET /busi-groups/alert-subscribes(或GET /alert-subscribe/:sid)获取现状 → 组装/修改对象 →POST /alert-subscribe/alert-subscribes-tryrun逐门验证 → 通过后再POST创建或PUT更新 → 等待约 9 秒缓存刷新后生效。这套"先验证、后保存、再确认生效"的闭环,能把订阅误配造成的告警漏发/错发风险降到最低。

参考资料(仓库内)

  • 接口规范原文:aiagent/skill/embedded/builtin/alert-subscribe-copilot/http-api.md
  • 路由注册与中间件链:center/router/router.go、权限中间件 center/router/router_mw.go
  • 处理器实现(含 Tryrun 逐门校验):center/router/router_alert_subscribe.go
  • 数据模型与序列化字段定义:models/alert_subscribe.go
  • 内存缓存与 9 秒轮询:memsto/alert_subscribe_cache.go
  • 站内内置工具实现(外部 Agent 可对照参考字段形状):aiagent/tools/subscribe.go

【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 17:47:23

2026高热密度CPU散热决策指南:风冷水冷选型与安装避坑

1. 这不是“买个风扇就完事”的事&#xff1a;为什么2026年选散热器比三年前更烧脑你拆开新买的AMD Ryzen 9 7950X3D或Intel Core i9-14900KS&#xff0c;手心冒汗——不是因为CPU贵&#xff0c;而是因为你突然意识到&#xff1a;这颗芯片的峰值功耗能冲到300W以上&#xff0c;…

作者头像 李华
网站建设 2026/9/14 17:47:15

微信小程序端侧人脸漫画风格迁移实战

简介&#xff1a;本资源是一套基于微信小程序平台的AI人脸漫画化转换源码&#xff0c;面向具备前端开发基础与图像处理兴趣的开发者&#xff0c;解决将真实人脸照片实时转化为卡通风格图像的技术实践需求。压缩包共121个文件&#xff0c;包含19个JS逻辑文件&#xff08;如index…

作者头像 李华
网站建设 2026/9/14 17:45:53

国产光编芯片KTO9512技术解析与应用实践

1. 国产光编芯片KTO9512技术解析 KTO9512是昆泰芯微电子推出的一款高性能光学旋转编码器芯片&#xff0c;采用相位阵列游标技术实现24位绝对位置检测。这款芯片在工业自动化、机器人关节定位、高精度伺服系统等领域具有重要应用价值。 1.1 核心架构与工作原理 KTO9512采用创新…

作者头像 李华
网站建设 2026/9/14 17:44:12

2026墨水屏选购核心逻辑:驱动算法、光谱调控与内容适配

1. 这不是“买什么最便宜”&#xff0c;而是“用三年不后悔”的墨水屏选购逻辑 2026年电纸书市场已经彻底告别了“参数堆砌”时代。我从2018年开始测评墨水屏设备&#xff0c;亲手拆解过23台不同品牌机型&#xff0c;跟踪过17个长期用户的真实使用数据——发现一个关键事实&…

作者头像 李华