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.json、agent-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 True、assert 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項測試之所以存在,是因為我學到了滿足完成標準不等於生產就緒
參考文獻
-
作者使用Claude Code鉤子實作的Ralph迴圈模式。審議基礎架構:3,455行Python程式碼、8個模組、141項測試,橫跨4次提交(2025-2026)。 ↩
-
Anthropic,“Claude Models,” 2025。 ↩
-
Liu, Nelson F. et al., “Lost in the Middle: How Language Models Use Long Contexts,” TACL, 2024。 ↩
-
Anthropic,“Claude Code Documentation,” 2025。鉤子生命週期事件。 ↩
-
作者的git日誌。提交
3cad08c、10df724、fbf1a0d、32bd711涵蓋完整的審議基礎架構建構。 ↩ -
作者的無預算限制代理生成經驗。記錄於
~/.claude/projects/*/memory/MEMORY.md錯誤條目中。 ↩ -
作者的bash除錯經驗。
((VAR++))在set -e下的退出碼行為,作為跨工作階段學習記錄於MEMORY.md中。 ↩