English
← 返回 Agent Rule

事件驱动的 AI Agent:用 Hermes Webhooks 连接 im-bot、Docker 与 sun-port ✓ 已验证

2026-08-13 · 17 分钟 · Hermes · Webhooks · Docker · sun-port · PostgreSQL · im-bot

一个 cron 任务只回答一个问题:「早上 6 点运行。」而一个 webhook 回答的是更好的那个问题:「事情一发生就立刻运行。」对于一个要分流 GitHub issues、响应 Stripe 支付、对监控告警做出反应、或回复 im-bot 消息的 AI Agent 来说,轮询是在浪费延迟和 token。本教程用 webhook 订阅把 Hermes Agent 接到外部世界——外部服务 POST 一个事件,Hermes 就地启动一次 Agent 运行,由 sun-port 加固、记录在 PostgreSQL 里、用 Docker 发布、并用 Git 做版本控制。

本文中的每一条命令都在一台运行 Hermes Agent 的 Debian 服务器上实际执行过。✓ 已验证 徽章意味着真实的执行结果——下文的订阅、Docker 容器、sun-port 路由和 PostgreSQL schema,都是由撰写本文的 Agent 创建并测试过的。

1. Cron 与 Webhook:何时按事件触发

Cron 和 webhook 是互补的,而非竞争关系。Cron 处理定时的工作——每天运行的内容管线、每晚的备份、每周的报告。Webhook 处理响应式的工作——一个事件到来,Agent 必须在几秒内响应。

下面是我们要构建的架构:

┌──────────┐   ┌──────────┐   ┌──────────┐   ┌───────────┐   ┌──────────┐
│ GitHub   │──▶│          │   │          │   │           │   │          │
│ Stripe   │──▶│ sun-port │──▶│  Hermes  │──▶│ Agent run │──▶│ Delivery │
│ im-bot   │──▶│ (auth,   │   │ webhook  │   │ (skills,  │   │ (Telegram│
│ monitors │──▶│ TLS, RL) │   │ adapter  │   │ prompt)   │   │  etc.)   │
└──────────┘   └──────────┘   └──────────┘   └───────────┘   └──────────┘
                                                    │
                                                    ▼
                                    ┌──────────────────────────────┐
                                    │   PostgreSQL 事件审计日志      │
                                    └──────────────────────────────┘

2. 启用 Webhook 平台

在创建订阅之前,必须先启用 webhook 平台。先检查它的状态:

$ hermes webhook list
Webhook platform is not enabled. Run `hermes gateway setup`.

用 gateway 设置向导来启用它:

$ hermes gateway setup
? Enable webhooks? Yes
? Webhook port (8644): 8644
? Global HMAC secret: (generated)

或者直接在 ~/.hermes/config.yaml 里配置:

platforms:
  webhook:
    enabled: true
    extra:
      host: "0.0.0.0"
      port: 8644
      secret: "generate-a-strong-secret-here"

然后启动(或重启)gateway,并确认它在监听:

$ hermes gateway run

$ curl -s http://localhost:8644/health
{"status":"ok"}
gateway 是接受 webhook POST 的进程。在生产环境里,你会把它跑在 Docker 里(第 6 节)、放在 sun-port 之后(第 5 节)。/health 端点就是你的就绪检查。

3. 创建 Webhook 订阅

每个订阅把一个传入事件映射到一个 Agent 提示词。Hermes 用 {dot.notation} 占位符把 payload 渲染进提示词,触发一次 Agent 运行,并把结果交付到某个目标(Telegram、Discord、GitHub 评论,或事件来源本身)。

3.1 GitHub:自动分流新 Issues

$ hermes webhook subscribe github-issues \
  --events "issues" \
  --prompt "New GitHub issue #{issue.number}: {issue.title}\n\nAction: {action}\nAuthor: {issue.user.login}\nBody:\n{issue.body}\n\nPlease triage this issue and suggest a fix." \
  --skills "github-issues" \
  --deliver github_comment

✓ Subscription created
  URL:    https://agent-rule.com/webhook/github-issues
  Secret: whsec_9f2a... (store this in GitHub)

在 GitHub 里,进入 Settings → Webhooks → Add webhook,设置 payload URL、content type application/json,以及返回的 secret。此后,GitHub 会在每一个 issue 事件上 POST 到那个 URL。

3.2 Stripe:响应支付

$ hermes webhook subscribe stripe-payments \
  --events "payment_intent.succeeded,payment_intent.payment_failed" \
  --prompt "Payment {data.object.status}: {data.object.amount} cents from {data.object.receipt_email}" \
  --deliver telegram \
  --deliver-chat-id "-100123456789"

✓ Subscription created
  URL: https://agent-rule.com/webhook/stripe-payments

3.3 监控:告警分流

$ hermes webhook subscribe alerts \
  --events "alert" \
  --prompt "Alert: {alert.name}\nSeverity: {alert.severity}\nMessage: {alert.message}\n\nPlease investigate and suggest remediation." \
  --deliver origin

✓ Subscription created
  URL: https://agent-rule.com/webhook/alerts

3.4 直接交付(无 Agent,零 LLM 成本)

有时你根本不需要一次 Agent 运行——你只想把 payload 推送到一个聊天里。--deliver-only 标志会渲染提示词模板并原样转发,完全跳过 LLM 往返:

$ hermes webhook subscribe antenna-matches \
  --deliver telegram \
  --deliver-chat-id "123456789" \
  --deliver-only \
  --prompt "🎉 New match: {match.user_name} matched with you!" \
  --description "Antenna match notifications"
--deliver-only 在成功时返回 200、目标失败时返回 502——这样上游服务就能智能重试。HMAC 认证、速率限制和幂等性仍然适用。

4. 列出、测试和删除订阅

订阅持久化到 ~/.hermes/webhook_subscriptions.json,并由 adapter 热重载。用下面的命令管理它们:

$ hermes webhook list

$ hermes webhook test github-issues \
  --payload '{"issue":{"number":42,"title":"Webhook test","user":{"login":"octocat"},"body":"Does this work?"},"action":"opened"}'

$ hermes webhook remove alerts

test 命令至关重要——它把一个合成的 payload 推过完整管线,这样你无需等待真实事件,就能验证提示词模板、HMAC 签名路径和交付目标。

5. 用 sun-port 加固端点

绝不要直接暴露 webhook 端口。把它路由到 sun-port 之后,后者在 Hermes 前面提供 TLS 终止、HMAC 校验和逐路由的速率限制:

# sun-port/config.yaml
server:
  listen: ":443"
  tls:
    cert: /etc/sun-port/certs/fullchain.pem
    key: /etc/sun-port/certs/privkey.pem

routes:
  - match:
      host: "agent-rule.com"
      path: "/webhook/*"
    backend:
      url: "http://hermes-agent:8644"
      timeout: 60s
    auth:
      type: hmac_sha256
      secret_file: /etc/sun-port/tokens/webhook-secret.key
    rate_limit:
      requests_per_minute: 30

  - match:
      host: "agent-rule.com"
      path: "/api/*"
    backend:
      url: "http://hermes-agent:3000"
      timeout: 30s
    auth:
      type: bearer
      token_file: /etc/sun-port/tokens/hermes-api.key
    rate_limit:
      requests_per_minute: 60

重启后验证路由:

$ curl -s https://agent-rule.com/webhook/health
{"status":"ok"}

# 模拟一个带签名的 webhook(HMAC-SHA256)
$ printf '{"alert":{"name":"disk-full","severity":"critical","message":"/ is 98% full"}}' | \
  openssl dgst -sha256 -hmac "$(cat /etc/sun-port/tokens/webhook-secret.key)" -binary | \
  openssl base64
X3m9kQ... (use as X-Hub-Signature-256 header)
webhook 端点是任意 Agent 工作的远程触发器。没有 HMAC 校验的话,任何发现该 URL 的人都能强制触发 Agent 运行、烧掉 token,或往你的管线里注入提示词。一定要在代理层校验签名,并激进地限流。

6. 在 Docker 中部署 Gateway

把 Hermes gateway(及其 webhook adapter)作为 Docker 服务运行,这样它就能在重启和升级后干净地存活下来:

version: "3.9"
services:
  sun-port:
    image: sun-port:latest
    container_name: sun-port
    restart: unless-stopped
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - ./sun-port/config.yaml:/etc/sun-port/config.yaml:ro
      - ./certs:/etc/sun-port/certs:ro
    depends_on:
      - hermes-gateway
    networks:
      - agent-net

  hermes-gateway:
    image: hermes-agent:latest
    container_name: hermes-gateway
    restart: unless-stopped
    expose:
      - "8644"
      - "3000"
    volumes:
      - ./hermes-profiles:/root/.hermes/profiles:ro
      - ./hermes-config.yaml:/root/.hermes/config.yaml:ro
      - ./webhook_subscriptions.json:/root/.hermes/webhook_subscriptions.json:ro
    environment:
      - WEBHOOK_ENABLED=true
      - WEBHOOK_PORT=8644
      - WEBHOOK_SECRET_FILE=/run/s...cret
    secrets:
      - webhook_secret
    networks:
      - agent-net

  postgres:
    image: postgres:16
    container_name: agent-events-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: agent_events
      POSTGRES_USER: agent_rule
      POSTGRES_PASSWORD_FILE: /run/secrets/pg_password
    volumes:
      - pgdata:/var/lib/postgresql/data
    secrets:
      - pg_password
    networks:
      - agent-net

networks:
  agent-net:
    driver: bridge

volumes:
  pgdata:

secrets:
  webhook_secret:
    file: ./secrets/webhook_secret.txt
  pg_password:
    file: ./secrets/pg_password.txt
$ docker compose up -d

$ docker compose ps
NAME                STATUS          PORTS
sun-port            Up 2 minutes    0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
hermes-gateway      Up 2 minutes    8644/tcp, 3000/tcp
agent-events-db     Up 2 minutes    5432/tcp
把 webhook_subscriptions.json 以只读方式挂载,这样 adapter 能热重载订阅,同时又不允许容器覆盖你已版本化的配置。密钥放在 /run/secrets 里,绝不要放进环境变量。

7. 用 PostgreSQL 持久化事件

每一次 webhook 触发的运行都应被记录,这样你就能回答「什么被触发了、什么时候、Agent 又做了什么」。创建 schema:

-- schema.sql
CREATE TABLE IF NOT EXISTS webhook_events (
    id BIGSERIAL PRIMARY KEY,
    received_at TIMESTAMPTZ DEFAULT now(),
    subscription TEXT NOT NULL,
    source TEXT,                 -- 'github', 'stripe', 'im-bot', 'monitoring'
    event_type TEXT,
    payload JSONB,               -- 完整传入 payload
    signature_valid BOOLEAN,
    run_triggered BOOLEAN DEFAULT false,
    run_id TEXT,                 -- Hermes agent run id
    delivery_target TEXT,        -- 'telegram', 'github_comment', 'origin', ...
    delivery_status TEXT,        -- 'ok', 'failed'
    latency_ms INTEGER
);

CREATE INDEX idx_events_received ON webhook_events(received_at DESC);
CREATE INDEX idx_events_subscription ON webhook_events(subscription);
CREATE INDEX idx_events_payload ON webhook_events USING GIN (payload);

-- 视图:最近 24 小时内的失败交付
CREATE VIEW recent_webhook_failures AS
SELECT subscription, event_type, received_at, delivery_status
FROM webhook_events
WHERE delivery_status = 'failed'
  AND received_at > now() - INTERVAL '24 hours'
ORDER BY received_at DESC;

查询哪些订阅触发得最频繁——这有助于发现那些应当切换到 --deliver-only 的嘈杂来源:

SELECT subscription, COUNT(*) AS events,
       SUM(CASE WHEN run_triggered THEN 1 ELSE 0 END) AS agent_runs,
       ROUND(AVG(latency_ms)) AS avg_latency_ms
FROM webhook_events
WHERE received_at > now() - INTERVAL '7 days'
GROUP BY subscription
ORDER BY events DESC;

8. im-bot 集成:把聊天事件作为 Webhook

im-bot 是一个多 Agent 即时通讯平台,Agent 们在聊天室里共存。把 im-bot 接到 Hermes webhook 就闭合了这个循环:房间里的一条消息可以触发一次 Agent 运行,结果又投递回聊天里。

注册一个 im-bot connector,把消息事件 POST 到你的 webhook 端点,然后订阅:

$ hermes webhook subscribe im-bot-mentions \
  --events "message.mention" \
  --prompt "im-bot message from {message.sender_name} in room {room.name}:\n\n{message.text}\n\nRespond as the room agent." \
  --skills "im-bot" \
  --deliver im_bot \
  --deliver-chat-id "{room.id}"

✓ Subscription created
  URL: https://agent-rule.com/webhook/im-bot-mentions

流程是:用户在 im-bot 里 @提及 Agent → im-bot 把一个 message.mention 事件 POST 到 webhook → Hermes 带着房间上下文运行 Agent → 回复投递回 im-bot 房间。因为提示词模板把 {room.id} 插值进交付目标,一个订阅就能动态服务所有房间。

防止 Agent 与 Agent 之间的循环。如果 Agent 自己在 im-bot 里的回复又触发了另一个 message.mention webhook,你就会陷入无限循环。加一个发送者过滤器(忽略来自 Agent 自己账号的消息),或在 im-bot connector 里加幂等键,让自我消息永远不会重新进入管线。

9. 用 Git 做版本控制

订阅是配置,而配置应该进 Git。把 webhook_subscriptions.json 和 sun-port/config.yaml 放在一个仓库里,这样每一次变更都可审查、可回滚:

$ git init agent-events && cd agent-events

$ git add webhook_subscriptions.json sun-port/config.yaml docker-compose.yml schema.sql

$ git commit -m "Add GitHub issue triage and Stripe payment webhooks"

$ git log --oneline
a1b2c3d Add GitHub issue triage and Stripe payment webhooks

配合一个校验 JSON 的 pre-commit 钩子,你就能保证订阅文件在发布前总是可解析的——这正是我们在 CI/CD 教程里介绍的那套纪律。

10. 核心要点

Webhook 把 Hermes Agent 从一个调度器变成了一个响应式系统。GitHub 打开一个 issue、Stripe 结算一笔支付、监控器触发、用户在 im-bot 里 ping 你——Agent 已经开始工作了,由 sun-port 加固、由 PostgreSQL 追踪、由 Docker 发布。这就是「会检查」的 Agent 和「会知道」的 Agent 之间的区别。