跳轉到主要內容
OpenClaw 有兩個相互配合的防護機制,用來防止重複的工具呼叫模式, 兩者都在 tools.loopDetection 下設定:
  1. 迴圈偵測enabled)- 預設停用。監看滾動式 工具呼叫歷史,找出重複模式與未知工具重試。
  2. 壓縮後防護postCompactionGuard)- 只要 enabled 未明確設為 false 就會啟用。每次壓縮重試後啟動, 如果代理在視窗內重複相同的 (tool, args, result) 三元組, 就會中止執行。
設定 tools.loopDetection.enabled: false 可關閉這兩個防護機制。

為什麼需要這個機制

  • 偵測沒有進展的重複序列。
  • 偵測高頻率、無結果的迴圈(相同工具、相同輸入、重複錯誤)。
  • 偵測已知輪詢工具的特定重複呼叫模式。
  • 中斷「上下文溢位 -> 壓縮 -> 相同迴圈」循環,而不是讓它無限執行。

設定區塊

全域預設值,以下顯示所有已文件化欄位:
{
  tools: {
    loopDetection: {
      enabled: false, // master switch for the rolling-history detectors
      historySize: 30,
      warningThreshold: 10,
      criticalThreshold: 20,
      unknownToolThreshold: 10,
      globalCircuitBreakerThreshold: 30,
      detectors: {
        genericRepeat: true,
        knownPollNoProgress: true,
        pingPong: true,
      },
      postCompactionGuard: {
        windowSize: 3, // armed after compaction-retry; runs unless enabled is explicitly false
      },
    },
  },
}
每個代理的覆寫設定(選用,位於 agents.list[].tools.loopDetection):
{
  agents: {
    list: [
      {
        id: "safe-runner",
        tools: {
          loopDetection: {
            enabled: true,
            warningThreshold: 8,
            criticalThreshold: 16,
          },
        },
      },
    ],
  },
}
每個代理的設定會逐欄覆蓋全域區塊(包含巢狀的 detectorspostCompactionGuard),因此代理只需要設定 想要變更的欄位。

欄位行為

欄位預設值效果
enabledfalse滾動歷史偵測器的主開關。false 也會停用壓縮後防護。
historySize30保留用於分析的近期工具呼叫數量。
warningThreshold10模式被分類為僅警告前的重複次數。
criticalThreshold20阻擋無進展迴圈模式的重複次數。如果設定錯誤,執行階段會將此值限制為高於 warningThreshold
unknownToolThreshold10在發生這麼多次未命中後,阻擋對同一個不可用工具的重複呼叫。不受 detectors 控制。
globalCircuitBreakerThreshold30跨所有偵測器的全域無進展斷路器。如果設定錯誤,執行階段會將此值限制為高於 criticalThreshold。不受 detectors 控制。
detectors.genericRepeattrue對重複的相同工具 + 相同參數呼叫發出警告;一旦這些呼叫也回傳相同結果,就會阻擋。
detectors.knownPollNoProgresstrue偵測已知的無進展輪詢模式(process 搭配 action: "poll"/"log"command_status)。
detectors.pingPongtrue偵測兩個呼叫之間交替出現的無進展 ping-pong 模式。
postCompactionGuard.windowSize3壓縮後防護保持啟動的嘗試次數,以及會中止執行的相同三元組數量。
對於 exec,無進度雜湊會比較穩定的命令結果(狀態、 退出碼、逾時旗標、輸出),並忽略易變的執行階段中繼資料,例如 持續時間、PID、工作階段 ID 和工作目錄。對外傳送訊息的結果會在雜湊時移除 易變的每次呼叫 ID(訊息 ID、檔案 ID、時間戳記),因此一個「已傳送」結果 不會看起來與另一個不同的「已傳送」結果完全相同。當可取得執行 ID 時,歷史記錄 只會在該次執行內評估,因此排程心跳偵測週期和新的執行不會繼承 先前執行的過時迴圈計數。

建議設定

  • 對於較小的模型,請設定 enabled: true,並將臨界值保留為其 預設值。旗艦模型很少需要滾動歷史偵測,並且可以 將主開關 false 保持關閉,同時仍受益於 壓縮後防護。
  • 保持臨界值順序為 warningThreshold < criticalThreshold < globalCircuitBreakerThreshold;如果你將 criticalThresholdglobalCircuitBreakerThreshold 設定為等於或低於它們 必須超過的臨界值,執行階段會將它們向上調整。
  • 如果出現誤判:
    • 提高 warningThreshold 和/或 criticalThreshold
    • 可選擇提高 globalCircuitBreakerThreshold
    • 只停用造成問題的特定偵測器(detectors.<name>: false)。
    • 降低 historySize 以縮短歷史視窗。
  • 若要停用所有功能,包括壓縮後防護,請明確設定 tools.loopDetection.enabled: false

壓縮後防護

在內容溢位後進行壓縮重試之後,執行器會在接下來幾次工具呼叫上啟動 短視窗防護。如果代理在該視窗內發出相同的 (toolName, argsHash, resultHash) 三元組達 postCompactionGuard.windowSize 次,防護就會判定壓縮沒有打破 迴圈,並以 compaction_loop_persisted 錯誤中止執行。 此防護由主 tools.loopDetection.enabled 旗標控制,但有一個 差異:它會在旗標未設定或為 true 時保持啟用,並且只在 旗標明確為 false 時關閉。這是刻意設計的 - 此防護 用於脫離否則會耗盡無上限權杖的壓縮迴圈, 因此未設定組態的使用者仍會獲得保護。
{
  tools: {
    loopDetection: {
      // master switch; set false to disable the guard along with the rolling detectors
      enabled: true,
      postCompactionGuard: {
        windowSize: 3, // default
      },
    },
  },
}
  • 較低的 windowSize 較嚴格(中止前的嘗試次數較少)。
  • 較高的 windowSize 會給代理更多恢復嘗試。
  • 防護永遠不會在結果正在變化時中止;只有整個視窗中 位元組完全相同的結果才會觸發它。
  • 它只會在壓縮重試後立即啟動,不會在執行中的其他 時點啟動。
只要主旗標未明確設為 false,壓縮後防護就會執行,即使你從未撰寫 tools.loopDetection 區塊。若要驗證,請在壓縮事件後立即於閘道記錄中尋找 post-compaction guard armed for N attempts

記錄與預期行為

當偵測到迴圈時,OpenClaw 會記錄迴圈事件,並依嚴重程度警告或封鎖 下一個工具週期,在保留正常工具存取的同時,防止權杖 失控花費和鎖死。
  • 警告會先出現。
  • 當模式持續超過警告臨界值後,就會進行封鎖。
  • 重大臨界值會封鎖下一個工具週期,並在執行記錄中呈現清楚的 迴圈偵測原因。
  • 壓縮後防護會發出 compaction_loop_persisted 錯誤,指出 違規工具與相同呼叫計數。

相關

Exec 核准

Shell 執行的允許/拒絕政策。

思考層級

推理努力層級與提供者政策互動。

子代理

產生隔離的代理以限制失控行為。

組態參考

完整的 tools.loopDetection 架構與合併語意。