20 · 用 Claude Code 跑模擬調參:把 agent 變成實驗員的工作法
19 篇講實驗怎麼設計;這一篇講執行層:當實際操作者是一個 AI coding agent(Claude Code),要怎麼組織工作,讓「單輪十幾分鐘、要跑幾十輪、隨機失敗」的調參工作有效率地推進,而不是燒 token 空轉。
素材同樣來自 6.0 場景調校專案。這裡的每一條都有對應的失敗實例——多數是 agent(也就是筆者自己)先犯過一次,才變成規則。
1. 核心約束:agent 不是常駐進程
Claude Code 回答完一輪就結束,要有人說話或有通知進來才會再醒。把實驗 nohup 丟到遠端主機之後,它跑完不會通知任何人——「跑完」到「有人發現跑完」之間的時間全是空轉。實測案例:單輪驗證 15:00 跑完,16:27 才因使用者問「are you alive?」去查,模擬器空轉近 90 分鐘,而那是週末唯一的機時。
所以第一條規則:丟到遠端的長工作,一定要掛「會回叫 agent」的機制,不可 fire-and-forget。
2. 兩層監看:事件層 + 後備層
| 層 | 機制 | 頻率 | 負責 |
|---|---|---|---|
| 事件層 | Monitor 型工具:輪詢遠端 log,新事件行變成通知 | 1~2 分鐘 | 逐輪進度、閘門異常、批次死亡、DONE、主機失聯 |
| 後備層 | 背景 shell 迴圈,結束時喚醒 agent | 10~15 分鐘 | 事件層本身死掉時兜底;附總時長保險上限 |
設計要點(每一條都踩過):
- 安靜 ≠ 順利。 監看必須讓每一種終態都有訊號:正常結束(批次 driver 最後印一行
可辨識的
DONE)、進程死掉(ps -p <pid>消失且無 DONE)、主機失聯(連續 N 次 ssh 失敗要出聲,不能無限安靜重試)。只 grep 成功標記的監看,在崩潰、卡死、被殺時 跟「還在跑」長得一模一樣。 - 啟動先記基線行號,不重播舊事件——否則掛監看的瞬間會把跑到一半的歷史整批噴成通知。
- 掛好之後做一次正對照:手動跑同一條查詢,確認抓得到已存在的事件。實測抓過一次
真 bug:事件 pattern 用了 ERE 不支援的負向預查
(?!ok),整類異常事件無聲消失。 - 監看只做唯讀查詢(
grep+ps -p),不碰批次本身;pid 反查用ps -eo pid,etime,cmd,不要pgrep -f(會自匹配到自己的指令字串)。
3. 批次腳本自己要會守門
agent 醒來只看得到 log,所以驗證邏輯要寫進批次腳本,不能靠 agent 記得檢查:
- 每輪自動閘門(臂別確認、生效證據、輪數對帳——見 19 篇 §6)
- 每輪結束把生效組態印進 log 開頭(事後才問「那輪到底什麼組態」是查不到的)
- 失敗要長得像失敗:不要
| tail吞結束碼、不要無條件印「✅ 完成」、 字串替換要assert有匹配到 - 飛行守衛一類的中止條件內建在迴圈裡(偵測到就記下當時步驟並停), 不要「跑完整批回頭看」——殘局上疊殘局之後,分不清哪一步先壞
4. 每輪結論當場寫進 repo,不要等做完
逐輪紀錄(ROUNDS.md 形式:改了什麼 → 結果如何)+ 編號文件(結論、證據、
已證偽清單)+ 失敗清單(FAILURES.md:已證偽假設與方法論的坑,不讀就會重試)。
為什麼對 agent 特別重要:agent 的 session 會被壓縮、會重開,「我記得」不保證下一輪還在。寫進 repo 的才是跨 session 的記憶;而且 compact 之後接手的(可能是另一個 session 的自己)第一件事就是讀這三份。
配套:
- 腳本與參數一律進 repo commit 管理,主機與容器只是部署目標(
/tmp會被清、容器層重建就消失) - 讀到過期、錯誤、互相衝突的斷言,當下改對或刪掉,不要加註、不要刪除線—— 沒作用的內容每次載入都吃 token,還會讓下一輪照著錯的前提動手
- 編號只增不改(別的文件在引用);保留編號、改正內容
5. 模型成本分工:貴的做判斷,便宜的做機械活
agent 派 sub-agent 時,預設會繼承主迴圈的模型——在旗艦模型的 session 裡 fan-out 四個 review agent 等於燒四倍。分工原則:
| 工作性質 | 給誰 |
|---|---|
| 架構判斷、實驗設計、結論定案、寫作 | 主迴圈(旗艦) |
| 對碼盤點、逐斷言核實、引文抽取、批次驗證、盯 CI | 便宜模型 sub-agent(明確指定 model) |
| 拿不準 | 先用 inline 的少量 grep 抽驗,不 fan-out |
派工 prompt 的要素:完整的工具呼叫範例、已知錨點(檔案/行號/先讀清單)、明確產出格式、「只動哪些檔」的邊界、「不 commit、回報 X」的收尾協定。沒寫的等於允許——agent 會為了「做得完整」自行加碼。
6. 實驗迴圈的紀律:固定參數重試 N 次不是實驗,是抽樣
每一輪失敗之後、開下一輪之前,回答三個問題:
- 這次跟上一次哪裡不同?(拿數據逐步比對,不是印象)
- 根據這次觀察,假設要怎麼改?
- 下一輪改哪一個變因?若答案是「什麼都不改」,就不該再跑一輪。
agent 特別容易在長跑巡檢裡違反這條:cron 喚醒的每一輪看起來都像「延續上一輪」,不像「新任務開始」,於是檢討步驟一次都不會觸發。實測踩過:一整天把長跑寫成固定參數重試 12 次。解法是把「連續兩輪同類失敗」明文定義為新任務的開始。
連帶的兩條:
- 「再差一點」卡很久、特例越補越多 → 停下來重想整條路線是不是錯的 (實測:連續三輪為一個佔比 9% 的失敗模式設計實驗,才發現該追的是佔 31% 的主導模式)
- 高成本動作事前報數字:「N 輪 × 每輪 M 分 ≈ 總時長,期間佔用模擬器」, 在開跑前講,不是等使用者問「是不是把機器吃掉了」
7. 長時間工具必須自己冪等
呼叫端(LLM)在工具還在跑的期間,會用完全相同的參數再打進來——實測一筆搬運命令被打了 4 次。第二次呼叫看到半途的世界就回失敗,於是同一件事同時有成功與失敗兩個回報,LLM 挑了那個失敗的,把已經成功的搬運敘述成失敗。
三條可複用:①呼叫端會重試是常態,「重複呼叫無害」是工具的責任;②能用程式收斂的不要寫進 prompt(「EXACTLY ONCE」試過,完全沒用);③補早退路徑會漏,要找「一次擋掉整類」的位置(入口 per-key lock)。
8. 驗證用與執行不同的機制
agent 印了「✅ 已修正」不代表修正發生了。一天之內的四個實例:字串替換沒 assert(全形/半形不匹配,原文原封不動)、拿 grep log 判斷修復有沒有套用(工具根本不涵蓋那類屬性)、看門狗殺實驗(殺了父進程、子進程活著)、印成功訊息但檔案沒寫入。
共同形狀:失敗長得像成功。 每一項工作都要有獨立的驗證動作,而且驗證要用跟執行不同的機制:改檔案 → 讀回來確認;殺進程 → ps 數一次;設參數 → 極端值看行為(19 篇 §1)。
9. 檢查清單
開一個「Claude Code 跑調參」的工作階段之前:
- [ ] 遠端批次有兩層監看(事件層 + 後備層),所有終態都有訊號
- [ ] 批次腳本內建每輪閘門與中止條件,失敗長得像失敗
- [ ]
ROUNDS.md/FAILURES.md/ 編號文件的骨架存在,每輪當場寫 - [ ] 腳本與參數在 repo,不在
/tmp或容器層 - [ ] sub-agent 派工有模型分工、檔案邊界、收尾協定
- [ ] 連續同類失敗有明文的「停下檢討」觸發條件
- [ ] 高成本批次開跑前報過數字
- [ ] 每個「做完了」都有獨立機制的驗證
證據等級
本篇是單一專案的工作法紀錄,不是通則驗證過的方法論。每一條都有對應的失敗 實例——多數是 agent(也就是筆者自己)先犯過一次,才變成規則。沒有對照組, 也沒有在第二個專案上複驗過,所以它的證據等級是「一次有效的做法」,不是 「這樣做會有效」。
沒有官方出處可引;這一類工作法目前也沒有可對照的公開基準。
內部回溯用(未公開):該專案的 ROUNDS.md 與 FAILURES.md 方法論條目、
以及批次腳本裡的閘門結構。