给 Hermes 装上 Webhook:让业务系统主动把通知推到 Telegram/Discord/飞书

很多业务系统的第一个「自动化通知」需求都长得差不多:当订单状态变化、支付成功、监控告警或 CI 跑完时,往群里发一条消息。最省事的方案通常是写一段脚本轮询数据库或 API,然后把消息丢到 Telegram、Discord、Slack 或飞书。
轮询能跑,但长期看有几个硬伤:
- 延迟和 API 配额:每分钟扫一次数据库,大多数请求都是空跑;想降到秒级,API 压力和成本又翻倍。
- 耦合:业务系统里要硬编码消息格式和聊天平台 token,哪天换平台或换群,就得改业务代码。
- 扩展性:同样的告警,有时候要发给 Telegram,有时候要发给飞书,有时候要让 AI 先总结一遍再发。轮询脚本会越写越复杂。
Hermes Agent 自带一个 webhook 平台,本质上是一个 HTTP 事件接收器:业务系统有事件就 POST 过来,Hermes 验证签名、渲染模板,然后直接把消息推送到你配置好的聊天平台。如果事件只需要纯通知、不需要 AI 总结,可以开启 Direct Delivery 模式——不调用 LLM、不进 agent loop,亚秒内完成投递,且完全不消耗 token。
这篇文章是一份面向业务系统的 webhook 实战指南:从启用平台、配置路由,到三个真实场景的完整配置,再到安全加固和排错。
两种工作模式:Agent 处理 vs. Direct Delivery
Hermes webhook 支持两种投递路径,先搞清楚区别,后面选型不会错。
| 模式 | 是否调用 LLM | 适用场景 | 延迟 | 成本 |
|---|---|---|---|---|
| Agent 处理 | 是 | 需要 AI 理解、总结、决策后再回复或转发 | 秒级 | 消耗 token |
| Direct Delivery | 否 | 纯通知:订单状态、支付成功、监控告警 | 亚秒级 | 零 LLM 成本 |
Direct Delivery 是这篇文章的重点。它的逻辑很简单:
- 业务系统按约定签名,POST 一个 JSON 到
https://your-server:8644/webhooks/<route-name>。 - Hermes 验证 HMAC 签名,确认请求来自可信来源。
- 用模板把 JSON 字段渲染成一条可读消息。
- 直接投递到 Telegram、Discord、Slack、飞书等平台,返回
200 OK。
整个流程没有 LLM 调用,所以速度、成本和稳定性都更接近一个传统的消息网关——但配置和扩展性又保留了 Hermes 的灵活。
第一步:启用 webhook 平台
有两种方式启用。简单场景用环境变量,复杂或长期部署用 config.yaml。
环境变量方式(最快)
在 ~/.hermes/.env 里加三行:
WEBHOOK_ENABLED=true
WEBHOOK_PORT=8644 # 默认值
WEBHOOK_SECRET=your-global-secret
然后重启 gateway:
hermes gateway restart
确认服务在跑:
curl http://localhost:8644/health
返回 {"status": "ok", "platform": "webhook"} 即成功。
配置方式(推荐生产用)
在 ~/.hermes/config.yaml 里声明 platforms.webhook:
platforms:
webhook:
enabled: true
extra:
port: 8644
secret: "your-global-secret"
重启 gateway 后,Hermes 会监听 8644 端口。如果服务器在公网,需要确保防火墙放行该端口;如果在内网或没有公网 IP,可以用 Cloudflare Tunnel 或 ngrok 暴露出来。
第二步:配置一个业务通知路由
路由定义在 platforms.webhook.extra.routes 下。每个路由有名字、事件、secret、模板和投递目标。下面是一个电商订单通知的完整例子,把新订单推送到 Telegram。
platforms:
webhook:
enabled: true
extra:
port: 8644
secret: "global-fallback-secret"
routes:
order-notify:
events: ["order.created"]
secret: "shopify-webhook-secret"
prompt: |
🛒 新订单 #{order.id}
金额:{order.total_price} {order.currency}
客户:{order.customer.email}
商品:{order.line_items[0].title}
deliver: "telegram"
deliver_only: true
deliver_extra:
chat_id: "-1001234567890"
配置要点:
events是可选的。如果业务系统通过X-Webhook-Event或event_type字段发送事件类型,这里可以只接收order.created。secret用于 HMAC 签名验证。如果路由没写 secret,会回退到全局secret。deliver_only: true开启 Direct Delivery,不调用 LLM。prompt是模板,用{order.total_price}这种点号语法访问 JSON 字段。{__raw__}可以 dump 整个 payload。deliver_extra.chat_id指定目标群。如果不写,Hermes 会投递到该平台的 home channel(前提是该平台已在 gateway 里启用并连接)。
第三步:业务系统发送事件
业务系统需要把事件 POST 到正确的 URL。以电商系统为例,URL 是:
POST https://your-server:8644/webhooks/order-notify
建议用 Generic V2 签名:把 timestamp.body 拼起来算 HMAC-SHA256,放在 X-Webhook-Signature-V2 头里,同时带上 X-Webhook-Timestamp 头。时间戳有效窗口是 ±300 秒,能防止重放攻击。
import hmac
import hashlib
import time
import json
import requests
secret = b"shopify-webhook-secret"
body = json.dumps({
"event_type": "order.created",
"order": {
"id": 10086,
"total_price": "199.00",
"currency": "USD",
"customer": {"email": "[email protected]"},
"line_items": [{"title": "Hermes 贴纸包"}]
}
}).encode()
timestamp = str(int(time.time()))
signature = hmac.new(secret, f"{timestamp}.{body.decode()}".encode(), hashlib.sha256).hexdigest()
requests.post(
"https://your-server:8644/webhooks/order-notify",
data=body,
headers={
"Content-Type": "application/json",
"X-Webhook-Signature-V2": signature,
"X-Webhook-Timestamp": timestamp,
}
)
GitHub/GitLab 也支持,它们的签名机制会被自动识别。如果是自己写的业务系统,优先用 Generic V2。
第四步:测试路由
不用等业务系统上线,先在本地测试:
hermes webhook test order-notify \
--payload '{"event_type":"order.created","order":{"id":10086,"total_price":"199.00","currency":"USD","customer":{"email":"[email protected]"},"line_items":[{"title":"Hermes 贴纸包"}]}}'
hermes webhook test 会模拟一次 POST,帮你验证模板渲染和投递是否正确。如果消息没收到,打开 gateway.log 或在前台运行 hermes gateway run,看是签名失败、事件被过滤,还是投递目标没连上。
场景一:电商订单通知 → Telegram
上面的 order-notify 已经是一个完整例子。关键细节:
- Telegram 群聊
chat_id通常是负数(-100开头)。 - 如果希望发到特定论坛话题,在
deliver_extra里加message_thread_id: "42"。 - 模板里
{order.line_items[0].title}只取第一个商品;如果要列出全部,建议用{__raw__}让 AI 在 agent 模式下总结,或者业务系统预处理成简短字符串。
场景二:支付成功 → Discord
支付系统通常已经有 webhook。这里把 Stripe/支付宝/微信支付的成功事件转成 Discord 消息:
routes:
payment-success:
events: ["payment_intent.succeeded"]
secret: "stripe-webhook-secret"
prompt: |
💰 收到付款 {amount} {currency}
订单:{metadata.order_id}
客户:{receipt_email}
deliver: "discord"
deliver_only: true
deliver_extra:
chat_id: "123456789012345678"
对于 Stripe,events 对应 X-Stripe-Event 头,但 Hermes 的 webhook 平台识别的是 X-GitHub-Event、X-GitLab-Event 或 event_type。如果 Stripe 没有 event_type,可以在业务系统侧包装一层,或者使用空 events 接收所有 POST,再用 filters 过滤。
routes:
payment-success:
secret: "stripe-webhook-secret"
filters:
- field: "type"
equals: "payment_intent.succeeded"
prompt: "..."
deliver: "discord"
deliver_only: true
场景三:监控告警 → 飞书
Grafana、Datadog 或自研监控系统的告警通常带严重级别。只把 critical 发到飞书:
routes:
critical-alert:
events: ["alert"]
secret: "monitoring-webhook-secret"
filters:
- field: "severity"
equals: "critical"
prompt: |
🚨 严重告警
服务:{service}
指标:{metric}
当前值:{current_value}
阈值:{threshold}
deliver: "feishu"
deliver_only: true
deliver_extra:
chat_id: "your_feishu_chat_id"
飞书平台需要在 gateway 里启用并配置好。如果只用 webhook 通知,不需要把飞书作为主聊天平台,只要正确配置 platforms.feishu 的 home channel 或在 deliver_extra 里指定 chat_id 即可。
更高级:用脚本过滤和转换 payload
如果业务系统的 JSON 很脏,或者只想通知特定条件,可以写一个脚本做预处理。脚本必须放在 ~/.hermes/scripts/ 下,相对路径解析到这个目录。
# ~/.hermes/scripts/alert-filter.py
import json
import sys
payload = json.load(sys.stdin)
if payload.get("severity") != "critical":
print("[SILENT]")
raise SystemExit(0)
payload["body"] = f"{payload['service']} critical: {payload['metric']}"
print(json.dumps(payload))
然后在路由里引用:
routes:
critical-alert:
events: ["alert"]
secret: "monitoring-webhook-secret"
script: "alert-filter.py"
prompt: "{body}"
deliver: "feishu"
deliver_only: true
脚本输出 JSON 会替换 payload,文本输出会作为 script_output 注入,空输出或 [SILENT] 会让 Hermes 忽略这次 webhook。
安全:不要只依赖 HMAC
HMAC 验证只能证明发送方可信,不能证明 payload 里的内容可信。PR 标题、issue 内容、订单备注、告警消息都是第三方生成的文本,理论上可以被注入指令。因此:
- ** webhook 脚本和 agent 运行环境尽量隔离**。如果暴露在公网,用 Docker/SSH 终端后端,不要把 host 直接暴露给事件。
- Direct Delivery 模式天然更安全,因为不调用 LLM,不存在 prompt injection 触发 agent 动作的风险。
- 按需限制工具集。如果某条路由必须进入 agent 模式,可以在
skills或工具集层面禁用terminal、file等危险工具。 - 审批保持开启。如果 webhook 触发的 agent 需要执行命令,保持审批流程,防止被注入的指令自动执行。
- 模板尽量窄。不要滥用
{__raw__},只把必要的字段写进prompt。
另外,每条路由默认有 30 次/分钟 的速率限制、1 MB 的 body 大小限制,以及 1 小时 的 idempotency 缓存。这些默认值对大多数业务通知够用,必要时可在 extra 里调整:
extra:
rate_limit: 60
max_body_bytes: 2097152
排错清单
| 症状 | 可能原因 | 排查方法 |
|---|---|---|
| 业务系统 POST 失败 | 端口/防火墙未开放 | curl http://your-server:8644/health |
| 返回 401 | 签名错误 | 检查 secret 和 HMAC 算法;看 gateway 日志 |
| 返回 200 但消息没收到 | 事件类型不匹配 | 检查 events 列表和 event_type 字段 |
| 重复消息 | 上游重试 + idempotency 没命中 | 确保上游带 X-Request-ID 或 X-GitHub-Delivery |
| 模板变量没展开 | 字段名写错 | 用 hermes webhook test 反复调试 |
动态订阅 vs. 配置文件
除了 config.yaml 里的静态路由,还可以用 CLI 动态创建订阅:
hermes webhook subscribe order-notify \
--events "order.created" \
--prompt "新订单 #{order.id},金额 {order.total_price} {order.currency}" \
--deliver telegram \
--deliver-chat-id "-1001234567890" \
--deliver-only \
--description "电商订单通知"
动态订阅存在 ~/.hermes/webhook_subscriptions.json,gateway 会热加载,不需要重启。静态路由同名时优先于动态订阅。具体子命令参考我们站上的 hermes webhook 命令文档。
总结
Hermes 的 webhook 平台不是一个聊天机器人,而是一个事件驱动的消息路由器。对业务系统来说,它最大的价值是:
- 把「轮询」变成「推送」,省 API 配额、降延迟。
- 把「业务系统里写消息格式」变成「Hermes 里统一配置模板」,换平台不改业务代码。
- Direct Delivery 模式让纯通知场景零 LLM 成本、亚秒投递。
如果你已经用 Hermes 处理日常任务,那给它加一个 webhook 入口几乎是免费的——同一个 gateway 已经在跑,只是多监听一个端口。业务系统从此可以主动说话,而你只需要决定它说什么、说到哪。
还没安装 Hermes?从安装指南开始。想了解 webhook 的完整命令和参数,参考hermes webhook 命令文档。对事件驱动的自动化感兴趣,也可以看看我们的 cron 脚本任务指南和yolo 模式解析。