Skip to content

通道总览

Ongrid 通过通道和人交流。一个通道要么是:

  • 通知通道 —— Ongrid 把告警推到聊天界面;人能看到,但通道本身永远不会把回复带回 Agent。在数据库里对应 notify_channels,代码上对应 internal/pkg/notify/webhook.go 里的 Senders。
  • IM 通道 —— Ongrid 作为机器人常驻一个工作区聊天界面,接收用户消息,跑跟 web UI 同一套 Agent 推理,并以"占位消息编辑"的形式把答案流式回传。在数据库里对应 im_apps,代码上对应 internal/manager/biz/imbridge/provider/*

两者互不依赖。配通了 Telegram 告警(sendMessage 出)并不代表用户能在 Telegram 上找 Agent 聊天,反之亦然。

两张表、两套凭据、同一个 UI

两类入口都在 web UI 的 Settings → Channels 配。通知通道在 Notify 标签页;IM 通道在 IM bridge 标签页。

我要哪一种?

目标文件
把告警推到 Slack 频道Slack incoming webhook(Notify)internal/pkg/notify/webhook.go NewSlackSender
把告警推到飞书群飞书自定义机器人(Notify)NewFeishuSender
把告警推到钉钉群钉钉自定义机器人(Notify)NewDingTalkSender
把告警推到企业微信群企业微信群机器人(Notify)NewWeComSender
通用 JSON POST 到你自己的服务Webhook(Notify)NewGenericWebhookSender
Telegram 告警(单向)Telegram bot sendMessage(Notify)NewTelegramSender
在 Slack 里和 Agent 对话Slack Socket Mode app(IM bridge)imbridge/provider/slack/
在 Telegram 里和 Agent 对话Telegram bot getUpdates(IM bridge)imbridge/provider/telegram/
在飞书/Larksuite 里和 Agent 对话飞书长连接(IM bridge)imbridge/provider/feishu/

一个工作区可以同时跑两类:比如 Slack incoming webhook 推告警 + 单独的 Slack Socket Mode app 做对话。它们不共享任何状态。

通知通道

通知通道是一个无状态的 Send(ctx, Message) Sender。告警触发(或恢复、或冷却窗口结束)时由告警流水线调用。所有类型最终都渲染同一个 notify.Message 形状;只有载荷格式签名协议因 provider 而异。

Sender 收到的公共字段

go
type Message struct {
    Severity   Severity            // critical | warning | info
    Subject    string              // alert rule name + target
    Body       string              // human-readable detail
    Source     string              // "alert" | "test" | ...
    Labels     map[string]string   // rule, incident_id, device_id, ...
    DedupeKey  string              // pipeline:rule:label-set
    OccurredAt time.Time
}

Slack sender 渲染成 attachments 格式,带按 severity 染色的侧栏、结构化字段(Severity、Source、Rule、Incident、Device、Dedupe)和 ts 页脚。飞书 / 钉钉 / 企微 sender 因为机器人 API v1 只接受纯文本,所以扁平化成 [SEV] subject\nbody\nsource:…\ndedupe:…

签名模型一览

Provider怎么鉴权
Slackwebhook URL 本身就是秘密。无额外签名。
飞书HMAC-SHA256(ts\nsecret),作为 sign 字段放进 JSON body。
钉钉HMAC-SHA256(ts\nsecret),作为 sign 放进 URL query。
企微URL query 里带 bot key。无额外签名。
Telegram路径里带 bot token(/bot<TOKEN>/sendMessage)。
Webhook可选 X-Ongrid-Signature: sha256=<HMAC> 对 body 签名。

具体实现在 internal/pkg/notify/webhook.gosignFeishusignDingTalkURLsignGenericWebhook

Slack 静默丢弃 secret 字段

Slack incoming webhook 的 URL 本身就是凭据。如果你给 Slack 通道填了 Secret,通道构造器会在创建 Sender 之前把它丢掉。这是有意的 —— Slack 的协议里 incoming webhook 没有独立签名面。

IM 通道

一个 IM 通道是一个长跑 goroutine,做三件事:

  1. 主动连出到 provider(飞书/Slack 用 websocket,Telegram 用长轮询)。manager 上不开任何入站端口
  2. 收到入站消息,过一道 allow_from,然后把文本交给 bizbridge.Bridge.HandleInbound
  3. HandleInbound 发一个占位回复,跑完整 Agent 图(和 web UI 用的同一个),把流式编辑回写到占位消息 id 上。

同一套 Agent kernel、技能和 persona 注册表同时驱动 web UI 和 IM 通道。没有"专门给 IM 的 Agent" —— 你在 /chat 上看到的 coordinator 就是它。

入站按 provider 区分,出站一致

provider 差异(Slack envelope_id ack、Telegram update offset、飞书 encrypt_key)放在 imbridge/provider/*/stream.go 里。一旦消息进了 bizbridge.HandleInbound,下游全部 provider 无关。

default_locale

IM 和 Notify 两类行上都带可选的 default_locale。校验器只接受空字符串(auto)、enzh —— en-US / zh-CN 会折叠到主子标签,EN-us 或拼错的 locale 在 AppInput.validate 里直接拒掉。

取值行为
""(auto)不加 directive。LLM 跟随用户语言。历史默认值。
en给 system prompt 加 Respond in English directive。
zh给 system prompt 加「请用中文回复」directive。

这与 UI locale(ONGRID_DEFAULT_LOCALE 环境变量或浏览器语言)相互独立。IM 通道接收到的消息一律以通道自己的 locale 为准。manager 主动触发的输出走自动回退规则,参见 AI 输出语言反馈

allow_from

发送方白名单。Telegram 和 Slack 必填,飞书/钉钉可选。只在 ParseAllowFrom 里解析一次,校验器和每个 provider 的轮询/流式循环共享同一份定义。

语法。 逗号、空格、换行、tab、分号 —— 任意组合都作为分隔。保持顺序、去重。telegram:tg: 前缀会被静默剥掉(兼容 OpenClaw)。

按 provider 的格式。

  • Telegram。 仅数字 user ID。非数字 token 和负值(群聊 ID)在校验时被丢掉。至少一个 ID 是必须的 —— 机器人能被任何人通过用户名搜到,空白名单等于把带工具的 Agent 暴露给任何 Telegram 用户。见 ADR-031
  • Slack。 U 开头的 user ID(Enterprise guest 是 W)。至少一个。怎么查自己的 ID:工作区个人资料 → Copy member ID
  • 飞书 / 钉钉。 可选。这两个平台天然按企业租户成员关系门控,只有组织成员才能联系到机器人。

Telegram 或 Slack 空白名单会被拒

校验器在解析后列表为空时返回 telegram requires allow_from / slack requires allow_from。没有"默认拒绝、之后再放开"的模式。机器人是公开可达的;运维必须主动放门。

失败模式是静默丢弃。 不在白名单里的发送者来的消息在 WARN 等级带 user_id 记一条日志,然后丢掉。不回复、不发占位、不跑 Agent。机器人甚至不会确认自己存在 —— 形态对齐 OpenClaw 的 dmPolicy: allowFrom

一次告警送达的样子

text
alert evaluator         → produces notify.Message

notify.Sender (per channel)   ↓ buildBody(msg) → JSON payload
                              ↓ signTarget(endpoint, secret, body) → headers / URL params
                              ↓ POST endpoint
                              ↓ resp.StatusCode in [200, 299] → success

                          chat surface

深入细节看分通道页面: