排程自主:Loop、Wakeup 與 Cron
精通 Claude Code 的排程自主能力:/loop 動態模式、ScheduleWakeup 的時機掌握、提示快取經濟學,以及用 cron 建立常駐自動化。
你將學到什麼
在第 11 堂課你已經見過不需要人在迴圈中的 agent。本堂課補上先前缺少的那個維度:時間。一個監看部署或執行夜間稽核的 agent,不只要決定做什麼,還要決定什麼時候醒來去做 — 而在 Fable 5 時代,這個決定背後有著實實在在的經濟學。
完成本堂課後,你將理解:
/loop如何運作,以及「動態模式(dynamic mode)」是什麼意思- ScheduleWakeup(排程喚醒)工具:延遲鉗制(delay clamping)、自我排程,以及
stop的語義 - 提示快取(prompt cache)經濟學 — 為什麼睡 300 秒是兩頭皆輸
- 什麼該輪詢(外部狀態),什麼永遠不該輪詢(harness 追蹤的工作)
- 常駐 cron job 與一次性喚醒的差別
- 為什麼前景 sleep 被封鎖,以及該改用什麼(Monitor、背景 Bash)
問題所在
假設你推了一個 commit,希望 Claude Code 在 CI 轉綠後合併 PR。CI 大約需要八分鐘。這八分鐘 agent 該做什麼?
天真的選項全都不好:
Option 1: Busy-wait Option 2: sleep 480 Option 3: poll every 60 s
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Check. Check. │ │ Block the whole │ │ 8 wake-ups, 8 │
│ Check. Check... │ │ session on a │ │ full turns, for │
│ Burns tokens │ │ foreground sleep │ │ one bit of info │
│ continuously │ │ (blocked anyway) │ │ ("done yet?") │
└──────────────────┘ └──────────────────┘ └──────────────────┘
底下還有一個更微妙的問題:睡覺也不是免費的。Anthropic 的提示快取存活時間(TTL)大約五分鐘。在 TTL 內醒來,下一回合就能從快取讀到你累積的上下文 — 便宜。睡過頭,整個上下文就得在每次醒來時以原價、未經快取地重讀一遍。
所以排程自主其實是兩個問題:一個機制問題(agent 如何讓自己恢復執行?)和一個經濟學問題(哪些間隔便宜,哪些是陷阱?)。Fable 5 時代的 harness(執行框架)同時回答了這兩者。
如何運作
/loop 與動態模式
/loop 會重複執行一個任務。你可以給它固定間隔,或以動態模式執行 — 完全沒有固定間隔。在動態模式下,模型用一個叫 ScheduleWakeup 的工具自己掌握節奏:在每次迭代結束時,它排程自己的下一次恢復,把同一段迴圈提示傳回給自己。
Dynamic loop lifecycle:
┌────────────┐ ScheduleWakeup ┌────────────┐
│ Iteration N │ ──(delaySeconds)──▶ │ sleep │
└────────────┘ └─────┬──────┘
▲ │ harness re-invokes
│ same loop prompt │ with the loop prompt
└──────────────────────────────────┘
Exit: ScheduleWakeup with stop: true → loop ends
間隔是模型每一輪做出的決定,不是一個設定值。監看 CI 和夜間整理任務需要完全不同的節奏,而只有身在迴圈中的 agent 知道自己處於哪種情境。
ScheduleWakeup 的語義
有兩個欄位是關鍵:
delaySeconds— 被鉗制在 [60, 3600] 的範圍內。要求 10 秒,你會拿到 60。要求兩小時,你會拿到 3600。下限阻止失控的緊迴圈;上限保證迴圈至少每小時回報一次。stop: true— 結束迴圈。當目標達成(或確定無法達成)時,agent 停止排程,迴圈就乾淨地終止。
ScheduleWakeup(delaySeconds: 270) # resume in 4.5 minutes
ScheduleWakeup(stop: true) # goal reached — end the loop
提示快取斷崖
以下這個數字應該主導你選擇的每一個延遲:提示快取的存活時間大約五分鐘(300 秒)。光是這一個事實,就把延遲範圍切成三個區間:
delaySeconds chosen
60 ──────── 270 ─ 300 ─────────── 1200 ──────── 1800 ────── 3600
│ WARM ZONE │ CLIFF │ DEAD ZONE │ COMMITTED SLEEP ZONE │
│ cache hit │ worst │ cache miss, │ cache miss, but one │
│ every wake │ of │ yet you're │ miss buys 20–60 min │
│ │ both │ still waking│ of silence — worth it │
│ │ │ frequently │ │
- 低於約 270 秒 — 你在快取過期前醒來;每次醒來都讀到溫熱的上下文。這是主動輪詢的區間,而選約 270(不是 290)是為了在斷崖之下留有餘裕。
- 約 300 秒 — 兩頭皆輸的選擇:剛好長到失去快取,所以每次醒來都要付一次完整的未快取讀取 — 卻仍然每五分鐘醒一次。
- 1200 秒以上 — 承諾式沉睡(committed sleep)。醒來是一次快取未命中,但一次未命中換來 20–60 分鐘完全不執行。對於閒置的例行檢查,1200–1800 秒是合理的預設值。
反模式是斷崖與承諾式沉睡之間的死區:付著冷讀取的價格,卻維持溫熱輪詢的頻率。刻意選定一個區間 — 這正是第 21 堂課的成本優化思維套用到時間軸上。
輪詢外部狀態 — 永遠別輪詢 harness 追蹤的工作
第二條紀律:知道什麼東西才值得輪詢。
harness 本身追蹤的工作 — 背景 Bash 指令、生成的 agent、執行中的 workflow(工作流程;也就是第 8 堂課介紹的那套機制)— 完成時會自動重新喚起 agent。輪詢它純屬浪費:harness 本來就會告訴你。針對 harness 追蹤的工作,唯一值得排程的喚醒是一個遙遠的後備檢查(fallback):一次距離很遠的檢查,以防有東西卡住。
值得輪詢的是外部狀態:CI 執行、部署推進、遠端佇列 — 在 harness 視野之外變化的東西。把延遲對準那個狀態變化的速度:八分鐘的 pipeline 不需要 60 秒一次的輪詢;一小時才清空一次的佇列也不需要五分鐘一次的。
Cron Job 與一次性喚醒
ScheduleWakeup 是一次性的:它只觸發一次,迴圈必須每一輪重新排程。對於常駐任務 — 夜間報告、定期健康檢查 — harness 提供了 cron 工具:CronCreate、CronList 和 CronDelete 管理獨立於任何迴圈之外、持續存在的週期性排程任務。
這兩種機制都會驅動自主迴圈,而 harness 會用哨兵提示(sentinel prompt)標記那些回合 — cron 模式是 <<autonomous-loop>>,ScheduleWakeup 模式是 <<autonomous-loop-dynamic>> — 讓 agent 知道自己是以無人看管的迴圈在執行,而不是在回答人類。你會在對話紀錄(transcript)裡看到這些哨兵;你不需要自己管理它們。
沒有前景 Sleep
兩條配套規則補完整個藍圖:
- 前景 sleep 被封鎖。 你不能把一個回合停在
sleep 480上。要等待某個條件,改用 Monitor 工具 — 對一個狀態跑 until 迴圈 — 而不是卡住整個 session。 - 背景 Bash 透過
run_in_background以分離(detached)方式執行指令(不需要&)。它會跨回合持續執行,並在結束時重新喚起 agent — 這正是你永遠不該輪詢它的原因。
實作範例
我們來為一個真實的 CI 監看掌握節奏。你剛推了一個 commit;這條 pipeline 歷來大約需要八分鐘。迴圈的任務是:醒來、檢查 PR 的 checks、轉綠就合併,然後停止。
首先比較三個候選排程:
Pipeline duration: ~480 s (8 min)
Strategy A: 60 s polls W─W─W─W─W─W─W─W─✓ 8 wakes, all warm
8 turns for 1 bit of information
Strategy B: one 480 s sleep ────────cold────✓ 1 wake, full uncached
context re-read at the moment you act
Strategy C: two 270 s sleeps ───warm───┬──warm──✓ 2 wakes, BOTH cached
(270 s) (540 s ≈ 9 min — CI done)
策略 C 勝出:每次沉睡都停留在約 300 秒的快取 TTL 之下,所以兩次醒來都讀到溫熱的上下文,而兩個回合的開銷只有策略 A 的四分之一。迴圈的迭代看起來像這樣:
# Iteration 1 (t = 0): just pushed. CI will take ~8 min.
gh pr checks 142 # → pending
# Too early to poll tightly. Sleep just under the cache TTL.
# ScheduleWakeup(delaySeconds: 270)
# Iteration 2 (t = 270 s): warm wake — context read from cache.
gh pr checks 142 # → still running (expected; ~4.5 min elapsed)
# ScheduleWakeup(delaySeconds: 270)
# Iteration 3 (t = 540 s): warm wake again. ~9 min elapsed.
gh pr checks 142 # → all checks passed
gh pr merge 142 --squash
# Goal reached:
# ScheduleWakeup(stop: true)
有兩個值得留意的判斷:
- 如果 pipeline 要花的是 25 分鐘,溫熱輪詢就不再合理。改為承諾另一個區間 — 單次
delaySeconds: 1500的沉睡。一次快取未命中,換來整段等待。 - 如果那條「pipeline」其實是用背景 Bash 啟動的本地測試套件,你根本不會排任何輪詢 — 指令結束時 harness 會重新喚起你。頂多設一個遙遠的後備檢查(
delaySeconds: 1800),以防它卡住。
而當迴圈終於要行動時,第 11 堂課的規範依然適用:可逆、在範圍內的動作(合併你被要求合併的那個 PR)直接執行,不必詢問;破壞性或改變範圍的決策,則是自主迴圈該停下來回報的地方。
前後對比
| 之前 | Fable 5 時代 |
|---|---|
| 自主意味著一個你得在旁看顧的前景 session | 迴圈用 ScheduleWakeup 排程自己的喚醒 |
| 固定的輪詢間隔,選一次就定了 | 延遲按迭代、按成本區間逐輪選擇 |
在 shell 裡 sleep 480 | 前景 sleep 被封鎖;用 Monitor 等待條件 |
| 什麼都輪詢,包括自己的背景任務 | harness 追蹤的工作自動重新喚起;只輪詢外部狀態 |
| 週期性任務接在外部 cron 上 | CronCreate/CronList/CronDelete 在 harness 內建立常駐任務 |
| 睡多久是事後才想到的事 | 約 5 分鐘的快取 TTL 讓它成為一個被標價的決定 |
關鍵洞見
一個知道何時該醒來的 agent,比一個從不睡覺的 agent 更便宜。
本堂課的一切 — 鉗制、TTL、哨兵 — 都服務於同一個想法:在排程自主中,間隔本身就是答案的一部分。睡 300 秒的迴圈和睡 270 秒的迴圈看起來幾乎一樣,卻站在價格斷崖的兩側。約 5 分鐘的快取 TTL 剛好留下兩個好區間 — 保持溫熱(低於約 270 秒,正在主動收斂)或承諾沉睡(1200 秒以上,處於閒置)— 而最便宜的排程是不排程,因為 harness 追蹤的工作會自己回報。
固定 cron 回答的是「每天晚上跑這個」。動態喚醒回答的是更難的問題 — 「這一次的等待值得等多久?」— 而這份判斷力,正是你把時鐘交給模型所換來的。
下一堂課
第 32 堂課以**全新工具腰帶:Artifacts、任務與型別化輸出**為本模組收尾 — Fable 5 時代面向使用者的工具:託管的 Artifacts 頁面、取代 TodoWrite 的任務系統、型別化的審查發現,以及背景工作如何以被追蹤的任務形式浮現。