一次性的 Agent 任務失敗時,通常會在你面前報錯。讓它跨越數天、數十個 session 工作,失敗可能變得安靜:它重裝已經裝過的工具、把逾時的子任務當成完成,或繼續花錢卻沒有留下任何可追查的訊號。
Google Cloud Tech 在 2026 年 8 月 20 日發布的 〈5 design patterns for long-horizon agent harness〉正是從這種經驗出發。作者以 Google ADK 的 Long Horizon reference implementation整理五個模式:stable prefix、background learning、persistent workspace、explicit failure 與 guard chain。本文不把它們當成另一套 harness 清單,而是把每個模式改寫成一個問題:能不能主動注入這種失敗,並用外部 probe 證明它沒有悄悄發生?
五種靜默失敗,五個最小 probe
| 模式 | 靜默失敗的徵兆 | 最小修正 | 可執行的 probe |
|---|---|---|---|
| Stable prefix | 每輪記憶都被塞進 system prompt 前段,prefix cache 命中率持續是 0 | 把不變的 instructions 與 tools 放前面,把會變的記憶和計數器放到尾端 | 執行兩輪,檢查第二輪 response metadata 的 cached token count 是否大於 0 |
| Background learning | 回覆前同步抽取記憶,延遲變長;背景 task 沒有完成紀錄就被中止 | 回覆先返回,再用有明確 task reference 的隔離 worker 寫入記憶,限制寫入目錄並節流 | 回覆送出後中止 worker,確認持久狀態可查到 completed 或 failed,而不是消失 |
| Persistent workspace | 部署清掉 CLI 與半成品,Agent 從頭重做卻照樣回報進度 | 把檔案、工具與未完成工作放在可重新連接的 user-scoped environment | 模擬部署後重新 attach,驗證同一個檔案、工具版本與未完成步驟仍存在 |
| Explicit failure | child timeout、step limit 或等待批准,最後都被 parent 摘要成成功 | 讓終止狀態成為 typed envelope,並把不完整狀態寫進模型看得到的摘要 | 強制 child timeout,斷言 parent 不能產生成功摘要,必須回傳 INCOMPLETE 或升級人工 |
| Guard chain | 用字串比對封鎖 metadata IP,卻被整數或 IPv6 表示法繞過 | 先把輸入正規化成地址或 argv,讓 heuristic guard 快速拒絕,再由模型外的 default-deny 網路邊界限制實際目的地 | 送入多種表示法與 redirect/DNS 案例,確認網路邊界拒絕,並只留下遮罩後的 audit |
這張表描述的是故障注入與觀測方式,不是 Google Cloud 的 SLA。Long Horizon README 也明確標示它是供研究與移植的 sample,並非 Google 官方支援產品;可移植的是介面與測試想法,不是它的預設設定。
Stable prefix:成本圖表看不出 prompt 正在重建
Prompt cache 需要前段保持位元組級穩定。若每輪都把 recalled memories 或 runtime counters 放到 system prompt 最前面,內容雖然只是多幾行,整個 prefix hash 仍會改變,cache 就無法形成。這種失敗很容易被正常的延遲與 API 帳單掩蓋。
做法是按變動速度排列 prompt:system instructions、persona 與 tool definitions 放在最前;profile 和較慢變動的 context 放中間;記憶、步數與 runtime warning 放最後。同一篇 X 長文中,作者回報把 dynamic memories 移到尾端後,自己的 cache hit rate 到了約 95%,prompt 也從 70,000 字元縮到 22,000;這是該團隊的案例,不是供應商保證。不要用「看起來相同」推測 cache 有效,直接在第二輪讀 usage.total_cached_tokens。Google 的 Gemini context caching 文件也把 cache 命中與 token usage 當成應觀察的訊號。
Background learning:回覆完成不等於背景寫入完成
長時間 Agent 往往需要在 session 之間保存記憶或整理技能。若把抽取工作放在回覆之前,每一輪都會支付使用者看不見的延遲;若丟出沒有強 reference 的 asyncio task,寫入又可能在 runtime 清理時無聲消失。Python 官方文件提醒,asyncio.create_task() 建立的 task 應保留強參考直到完成。asyncio task 文件
較小的設計是 write-behind:先把答案交給使用者,再讓隔離的 sibling worker 以同一個 user identity 寫回受限的 memory directory。worker 要有明確完成/失敗狀態、節流與 shutdown drain;原文把背景抽取 pass 的間隔描述為 120 秒,而官方 Long Horizon sample 對應的是 post-turn auto-capture/review fork 的 cooldown,nightly Memory Bank consolidation 是另一條路徑。它也在 5 秒的 runtime cleanup 前只等待 4 秒收尾。這些是實作參數,不是通用的最佳值;如果狀態無法被查詢,背景工作只是把錯誤移到視線之外。
Persistent workspace:跨 session 保存的不只是檔案
一次 command executor 可以執行程式碼,卻不保證下一次 session 還找得到它安裝的工具、半成品與進度。Long Horizon 的做法是讓工具呼叫通過擁有狀態的 environment,依 user scope 保存並在下一次訊息重新 attach;Google Developers 的 ADK 教程也把 durable state 與 pause/resume 放在長任務的基本路徑上。
這裡的 probe 不必先建完整雲端平台。先在測試中模擬 process 或部署重啟,重新連接同一個環境,再比較檔案、工具與 checkpoint。若 Agent 只能從對話摘要猜測「上次做到哪裡」,它還沒有真正的 workspace。
Explicit failure:讓 parent 能分辨「沒完成」
當一個 Agent 的輸出會成為另一個 Agent 的輸入,純文字摘要不是可靠的協定。timeout、step limit、crash、等待批准與正常完成若共享同一種 envelope,parent 就只能把部分 commentary 猜成結果。
最小修正是把 terminal state 寫成明確值,例如 completed、timeout、halted 與 pending,並在摘要中重寫成模型不能誤讀的訊息:INCOMPLETE: child timed out; no tests ran。probe 應故意讓 child 逾時,再確認 parent 的路徑會停下或升級,而不是製造一個漂亮的成功報告。若 loop 仍會在「看起來有進展」時無限執行,原文的參考實作把每輪 tool call 上限設為 200、每個 session 的 iteration 上限設為 50,然後在乾淨邊界產生 handoff;你的上限應依任務成本與驗收時間重新量測。
Guard chain:先消除確定的危險,再使用人的注意力
Shell 有權限碰到的網路,通常比模型以為的更大。只用字串比對 169.254.169.254,無法攔住同一個地址的整數、十六進位或 IPv6-mapped 表示法;輸入要先解析成結構化地址,再做規則判斷。
護欄也有順序:先跑便宜、可確定拒絕部分命令的 heuristic exfiltration guard;再跑回傳 allow/ask/deny 的政策層;最後才詢問人。Long Horizon sample 自己也把這一層定位成 heuristic,不能當成唯一的 egress 邊界;真正不可由 session 設定放寬的限制,要放在模型與 session 之外的 sandbox、host firewall 或 egress proxy。地址正規化只是快速拒絕,仍要處理 DNS、redirect、proxy 與檢查和連線之間的 TOCTOU,並涵蓋雲端提供者的原生 IPv4/IPv6 metadata 端點(GCP metadata 文件)。
Audit 只記錄遮罩後的主體、規則、決策與目的地,不保存完整 argv、body、環境值或 token;執行環境也應使用最小權限、短效且按使用者/任務隔離的 credentials。若 guard 對所有命令都詢問,人最後只會習慣按下批准;若 guard 自己也是模型,則無法成為模型之外的邊界。
先測已經發生的失敗
五個模式不需要一次全部移植。先從最近看見的 failure signature 開始:
- 先寫出「應該看見什麼」與「不應該發生什麼」;不要只寫 Agent 應該更可靠。
- 注入一個可撤回的失敗,例如 child timeout、環境重連或禁止 egress。
- 讓 probe 讀取模型之外的訊號:metadata、持久狀態、typed envelope、audit 或網路結果。
- 只有 probe 失敗時,才增加一個最小控制;再把這個 probe 留在回歸檢查裡。
這個順序讓「長時間自治」變成可測的可靠度問題。先把無聲失敗變得可見,再討論要不要讓 Agent 多跑幾小時或跨更多 session。
和站內文章的分工
本文只談五個 failure probe 以及它們各自要觀察的外部訊號。需要系統邊界的完整說明,可接到站內既有文章:
| 要深入的問題 | 站內文章 |
|---|---|
| harness 的工具、狀態、驗證、權限與 trace 控制面 | AI Agent 不可靠,先別急著換模型:Harness Engineering 的七個控制面 |
| ReAct、FSM、memory 與 sandbox 如何組合 | AI Agent 狀態機架構演進 |
| 跨 session 的記憶生命週期 | Memory Engineering:讓 AI Agent 記得該記得的事 |
Long Horizon 提供的是一組可讀、可移植的參考實作;它不會替你的任務定義正確的 retention、權限或成本界線。那些界線仍要由你的 domain、資料風險與 verifier 決定。
