🤖HermesBlog
Hermes 消息平台接入 · 第12篇2026/8/9· Easy Understand Hermes Agent

Webhooks 接入——通用事件入口

Hermes 消息平台接入第12篇:Webhooks。任何系统都能给 Hermes 发消息。

Webhooks 接入:智能门铃

Webhooks 接入——通用事件入口

嘿,朋友们!今天咱们来聊聊 Hermes Agent 的 Webhooks 接入——这可是个万能入口,让 GitHub、GitLab、JIRA、Stripe 这些外部服务直接“喊”你的 Agent 干活。想象一下:有人提了个 PR,你的 Agent 自动去 review 并留言;代码推送到 main 分支,它自动发个 Telegram 通知。是不是很爽?

快速上手

启用 Webhooks 有两种方式:

方式一:交互式向导

hermes gateway setup

跟着提示走,启用 webhooks、设置端口和全局 HMAC 密钥就行。

方式二:环境变量

在 ~/.hermes/.env 里加上:

WEBHOOK_ENABLED=true
WEBHOOK_PORT=8644        # 默认端口
WEBHOOK_SECRET=your-global-secret

启动后验证一下:

curl http://localhost:8644/health

看到 {"status": "ok", "platform": "webhook"} 就说明跑起来了。

配置路由

路由就是告诉 Agent“哪种事件该怎么处理”。在 config.yaml 的 platforms.webhook.extra.routes 下定义:

platforms:
  webhook:
    enabled: true
    extra:
      port: 8644
      secret: "global-fallback-secret"
      routes:
        github-pr:
          events: ["pull_request"]
          secret: "github-webhook-secret"
          prompt: |
            Review this pull request:
            Repository: {repository.full_name}
            PR #{number}: {pull_request.title}
            Author: {pull_request.user.login}
            URL: {pull_request.html_url}
            Action: {action}
          skills: ["github-code-review"]
          deliver: "github_comment"
          deliver_extra:
            repo: "{repository.full_name}"
            pr_number: "{number}"

几个关键点:

  • events:要监听的事件类型,不填就全收
  • secret:HMAC 签名密钥,不设就用全局的。测试时可以设 "INSECURE_NO_AUTH" 跳过验证(仅限测试!)
  • profile:开了 gateway.multiplex_profiles 时,指定哪个 profile 有权跑这条路由。比如设成 coder,路由就绑到 /p/coder/webhooks/<route>。动态订阅也能用 hermes webhook subscribe <name> --route-profile coder 指定。
  • prompt:模板字符串,用 {点.语法} 访问 payload 字段。不填就把整个 JSON 丢给 Agent
  • deliver:响应发到哪,支持 github_comment、telegram、discord、slack、signal、sms、whatsapp、matrix、mattermost、homeassistant(插件)、email、dingtalk、feishu、wecom、weixin、bluebubbles、qqbot,或者 log(默认值)
  • mirror_to_session:默认 false。设为 true 时,成功投递到聊天平台后,这条消息还会写进该聊天的会话记录(以带标记的用户消息形式),这样你在那个聊天里回复时,Agent 知道它刚给你发过什么
  • filters:声明式过滤器,不匹配的请求直接返回 {"status":"ignored","reason":"filter"},不打扰 Agent

其中 homeassistant 会把响应投递到 Home Assistant(需要 homeassistant 插件)。它默认用 home channel(HASS_HOME_CHANNEL),也可以在 deliver_extra 里指定 chat_id。

回复投递消息

默认情况下投递是“发完即忘”:每个 webhook 事件都在自己的会话里跑,所以你在聊天里回复那条投递消息时,那边的 Agent 并不知道自己发过什么。给路由设 mirror_to_session: true(或给 hermes webhook subscribe 传 --mirror-to-session),投递的文本就会以 [Webhook delivery: <route>] 开头追加到目标聊天的会话里,后续追问(比如“所以他出去了?”)就有上下文了。

  • 镜像写入是尽力而为:不会让投递失败;如果该聊天还没有 gateway 会话(没人跟 Agent 在那聊过),就跳过。
  • 在 /p/<profile>/ 路由上,它只会写进该 profile 对应这个聊天的会话,不会写到别的 profile。
  • 镜像文本会像你自己发的一样进入聊天历史。在 deliver_only 路由上,它是渲染后的原始 payload,所以只对内容可信的来源开启(比如你自己的服务,而不是公开的 issue 跟踪器)。

新特性:直接投递模式

有个超省钱的玩法——deliver_only: true。开启后完全跳过 Agent,直接把渲染好的 prompt 模板作为消息发出去。零 LLM 成本,亚秒级响应。适合那些不需要动脑子的通知场景。

事件合并:别让 Agent 被刷屏

同一个 PR 被连推五次、一张工单被反复编辑、监控告警来回抖动……这些事件每次的 delivery ID 都不一样,幂等缓存拦不住,结果就是 Agent 被叫醒好几遍。给路由加个 coalesce 就能把它们合并成一次运行:

coalesce:
  key: "{repository.full_name}#{pull_request.number}"
  window_seconds: 30      # 静默窗口,默认 30
  max_wait_seconds: 300   # 最长等待,默认 300

简单说:同一个 key 的事件会被归到一组,每来一个新事件就把计时器往后推;静默窗口内没新事件了,就用最新那条 payload 跑一次 Agent。max_wait_seconds 兜底,防止事件流不断把派发无限推迟。合并后的请求返回 HTTP 202 和 {"status": "coalesced"}。

注意 key 得是路由接受的每种事件都有的字段,否则取不到 key 的事件会立刻派发(不会被合并)。另外 coalesce 只对 Agent 模式的路由有效,跟 deliver_only、cron_job 不能同时用。

用事件触发定时任务

如果你已经有一个 cron job,不想让它按固定节奏空跑,可以给路由设 cron_job,让事件一来就触发它:

cron_job: "pr-review"

事件照样过 HMAC 认证、限流、events/filters/script 过滤和幂等检查;路由的 prompt 渲染后作为本次运行的临时上下文注入,job 自己的 prompt、skills、模型和投递设置不变。job 走的是调度器那套“至多一次”的认领机制,所以事件爆发也不会把正在跑的 job 重复触发。跟 deliver_only 互斥。

幂等与去重

Webhook 服务经常会重试,Hermes 靠 delivery ID 来去重。这个 ID 会从 X-GitHub-Delivery、svix-id、webhook-id、X-Request-ID 这些请求头里取,都没有就随机生成一个。每个 ID 会缓存 1 小时,重复投递(比如 webhook 重试)会被静默跳过并返回 200,避免 Agent 被重复唤醒。

安全提醒

⚠️ 认证过 ≠ 可信。Webhook payload 里的字段都是外部传入的,别盲目信任。prompt 模板里引用的数据要当心注入风险。

另外,路由的增删改查是串行执行的,会原子地替换整个存储;如果有并发的旧更新,会被直接拒绝,而不是把别人已经删掉或禁用的路由又“复活”回来。动态订阅文件 ~/.hermes/webhook_subscriptions.json 会在每次请求时热加载一份一致的快照(按 mtime/size/inode 判断是否变化,无锁)。

用 --route-profile 重新绑定一条已有路由时,它的 HMAC 密钥会自动轮换。拿着旧 profile 密钥的调用方就认证不了这条新路由了;只有接收服务确实需要你自己指定密钥时,才去传 --secret。

路由级 toolsets 只能手改配置文件,不能通过 hermes webhook subscribe 设置,所以 Agent 自己创建的订阅没法给自己加权限;名字的校验方式和 platform_toolsets 一样,未知或平台受限的名字会被丢弃。

动态订阅

除了手改配置文件,还能用命令动态创建路由:

hermes webhook subscribe

不过注意,通过命令创建的路由不能设置 toolsets,防止 Agent 自己给自己偷偷加权限。


配置好后,把外部服务指向 http://your-server:8644/webhooks/<route-name> 就完事了。GitHub 上设个 Webhook,PR 一来,你的 Agent 就开始干活了!

📖 官方文档

本文根据 Hermes Agent 官方文档编写,原文见:官方文档 › user-guide/messaging/webhooks