本指南帶你將 Hermes Agent 連接到 GitHub,使其自動取得拉取請求的差異、分析程式碼變更,並發佈評論 — 由 Webhook 事件觸發,無需手動提示。
當 PR 被開啟或更新時,GitHub 會向你的 Hermes 實例發送 Webhook POST。Hermes 使用提示詞執行代理程式,指示其透過 gh CLI 取得差異,回應會發回 PR 線程。
提示 — 想要更簡單的設定,不需要公開端點?
如果你沒有公開 URL 或只想快速開始,請查看建立 GitHub PR 評論代理 — 使用 Cron 工作按排程輪詢 PR,在 NAT 和防火牆後也能運作。
資訊 — 參考文件
完整的 Webhook 平台參考(所有設定選項、傳遞類型、動態訂閱、安全模型)請參見 Webhook。
警告 — 提示注入風險
Webhook 負載包含攻擊者控制的資料 — PR 標題、提交訊息和描述可能包含惡意指令。當你的 Webhook 端點暴露在網際網路上時,在沙箱環境(Docker、SSH 後端)中運行閘道。參見下方的安全說明。
前置需求
- Hermes Agent 已安裝並運行(
hermes gateway) - 閘道主機上已安裝並認證的
ghCLI(gh auth login) - 你的 Hermes 實例的公開可達 URL(如果在本機運行,參見使用 ngrok 進行本機測試)
- GitHub 儲存庫的管理員存取權限(管理 Webhook 所需)
步驟 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 密鑰
events:
- pull_request
# 代理程式被指示在審查前取得實際差異。
# {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 僅寫入閘道日誌。 |
deliver_extra.repo | 從負載解析為例如 org/repo。 |
deliver_extra.pr_number | 從負載解析為 PR 號碼。 |
注意 — 負載不包含程式碼
GitHub Webhook 負載包含 PR 中繼資料(標題、描述、分支名稱、URL),但不包含差異。上方的提示詞指示代理程式執行
gh pr diff以取得實際變更。terminal工具包含在預設的hermes-webhook工具組中,因此不需要額外設定。
步驟 2 — 啟動閘道
hermes gateway
你應該看到:
[webhook] Listening on 0.0.0.0:8644 — routes: github-pr-review
驗證它正在運行:
curl http://localhost:8644/health
# {"status": "ok", "platform": "webhook"}
步驟 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 級別記錄,因此在預設日誌級別下不會出現在控制台中。
步驟 4 — 開啟測試 PR
建立一個分支,推送一個變更,並開啟一個 PR。在 30-90 秒內(取決於 PR 大小和模型),Hermes 應該會發佈評論。
要即時追蹤代理程式的進度:
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 每次 ngrok 重啟時都會改變 — 每個工作階段更新你的 GitHub Webhook。付費 ngrok 帳戶獲得靜態網域。
你可以直接使用 curl 對靜態路由進行煙霧測試 — 不需要 GitHub 帳戶或真實的 PR。
提示 — 本機測試時使用
deliver: log在測試時將
deliver: github_comment改為deliver: log。否則代理程式會嘗試向測試負載中的假org/repo#99儲存庫發佈評論,這會失敗。一旦你對提示詞輸出滿意,切換回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"
# Expected: {"status":"accepted","route":"github-pr-review","event":"pull_request","delivery_id":"..."}
然後觀察代理程式運行:
tail -f "${HERMES_HOME:-$HOME/.hermes}/logs/gateway.log"
注意
hermes webhook test <name>僅適用於使用hermes webhook subscribe建立的動態訂閱。它不會從config.yaml讀取路由。
過濾到特定動作
GitHub 為許多動作發送 pull_request 事件:opened、synchronize、reopened、closed、labeled 等。events 清單僅按 X-GitHub-Event 標頭值過濾 — 它無法在路由層級按動作子類型過濾。
步驟 1 中的提示詞已經透過指示代理程式在 closed 和 labeled 事件時提前停止來處理此問題。
警告 — 代理程式仍會運行並消耗代幣
「在這裡停止」指令防止了有意義的審查,但代理程式仍會為每個
pull_request事件完成執行,無論動作如何。GitHub Webhook 只能按事件類型(pull_request、push、issues等)過濾 — 不能按動作子類型(opened、closed、labeled)過濾。沒有路由層級的子動作過濾器。對於高吞吐量的儲存庫,接受此成本或在上游使用條件性呼叫你的 Webhook URL 的 GitHub Actions 工作流程過濾。
沒有 Jinja2 或條件模板語法。
{field}和{nested.field}是唯一支援的替換。其他任何內容都會逐字傳遞給代理程式。
使用技能獲得一致的審查風格
載入一個 Hermes 技能以給代理程式一致的審查人物。在 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}"
注意: 只會載入清單中找到的第一個技能。Hermes 不堆疊多個技能 — 後續條目被忽略。
將回應改為發送到 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(省略以使用主頻道)
目標平台也必須在閘道中啟用並連線。如果省略 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 Hook
GitLab 負載欄位與 GitHub 不同 — 例如 {object_attributes.title} 用於 MR 標題,{object_attributes.iid} 用於 MR 號碼。發現完整負載結構最簡單的方法是 GitLab Webhook 設定中的 Test 按鈕,搭配 Recent Deliveries 日誌。或者,從你的路由設定中省略 prompt — Hermes 會將完整負載作為格式化的 JSON 直接傳遞給代理程式,代理程式的回應(在閘道日誌中使用 deliver: log 可見)將描述其結構。
安全說明
- 永遠不要在生產環境中使用
INSECURE_NO_AUTH— 它完全停用簽章驗證。僅用於本機開發。 - 定期輪替你的 Webhook 密鑰 並在 GitHub(Webhook 設定)和你的
config.yaml中更新它。 - 速率限制 預設為每個路由每分鐘 30 個請求(可透過
extra.rate_limit設定)。超過時傳回429。 - 重複傳遞(Webhook 重試)透過 1 小時的冪等性快取去重複。快取鍵是
X-GitHub-Delivery(如果存在),然後是X-Request-ID,然後是毫秒時間戳。當兩者都未設定時,重試不去重複。 - 提示注入: PR 標題、描述和提交訊息由攻擊者控制。惡意 PR 可能嘗試操縱代理程式的行為。在暴露於公共網際網路時在沙箱環境(Docker、VM)中運行閘道。
疑難排解
| 症狀 | 檢查 |
|---|---|
401 Invalid signature | config.yaml 中的密鑰與 GitHub Webhook 密鑰不匹配 |
404 Unknown route | URL 中的路由名稱與 routes: 中的鍵不匹配 |
429 Rate limit exceeded | 超過每個路由 30 個請求/分鐘 — 從 GitHub 的 UI 重新傳遞測試事件時常見;等一分鐘或提高 extra.rate_limit |
| 未發佈評論 | gh 未安裝、不在 PATH 上,或未認證(gh auth login) |
| 代理運行但無評論 | 檢查閘道日誌 — 如果代理輸出為空或僅為「SKIP」,仍會嘗試傳遞 |
| 連接埠已被使用 | 變更 config.yaml 中的 extra.port |
| 代理運行但僅審查 PR 描述 | 提示詞未包含 gh pr diff 指令 — 差異不在 Webhook 負載中 |
| 看不到 ping 事件 | 被忽略的事件僅在 DEBUG 日誌級別傳回 {"status":"ignored","event":"ping"} — 檢查 GitHub 的傳遞日誌(repo → Settings → Webhooks → your webhook → Recent Deliveries) |
GitHub 的 Recent Deliveries 標籤(repo → Settings → Webhooks → your 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: [] # 載入第一個匹配的技能(僅一個)
deliver: "log" # log | github_comment | telegram | discord | slack | signal | sms
deliver_extra: {} # github_comment 需要 repo + pr_number;其他需要 chat_id
接下來要做什麼?
- 基於 Cron 的 PR 審查 — 按排程輪詢 PR,不需要公開端點
- Webhook 參考 — Webhook 平台的完整設定參考
- 建立外掛 — 將審查邏輯打包為可分享的外掛
- 設定檔 — 執行具有自己記憶和設定的專用審查設定檔