overflow loop,是假修復
上禮拜 r/openclaw 有人回報 v2026.7.1-2 的 bug,症狀是這樣的:agent 進 preflight,判定 context overflow,觸發 compaction,compaction 跑完回報「沒有對話訊息可以壓縮」,然後流程走回原點,preflight 再跑一次,又 overflow,無限循環。
這個 bug 的數字很清楚。preflight 看到的狀態:systemPromptChars: 34549、estimatedPromptTokens: 10698、contextTokenBudget: 40960、reserveTokens: 32960,算出來留給 prompt 的只剩 promptBudgetBeforeReserve: 8000,而 overflowTokens: 2698。問題是 messages: 0、historyTextChars: 0、promptChars: 10。
翻成白話:session 裡根本沒有對話紀錄,是 system prompt 本身就把 budget 壓爆了。compaction 被叫起來,掃了一圈,空的,乖乖回報完成,流程繼續,preflight 再跑,結果還是一樣。
這是 deterministic 的無限迴圈。不是 flaky 的 race condition,不是偶發,第一次跑就會卡死,而且不會 crash、不會 timeout,從外部看系統沒死,但 agent 也沒辦法工作。這種狀態比直接報錯還難 debug,因為你不知道它到底在幹嘛。
說穿了就是 compaction 跟 preflight 看的不是同一份東西。
compaction 做的事是壓縮 session transcript,前提是「有歷史訊息存在」。messages: 0 的情況下它真的沒辦法做任何事,這不是它的 bug。
preflight 估的是整體 prompt 壓力,system prompt、tool schema、各種 injection 加總起來有多少 token,還剩多少空間。這兩個計算完全分開跑,看的是不同的輸入。
recovery path 的邏輯是「preflight overflow → 呼叫 compaction → 再試」,但這背後隱含了一個假設:overflow 是因為 transcript 太長。這個假設在 system prompt 超重的情況下完全失效。
實務上這不是罕見場景。複雜的 tool schema、injection rules、persona config,system prompt 輕鬆吃掉大半 context budget。以這個 bug 的數字粗估,34549 chars 的 system prompt 大約要 11000 多 token,但整個 budget 扣掉 reserve 只有 8000,根本進不去。你就算把 transcript 壓到 0,overflow 一樣還在,compaction 永遠是安慰劑。
留言區有人補充說:recovery path 根本不應該在這個情況下被觸發。我覺得說得準。compaction 沒有錯,是呼叫者沒有先確認「這次 overflow 是 compaction 能解的那種嗎」。
要修這個問題,recovery flow 的入口要先做分類。至少要分兩種:一是 transcript 太長的 overflow,compaction 可以處理;二是 static prompt weight 太重,compaction 幫不了,這時要 gracefully 拒絕或往上報錯,不能繼續 retry。
一個實際可以加的判斷很簡單:進 compaction 之前先檢查 messages 是不是 0,或 historyTextChars 低於某個閾值。是的話直接跳過,回傳「無法 recover」的狀態。不然就是在叫廚師去整理一個空冰箱,整理完問題還在。
設計 agent recovery flow 的時候,不能把所有 overflow 都丟給 compaction。overflow 至少要拆成兩個來源分開處理,壓 transcript 是一條路,但 system prompt 太肥是另一個問題,那條路根本不存在。
作者:CtrlC