[Claude Code] · · 23min read

Ralph Loop 實戰:6 個 README 沒講的 Claude Code Stop Hook 踩坑(含 TDD 收斂 prompt 模板)

Ralph Loop 不是新模型也不是黑科技,本質就是 Claude Code Stop hook 攔住 exit + 同 prompt 重餵。本文拆解機制、實戰場景、4 個踩坑、2 個進階組合,以及一份可複製貼上的 TDD 收斂 prompt 模板。配合 ralph-loop-skill 那篇一起讀。

章節目錄 · 16

# Ralph Loop 實戰:6 個官方 README 沒講的坑(重度使用者觀點)

結論先講:Ralph Loop 不是新模型也不是黑科技,本質就是「Stop hook 攔住 exit + 同 prompt 重餵」。它解的是「Claude 太早收手」這個痛點,但用錯場景就是把錢包燒穿。本文是我用了一段時間後,把官方 README 沒寫的踩坑整理成 6 條進階心法,特別針對 TDD 收斂這類最常見的場景。

---

TL;DR

  • Ralph Loop = Stop hook + 同 prompt 重餵 + completion-promise(XML 標籤)退出
  • 真正的價值不是「自動化」,而是把人類從「按下重新發送」這個低價值動作拔掉
  • 最大的坑:Claude 為了脫身會亂講 promise(後面會拆解怎麼破)
  • 適合丟:TDD 收斂、批次測試修復、規格清楚的重複工作
  • 不適合丟:規格未定的探索、設計判斷、production debug
---

TOC

---


Ralph Loop 是什麼(30 秒理解)

官方 plugin 名字叫 ralph-loop(社群通稱 Ralph Wiggum loop,致敬辛普森家庭那個記性差但永不放棄的角色)。

機制超簡單:

# 你只跑一次:
/ralph-loop "把 auth 模組的測試修到全綠" --completion-promise "DONE" --max-iterations 20

# 然後 Claude Code 自動:
# 1. 動手做事
# 2. 想要結束 → Stop hook 攔住
# 3. Stop hook 把同一段 prompt 餵回去
# 4. Claude 看到自己上一輪的修改(檔案 + git history)
# 5. 繼續做 → 直到輸出 <promise>DONE</promise> 或撞到 max-iterations

關鍵是這句:「Claude 看到自己上一輪的修改」。它不是憑記憶硬幹,是看著檔案系統現況做下一輪。所以這個迴圈才有「收斂」的可能。

Geoffrey Huntley 用這個技巧做了一個程式語言(ghuntley.com/loop),3 個月跑完。Y Combinator 黑客松有人用 \$297 的 API cost 寫完一張 \$50k 的合約。

anthropics/claude-code 是 ralph-loop 官方 plugin 的家

---


機制拆解:Stop hook 怎麼攔住 exit

如果你只想用,這段可跳。但理解機制能幫你避開 80% 的坑。

ralph-loop 的核心檔案就 3 個:

檔案角色
commands/ralph-loop.mdslash command 入口,呼叫 setup script 建狀態檔
scripts/setup-ralph-loop.sh建立 .claude/ralph-loop.local.md(含 prompt + iteration + max + promise)
hooks/stop-hook.shsession 想結束時觸發,檢查狀態決定要放行還是再餵 prompt
Stop hook 做的事(簡化版):

  • .claude/ralph-loop.local.md,沒有就放行

  • 檢查 iteration 是不是已經 >= max_iterations,是就停

  • 撈 transcript 最後一段 assistant text,用 perl... 裡的內容

  • 如果 promise 文字 = 你設定的 completion-promise,停

  • 都不是 → iteration + 1,輸出 JSON {"decision": "block", "reason": <你原本的 prompt>} 把 prompt 餵回去
  • 整個機制就這樣。沒有 LLM、沒有黑科技,純 bash + jq + perl。

    這也意味著一件事:你能寫的所有「自動化迴圈」都是這個模式的變體。Stop hook 是 Claude Code 給你的 hook 點,剩下都是 bash 工夫。

    ---


    實戰場景 1:TDD 紅綠循環

    這是我最常用的場景,也是 Ralph 真正發光的地方。

    傳統流程:

    你:寫測試
    Claude:實作
    你:跑測試 → 紅了
    你:把錯誤丟回去
    Claude:再改
    你:跑測試 → 又紅了不同地方
    你:再丟錯誤回去
    ... 重複 5-10 次

    每一次「貼錯誤訊息回去」都是你在做機械操作。Ralph 把這段拔掉:

    /ralph-loop "把 src/auth/ 底下所有測試修到全綠。
    流程:
    
  • 跑 npm test src/auth/
  • 看失敗訊息
  • 修對應的實作(不要改測試本身)
  • 再跑一次
  • 全綠才輸出 <promise>ALL GREEN</promise>
  • 紅線:

    • 不准 skip 測試
    • 不准改測試本身(除非測試本身寫錯,這時要先回報)
    • 不准用 mock 取代實際邏輯" \
    --completion-promise "ALL GREEN" \
    --max-iterations 15

    跑下去你就可以去喝咖啡。回來時一個結局:

    • 全綠 → Ralph 自動退出
    • 撞到 15 輪還沒綠 → Ralph 強制停,你回來看卡在哪
    第二種結局其實也是有用的訊息:「這個任務 15 輪修不好,代表規格有問題或測試本身不合理」。

    ---


    踩坑 1:Claude 為了脫身會亂講 promise

    這是我最痛的坑

    當你 prompt 寫得有點模糊、或任務真的很難收斂,會看到 Claude 在第 8、9 輪左右開始動搖。它會想盡辦法輸出那個 ... 來脫身。常見手法:

    • 「測試大致都通過了,輸出 DONE」(其實還有 3 個紅)
    • 「主要功能已實作,剩下的都是邊界情況,輸出 COMPLETE」(其實邊界就是規格)
    • 「已達合理的完成度」← 這種話一出現就要警覺
    官方 plugin 在 commands/ralph-loop.md 裡有寫一段警告:
    CRITICAL RULE: If a completion promise is set, you may ONLY output it when the statement is completely and unequivocally TRUE. Do not output false promises to escape the loop, even if you think you're stuck or should exit for other reasons.

    但這段只在啟動那一輪生效,後面幾十輪它記性會淡。

    我的解法(3 個一起上):

  • completion-promise 設成「可驗證的事實陳述」
  • ❌ 「DONE」、「COMPLETE」、「FINISHED」← 這些太抽象,Claude 容易用主觀判斷

    ✅ 「ALL TESTS PASS WITH ZERO FAILURES」← 這是事實陳述,Claude 要說謊難度高很多
    ✅ 「BUILD SUCCESS AND COVERAGE >= 80%」

  • prompt 裡明寫驗收指令
  •    退出前必跑(缺一不可):
       - npm test 必須 0 fail
       - npm run build 必須成功
       - 兩個都過,才能輸出 <promise>ALL TESTS PASS WITH ZERO FAILURES</promise>
       

  • prompt 結尾加一句反作弊
  •    嚴禁:在條件未滿足時輸出 promise。如果你做不到,就把實際狀況寫進 status.md 然後正常結束(不要輸出 promise,讓迴圈自然結束)。
       

    第三招很關鍵:給 Claude 一個「正當的退出路徑」。不然它被夾在「不准輸出 promise」跟「想結束」之間會更亂。

    ---


    踩坑 2:max-iterations 不設就是燒錢儀式

    官方 README 講過這條,但我要再講一次因為我自己被燒過

    Ralph 的預設 max_iterations 是 0(無限)。如果你 completion-promise 沒設好(見上一段)、或根本沒設,這個迴圈會跑到天荒地老。

    實測一個輕量任務(修 1 個測試,平均每輪 \$0.30),我有一次設了 --max-iterations 100,半夜跑完一看:80 輪、\$24。任務早在第 12 輪就完成了,但 promise 沒對齊(我打 complete,Claude 一直輸出 COMPLETE),剩下 68 輪在原地踏步。

    規則

    任務類型建議 max-iterations
    修 1-2 個測試10-15
    修一整個模組的測試20-30
    重構或大改30-50
    「探索性任務」不要用 Ralph
    永遠不要用 --max-iterations 0,除非你正在寫一個會自動跑很久的 batch job 而且你親眼盯著。

    ---


    踩坑 3:prompt 寫太抽象會原地打轉

    Ralph 的 prompt 比一般對話 prompt 要求高很多,因為這段文字會被原封不動餵 N 次。

    抽象 prompt 的失敗模式:

    # 爛例:
    /ralph-loop "讓這個專案變得更好" --max-iterations 20

    Ralph 會跑 20 輪每輪做不同的事情,沒有累積方向:第一輪改了 README、第二輪加了個 lint config、第三輪重構了某個 helper、第四輪又改回去...

    好 prompt 三要素

  • 可驗證的成功條件(前面講過了)

  • 明確的禁區(避免 Claude 為了 progress 亂改)

  • 下一輪該做什麼的線索
  • 第 3 點是 Ralph 特有的:因為每輪都看自己上一輪的成果,prompt 裡要告訴它「看到 X 狀態時做 Y」。

    範例:

    任務:把 src/api/ 底下所有 endpoint 都加上 OpenAPI annotations。
    

    每一輪開始前:

  • 跑 grep -L "@OpenAPI" src/api/*.ts 列出尚未加註解的檔案

  • 如果列表為空 → 跑 npm run validate-openapi 確認有效 → 通過才輸出 <promise>OPENAPI COMPLETE</promise>

  • 如果列表非空 → 挑第一個檔案加 annotation → 結束本輪
  • 禁區:

    • 不准改 endpoint 的實際邏輯
    • 不准刪除既有 annotations
    • 不准 import 新套件
    這樣 Ralph 每輪都有清楚的下一步,不會往奇怪的方向發散

    ---


    踩坑 4:Stop hook 跟其他 plugin 打架

    如果你有裝其他 plugin(例如 superpowers、zenbu-powers、或自己寫的 Stop hook),ralph-loop 跟它們會搶 Stop 事件。

    Claude Code 的 hook 機制是多個 Stop hook 都會跑,但任何一個輸出 {"decision": "block"} 就會 block exit。實際發生的衝突場景:

    • superpowers 的 evaluator 會在 Stop 時評估意圖對齊,沒過會重派 → ralph-loop 也會 block,兩個一起 block 就變成「同時跑兩種重派邏輯」
    • 自己寫的 Stop hook 做 lint / test → ralph-loop 不在乎這些 hook 結果,照樣 block
    檢查方式
    # 看當前專案有哪些 hook 啟用
    cat .claude/settings.json 2>/dev/null
    ls ~/.claude/plugins/marketplaces/*/plugins/*/hooks/hooks.json 2>/dev/null
    

    # 跑 ralph-loop 前最好確認沒有競爭的 Stop hook

    建議做法:跑 ralph-loop 前暫時把其他 Stop hook plugin 停掉。或者乾脆不要混用,ralph-loop 跑的時候就純粹跑 ralph-loop。

    ---


    進階組合 1:搭 git worktree 平行跑多任務

    這招我最近才開始試,超有用。

    問題:Ralph 一次只能跑一個任務。如果你想同時修 3 個獨立模組,要等 1 個跑完才能跑下一個。

    解法:每個任務開一個 git worktree,每個 worktree 一個 Claude Code session 跑自己的 ralph-loop。

    # 主 repo 在 ~/workspace/myproject
    

    git worktree add ../myproject-auth feature/auth-tests
    git worktree add ../myproject-billing feature/billing-tests
    git worktree add ../myproject-notifications feature/notif-tests

    # 開 3 個 terminal,每個 cd 進去
    # 每個都跑自己的 ralph-loop

    3 個 ralph-loop 並行跑,用 3 倍 token 換 1/3 時間。

    注意事項

    • 不同 worktree 的 .claude/ralph-loop.local.md 是隔離的(plugin 有處理 session_id 隔離)
    • 但你 token quota 是共用的,3 條線同時跑會三倍燒
    • 不要對共用模組做修改,否則 merge 會打架
    ---


    進階組合 2:在 prompt 裡塞驗收腳本

    把驗收邏輯寫進 prompt,比靠 Claude 自己判斷可靠 100 倍。

    /ralph-loop "重構 src/services/payment.ts,把 if-else 鏈改成策略模式。
    

    每輪結束前必跑(缺一不可):
    \\\
    bash scripts/check-payment-refactor.sh
    \
    \\

    腳本會檢查:

    • 沒有 if-else 鏈超過 3 層
    • 所有原本的測試還是綠的
    • 新增的 strategy classes 有對應測試
    腳本退出碼 0 才能輸出 <promise>REFACTOR COMPLETE</promise>。
    退出碼 != 0 時,把 stderr 內容當作下一輪要修的問題。" \
    --completion-promise "REFACTOR COMPLETE" \
    --max-iterations 25

    對應的 scripts/check-payment-refactor.sh

    #!/bin/bash
    set -e
    

    # 檢查 1:if-else 深度
    MAX_DEPTH=$(awk '/if|else/ {gsub(/[^{]/,""); if (length > max) max = length} END {print max}' src/services/payment.ts)
    if [ "$MAX_DEPTH" -gt 3 ]; then
    echo "❌ if-else 深度 $MAX_DEPTH 超過 3" >&2
    exit 1
    fi

    # 檢查 2:原測試還綠
    npm test src/services/payment.test.ts || { echo "❌ 原測試紅了" >&2; exit 1; }

    # 檢查 3:strategy 都有測試
    for strategy in src/services/payment-strategies/*.ts; do
    test_file="${strategy%.ts}.test.ts"
    [ -f "$test_file" ] || { echo "❌ $strategy 沒有對應測試" >&2; exit 1; }
    done

    echo "✅ 全部通過"

    這樣做兩個好處:

  • Ralph 不能用主觀判斷脫身(腳本退出碼是事實)

  • 你之後可以重複用這個 script,不只 Ralph 場景
  • ---


    我的常駐 prompt 模板(複製貼上)

    這是我跑 TDD 收斂任務的標準模板,省去每次重寫:

    /ralph-loop "任務:__在這裡寫一句任務__
    

    每輪流程:

  • 跑 __你的測試指令__

  • 看失敗訊息

  • 修對應實作

  • 再跑一次
  • 退出條件(必須全部 true):

    • __指令 1__ 退出碼 0
    • __指令 2__ 退出碼 0
    • __額外條件__
    退出條件全 true → 輸出 <promise>__可驗證的事實陳述__</promise>

    禁區:

    • 不准改測試本身
    • 不准 skip 測試
    • 不准 import 新套件(除非 prompt 明寫允許)
    • 不准輸出 promise 如果上面任一條件未滿足
    正當退出路徑:
    如果你 5 輪內都無法推進,停手並寫 status.md 描述:
    • 試過什麼
    • 各自為什麼失敗
    • 你建議的下一步
    然後正常結束本輪(不輸出 promise,讓迴圈自然停)。" \
    --completion-promise "__同上面那段事實陳述__" \
    --max-iterations 20

    把 4 個 __...__ 填好就能跑。注意 completion-promise 要跟 prompt 內的字一字不差(否則 Stop hook 字串比對會失敗)。

    ---


    什麼時候別用 Ralph

    Ralph 不是萬能。以下場景會虧錢虧時間:

    場景為什麼不適合
    規格未定的探索Ralph 會把模糊規格反覆執行,越跑越歪
    UI/UX 設計沒有客觀驗收條件,Claude 會主觀宣告完成
    Production debug缺 production 上下文,Ralph 會錯改錯
    一次性操作(rename 一個變數)殺雞用牛刀,直接做就好
    規格本身就在變(pre-MVP)跑兩輪 spec 變了,前面工夫白費
    通則:規格越固定、驗收越客觀、操作越重複,Ralph 越強

    ---


    FAQ

    Q1:Ralph Loop 跟 Claude Code 內建的 agent 模式差在哪?

    Agent 模式是「分派一個任務給子 agent,等它回來」,子 agent 自己決定何時結束。Ralph Loop 是「攔住主 session 的 exit,逼它繼續」。Agent 適合「我清楚這事可以一次做完」,Ralph 適合「這事需要多輪迭代收斂」。兩個可以組合(在 ralph-loop 的 prompt 裡呼叫 sub-agent)。

    Q2:跑到一半想停怎麼辦?

    /cancel-ralph,它會刪掉狀態檔,Stop hook 下次就放行。但如果 Claude 正在思考中,得等它這輪結束才會觸發 Stop。所以最快是 Ctrl+C 中斷當前輪 + /cancel-ralph 二連發。

    Q3:completion-promise 的 XML 標籤一定要 嗎?

    是。Stop hook 用 perl 寫死 regex 抓 ...,改不了。但你 promise 內文可以隨便寫。

    Q4:Ralph 跑完會不會把 commit 一起搞了?

    不會。Ralph 不主動 commit,git history 是你自己平常的 commit。但 Ralph 看得到 git history,所以建議每輪結束自己 commit(用一條 git hook 或 prompt 裡明寫「每輪結束 git add . && git commit -m 'iteration X'」),這樣下一輪才有清楚的「上一輪做了什麼」訊號。

    Q5:能不能跨機器跑(例如 Mac 上發起、雲端執行)?

    理論可以但要自己搭 infra。Ralph 設計上就是 local Stop hook + local 檔案系統。要遠端跑得搭配 Claude Code Remote Control 或自己寫 SSH wrapper。

    ---

    延伸資源

    ghuntley.com 是 Ralph Wiggum 概念的原始來源

    ---

    結語

    Ralph Loop 不是魔法,本質就是 Stop hook + 同 prompt 重餵 + XML 標籤偵測。但這個簡單機制配上夠好的 prompt,能解掉一大堆「人類在 push 重新發送鍵」的低價值動作。

    最大的坑是 Claude 為了脫身會亂講 promise,破解方式是 promise 設成可驗證的事實陳述 + prompt 裡塞驗收腳本 + 給它正當的退出路徑。三招齊下,Ralph 才能真的跑出可信賴的結果。

    最後一個提醒:永遠設 max-iterations。沒有例外。

    author
    陳彥彤

    AI 工程師 · AI 顧問。Java 後端 8 年、AI 工程師 2 年。AI 內訓 · AI 導入顧問 · 前後端與雲端培訓。

    support

    覺得文章有用可以到 GitHub 給個 star,或是透過信箱聊聊 AI 內訓、AI 導入顧問或前後端 / 雲端培訓。