前幾天處理 Hermes 多機器人系統時,我遇到一個看似簡單、實際上不能讓 Bot 自己完成的需求:Gateway 更新設定後,需要重新啟動,讓新的路由與 Profile 設定載入。
直覺解法是讓 Bot 執行重啟指令。但這裡有一個基本矛盾:如果 Bot 正在執行 Gateway 裡面,它把自己停掉之後,還有誰負責把它叫回來?
這篇記錄我如何把「服務本身」與「服務的監督者」分開,並用可驗證的方式完成遠端重啟。
先確認問題邊界
我的 Gateway 不是手動開一個終端機後一直放著,而是由 macOS launchd 管理。launchd 的責任是維持程序生命週期;Gateway 的責任則是接收訊息、依路由分派到正確的 Profile,再呼叫模型與外部工具。
這兩個角色不能混在一起:
| 元件 | 責任 | 能否可靠重啟自己 |
|---|---|---|
| Gateway | 收訊息、路由、執行任務 | 不可靠,停止自己後沒有執行者 |
| launchd | 啟動、監督、重啟程序 | 可以,位於 Gateway 外部 |
| 外部操作端 | 發出 kickstart、查看狀態與 log | 可以,與 Gateway 解耦 |
一開始我把「重啟服務」當成普通工具呼叫,忽略了 supervisor 的邊界。結果不是指令語法問題,而是執行者和被重啟者是同一個生命週期。
真正的解法:讓外部 supervisor 執行
修正後的原則很簡單:Bot 可以判斷需要重啟,也可以回報建議,但真正的 launchctl kickstart 必須由 Gateway 外部的程序執行。
在這台 Mac 上,launchd 負責管理 Gateway。外部 Terminal 執行 kickstart,讓 launchd 先停止舊程序,再依原本的 plist 設定啟動新程序。這比在 Bot 內直接 kill 自己安全,因為啟動責任不會隨著 Gateway 一起消失。
實際流程分成四步:
第一步,先讀取目前服務狀態,確認目標 label、程序是否存在,以及目前是否真的需要重啟。不能只看到一個設定檔被修改,就直接重啟所有服務。
第二步,從外部執行 launchctl kickstart。這個動作的重點不是「下了一個成功的指令」,而是把重啟交給仍然活著的 supervisor。
第三步,等待 Gateway 回到可服務狀態,再讀取最新 log。若程序雖然存在,但沒有完成啟動,仍然不能算恢復。
第四步,使用實際訊息做端到端驗證:送入一個受控測試請求,確認 Gateway 收到訊息、路由到預期 Profile,並取得回覆。只看 process list 不足以證明 Bot 已恢復工作。
遠端操作還有一層風險
當重啟動作不是在同一台機器的本機 Terminal 執行,而是透過遠端桌面或 computer_use 操作 Mac mini,就必須再區分兩件事:遠端控制通道是否還活著,以及 Gateway 是否已恢復。
Gateway 暫停時,遠端桌面不應該跟著失效;否則你會同時失去服務和修復入口。這也是為什麼 supervisor 要放在服務外部,控制通道也要獨立於被管理的程序。
我現在會把修復路徑畫成這樣:
控制端 → launchd → Gateway → Profile → Provider
故障時,控制端不經過 Gateway 直接找 launchd。只有 Gateway 恢復後,訊息流才重新走回正常路徑。
不要把重啟權限直接塞給每個 Bot
多 Bot 系統裡,最容易犯的錯是:為了讓自動修復看起來完整,就讓每個 Bot 都擁有重啟 Gateway 的權限。這會放大風險:一個錯誤判斷可能造成頻繁重啟,甚至讓所有 Profile 一起中斷。
比較穩定的設計是把權限分層:
| 操作 | Bot 可做 | 需要外部 supervisor |
|---|---|---|
| 判斷疑似設定未載入 | 可以 | 不需要 |
| 回報需要重啟 | 可以 | 不需要 |
| 讀取受限狀態與 log | 依授權 | 不一定 |
| 執行 kickstart | 不直接執行 | 需要 |
| 重啟後端到端驗證 | 可以協助 | 必須共同完成 |
這樣的設計犧牲了一點「Bot 什麼都能做」的想像,換來的是較清楚的責任邊界與回滾路徑。
這次踩雷留下的檢查清單
每次要重啟 Gateway,我會依序確認:
最後一點尤其重要。重啟可能已經成功,只是回應通道延遲;這時不能因為看不到立即回覆就再次按下重啟。先讀回狀態與 log,再決定下一步。
實際證據比「看起來成功」重要
這次的結論不是「launchctl 可以重啟 Gateway」,而是重啟必須被拆成三段驗收:
本機變更:設定檔確實寫入,目標 service 也由 launchd 管理。
服務載入:外部 supervisor 執行 kickstart 後,Gateway 程序重新啟動,並在最新 log 中完成初始化。
外部效果:從實際訊息入口送出測試請求,讀回預期 Profile 的回覆,確認路由真的恢復。
如果只完成第一段,代表「檔案改了」;完成第二段,代表「程序起來了」;只有三段都完成,才代表使用者真的拿得回服務。
結語
Bot 自我修復不是讓 Bot 擁有所有 root 權限,而是把修復流程設計成它能安全觸發、但不能破壞自身生命週期的系統。
對 Gateway 類服務來說,外部 supervisor 不是額外的複雜度,而是自動化能夠可靠運作的前提。下一篇會繼續處理上下文壓縮、快取與長任務,因為服務重新啟動之後,另一個問題才會浮現:如何讓長任務不要因為上下文變長而失控。