去年我做的是處理會議紀錄的 Agent,今年做的是常駐 Agent,這兩件事的難點不在同一個地方。
會議 Agent 的問題是一次性的:語音轉文字、摘要、寄信,串通就結束,常駐 Agent 的問題要跑幾天才出現,換更強的模型也不會消失。
這三個不是常駐 Agent 問題的全部,是我自己遇到、並記錄下來的
重新啟動後,先前寫入的偏好讀不回來。Hermes 內建 MEMORY.md 與 USER.md 搭配 FTS5 索引,但寫入是條件觸發的,未觸發就不會寫入檔案。
模型本身無狀態,context window 載入什麼就只知道什麼,要跨 session 保留資訊,必須有一層機制在對話結束前寫出、下次啟動時讀回,而這層機制不屬於模型。
Discord 群組頻道中,訊息以 顯示名稱: 內容 的格式送進 context,模型把前綴解析成自身 identity,回覆時自稱發話者的名稱。
根本原因是模型沒有自我指涉,identity 由注入內容決定。SOUL.md 的身分宣告只在 system prompt 出現一次,經 context 壓縮後被移除,剩下唯一可判斷 identity 的線索就是訊息開頭那個名字。
agent 的執行權限上限由它持有的 API 金鑰決定:在 prompt 裡寫「不要寄信」只是寫在 prompt 裡,模型可能不遵守,事後也無法證明它遵守過。OAuth scope 未授予 gmail.send 則是在協定層就擋掉,請求根本送不出去。
兩者的差異不在嚴格程度,而在能不能驗證
| 問題 | 實際缺的機制 |
|---|---|
| 跨 session 狀態沒有保存 | 狀態的寫入與讀取 |
| 身分認知不穩定 | 能保存下來且不可覆寫的身分宣告 |
| 操作超出授權範圍 | 在協定層擋得住的授權邊界 |
換上參數量更大的模型,它仍然無狀態、沒有自我指涉、持有同一組全權限金鑰。
這類問題屬於 Harness Engineering 的範圍:
Agent = Model + Harness
Harness 指包覆在 Model 外層的執行框架,決定它看得到什麼、被允許做什麼,以及如何驗證輸出。
Mitchell Hashimoto 的定義是:每當發現 agent 犯了一個錯,就花時間工程化一個解法,讓它再也不會犯同一個錯。
| 世代 | 處理對象 | 約略年份 |
|---|---|---|
| Prompt Engineering | 單次請求的指令構造 | 2023 |
| Context Engineering | 進入 context window 的內容與配置 | 2025 |
| Harness Engineering | 模型被授權執行的操作、回饋迴圈與驗證機制 | 2026 |
三者是疊加不是取代
| 層 | 名稱 | 負責什麼 |
|---|---|---|
| ⑦ | Verification | 輸出驗證 |
| ⑥ | Loop | 控制迴圈 |
| ⑤ | Boundaries | 授權邊界 |
| ④ | Tools | 工具註冊 |
| ③ | Identity | 身分宣告 |
| ② | Memory | 狀態保存 |
| ① | Context | 輸入控制 |
Model 位在這七層之下,是可替換的推論端點,本身不屬於 harness。
層間有依賴順序:Context 先於 Memory,未先界定什麼進得了 context,就無從決定什麼值得長期保留,Tools 先於 Boundaries,工具未註冊前無邊界可劃。
| 元件 | 角色 | 選它的理由 |
|---|---|---|
| Hermes Agent | runtime 與狀態層 | 開源,記憶、排程、身分三層內建 |
| Gemini | 推論 | 原生支援平行與組合式函式呼叫,多步驟的工具呼叫不必由 harness 自行串接 |
| MCP | 工具介接協定 | 換模型或換框架,工具側不必重寫 |
第一階段接上 Model 這一層並確認可替換性:換掉推論端點後,其餘各層不需跟著改。
第二階段處理內部狀態:context window 的容量固定,system prompt 與工具 schema 會先佔去一部分,掛越多 MCP 剩越少。這段要量出每一項的實際佔用,再決定什麼該進 context、什麼該寫入長期記憶、身分宣告放在哪一層才不會被壓縮掉。
第三階段處理外部行為:工具要能被呼叫,而且要能證明它確實被呼叫過,權限邊界要落在協定層而不是 prompt。排程觸發之後,agent 要自己回讀狀態確認結果符合預期。
三十天結束時,這個 agent 應該能在無人值守的情況下依排程執行任務、驗證自己的輸出,並在失效時留下可追溯的紀錄。