你在 GitHub Issue 留下「幫我修一下」,Bot 很快回答「完成了」。但它究竟改了哪個資料夾?同一則事件重送,會不會又做一次?這篇 Hermes+Pi GitHub 派工教學,就從這些比「叫得動 AI」更實際的問題開始。
本文專為第一次接觸 Agent 整合的讀者寫:先用接單櫃台、工程師與驗收員理解分工,再看六步接線與一個小型修補任務。程式片段會說明各自做到哪裡;你需要一個自己管理的練習 Repo,以及已設定模型的 Hermes、Pi 環境。初學者可以先完成不連外的接單邏輯,再找熟悉部署的夥伴接上 HTTPS。
以下文件與程式核對至 2026 年 10 月 25 日,Pi bridge 固定提交 0faf5496。本站執行了本機接單與狀態判斷檢查;沒有連接 GitHub App 或模型跑過完整派工。真正的 Bot 留言、模型修改與回寫,要依下列驗收單在你的隔離環境確認。
先說結論:可靠派工=接單規則+隔離執行+獨立驗收
可靠派工=接單規則+隔離執行+獨立驗收。把它想成維修店:GitHub Issue 是工單,入站 adapter 是接待員,Hermes 是領班,Pi 是修理師傅;最後由另一個驗收程序決定回報內容。Bot 身分讓你知道誰留言,驗收證據才讓你知道工作有沒有完成。
- 先限制一個練習 Repo、一組允許的使用者,以及一個固定指令
/fix-fixture。 - 同時記錄 delivery ID 與留言 ID;先把工單可靠存下來,再啟動模型。
- GitHub 私鑰留在可信回報端;執行 Hermes 與 Pi 的工作環境不帶 GitHub 寫入憑證。
一、Hermes+Pi GitHub 派工教學:先把四個零件分清楚
① GitHub App「制服」:App 安裝到指定 Repo,使用 installation token 代表該安裝執行 API 操作。GitHub 的身分文件明確說明,這類請求歸屬 App。它與拿你的個人 token 代做,是不同身分。
② 入站 adapter「接待員」:接收 webhook(事件通知),核對誰來、要做什麼、是否收過。③ Hermes「領班」:把核准的任務交給工具。④ Pi bridge「派工電話」:讓 Hermes 呼叫 Pi 編碼工具;本文採一次性 pi_task,方便先追一件小工作。想補 Agent 基礎,可先讀Agent Harness 是什麼。
這裡有兩個需要分開的現成能力。Hermes 官方已有GitHub PR webhook 教學,示範事件觸發與 gh 回覆;本文另選自建接單器,目的是教持久化去重、固定任務與 App 身分。另一方面,github_app 社群外掛列的是 GitHub 讀寫工具,每次寫入另有核准關卡。兩個外掛的安裝,與本文整條流程的驗收,應各自確認。

二、六步接線:先做一張範圍很小的工單
①「穿對制服」:先驗 Bot 身分,再接任務
依GitHub App 註冊步驟進入 Settings → Developer settings → GitHub Apps → New GitHub App。Webhook URL 填你控制的 HTTPS 接收端,設定隨機 secret,訂閱 Issue comment。把 App 只安裝到練習 Repo;這個留言練習給 Issues 讀寫,Metadata 維持讀取即可,不加入 PR 合併或程式推送權限。
可信回報端安裝 catalog 固定版本:hermes plugins install github_app,再執行 hermes plugins enable github_app 並重啟。把 GITHUB_APP_ID、GITHUB_APP_INSTALLATION_ID、GITHUB_APP_PRIVATE_KEY_PATH 放在該專用 profile 的秘密設定;私鑰檔留在 Repo 外。先請 Hermes 呼叫 github_identity,確認 attribution.auth_method 與 actor 是預期的 App,而非個人憑證 fallback。
接著在該 profile 的 plugins.entries.github_app.settings 設 allow_write_actions: true。這只是開放寫入工具;每一筆留言仍要走外掛的逐次核准。核准一則寫到練習 Issue 的短留言,再到 GitHub 檢查 Bot 帳號。第一版保留這個有人驗收的回寫路徑;不要把開關誤當成無人值守授權。
②「先查證件」:驗簽與授權,各問一個問題
驗簽回答「內容是否由共享 secret 所對應的來源送來、途中有沒有改」;白名單回答「這個人是否被允許派工」。GitHub 驗簽文件要求用原始 request body 計算 HMAC-SHA256,再與 X-Hub-Signature-256 作固定時間比較。不要先 parse JSON 再重新序列化,那已可能改變原始位元組。
下面是可執行的接單核心。存成 intake.py;接收器要把原始 bytes、標頭及自己管理的 config 傳給它。users 放數字帳號 ID,repo_id、installation_id 取自你的練習安裝;示例不接受留言指定新的目錄、模型、Repo 或 shell 指令。
import hashlib, hmac, json, sqlite3
def accept(db, raw, headers, secret, config):
signature = 'sha256=' + hmac.new(secret, raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(signature, headers.get('X-Hub-Signature-256', '')):
return 'rejected-signature'
if headers.get('X-GitHub-Event') != 'issue_comment':
return 'ignored-event'
delivery = headers.get('X-GitHub-Delivery', '')
if not delivery or len(delivery) > 128:
return 'rejected-delivery'
try:
p = json.loads(raw)
r, c, i = p['repository'], p['comment'], p['issue']
allowed = (
p['action'] == 'created'
and p['installation']['id'] == config['installation_id']
and r['id'] == config['repo_id']
and r['full_name'] == config['repo']
and c['user']['id'] in config['users']
and p['sender']['id'] == c['user']['id']
and c['user']['type'] == 'User'
and 'pull_request' not in i
and i['state'] == 'open'
and type(i['number']) is int and i['number'] > 0
and type(c['id']) is int and c['id'] > 0
and c['body'].strip() == '/fix-fixture'
)
except (ValueError, KeyError, TypeError, AttributeError):
return 'rejected-payload'
if not allowed:
return 'rejected-policy'
try:
with db:
db.execute('INSERT INTO jobs VALUES(?,?,?,?,?,?)',
(delivery, r['id'], c['id'], i['number'], 'queued', 'fix_fixture'))
except sqlite3.IntegrityError:
return 'duplicate'
return 'queued'
def open_db(path):
db = sqlite3.connect(path)
db.execute('''CREATE TABLE IF NOT EXISTS jobs(
delivery TEXT UNIQUE NOT NULL, repo INTEGER NOT NULL,
comment INTEGER NOT NULL, issue INTEGER NOT NULL,
state TEXT NOT NULL, task TEXT NOT NULL,
PRIMARY KEY(repo,comment))''')
return db
HTTP 層請保留這些具體邊界:在讀 body 前限制大小、設定逾時與速率上限;捕捉 SQLite 不可用時回 5xx,別在資料尚未落盤就回成功。簽章不符回 403;忽略與去重回 2xx;queued 可回 202。這段核心未實作 HTTP server、TLS 與部署設定;它們應在你的接收框架與反向代理完成,接上前先照GitHub webhook 最佳實務確認能快速回應。
③「蓋收件章」:兩種單號,擋兩種重複
X-GitHub-Delivery 像物流單號,comment.id 像客人的原始報修單。GitHub 說明,手動 redelivery 沿用原 delivery ID;上面的資料庫另把 (repo, comment) 設成主鍵。HMAC 簽的是 body,並不把這些事件標頭一起簽進去,因此第二層依 body 留言 ID 去重很有價值。
只開一個 worker 時,也要以資料庫條件更新原子領單:UPDATE jobs SET state='running' WHERE delivery=? AND state='queued',只在更新筆數為一時執行。資料庫要放持久磁碟。若 worker 中斷,先檢查該任務工作目錄與輸出,再由控制端決定恢復;不要一重開就把所有 running 任務重新派出。要理解這類迴圈,可接著讀自己搭 Agent Harness。
④「交給師傅」:固定 Pi bridge 版本與工作目錄
在另一個專用 VM 或受限工作環境準備 Hermes、Pi 與模型。Pi 現行官方安裝文件列出 npm install -g --ignore-scripts @earendil-works/pi-coding-agent,並要求 Node.js 22.19 以上。bridge README 仍列舊套件名稱;先確認 pi --version 與你的既有模型設定可用,不要因為 README 的例子而另外切換模型。
以下在專用環境中取回固定提交,讓插件目錄指向該版本;如果目標目錄已存在,先檢查其內容,不覆蓋它。pi-bridge 是 manifest 名稱,pi_bridge 是工具集名稱,兩者拼法不同。
git clone https://github.com/szabo-agent/hermes-pi-bridge.git
cd hermes-pi-bridge
git checkout 0faf5496f3041f41fd46ef032b075dcdbb334999
mkdir -p ~/.hermes/plugins
ln -s "$PWD/plugin" ~/.hermes/plugins/pi-bridge
hermes plugins enable pi-bridge
重啟 worker 的 Hermes,在只啟用 pi_bridge 的會話先呼叫 pi_check,再給一件受控任務。工具 schema的 pi_task 接受 prompt、working_dir、tools、timeout 等欄位。把 working_dir 寫成 worker 內的絕對路徑,別依賴目前終端所在位置。
{
"prompt": "修正 fixture.py 的 normalize_tag:去除前後空白並轉小寫。只修改 fixture.py;完成後列出差異,勿提交、推送或更動測試。",
"working_dir": "/work/fixture-lab/task-001",
"tools": "read,edit,write",
"timeout": 120
}
這是給 Hermes 呼叫工具的參數,不是 shell 命令。控制端從佇列讀到的 fix_fixture,應映射到這份固定任務模板;不要把任意 Issue 原文直接拼進終端命令。程式整合可依Hermes CLI用 hermes chat --oneshot --toolsets pi_bridge --query-file task.txt,以參數陣列啟動程序並保留輸出。task.txt 由控制端產生,內容含固定任務與路徑;模型選擇不呼叫工具、工具失敗或超時,都要留下失敗狀態。
目錄限制與提示詞不是安全沙盒。核對bridge 程式可見,它以 subprocess.run、cwd=working_dir 啟動 Pi。這不會因你設定 Hermes terminal backend,就自動證明該子程序也在容器內。請隔離整個 worker 與插件程序,只掛載練習副本,不帶 App 私鑰、個人 token 或日常家目錄;工具清單則再縮小可做的工作。
⑤「拿驗收單」:修好一個 fixture,比十段完成宣言有用
建立只有一個函式的練習 Repo:def normalize_tag(value): return value.strip()。驗收要求 normalize_tag(" ALPHA ") == "alpha",以及 normalize_tag(" beta ") == "beta"。控制端先記住基準 commit,每張任務複製乾淨工作目錄,再讓 Pi 只修 fixture.py。
模型回來後,把差異交給新的隔離驗收程序:只允許 fixture.py 改動,測試由控制端維護,不能採用 Agent 改過的測試。先檢查差異,再在無秘密憑證的環境執行測試;測試輸出、退出碼、基準 SHA 與 patch 都保留在任務紀錄。即使只有一個檔案,執行其程式碼仍需要隔離。
為什麼要多這一道?這版 bridge 在有輸出時會解析並回傳 status: completed,另帶 is_error 等欄位。completed 不能直接換成「修補通過」。回報資格要同時看工具錯誤、是否有預期變更、檔案範圍及獨立測試;缺一項就停在待查或失敗。
⑥「把回條送回原單」:先保留逐次核准
把佇列中保存的 Repo 與 Issue 編號交給可信回報端,請 github_comment_issue 寫入固定格式:「任務 ID、驗收狀態、改動檔案、測試退出碼」。依外掛逐次核准,回傳後保存 comment ID,再把任務標為 reported。原始模型全文與執行日誌先過秘密資訊檢查,不直接整份貼回公開留言。
回寫失敗時只重試回報,不重跑 Pi。若請求逾時,先到原 Issue 核對帶有該任務 ID 的 Bot 留言;確認未寫入後才重送。GitHub 的建立留言 API支援 installation access token 與 Issues 寫入權限。若日後改成無人值守的獨立 reporter,那是另一個需要明確授權的服務:固定 Repo、固定格式、驗收憑證與去重紀錄都要由程式強制;不要移除這個外掛原有的逐次核准來冒充同一路徑。
三、Hermes+Pi GitHub 派工教學的驗收:刻意讓它出錯
先不付模型費用,對接單核心送出自己建立的 JSON fixture。本機檢查跑過十種情況:合法接單、相同事件、同留言換 delivery 標頭、未授權帳號、body 竄改、Bot 自己回覆、PR 留言、錯誤 installation、指令夾帶文字,以及資料庫重開。合法事件留下單一 queued 紀錄,其餘依規則拒絕或去重。這個結果只涵蓋本機核心,未包含公開 HTTP 與真實 Agent。
- 重播:在 GitHub App 的 Recent deliveries 重送同一件事件,查資料庫仍是一筆;再測服務重啟後的重播。
- 未授權:用不在白名單的帳號留言,確認 worker 沒啟動、沒產生新工作目錄,也沒請求模型。
- 失敗任務:讓固定測試失敗或讓 Pi 超時;Issue 回條只能寫 failed/待查,不能寫成功。
- 回寫故障:暫停回報端,讓已驗收結果留在佇列;恢復後只補留言,確認沒有新增第二份修補。
另外,本機四個狀態判斷案例確認:工具有錯、測試非零、變更超出範圍,都不能得到 verified;只有各項符合才可通過。真實接線還要確認 Hermes 確實呼叫 pi_task,保留工具事件與工作目錄。這與Hermes Memory/Skills/Cron 進階教學互補:那篇教累積能力,這篇教每張工單如何對帳。
四、三個容易漏掉的成本與邊界
第一,授權不等於內容可信。白名單使用者也可能引用外部惡意文字。這個練習只接受固定指令,不把 Issue 敘述當系統規則;Repo 內的說明檔、插件與模型輸出也要視為工作資料,不能自行擴權。
第二,去重不等於全流程恰好一次。接單、改檔與遠端留言是不同副作用;中途斷線可能讓你不知道留言是否已成功。因此狀態要分 queued、running、verified/failed、reported,保留可恢復紀錄,別靠一個 done 旗標包辦全部。
第三,兩層 Agent 有各自用量。Hermes 的協調與 Pi 的編碼,可能各自呼叫已設定的模型。這篇不估一個通用價格;先固定小任務、一次一個 worker、設定 timeout,再從兩端用量紀錄量出每件已驗收工作的成本。若你只是想接聊天工具,Channels SDK 通道整合處理的是另一層問題。
常見問題:八個直接答案
1. 裝好 github_app 就會自動收到 Issue 嗎?
不能由安裝結果推定。該外掛列的是 GitHub 工具;本文入站接單器另負責 webhook、授權與排隊。先用事件紀錄證明接線。
2. Hermes 已有 webhook,為什麼還要寫 adapter?
因為本文要練習跨重啟去重與固定任務政策。官方 PR webhook 是可比較的路徑;選自建是為了控制與觀察這些環節。
3. allow_write_actions 打開後,可以無人批准嗎?
不是。這版 github_app 還要求逐次 operator approval;本文保留它,沒有把設定開關當作批准。
4. Git worktree 就是隔離環境嗎?
不是。它分開檔案工作目錄,程序仍可能有原系統使用者的存取能力。執行區要另外用 VM/受限環境隔離。
5. Pi 的 tools 清單能完全保護私鑰嗎?
不能單靠清單。本文從配置上讓執行區拿不到 GitHub 私鑰;選少量工具只是在這個邊界內再減少工作面。
6. 看到 completed 就能關閉 Issue 嗎?
不能據此決定。先核對工具錯誤、變更範圍與獨立測試;本練習回報結果,不自動關單或合併。
7. 沒有模型帳號,現在能練哪一段?
可以先做本機接單:建立假 payload、計算簽章、重播與核對 SQLite。做到 queued 為止,先學會把模型放在正確的入口後面。
8. 最適合的第一張工單是什麼?
一個可用固定測試驗收、只改單一檔案的 fixture。先證明同單不重做、失敗不報喜,再擴大任務。
給新手的三個重點
- 接單先於模型:誰能派、派什麼、收過沒,都由程式決定。
- 執行與回寫分開:改程式的 worker 不拿 GitHub 寫入鑰匙;回報端只送驗收過的結果。
- 證據先於完成:留住工作目錄、patch、基準與測試,讓每張工單都有回條。
接著閱讀
左右滑動查看更多推薦
下一步:先讓同一張單只進一次
今天先建立一個練習 Repo,把 /fix-fixture 設為唯一任務,重播同一則留言,確認資料庫只留一筆。接著接上隔離的 Hermes+Pi,最後才開啟經核准的 Bot 回寫。回到開頭那個等式:可靠派工=接單規則+隔離執行+獨立驗收。你要交出的第一個成果,是一張能追到證據的工單。想繼續循序學習,可從AI 教學文章選下一個主題,或到AlphaLab 課程安排完整學習路線。






