Cron 会问“现在到时间了吗?”,Webhook 则会说“刚刚发生了这件事,请采取行动”。对智能体工作而言,这一区别很重要。定时检查收件箱,与“Stripe 争议已创建”或“有人发起了以 main 为目标分支的 PR”是完全不同的任务。
Hermes Agent Webhook 会把经过身份验证的 HTTP POST 事件转换为智能体运行,并将结果发送到已配置的交付目标。使用得当,它们是带锁、任务明确的窄入口。使用不当,它们就会变成暴露在外的端口,试图用一个巨型提示处理互联网发来的任何 JSON 负载。
本文涵盖路由设计、符合事件来源要求的身份验证、文档所述的健康检查,以及一项实用的冒烟测试。在把任何接口暴露到本机之外前,请先落实 Hermes 第一周中的加固措施。
何时 webhook 是正确触发器
在以下情况优先用 webhook:
- 延迟很重要(趁作者还没有完全切换到别的任务时评审 PR)。
- 源系统本身会发送事件(GitHub、GitLab、Jira、Stripe、内部表单)。
- 每个事件应成为一个目标明确的任务,而非漫长对话会话。
在以下情况优先用 cron:
- 你在轮询状态变化(“是否有证书将在 14 天内过期?”)。
- 源系统无法主动推送。
- 你需要定期得到简报,而不是被每个事件打断。
官方 Hermes 指南与这种划分一致:使用 Cron 进行定时检查,使用 Webhook 进行事件触发的运行。
一图看架构
来源系统(GitHub / GitLab / n8n / 自定义应用)
| HTTPS POST + 符合来源要求的身份验证
v
Hermes webhook 适配器(默认端口 8644)
| 路由:/webhooks/<name>
v
命名路由配置(过滤条件 + 提示 + 交付目标)
v
智能体运行(技能和工具受你的审批策略约束)
v
已配置的交付目标(聊天频道、GitHub 评论或日志)
n8n 可以位于左侧,充当验证器和聚合层:验证字段、过滤无用数据,再把精简后的载荷 POST 到 Hermes。这是 Hermes 与 n8n:按任务选择中的示例架构,不是供应商已记录的开箱即用集成。如果 n8n 需要把智能体结果带回工作流,应使用 Bearer 认证调用默认端口 8642 上独立的 Hermes API 服务器,而不是把 Webhook 适配器当作同步回调。
设置路径(对照线上文档核实)
官方 Webhook 文档 描述了以下路径:
- 启用 webhook 平台(
hermes gateway setup或环境变量如WEBHOOK_ENABLED=true)。 - 为每条路由配置一个密钥。根据源系统选择使用 GitHub 的 HMAC 请求头、GitLab 的明文令牌请求头,或通用 V2 时间戳 HMAC。
- 在配置文件中创建一个 命名 的路由,或通过
hermes webhook subscribe(根据当前文档的命令)创建。 - 健康检查:
curl http://localhost:8644/health - 将外部系统指向
https://your-host/webhooks/<name> - 发送一个经过身份验证的测试负载;确认你期望的路由、提示、工具范围和交付目标。
文档规定的默认端口为 8644。文档所述的默认值还会把每条路由限制为每分钟 30 个请求,并拒绝大于 1 MB 的请求体。如果你更改了这些值,请测试实际配置的限制,不要再依赖默认值。
更改静态配置后,可能需要按已安装版本的文档重新加载或重启网关。通过
hermes webhook subscribe创建的动态路由无需重启即可热加载,并会自动生成密钥。无论哪种情况,请确认网关进程读取的是预期的配置文件和环境;在交互式 shell 中成功执行命令,并不能证明守护进程使用了同一套配置。
路由认证是强制性的
每条路由必须继承或定义一个密钥,否则适配器将在启动时失败。认证方式因提供方而异:GitHub 使用 X-Hub-Signature-256,GitLab 使用精确匹配的 X-Gitlab-Token,而通用自定义发送方应使用 V2 时间戳 HMAC。发送方认证用于证明持有密钥的发送者发送了请求,但不会使负载指令变得可信。
适用于生产环境的可靠规则:
- 生成一个长随机密钥;将其存储在密钥管理器或权限受限的环境文件中,绝不要放在智能体可随意读取的技能 Markdown 中。
- 当系统具有不同的信任级别(如 GitHub 应用、内部表单、合作伙伴 webhook)时,优先使用 每条路由的独立密钥。
- 在边缘层拒绝未签名的请求或签名无效的请求;不要执行“记录并继续”操作。
- 仅将
INSECURE_NO_AUTH用于临时的回环测试。如果该值与非回环绑定(如0.0.0.0或 LAN 地址)组合使用,适配器将拒绝启动。
使用 Hermes 当前的 通用 V2 方案处理自定义发送方:X-Webhook-Timestamp 是 Unix 秒数;X-Webhook-Signature-V2 是 <timestamp>.<raw-body> 的小写十六进制 HMAC-SHA256 摘要。Hermes 会拒绝超出 ±300 秒窗口的时间戳。V1 只对正文签名,缺少重放保护,因此不要基于它构建新的发送方(官方 Webhook 安全协议)。
可复现的签名冒烟测试
创建名为 support-triage 的路由后,把非敏感负载写入 payload.json。启动该 shell 前,使用已经批准的密钥注入机制设置 WEBHOOK_SECRET;不要在命令历史中输入生产密钥。下面的 Node 命令从环境变量读取密钥,不会把它展开到进程参数中,并按文件的原始字节计算签名:
: "${WEBHOOK_SECRET:?inject a disposable route secret before running this test}"
timestamp="$(date +%s)"
signature="$(TIMESTAMP="$timestamp" node -e '
const { createHmac } = require("node:crypto");
const { readFileSync } = require("node:fs");
const hmac = createHmac("sha256", process.env.WEBHOOK_SECRET);
hmac.update(`${process.env.TIMESTAMP}.`, "utf8");
hmac.update(readFileSync("payload.json"));
process.stdout.write(hmac.digest("hex"));
')"
curl --fail-with-body \
-H 'Content-Type: application/json' \
-H "X-Webhook-Timestamp: $timestamp" \
-H "X-Webhook-Signature-V2: $signature" \
--data-binary @payload.json \
http://127.0.0.1:8644/webhooks/support-triage
然后分别在同时去掉两个签名请求头、以及使用超过 300 秒窗口的旧时间戳时重复测试。两种请求都必须被拒绝。不要把真实密钥粘贴到截图、工单或 shell 历史中;文档测试应使用一次性路由密钥,并在测试后轮换。
暴露的 Webhook 可能把攻击者控制的文本送到具备终端能力的智能体面前,带来远程工具执行风险。身份验证限制谁能提交事件,但通过验证的载荷文本仍可能带有对抗性。请使用 TLS 和网络控制,尽量缩减载荷内容,限制或禁用终端、文件和出站操作工具,并让执行环境与主机隔离。审批提示用于确认操作者意图,不是隔离对抗性输入的沙箱。
负载中应包含什么
给智能体一个契约,而不是未经筛选的原始数据洪流:
{
"event_type": "github.pull_request.opened",
"repo": "acme/api",
"pr_number": 1842,
"title": "Add billing retry worker",
"author": "ada",
"base_ref": "main",
"html_url": "https://github.example.invalid/acme/agent-service/pull/1842",
"task": "Summarize risk for main. List missing tests. Do not approve or merge."
}
删除未使用的字段。庞大的工作流数据转储会浪费上下文,并诱发混乱的工具调用。本文建议采用「小而明确的负载,并给出清晰任务」,这是一项设计建议,并不是在宣称存在官方 n8n 集成。
路由设计:多个窄入口
不要建 /webhooks/everything。建带过滤器与提示的命名路由:
| 路由名称 | 来源 | 任务 | 交付 |
|---|---|---|---|
gh-pr-opened | GitHub PR 创建 | 风险摘要 + 测试缺口 | 工程 Telegram 主题 |
stripe-dispute | Stripe 争议创建 | 检查清单草案 | 财务 Slack + 日志 |
support-form | n8n 验证后 | 分类 + 草拟回复 | 配置的私有 Slack 频道 |
uptime-alert | 监控 Webhook | 收集最近部署上下文 | 值班人员频道 |
每个路由应回答:
- 接受哪些事件?
- 期望的单一输出是什么?
- 此路由的智能体配置允许哪些工具?
- 结果去哪里?
- 失败时怎么办(重试、进入死信队列,还是通知人工值守人员)?
Webhook 载荷通常包含电子邮件、账户 ID 或消息正文。在到达 Hermes 之前尽量减少字段数量。路由 schema 并未提供每条路由独立的记忆写入开关。请使用已禁用记忆功能的专用配置文件,或启用
memory.write_approval,并测试哪些内容会被持久化。如果不需要智能体推理,请改用文档中说明的deliver_only模式,而不是运行智能体。
健康检查与可运维性
文档列出的健康端点是 http://localhost:8644/health (或你的主机/端口)。它可用于:
- 启用后的本地冒烟测试
- Docker/Kubernetes readiness 探针
- 从外部检查私有健康 URL 的可用性,而不是探测未经认证的 webhook 路由
同时记录:
- 签名失败(可能攻击或密钥配置错误)
- 载荷校验失败
- 智能体运行时长与工具审批拒绝
- 下游交付失败(聊天 API 宕机等)
如果没有这些信号,「智能体感觉不稳定」就会成为你唯一的事故报告。这些记录可以提高可观测性,但不会自动构成完整且防篡改的审计记录。
示例:GitHub PR 创建 → 目标明确的运行
目标: 当有人向 main 开 PR 时,Hermes 为人类起草风险说明。除非你后来添加经审核的交付路径,否则不合并、不批准、不评论。
- 创建路由
gh-pr-opened。如果由 GitHub 直接发送事件,请配置用于验证X-Hub-Signature-256的共享密钥;如果通过通用 n8n 中继发送,则改用 V2 时间戳 HMAC 协议。 - 仅接受
pull_request/opened/ 目标分支main。 - 提示契约:总结目的、影响范围、缺失的测试和上线风险;标记未知项;不得给出合并指令。
- 工具:如果已配置 GitHub 访问,则只允许读取;shell 应禁用或必须经过审批。
- 交付:将 Markdown 发布到内部频道;由人工决定下一步操作。
下面展示一份合格智能体输出的结构(仅为示例,实际模型措辞可能不同):
PR #1842:添加账单重试 worker(ada 提交到 main)
事实
- 涉及账单 worker 与队列配置(根据已提供的标题和文件列表)。
- 关联 URL:`https://github.example.invalid/acme/agent-service/pull/1842` (示例)
风险
- 若缺少退避机制,可能发生重试风暴 [inference; verify in diff]
- 标题未提及幂等键 [unclear]
需要确认的缺失测试
- 重复交付与毒消息处理
- 重试预算耗尽时的告警
不得根据这份说明合并。需要人工审查。
这是一次带有停止规则的事件驱动智能体运行,不是一个可以自主行使权限的代码负责人。
练习:启用一个之前先设计三个路由
在纸上(或运行手册中)为你的技术栈写三个 webhook 路由。对每个填写:
- 名称
- 来源 + 事件过滤器
- 认证方法和密钥所有者
- 载荷字段:从最多 10 个开始,作为有意设置的小规模练习预算
- 提示:先限制在最多 8 行,然后只补充该路由评测确实需要的内容
- 允许的工具
- 交付目标
- 失败行为
先只实施风险最低的路径,通常是内部警报或仅生成草稿的 PR 摘要。先用 curl 检查 /health,再发送一次通过身份验证的测试 POST 和一次身份验证无效的测试,最后在非生产仓库或预发布项目中触发一次真实事件。
预期的失败模式
- 轮换后密钥不匹配: 身份验证失败;修复网关进程实际使用的环境,而不只是你笔记本电脑上的 shell 环境。
- 提示过于宽泛: 智能体会临场选择工具;拆分该路由。
- 重试风暴: 源端会重复发送 POST 请求。Hermes 会缓存交付 ID 一小时,但有效去重需要稳定的
X-GitHub-Delivery或X-Request-ID。客户可见的操作仍需要持久的业务幂等性,其保留时间应与重放窗口匹配。 - 记忆污染: 高频警报会进入持久记忆;请使用专用配置文件和明确的记忆设置。
- 端口暴露: 健康检查和 webhook 接口在缺少预期 TLS 与网络控制的情况下即可访问;在添加工具之前先修复网络配置。
值得随时查阅的参考资料
只有当每条路由职责明确、经过身份验证、可观测,并配置了交付目标时,事件驱动智能体才真正有用。Webhook 适配器是事件入口,不是使用 Bearer 认证的请求/响应 API。请按照这一区别进行设计和测试。



