跳至主要內容

    Loop Engineering 是什麼?怎麼讓 AI 自己把事情做完再叫你?(2026 實戰教學)

    先寫好誰判定做完了,用測試與獨立 reviewer 守住停止條件,才能放心讓 AI 自己把事情做完

    sunny 桑尼· · 42 MIN READ · 308 次瀏覽

    Loop Engineering 是什麼?怎麼讓 AI 自己把事情做完再叫你?(2026 實戰教學)
    本文目錄

    想讓 AI 自己把事情做完、測試全過再叫你,結果它做到一半就停下來等你?這篇帶你從一行 while 開始,一步步做出會自己檢查成果的 loop!

    你想把一件事整個交給 AI:把專案的依賴升到新版,測試全過再叫你,你先去開會。現在多半做不到。它做到一半就停下來問你下一步;就算一路做完,它回報「全部完成」,你還是得自己跑一次 pytest,才知道是不是真的。AI 寫得越快,這種等你確認的事就越多,你審不過來。

    讓 AI 自己一輪一輪做下去、不用你每一步回應,這種做法今年有了名字:Loop Engineering。它的原理和 CI 很像:沒過就再跑一輪,過了才停。問題在「過了」由誰判定。AI 自己說的「完成」不能算數,你又不在場,就要有它以外的東西去看證據。

    聊天式開發中人是每一步的開關與瓶頸;loop 式架構將中間交由 worker 與 reviewer 迭代,人只守前後兩端(Sunny DataScience 整理)。

    左邊是你現在的用法,右邊是 loop:中間交給兩個角色來回,worker 負責寫程式,reviewer 負責檢查它做完沒、做對沒。

    我的判斷是:誰來判定做完了,決定 loop 能不能放著讓它跑。這篇用一個練習專案,從一行 while 開始,每一步解掉一個讓你得回來盯著的瓶頸,每一步都附設定檔、實際跑出來的畫面,和「怎樣算做對了」的檢查。

    快速回答 Loop Engineering 是讓 AI 一輪一輪自己做下去,達到事先寫好的條件才停。能不能放著讓它跑,看誰判定做完了:寫程式的 AI(worker)自己說「完成」不算數。Claude Code 用 /goal 寫終點,用 Stop hook 在 worker 想停時跑測試,沒過不准停;測試沒涵蓋的地方交給另一個 AI(reviewer)。每道檢查都要有上限,Anthropic 的團隊讓 reviewer 審到第三輪就重想做法。

    開始之前:準備有 5 條測試的練習專案與 auto 模式,由客觀測試判定做完了沒

    跟著做之前,先準備三樣東西。

    一個有測試的小專案。這篇用的是一個叫 sales-report 的 Python 小專案:用 pandas 算每月營收、各區占比、各區平均,五條 pytest 測試。任務是把 pandas 從 1.5 升到 2。pandas 2 拿掉了 DataFrame.append 和 Series.iteritems,groupby().mean() 碰到文字欄位也不再默默略過、會直接報錯,所以一升上去測試就全紅。這是一個典型的依賴升級:有東西要修,修好沒有,測試說了算。report/summary.py 全文如下:

    import pandas as pd
    
    COLUMNS = ["region", "month", "amount"]
    
    
    def load(rows):
        df = pd.DataFrame(columns=COLUMNS)
        for row in rows:
            df = df.append(row, ignore_index=True)
        df["amount"] = df["amount"].astype(int)
        return df
    
    
    def monthly_total(df):
        return {month: int(total) for month, total in df.groupby("month")["amount"].sum().iteritems()}
    
    
    def region_share(df):
        totals = df.groupby("region")["amount"].sum()
        return {region: round(value / totals.sum(), 3) for region, value in totals.iteritems()}
    
    
    def average_by_region(df):
        means = df.groupby("region").mean()
        return {region: float(row["amount"]) for region, row in means.iterrows()}
    
    
    def top_region(df):
        totals = df.groupby("region")["amount"].sum()
        return totals.idxmax()
    

    tests/test_summary.py 準備了四筆銷售資料(台北、台中、高雄,2026 年 9 月和 10 月),五條測試分別檢查:筆數、每月合計、各區占比、各區平均、營收最高的地區。專案用 poetry 管理,測試指令是 poetry run pytest。你也可以用自己的專案,條件是有測試、在 git 裡、改壞了不心疼。

    決定 AI 能做什麼。loop 要能自己跑,就不能每做一個動作都停下來問你。Claude Code 有幾種權限模式,這篇用到兩種:

    情境用法誰擋危險動作

    互動視窗、claude -p 都可以
    用法
    auto mode(2.1.283 版起是互動模式的預設;--permission-mode auto)
    誰擋危險動作
    另一個模型代替你審每個動作
    完全不問
    用法
    --dangerously-skip-permissions
    誰擋危險動作
    沒有人;官方文件寫明只能在隔離的容器或虛擬機裡用

    Taichi 程式語言的作者、Meshy AI 創辦人胡渊鸣,在知乎寫過他怎麼一步步把 Claude Code 的產出拉高。他走到第二步就卡在權限:Claude Code「每十几秒就会问我各种权限问题」。他的解法是在 EC2 開一台機器,讓 Claude Code 在上面權限全開,順便提醒一句:記得寫一個每小時自動備份資料庫的功能,「別問我怎麼知道的」。這篇的畫面都在本機跑,所以用 auto mode。

    看一次全貌。做完這篇,練習專案裡會多出這些檔案:

    上半部檔案負責傳達任務與進度,下半部檢查設定決定何時可以停止;loop 能否無人值守關鍵在下半部的驗證邊界(Sunny DataScience 整理)。

    接下來每一步加一兩個檔案,加完就跑一次,看它擋住了什麼。下面的畫面都是 2026 年 10 月 20 日在這個專案實際跑出來的(Claude Code 2.1.303、Python 3.10、pandas 1.5.3 升 2.3.3)。截圖裡的指令比正文多兩個參數:--model sonnet 換成比較便宜的模型,--setting-sources project,local 略過示範機器上的個人設定;其他照正文。

    第 1 步:一行 while 的 Ralph loop 停不下來,第 1 輪測試全過後仍會無限重跑且 Ctrl+C 殺不死

    最短的 loop 是澳洲工程師 Geoffrey Huntley 在 2025 年 7 月公開的寫法,他叫它 Ralph(取自《辛普森家庭》的角色 Ralph Wiggum),原文只有一行:while :; do cat PROMPT.md | claude-code ; done。每一輪都從全新的對話開始,上一輪做了什麼,只留在檔案和 git 裡。

    練習專案的 PROMPT.md 只有兩行,每一輪都餵同一份:

    把這個專案的 pandas 從 1 升到 2,讓 poetry run pytest 全過。
    做完後用一句話說你改了什麼。
    

    把下面這段指令貼進終端機:它把原本的寫法換成現在 Claude Code 的指令,並印出輪數,方便看跑了幾輪。

    # -p:不進對話畫面,做完這一輪就結束
    # --allowedTools:-p 模式沒有人能按允許,沒列出來的動作會被拒絕
    i=0; while :; do
      i=$((i+1)); echo "=== 第 $i 輪 ==="
      cat PROMPT.md | claude -p --allowedTools "Read,Edit,Bash"
    done
    

    -p 是非互動模式:讀進 prompt,做完就結束,回到 shell,while 再開一個全新的 claude。上一輪做了什麼,這一輪只能從檔案和 git 看出來。這一行把 Bash 整個開放給它,所以只在練習專案裡跑。

    你猜它什麼時候會停?

    缺少停止條件的一行 while 停不下來:第 1 輪已修復並通過測試,loop 仍持續開啟新輪次,Ctrl+C 亦無法中斷迴圈(資料來源:2026-10-20 練習專案終端機實測)。

    它不會停。第 1 輪就把三個舊寫法改完、測試全過;第 2 輪讀到同一份 prompt,自己說「工作目錄裡已經有這次升級的改動,我沒有再動它們」,然後 loop 開第 3 輪。更麻煩的是 Ctrl+C:按下去只中斷當輪的 claude,畫面印出 Execution error,while 立刻開下一輪。要停,得關掉終端機,或另外 kill 掉那個 shell。

    怎樣算做對了:看到第 2 輪以後的回報都是「已經做完了」,而 loop 還在跑。這一步的目的就是看它停不下來。

    Anthropic 的 Justin Young 在 2025 年 11 月一篇談長任務 agent 的文章裡,記錄過 agent 自己做事時常見的毛病:還沒做完就宣告完成、沒有實際跑過就標成完成。這一行 while 一次撞到四個問題:

    1. 停不下來:它需要一個機器判得了的停止條件。
    2. 它說做完了,不一定做完:判定完成的是 worker 自己,需要一個和 worker 分開的 reviewer。
    3. 每一輪都從零開始:進度要寫在對話外面。
    4. 它能碰的東西沒有邊界:要先劃好它能碰什麼、錯了由什麼擋下。

    這一年各家提出的做法,各自補上其中幾個。2025 年 9 月,Simon Willison 寫〈Designing agentic loops〉,提醒要放進沙箱、只挑成功標準清楚的任務;到了 2026 年 6 月,Addy Osmani 用〈Loop Engineering〉當標題,把誰觸發下一輪、什麼時候停、誰檢查、進度記在哪,合起來當成一件要設計的事。下面幾步照這四個問題,一個一個補。

    第 2 步:進度寫在 PROGRESS.md 讓 4 輪修完自動停下,但判定做完的依然是 worker 自己

    每一輪都是全新的對話,worker 不記得上一輪踩過什麼坑。所以進度要寫在 repo 裡,下一輪先讀。

    胡渊鸣的做法是一份任務清單加一個啟動器:Claude Code 每次從清單拿一件事做,做完就結束,啟動器再開一個新的,直到清單空了。他給的 prompt 只有一句「干活;干完活退出」,「干活」是什麼,寫在 CLAUDE.md 裡。他還把規則和進度拆成兩個檔案:CLAUDE.md 很少改,因為「容易改坏掉」;經驗教訓讓 Claude Code 自己寫進 PROGRESS.md。

    練習專案照這個做法,PROMPT.md 改成:

    你在一個會重複執行的 loop 裡,每一輪都是全新的對話,上一輪的事你不記得。
    1. 先讀 PROGRESS.md,看上一輪做到哪。
    2. 只做下一件還沒完成的事,做完跑 poetry run pytest。
    3. 把這一輪做了什麼、測試結果寫進 PROGRESS.md,然後 git commit。
    4. 整個任務:把 pandas 從 1 升到 2,讓 poetry run pytest 全過。全部完成時,在 PROGRESS.md 最後一行單獨寫 DONE。
    

    loop 把 while : 換成「PROGRESS.md 還沒寫 DONE」:

    i=0; until grep -qx DONE PROGRESS.md; do
      i=$((i+1)); echo "=== 第 $i 輪 ==="
      cat PROMPT.md | claude -p --allowedTools "Read,Edit,Bash"
    done; echo "loop 停了,共 $i 輪"
    
    將進度寫在對話外的 PROGRESS.md 可跨輪次協作:4 輪依序修復各處錯誤,測試全過後寫入 DONE 讓 loop 順利自動停止(資料來源:2026-10-20 練習專案終端機實測)。

    這次四輪就停了,而且一輪只修一處:第 1 輪只升版本,測試 5 個全紅,它把後面要修的三件事寫進 PROGRESS.md;第 2 輪修 append,剩 3 個紅;第 3 輪修 iteritems,剩 1 個;第 4 輪修 mean(),全過,寫下 DONE。每一輪的 worker 都不記得上一輪,接得上全靠那份檔案。

    怎樣算做對了:git log 多了四個 commit,PROGRESS.md 裡每一輪都有一段紀錄,loop 自己結束,你沒有按 Ctrl+C。

    它停了,停止條件是 grep,機器判得了。可是 DONE 是誰寫的?是 worker 自己。它說做完了,grep 就相信。這一步解掉的是「每一輪從零開始」和「停不下來」,判定做完的還是 worker。

    第 3 步:用 /goal 讓獨立模型判定停止條件,但它只讀對話輸出、不會自己跑測試

    要讓 worker 以外的東西來判,Claude Code 有一個現成的指令 /goal:你寫一個完成條件,它每一輪結束後交給另一個小模型判斷達成了沒,沒達成就自動開下一輪,不用你按 Enter。Codex 也有同名的功能。

    條件怎麼寫,Claude Code 和 Codex 的官方指南要求幾乎一樣:

    要素練習專案的寫法

    一個量得到的終點
    pyproject.toml 的 pandas 是 ^2
    怎麼證明
    poetry run pytest 結束碼是 0
    路上不能動的東西
    不修改 tests/ 裡的任何檔案
    卡住時怎麼停
    最多 10 輪就停

    「把專案升級好」是壞條件,判斷的模型沒有東西可以判。官方文件建議把輪數直接寫進條件,例如「or stop after 20 turns」。打開互動視窗(auto mode),輸入:

    /goal pyproject.toml 的 pandas 升到 ^2,而且 poetry run pytest 結束碼是 0;不修改 tests/ 裡的任何檔案;最多 10 輪就停
    

    設好之後它立刻開始做,不用另外下指令。一輪就達成了;按 Ctrl+O 打開詳細紀錄,可以看到判斷的模型為什麼認定做完了:

    /goal 由獨立小模型判定是否達成終點,其判定理由完全依賴對話紀錄中出現過的指令輸出與 diff(資料來源:2026-10-20 Claude Code 實測)。

    怎樣算做對了:看到 Goal achieved,按 Ctrl+O 讀得到理由;單獨輸入 /goal 會列出條件、跑了多久、判過幾輪、花了多少 token。要中途取消,輸入 /goal clear。

    看理由裡的用詞:每一條都是「對話裡顯示了什麼」。官方文件寫得很清楚:判斷的模型不會自己跑指令、不會自己讀檔,只看對話裡出現過的東西。這次 worker 自己跑了 pytest、git status 和 git diff,輸出留在對話裡,判斷才有根據。worker 如果只說一句「測試都過了」,判斷的模型讀到的就只有這一句話。

    第 4 步:掛上 Stop hook 讓測試腳本自己跑,測試沒過不准停且連擋 5 次後交回給人

    要比 worker 的陳述更硬,就讓測試自己跑。Claude Code 的 Stop hook 是一支你掛在設定檔裡的腳本:worker 每次想停下來,腳本先跑;結束碼是 2,就不准停,寫到 stderr 的內容會交給 worker 當作繼續做的理由。上一步畫面裡的 running Stop hook,就是 /goal 在底下用的同一套機制,只是判斷的換成小模型。兩者的範圍也不同:/goal 只在當下的 session 有效;Stop hook 寫在專案設定裡,這個專案的每個 session 都會跑。

    /goal 依賴對話紀錄的文字陳述,而 Stop hook 則是主動執行測試腳本,測試未通過前強制將錯誤回傳 worker 繼續修改(Sunny DataScience 整理)。

    在專案的 .claude/settings.json 掛上這支腳本,順便加一條規則,不准 worker 改 tests/(第 5 步會用到):

    {
      "permissions": {
        "deny": ["Edit(tests/**)"]
      },
      "hooks": {
        "Stop": [
          {
            "hooks": [
              { "type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check_tests.py" }
            ]
          }
        ]
      }
    }
    

    腳本本身用 Python 寫,.claude/hooks/check_tests.py,只用到標準函式庫:

    #!/usr/bin/env python3
    """Stop hook: Claude may stop only after the tests pass."""
    import json
    import os
    import subprocess
    import sys
    from pathlib import Path
    
    hook_input = json.load(sys.stdin)
    counter = Path(os.environ["CLAUDE_PROJECT_DIR"]) / ".claude" / ".stop-blocks"
    
    # stop_hook_active is true when this stop was already blocked once in a row
    blocks = int(counter.read_text()) if hook_input.get("stop_hook_active") and counter.exists() else 0
    
    result = subprocess.run(["poetry", "run", "pytest", "-q"], capture_output=True, text=True)
    if result.returncode == 0:
        sys.exit(0)
    
    blocks += 1
    counter.write_text(str(blocks))
    if blocks > 5:
        # blocked five times in a row and still red: hand it back to a person
        print(json.dumps({"systemMessage": "Stop hook 連續擋了 5 次,測試還是沒過,交給人看。"}))
        sys.exit(0)
    
    tail = "\n".join(result.stdout.splitlines()[-15:])
    print(f"pytest 還沒全過(連續第 {blocks} 次擋下),修好再停。最後 15 行輸出:\n{tail}", file=sys.stderr)
    sys.exit(2)
    

    記得 chmod +x,再輸入 /hooks 確認 Claude Code 讀到了:在清單的 Stop 底下選標著 [Project] 的那一條,詳細頁會寫 Source: Project settings (.claude/settings.json)。

    腳本裡那個計數器,是停止條件的停止條件。Stop hook 如果一直擋,worker 會一直做下去。Claude Code 自己有一道保險:worker 沒有做任何動作、hook 卻連續擋了八次,就強制讓它停。官方文件建議讀輸入裡的 stop_hook_active 欄位,已經擋過一次就讓它停;那個做法擋一次就收手,適合提醒一下就好的 hook。這裡要的是測試沒過就繼續修,所以改成數次數:stop_hook_active 是 false,代表這是新的一次停下,計數歸零;連續擋滿五次,worker 第六次想停時測試還是沒過,就讓它停下來交給人。

    試一次。這次不用 /goal,故意只叫它做一半:「只用 poetry add 把 pandas 升到 ^2,其他檔案先不要動,做完就停下來回報。」

    Stop hook 主動跑測試擋下未達標的停工;當人類指示與測試要求衝突時,計數器連續擋滿 5 次後能安全中止並交回人手處理(資料來源:2026-10-20 Claude Code 實測)。

    worker 回報做完了,Stop hook 跑了測試,沒過,擋下。畫面上標的是 Stop hook error,這是 Claude Code 顯示結束碼 2 的方式,代表 hook 正常擋下,hook 本身沒有壞。

    接下來發生的事更值得看。worker 查出原因是 report/summary.py 第 9 行的 df.append,但提示裡寫了「其他檔案先不要動」,它選擇聽人的,不改程式,回頭問人要「改」還是要「回退」。hook 一擋再擋,擋滿五次之後,worker 第六次想停,計數器生效,畫面印出「Stop hook 連續擋了 5 次,測試還是沒過,交給人看」,worker 停下來等人回覆。回它一個「改」,它把三處舊寫法改完、測試全過,這次 Stop hook 沒有再擋,它正常停下。

    怎樣算做對了:故意讓測試不過時,看得到 Stop hook 擋下的訊息和測試的錯誤輸出;測試全過時,worker 停下來,hook 不出聲。這支腳本每次 worker 想停都會跑,所以測試要快,大專案只跑跟這次改動有關的那一組。

    這次實跑也說明了為什麼一定要有上限。你給 worker 的指示,可能和 hook 的條件衝突;沒有上限,兩邊就會僵在那裡,一直燒 token。

    第 5 步:reviewer 與 worker 角色分開,鎖定 tests/ 權限並讓獨立 subagent 審 diff

    到這一步,判定做完的是測試。可是測試本身也可能被 worker 動手腳。worker 修不好的時候,最省事的路是改測試,讓它變成一定會過。我自己的專案裡就遇過:worker 補的測試,在功能根本沒跑的時候照樣會過,每一次都是 reviewer 抓到的。

    先擋住最直接的那條路,就是上一步 settings.json 裡那條 "deny": ["Edit(tests/**)"]。如果寫成 Edit(...) 加 Write(...) 兩條,Claude Code 啟動時會直接提醒:Write(...) 不會被檔案權限檢查比對,只有 Edit(...) 規則會,而 Edit 規則涵蓋所有改檔的工具。

    這一關要自己驗。故意叫它:「把 tests/test_summary.py 裡 test_top_region 的預期改成 "台中"。」

    在設定檔加入 permissions.deny 能直接擋下 worker 竄改測試檔的企圖,防止 AI 為了測試過關而擅自修改驗證標準(資料來源:2026-10-20 Claude Code 實測)。

    怎樣算做對了:改檔的動作顯示 Error editing file,git status 看不到 tests/ 有變動。

    注意 worker 自己說的那句話:它「不會用 Bash 繞過去」。也就是說,繞得過去。這條規則只擋改檔工具,擋不住 shell。這次它選擇不繞,下一次不一定。要更硬,就在 CI 裡比對 tests/ 有沒有被改,或者再加一支 PreToolUse hook 檢查 Bash 指令。

    擋住改測試檔,只擋得住明顯的作弊。程式碼改得對不對、有沒有漏掉的情況,要另一個角色去看。Anthropic 在 10 月公開了 Claude Code 團隊自己怎麼審程式碼,第一層就是這件事:PR 開出來之前,Claude 先自己跑測試、build、lint,再交給一個 subagent,用全新的 context 審這次的 diff。subagent 不會看到 worker 一路的對話,比較不會被 worker 的說法帶著走。

    練習專案的 reviewer 放在 .claude/agents/reviewer.md:

    ---
    name: reviewer
    description: 審查這次的改動。寫 DONE 之前一定要先叫它。
    tools: Read, Grep, Glob, Bash
    ---
    
    你是 reviewer,只檢查,不改程式。
    
    1. 跑 `git diff` 和 `git diff --cached`,只看這次還沒 commit 的改動;都沒有的話,看 `git show HEAD`。
    2. 自己跑 `poetry run pytest -q`,把結果貼出來,不要相信別人說的「測試過了」。
    3. 每個問題都要附檔案和行號;拿不出證據的不要寫。
    4. 最後一行只寫 `VERDICT: PASS` 或 `VERDICT: FAIL`。
    

    tools 裡沒有 Edit 和 Write,reviewer 用不了改檔工具;它還有 Bash,所以指示裡也寫明只檢查、不改程式。第 3 條來自 Anthropic 那篇文章給 code owner 的建議:審查規則要「cap the nits」,要求「a file and line as evidence instead of a guess」,少挑小毛病,每個問題都拿出檔案和行號,不要用猜的。

    什麼時候叫 reviewer,寫在 CLAUDE.md,也就是胡渊鸣說的「干活」的規則:

    # sales-report
    
    ## 干活的規則
    - 每一輪先讀 PROGRESS.md,只做下一件還沒完成的事。
    - 寫 DONE 之前,先叫 reviewer subagent 審這次的改動,拿到 `VERDICT: PASS` 才算數。
    - reviewer 連續三次 FAIL:停下來,在 PROGRESS.md 寫下試過的做法和卡在哪,不要硬修。
    - 進度和教訓寫進 PROGRESS.md;這個檔案(CLAUDE.md)不要改。
    - 不准改 tests/ 底下的檔案。
    

    reviewer 本身也需要停止條件。Anthropic 的文章寫了一個例子:一個 agent 寫、一個 agent 審,兩邊可以一直互相找事做,有一個 PR 一夜之間來回審、改了五十多輪,直到作者自己按停。他們回頭查,reviewer 一直標的東西,大約 59% 是第一輪就已經在的舊程式碼,後面幾輪多半在挑舊毛病。他們的對策是三輪之後,reviewer 要作者退一步,重新想做法;幾輪之後只審新的 diff;已經貼過的問題不再重複。改完之後,一輪就過的 PR 從 65% 升到 78%,要五輪以上的從 8.9% 降到 1.5%(Anthropic 自己公布的數字)。CLAUDE.md 第三條就是這個上限。但要分清楚:這條寫在 CLAUDE.md 裡,是給 worker 的指示,沒有腳本強制;硬性的上限只有 Stop hook 那一道。

    第 6 步:用 stream-json 留存每輪日誌與 reviewer 報告,抓出測試沒涵蓋的行為改變

    loop 跑通宵,早上你只看到結果。它中間卡在哪一輪、為什麼卡,claude -p 預設只印最後一句話,看不出來。

    胡渊鸣寫了一個「管理 Claude Code 的 Claude Code」:一個 Python 程式負責派工,每件工作開一個 claude -p。一開始派出去的工作只有大約兩成真的做完。他把指令改成加上 --output-format stream-json --verbose,讓 manager 讀得到每一個動作的 JSON 紀錄,自己找出被管理的那個實例卡在哪。最後成功率到了將近九成五,他寫:「我也不知道怎么就成功率弄到几乎 95% 了」。

    我很喜歡這篇文章,但這一句是我和他分開的地方。成功率從兩成變九成五,卻說不出是哪一步改好的;換一個專案,也沒辦法知道能不能重現。紀錄要存下來,才知道是哪一輪、哪一個動作出錯。練習專案照他的做法,把前面幾步收成一支 Python 的 loop.py,每一輪的紀錄存成一個 .jsonl,再印出摘要:

    #!/usr/bin/env python3
    """One fresh Claude Code per round until PROGRESS.md ends with DONE, at most 10 rounds."""
    import json
    import subprocess
    import sys
    from pathlib import Path
    
    MAX_ROUNDS = 10
    progress, logs = Path("PROGRESS.md"), Path("logs")
    logs.mkdir(exist_ok=True)
    
    
    def done() -> bool:
        return "DONE" in progress.read_text().splitlines()
    
    
    def summarize(log: Path) -> None:
        """Print the tools this round used and how it ended."""
        for line in log.read_text().splitlines():
            event = json.loads(line)
            if event["type"] == "assistant":
                for block in event["message"]["content"]:
                    if block["type"] == "tool_use":
                        args = block["input"]
                        detail = args.get("command") or args.get("file_path") or args.get("subagent_type") or ""
                        print(f"  {block['name']}  {detail.splitlines()[0] if detail else ''}")
            elif event["type"] == "result":
                print(f"  結束:{event['subtype']},{event['num_turns']} 個 turn")
    
    
    for i in range(1, MAX_ROUNDS + 1):
        if done():
            print(f"loop 停了,共 {i - 1} 輪")
            sys.exit(0)
        print(f"=== 第 {i} 輪 ===", flush=True)
        log = logs / f"round-{i}.jsonl"
        with open("PROMPT.md") as prompt, open(log, "w") as out:
            subprocess.run(
                ["claude", "-p", "--model", "sonnet", "--permission-mode", "auto",
                 "--output-format", "stream-json", "--verbose"],
                stdin=prompt, stdout=out,
            )
        summarize(log)
    
    if done():
        print(f"loop 停了,共 {MAX_ROUNDS} 輪")
    else:
        print(f"{MAX_ROUNDS} 輪還沒做完,停下來交給人")
        sys.exit(1)
    

    輪數上限寫在腳本裡,就算沒有人在旁邊,最多十輪也會停下來交給人。--model sonnet 是因為這種工作用不到最貴的模型。

    獨立 reviewer subagent 能在乾淨 context 下自主審查 diff 與重測,並抓出既有測試未涵蓋的行為改變(資料來源:2026-10-20 練習專案實測紀錄)。

    摘要裡看得到 worker 照 CLAUDE.md 叫了 reviewer,reviewer 自己跑了 git diff 和 pytest;它交回的報告也存在紀錄裡,用 jq 就叫得出來。

    測試全過,reviewer 判 PASS,但它多抓到一件測試沒涵蓋的事:原本的 load() 用 append 一筆一筆加,遇到 COLUMNS 以外的欄位會多出一欄;worker 改成 pd.DataFrame(list(rows), columns=COLUMNS) 之後,多的欄位會直接被丟掉。現在沒有程式依賴這點,所以不擋;可是測試沒寫到的這個行為改變,這次是 reviewer 抓到的。worker 也把這件事寫進了 PROGRESS.md。

    怎樣算做對了:每一輪都有一個 .jsonl 檔,從裡面找得到用過的工具、被拒絕的指令和 reviewer 的報告,result 那一行看得到結束狀態。

    第 7 步:用 git worktree 平行開工、/loop 定時巡檢,但合併與審查成本會隨分支增加

    前六步都穩了,才值得往外擴。

    同時跑幾個。胡渊鸣下一步是用 git worktree 開好幾個資料夾,每個資料夾跑一個 Claude Code:五個實例、每個大約五分鐘一個 commit,平均一分鐘就有一個 commit。Claude Code 現在可以直接用 claude -w <名字> 開一個新的 worktree。但平行縮短的只有寫程式那一段,合併和審查會跟著分支數變長,這部分在〈Multi-Agent Coding 是什麼?同時開很多個 Agent,真的會比較快嗎?(2026 完整解析)〉拆過。

    定時跑。盯 PR、修 CI 這類工作,適合交給排程:

    做法指令在哪跑注意事項

    /loop
    指令
    /loop 5m 跑 poetry run pytest,失敗就修 report/,全過就用一句話回報
    在哪跑
    你的電腦,視窗要開著
    注意事項
    固定間隔的排程要請 Claude 取消,或 7 天後自動到期;不給間隔、讓 Claude 自己決定間隔的,按 Esc 就停;.claude/loop.md 可以寫預設指令
    雲端排程
    指令
    /schedule
    在哪跑
    Anthropic 的雲端,電腦關了也跑
    注意事項
    最短間隔 1 小時;每次從乾淨的 clone 開始,讀不到你本機的檔案
    /loop 指令會在工作階段中建立具備固定間隔與專屬 Job ID 的 cron 排程,讓 AI 能定時巡檢與修復程式碼(資料來源:2026-10-20 Claude Code 實測)。

    怎樣算做對了:輸入 /loop 之後,畫面回報間隔和一個 8 個字元的 job ID;問它「我有哪些排程」,列得出這個 ID 和下一次執行時間。Stop hook 和 tests/ 的規則照樣有效,排程跑的每一輪都受同一套檢查。

    人站在哪:依出錯風險分級審查,只有驗證便宜的維護工作才適合交給 loop

    做完七步,最後一個問題是:人還要看什麼?

    胡渊鸣的答案是幾乎不看。他的第十步是「坚持不去看除了 CLAUDE.md 以外的代码」,人該花時間的是把需求講清楚,「Context, not control」。這個答案有一個他自己講過的前提:那套軟體只給他一個人用,不用管多人登入、向前相容、穩定性,錯了也是他自己承擔。

    Anthropic 的答案比較細。他們在 6 月到 9 月之間,合併的 PR 增加一倍以上,需要還原(revert)的比例降了四成以上(Anthropic 自己公布的數字)。人沒有退出,退出的是「每一行都要人讀」:

    風險例子要幾個人核准

    低
    例子
    文件、文案、藏在 feature flag 後面的新增
    要幾個人核准
    自動檢查和安全審查全綠就不用人
    中
    例子
    一般產品改動;快取、自動更新、系統提示這類敏感區域
    要幾個人核准
    一個人
    高
    例子
    風險最高的程式碼
    要幾個人核准
    兩個人

    風險等級由分類器看變更內容決定,不看是誰寫的。文章裡有兩句話值得抄進自己的規則:「An agent's approval never counts as a person's.」agent 的核准不算人的核准;「A person is accountable for every change that ships, whoever typed it.」不管誰打的字,每個上線的變更都有一個人負責。

    張維峰在介紹這篇文章的短片最後,把它縮成個人明天就能做的三件事:寫一份短短的 CLAUDE.md,告訴 AI 怎麼檢查自己的成果;送出前先跑 /code-review,讓 AI 幫你挑一次錯;每次只改一小塊,出事才好退回。三件事剛好對到前面的 CLAUDE.md、reviewer,和錯了好還原。

    兩個答案放在一起看,差別在出錯會傷到誰。只給自己用、出錯也只由自己承擔的工具,你可以選擇不逐行看;要交給別人、跑在正式環境的東西,loop 再自動,也要先寫好哪些改動要人看、看的人是誰。

    哪些事先別交給 loop。Blake Crosley 整理過 Claude Code 負責人 Boris Cherny 公開的那些 loop,幾乎都是驗證便宜的維護工作:盯 PR、修 CI、整理使用者回饋。新功能要人看過才知道是不是想要的,架構好不好要幾個月後才看得出來,資料遷移做錯很難還原,這些先別交給 loop。連 Huntley 自己都說,他絕不會在既有的 codebase 上用 Ralph。

    成本與安全。loop 會一直花 token:間隔要配合被盯的系統多久變一次,簡單的工作交給便宜的模型。無人看管的寫入權限會出事,Crosley 公開過自己的事故:自動建 worktree 的功能,刪掉了一個它沒建過的資料夾。沙箱、預算上限、每小時的備份,都比事後補救便宜。

    結論:先定義誰判定做完了並設好停止條件,才把任務交給 loop 自動執行

    開頭那個任務,依賴升到新版、測試全過再叫你,照這七步走完,就能交代下去。拆開來,每一步都在回答同一個問題:你不在場的時候,誰能說它錯。

    完整的 Loop Engineering 架構將驗證關卡全數置於 worker 外部,並對測試、審查與權限皆設有重試上限,確保無人值守時系統不失控(Sunny DataScience 整理)。

    你的 loop 有沒有練習專案的做法

    機器判得了的終點
    /goal 的條件;pytest 結束碼;PROGRESS.md 的 DONE
    worker 以外的判定
    Stop hook 自己跑測試;reviewer subagent
    判定本身的上限
    Stop hook 最多連擋 5 次;CLAUDE.md 指示 worker 在 reviewer 連續三次 FAIL 後停下;loop.py 最多跑 10 輪
    對話外的進度
    PROGRESS.md、git
    worker 的改動限制
    Edit(tests/**) 擋改檔工具,Bash 擋不住,要另在 CI 比對;危險動作由 auto mode 擋
    看得到每一輪
    stream-json 紀錄
    人站在哪
    依風險決定;每個變更有一個人負責

    工具會換,/goal、Stop hook 下個月可能又多一個新名字。這張表不會變:先寫好誰判定做完了,再讓它自己跑。

    常見問題

    Ralph loop 是什麼?

    Geoffrey Huntley 在 2025 年 7 月公開的最短寫法:用 Bash 的 while 把同一份 PROMPT.md 一輪一輪餵給 coding agent,每一輪都從全新的對話開始,進度只留在檔案和 git 裡。原版沒有停止條件:用 while : 寫的話,任務做完了也會開下一輪,按 Ctrl+C 也只停得了當輪。改成讓 while 檢查進度檔裡有沒有寫 DONE,再加一個輪數上限,它就會自己停;但 DONE 是 worker 自己寫的,成果還要另外檢查。

    Claude Code 的 /goal 和 Stop hook 差在哪?

    /goal 由另一個小模型讀對話判斷做完沒,它不會自己跑指令,寫程式的 worker 說「測試都過了」,它讀到的就是這句話。Stop hook 是掛在 .claude/settings.json 的腳本,worker 每次想停,它就自己跑一次測試,結束碼回 2 就不准停。/goal 只在當前 session 有效,Stop hook 對專案裡每個 session 都有效。

    兩個 AI 互相審查,會不會停不下來?

    會。Anthropic 公開過一個 PR 一夜之間來回審、改了五十多輪的例子,reviewer 標的東西大約 59% 是第一輪就在的舊程式碼。他們的做法是三輪之後要作者退一步重想做法,之後只審新的 diff,已經貼過的問題不再重複。

    什麼任務適合交給 loop?

    驗證便宜、成功標準清楚的工作,例如盯 PR、修 CI,或有測試可跑、測試沒涵蓋的地方也有 reviewer 或人看的依賴升級。新功能、架構決定、資料遷移這類要人判斷或很難還原的工作,先別放著讓 loop 跑。

    延伸閱讀

    參考資料

    留言0

    登入後就能留言。

    登入