字节笔记本
2026年5月16日
Hermes Agent GitHub PR Webhook:Webhook 驱动的自动代码审查
Hermes Agent 可以通过 Webhook 与 GitHub 集成,实现 Pull Request 的自动代码审查。当 PR 被创建或更新时,GitHub 会向 Hermes 实例发送 Webhook POST 请求,Hermes 随后启动 Agent,通过 gh CLI 获取 PR 的 diff,分析代码变更,并将审查评论自动发布回 PR 线程——整个过程无需人工干预。
:::tip 不想配置公网端点? 如果你没有公网 URL 或只想快速上手,可以参考 Build a GitHub PR Review Agent——该方案使用定时任务轮询 PR,无需公网暴露,适合 NAT 和防火墙后面的环境。 :::
:::info 参考文档 关于 Webhook 平台的完整参考(所有配置选项、投递类型、动态订阅、安全模型),请参阅 Webhooks。 :::
:::warning Prompt 注入风险 Webhook 载荷包含攻击者可控数据——PR 标题、提交信息和描述可能包含恶意指令。当 Webhook 端点暴露在公网时,务必在沙箱环境(Docker、SSH 后端)中运行 Gateway。详见下方安全注意事项。 :::
前置条件
- Hermes Agent 已安装并运行(
hermes gateway) ghCLI 已在 Gateway 主机上安装并认证(gh auth login)- Hermes 实例拥有公网可达的 URL(本地运行可参考 使用 ngrok 进行本地测试)
- 拥有 GitHub 仓库的管理员权限(用于管理 Webhook)
Step 1 — 启用 Webhook 平台
在 ~/.hermes/config.yaml 中添加以下配置:
platforms:
webhook:
enabled: true
extra:
port: 8644 # 默认端口;若被占用可修改
rate_limit: 30 # 每个路由每分钟最大请求数(非全局限制)
routes:
github-pr-review:
secret: "your-webhook-secret-here" # 必须与 GitHub Webhook Secret 完全一致
events:
- pull_request
# Agent 被指示在审查前先获取实际的 diff。
# {number} 和 {repository.full_name} 从 GitHub 载荷中解析。
prompt: |
A pull request event was received (action: {action}).
PR #{number}: {pull_request.title}
Author: {pull_request.user.login}
Branch: {pull_request.head.ref} → {pull_request.base.ref}
Description: {pull_request.body}
URL: {pull_request.html_url}
If the action is "closed" or "labeled", stop here and do not post a comment.
Otherwise:
1. Run: gh pr diff {number} --repo {repository.full_name}
2. Review the code changes for correctness, security issues, and clarity.
3. Write a concise, actionable review comment and post it.
deliver: github_comment
deliver_extra:
repo: "{repository.full_name}"
pr_number: "{number}"关键字段说明:
| 字段 | 说明 |
|---|---|
secret(路由级别) | 该路由的 HMAC 密钥。省略时回退到 extra.secret 全局配置。 |
events | 接受的 X-GitHub-Event 请求头值列表。空列表 = 接受所有事件。 |
prompt | 模板;{field} 和 {nested.field} 从 GitHub 载荷中解析。 |
deliver | github_comment 通过 gh pr comment 发布评论;log 仅写入 Gateway 日志。 |
deliver_extra.repo | 解析为载荷中的 org/repo 格式。 |
deliver_extra.pr_number | 解析为载荷中的 PR 编号。 |
:::note 载荷中不包含代码
GitHub Webhook 载荷包含 PR 元数据(标题、描述、分支名、URL),但不包含 diff。上面的 prompt 指示 Agent 运行 gh pr diff 来获取实际变更。terminal 工具已包含在默认的 hermes-webhook 工具集中,无需额外配置。
:::
Step 2 — 启动 Gateway
hermes gateway你应该看到:
[webhook] Listening on 0.0.0.0:8644 — routes: github-pr-review
验证运行状态:
curl http://localhost:8644/health
# {"status": "ok", "platform": "webhook"}Step 3 — 在 GitHub 上注册 Webhook
- 进入你的仓库 → Settings → Webhooks → Add webhook
- 填写:
- Payload URL:
https://your-public-url.example.com/webhooks/github-pr-review - Content type:
application/json - Secret: 与路由配置中
secret值完全一致 - Which events? → 选择个别事件 → 勾选 Pull requests
- Payload URL:
- 点击 Add webhook
GitHub 会立即发送一个 ping 事件来确认连接。它会被安全忽略——ping 不在你的 events 列表中——并返回 {"status": "ignored", "event": "ping"}。该事件仅在 DEBUG 日志级别记录,默认日志级别下不会在控制台显示。
Step 4 — 创建测试 PR
创建分支、推送变更并开启 PR。30–90 秒内(取决于 PR 大小和模型),Hermes 应当会发布一条审查评论。
实时查看 Agent 执行进度:
tail -f "${HERMES_HOME:-$HOME/.hermes}/logs/gateway.log"使用 ngrok 进行本地测试
如果 Hermes 运行在你的笔记本上,可以使用 ngrok 暴露服务:
ngrok http 8644复制 https://...ngrok-free.app URL 作为 GitHub 的 Payload URL。免费版 ngrok 每次重启后 URL 会变化——每次会话需要更新 GitHub Webhook。付费 ngrok 账户可获得静态域名。
你也可以直接用 curl 对静态路由进行冒烟测试——不需要 GitHub 账户或真实 PR。
:::tip 本地测试时使用 deliver: log
测试时将配置中的 deliver: github_comment 改为 deliver: log。否则 Agent 会尝试向测试载荷中的假 org/repo#99 仓库发布评论,导致失败。满意 prompt 输出后切回 deliver: github_comment。
:::
SECRET="your-webhook-secret-here"
BODY='{"action":"opened","number":99,"pull_request":{"title":"Test PR","body":"Adds a feature.","user":{"login":"testuser"},"head":{"ref":"feat/x"},"base":{"ref":"main"},"html_url":"https://github.com/org/repo/pull/99"},"repository":{"full_name":"org/repo"}}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print "sha256="$2}')
curl -s -X POST http://localhost:8644/webhooks/github-pr-review \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: pull_request" \
-H "X-Hub-Signature-256: $SIG" \
-d "$BODY"
# 预期输出: {"status":"accepted","route":"github-pr-review","event":"pull_request","delivery_id":"..."}然后观察 Agent 运行:
tail -f "${HERMES_HOME:-$HOME/.hermes}/logs/gateway.log":::note
hermes webhook test <name> 仅适用于通过 hermes webhook subscribe 创建的动态订阅。它不会读取 config.yaml 中的路由。
:::
过滤特定 Action
GitHub 对 pull_request 事件发送多种 action:opened、synchronize、reopened、closed、labeled 等。events 列表仅按 X-GitHub-Event 请求头值进行过滤——无法在路由层面按 action 子类型过滤。
Step 1 中的 prompt 已通过指示 Agent 对 closed 和 labeled 事件提前终止来处理此问题。
:::warning Agent 仍会运行并消耗 Token
"stop here" 指令阻止了有意义的审查,但 Agent 仍然会对每个 pull_request 事件完整运行,无论 action 是什么。GitHub Webhook 只能按事件类型(pull_request、push、issues 等)过滤,不能按 action 子类型(opened、closed、labeled)过滤。路由层面没有子 action 过滤器。对于高流量仓库,请接受此成本,或在上游使用 GitHub Actions 工作流有条件地调用你的 Webhook URL。
:::
不支持 Jinja2 或条件模板语法。
{field}和{nested.field}是仅有的替换语法。其他内容会原样传递给 Agent。
使用 Skill 保持一致的审查风格
加载 Hermes Skill 可为 Agent 提供一致的审查风格。在 config.yaml 的 platforms.webhook.extra.routes 路由中添加 skills:
platforms:
webhook:
enabled: true
extra:
routes:
github-pr-review:
secret: "your-webhook-secret-here"
events: [pull_request]
prompt: |
A pull request event was received (action: {action}).
PR #{number}: {pull_request.title} by {pull_request.user.login}
URL: {pull_request.html_url}
If the action is "closed" or "labeled", stop here and do not post a comment.
Otherwise:
1. Run: gh pr diff {number} --repo {repository.full_name}
2. Review the diff using your review guidelines.
3. Write a concise, actionable review comment and post it.
skills:
- review
deliver: github_comment
deliver_extra:
repo: "{repository.full_name}"
pr_number: "{number}"注意: 只有列表中第一个被找到的 Skill 会被加载。Hermes 不会叠加多个 Skill——后续条目会被忽略。
将响应发送到 Slack 或 Discord
替换路由中的 deliver 和 deliver_extra 字段为目标平台:
# 在 platforms.webhook.extra.routes.<route-name> 中:
# Slack
deliver: slack
deliver_extra:
chat_id: "C0123456789" # Slack 频道 ID(省略则使用配置的默认频道)
# Discord
deliver: discord
deliver_extra:
chat_id: "987654321012345678" # Discord 频道 ID(省略则使用默认频道)目标平台也必须在 Gateway 中启用并连接。如果省略 chat_id,响应将发送到该平台的配置默认频道。
有效的 deliver 值:log · github_comment · telegram · discord · slack · signal · sms
GitLab 支持
同一适配器也适用于 GitLab。GitLab 使用 X-Gitlab-Token 进行认证(纯字符串匹配,非 HMAC)——Hermes 会自动处理两者。
事件过滤方面,GitLab 将 X-GitLab-Event 设置为 Merge Request Hook、Push Hook、Pipeline Hook 等值。在 events 中使用精确的请求头值:
events:
- Merge Request HookGitLab 载荷字段与 GitHub 不同——例如 MR 标题用 {object_attributes.title},MR 编号用 {object_attributes.iid}。发现完整载荷结构最简单的方法是使用 GitLab Webhook 设置中的 Test 按钮,结合 Recent Deliveries 日志。或者,从路由配置中省略 prompt——Hermes 会将完整载荷以格式化 JSON 直接传递给 Agent,Agent 的响应(通过 deliver: log 在 Gateway 日志中可见)会描述其结构。
安全注意事项
- 生产环境切勿使用
INSECURE_NO_AUTH——它会完全禁用签名验证。仅用于本地开发。 - 定期轮换 Webhook Secret,并在 GitHub(Webhook 设置)和
config.yaml中同步更新。 - 速率限制默认为每个路由 30 请求/分钟(可通过
extra.rate_limit配置)。超限返回429。 - 重复投递(Webhook 重试)通过 1 小时幂等缓存去重。缓存键依次为
X-GitHub-Delivery、X-Request-ID、毫秒时间戳。当两个投递 ID 请求头都未设置时,重试不会被去重。 - Prompt 注入: PR 标题、描述和提交信息是攻击者可控的。恶意 PR 可能试图操控 Agent 的行为。暴露在公网时,务必在沙箱环境(Docker、VM)中运行 Gateway。
故障排查
| 症状 | 检查项 |
|---|---|
401 Invalid signature | config.yaml 中的 Secret 与 GitHub Webhook Secret 不匹配 |
404 Unknown route | URL 中的路由名与 routes: 中的键不匹配 |
429 Rate limit exceeded | 超过每路由 30 请求/分钟限制——从 GitHub UI 重新投递测试事件时常见;等待一分钟或提高 extra.rate_limit |
| 没有评论发布 | gh 未安装、不在 PATH 中或未认证(gh auth login) |
| Agent 运行但无评论 | 检查 Gateway 日志——如果 Agent 输出为空或仅为 "SKIP",仍会尝试投递 |
| 端口被占用 | 修改 config.yaml 中的 extra.port |
| Agent 运行但仅审查 PR 描述 | prompt 中缺少 gh pr diff 指令——diff 不在 Webhook 载荷中 |
| 看不到 ping 事件 | 被忽略的事件仅在 DEBUG 日志级别返回 {"status":"ignored","event":"ping"}——检查 GitHub 的投递日志(仓库 → Settings → Webhooks → 对应 Webhook → Recent Deliveries) |
GitHub 的 Recent Deliveries 标签页(仓库 → Settings → Webhooks → 对应 Webhook)显示每次投递的精确请求头、载荷、HTTP 状态和响应体。这是无需查看服务器日志即可诊断故障的最快方式。
完整配置参考
platforms:
webhook:
enabled: true
extra:
host: "0.0.0.0" # 绑定地址(默认: 0.0.0.0)
port: 8644 # 监听端口(默认: 8644)
secret: "" # 可选的全局回退密钥
rate_limit: 30 # 每路由每分钟请求数
max_body_bytes: 1048576 # 载荷大小限制,单位字节(默认: 1 MB)
routes:
<route-name>:
secret: "required-per-route"
events: [] # [] = 接受所有;否则列出 X-GitHub-Event 值
prompt: "" # {field} / {nested.field} 从载荷中解析
skills: [] # 第一个匹配的 Skill 被加载(仅一个)
deliver: "log" # log | github_comment | telegram | discord | slack | signal | sms
deliver_extra: {} # github_comment 用 repo + pr_number;其他用 chat_id下一步
- 基于 Cron 的 PR 审查——按计划轮询 PR,无需公网端点
- Webhook 参考——Webhook 平台的完整配置参考
- 构建插件——将审查逻辑打包为可共享的插件
- Profiles——运行专用的审查者 Profile,拥有独立的记忆和配置