← 所有文章

Ralph 迴圈:我如何在夜間運行自主 AI 代理

我建構了一套自主代理系統,利用停止鉤子攔截退出嘗試、檔案系統記憶體在上下文視窗之間保存狀態,以及生成預算防止失控遞迴。這套系統在多個夜間工作階段中完成了我的9份PRD審議基礎架構(3,455行Python程式碼、141項測試)。1

TL;DR

Ralph架構透過同時解決三個問題來實現長時間運行的自主AI開發:上下文視窗耗盡(透過每次迭代使用全新上下文解決)、狀態持久化(透過檔案系統作為記憶體解決),以及任務連續性(透過停止鉤子迴圈防止代理在完成前終止來解決)。我在Claude Code鉤子系統中實作了這個模式,並用它來建構我的多代理審議基礎架構。這套系統確實可行,但它讓我學到了關於生成預算、標準品質和檔案系統污染的深刻教訓。


上下文視窗問題

每次AI對話都在一個上下文視窗內運作:這是一個固定大小的緩衝區,用於保存對話歷史、系統提示、工具輸出和工作記憶。Claude的上下文視窗可容納約200,000個token。一個複雜的開發工作階段可能在30至60分鐘的密集工作後耗盡這個額度。2

我在審議系統建構過程中直接測量了這一點。一開始能精確地跨8個Python模組進行多檔案編輯的工作階段,到了90分鐘時退化為只關注單一檔案的狹隘視野。代理不再參考先前讀取過的架構,因為那些上下文已被壓縮掉了。3

退化遵循一個可預測的曲線:

Iteration 1: [200K tokens] → writes code, creates files
Iteration 2: [200K tokens] → reads files from disk, continues
Iteration 3: [200K tokens] → reads updated files, continues
...
Iteration N: [200K tokens] → reads final state, verifies criteria

與單一長時間工作階段相比:

Minute 0:  [200K tokens available] → productive
Minute 30: [150K tokens available] → somewhat productive
Minute 60: [100K tokens available] → degraded
Minute 90: [50K tokens available]  → significantly degraded
Minute 120: [compressed, lossy]    → errors accumulate

每次迭代使用全新上下文的代理表現優於連續運行的代理,因為每次迭代都將完整的認知資源分配給當前狀態,而不是承載先前推理的負擔。


我的實作方式

停止鉤子

我的遞迴防護系統攔截代理的停止嘗試並檢查完成標準:

#!/bin/bash
# From recursion-guard.sh - simplified
CONFIG_FILE="${HOME}/.claude/configs/recursion-limits.json"
STATE_FILE="${HOME}/.claude/state/recursion-depth.json"

# Safe defaults with config override
MAX_DEPTH=2
MAX_CHILDREN=5
DELIB_SPAWN_BUDGET=2
DELIB_MAX_AGENTS=12

# Load config with validation
load_config() {
    if [[ -f "$CONFIG_FILE" ]] && command -v jq &>/dev/null; then
        config_depth=$(jq -r '.max_depth // 2' "$CONFIG_FILE")
        if [[ "$config_depth" =~ ^[0-9]+$ ]] && [[ "$config_depth" -gt 0 ]]; then
            MAX_DEPTH="$config_depth"
        fi
    fi
}

鉤子讀取定義成功條件的標準檔案。這些條件必須是機器可驗證的:測試通過/失敗、linter輸出、HTTP狀態碼、檔案存在性檢查。4

檔案系統作為持久化記憶體

關鍵洞察:檔案在上下文視窗之間持久存在。我的.claude/目錄作為代理的持久化記憶體:

目錄 內容 在Ralph迴圈中的角色
state/ recursion-depth.jsonagent-lineage.json 追蹤迭代次數、父子關係
configs/ 14個JSON檔案 編碼閾值、預算、規則(非硬編碼)
handoffs/ 49份上下文文件 保存跨工作階段的架構決策
hooks/ 95個生命週期處理器 在迭代之間強制執行品質閘門

每次新的迭代從磁碟讀取當前狀態,從上一次迭代結束的地方繼續。工作階段啟動鉤子初始化乾淨的狀態:

# From session-start.sh - recursion state initialization
RECURSION_STATE_FILE="$RECURSION_STATE_DIR/recursion-depth.json"
# Initialize with safe defaults
{
  "depth": 0,
  "agent_id": "root",
  "parent_id": null,
  "initialized_by": "session-start"
}

如果狀態損壞(在開發過程中發生了兩次),恢復模式會從安全預設值重新建立,而不是直接崩潰:

if ! jq -e '.depth' "$RECURSION_STATE_FILE" &>/dev/null; then
    # Corrupted state file, recreate with safe defaults
    echo "- Recursion state recovered (was corrupted)"
fi

任務規格格式

有效的Ralph任務包含三個要素:目標、完成標準和上下文指標:

OBJECTIVE: Implement multi-agent deliberation with consensus validation.

COMPLETION CRITERIA:
- All tests in tests/test_deliberation_lib.py pass (81 tests)
- post-deliberation.sh validates consensus above 70% threshold
- recursion-guard.sh enforces spawn budget (max 12 agents)
- deliberation-pride-check.sh passes 5 quality checks
- No Python type errors (mypy clean)

CONTEXT:
- Follow patterns in lib/deliberation/state_machine.py
- Consensus thresholds in configs/deliberation-config.json
- Spawn budget model: agents inherit budget, not increment depth

我用這個模式建構了什麼

審議基礎架構(9份PRD)

最大規模的Ralph迴圈專案:9份產品需求文件,跨多個工作階段實作。

PRD 交付成果 測試
PRD 1-4 鉤子、設定、遞迴防護擴充 提交為3cad08c
PRD-5 48項bash整合測試(7個測試套件) 提交為10df724
PRD 7-8 鉤子連接 + 81項Python單元測試 提交為fbf1a0d
PRD-9 12項端對端流程模擬測試 提交為32bd711

總產出: 橫跨8個模組的3,455行Python程式碼、141項測試、4次提交。每個工作階段都從前一個工作階段的檔案系統狀態接續。全新的上下文意味著每份PRD都能獲得代理的全部注意力,而不需要承載先前PRD的對話歷史。5

部落格品質系統(12個模組)

部落格linter最初是一個3模組的腳本,透過迭代的Ralph迴圈成長為12個模組。每次迭代新增一個模組、執行完整的測試套件,並驗證零回歸。完成標準逐步演進:

  • 迭代1:「全部77項測試通過」
  • 迭代5:「全部77項測試通過且linter對所有33篇文章報告0個錯誤」
  • 迭代8:「所有測試通過且0個錯誤且0個警告且所有文章深度評分≥2」

失敗與教訓

失敗1:生成預算災難

在審議系統建構初期,我在沒有生成預算限制的情況下執行了一個工作階段。代理生成了3個探索子代理。每個子代理又生成了自己的子代理。在幾分鐘內,遞迴防護鉤子攔截了數十次生成嘗試。這個工作階段以正常速率10倍的速度消耗API token,直到我手動終止。6

修正方案: 我在recursion-limits.json中加入了生成預算模型。代理從其父代理繼承預算,而不是遞增深度。一個預算為12的根代理可以在所有遞迴層級中總共生成最多12個代理。這是一個關鍵的架構洞察:預算繼承防止指數級增長,同時仍然允許深度代理鏈。

失敗2:輕易通過的標準

早期的一個任務要求代理「撰寫能通過的測試」。代理寫出了最簡化的測試:assert Trueassert 1 == 1。從技術上來說,標準確實被滿足了。但產出毫無價值。

修正方案: 標準必須同時指定數量和品質:

標準品質 範例 結果
模糊 「測試通過」 代理撰寫無實質內容的測試
可量化但不完整 「測試通過且覆蓋率>80%」 代理撰寫的測試覆蓋行數但不測試任何有意義的內容
全面 「所有測試通過且覆蓋率>80%且無型別錯誤且linter乾淨且每個測試類別測試一個獨立模組」 生產級品質的產出

失敗3:檔案系統污染

探索死胡同路線的迭代留下了殘留物:部分實作的功能、已棄用的檔案、衝突的設定。迭代5可能建構在迭代3中半完成、迭代4中已放棄的方案之上。

修正方案: 我在停止鉤子標準中加入了清理步驟:「不存在未被import或測試引用的檔案。」這迫使代理在迭代完成前清理死胡同殘留。

失敗4:((VAR++)) 事件

在bash整合測試期間,遞迴防護鉤子在第一次迭代時無聲地崩潰了。問題出在:((VAR++)) 當VAR為0時回傳退出碼1(因為0++求值為0,bash將其視為false)。在啟用set -e的情況下,這會終止腳本。

修正方案是用VAR=$((VAR + 1))取代((VAR++))。這個bash陷阱記錄在我的MEMORY.md中,已經在後續6個鉤子腳本中防止了相同的錯誤。7


Ralph何時有效、何時無效

適合的場景

  • 全新實作且有明確規格(新API、新模組、新測試套件)
  • 存在自動化驗證(測試、型別檢查器、linter、編譯)
  • 有界範圍,可以在單一任務檔案中描述

不適合的場景

  • 主觀品質(「讓UI看起來好看」)沒有機器可驗證的標準
  • 探索性工作,方向取決於中間發現
  • 大規模重構,需要理解跨數十個檔案的全域程式碼關係

關鍵要點

給建構自主代理系統的開發者: - 在啟動自主迴圈之前,先投資於機器可驗證的完成標準;我的審議系統之所以成功,是因為每份PRD都有可測試的成功標準(總計141項測試) - 從第一天就實作生成預算;預算繼承模型(而非深度遞增)在允許深度鏈的同時防止指數級代理生成 - 將檔案系統清理加入完成標準;來自被放棄迭代的死胡同殘留物會污染後續迭代

給評估自主AI開發的工程團隊: - Ralph架構用人工實作時間換取人工規格撰寫時間;投資回報取決於您的瓶頸是實作能力還是規格清晰度 - 以審查外部承包商程式碼的同等嚴謹度來審計自主產出;我的141項測試之所以存在,是因為我學到了滿足完成標準不等於生產就緒


參考文獻


  1. 作者使用Claude Code鉤子實作的Ralph迴圈模式。審議基礎架構:3,455行Python程式碼、8個模組、141項測試,橫跨4次提交(2025-2026)。 

  2. Anthropic,“Claude Models,” 2025。 

  3. Liu, Nelson F. et al., “Lost in the Middle: How Language Models Use Long Contexts,” TACL, 2024。 

  4. Anthropic,“Claude Code Documentation,” 2025。鉤子生命週期事件。 

  5. 作者的git日誌。提交3cad08c10df724fbf1a0d32bd711涵蓋完整的審議基礎架構建構。 

  6. 作者的無預算限制代理生成經驗。記錄於~/.claude/projects/*/memory/MEMORY.md錯誤條目中。 

  7. 作者的bash除錯經驗。((VAR++))set -e下的退出碼行為,作為跨工作階段學習記錄於MEMORY.md中。