第 3 章 workflow 定義
一句話:把每次都一樣的多 agent 流程寫成一支腳本,變成一個 /指令。
上一章你派過一個 subagent;workflow 就是把「派誰、什麼順序、平行還是接力」寫死成腳本。
它是一支放在 .claude/workflows/<name>.js 的 JavaScript,描述一條「產線」:先開幾個 agent 做什麼、等它們回來再做什麼、最後回傳什麼——腳本裡的 agent(),開的就是上一章那種 subagent。Claude Code 在背景照著跑,你的主對話保持可用。
它解決的是「同一件事每次都要重新指揮」的問題:審一整個目錄、把每個檔案各自遷移、對每個 finding 再派人驗證。用對話一步一步指揮,既慢、每次跑出來又都不一樣;寫成 workflow 之後,就是一個 /name 指令,每次的結構都相同。
本章的目標不是背 API,而是學會把自己的流程拆成站,再把站寫成腳本。
原理:站與站之間流的是資料,不是對話
Section titled “原理:站與站之間流的是資料,不是對話”拿本章要寫的 review-flow 實跑一次當例子,資料是這樣流的:
- Review 站:兩個 agent 同時出發,各自讀一個檔。它們不是「聊天」,而是被要求回固定形狀的 JSON——
findings陣列,每筆一定有file、line、note(這個形狀就是schema,回來時已經驗證過,不用自己 parse)。 - 腳本接手:
filter(Boolean)先把失敗的 agent(回null)濾掉,再把兩份 findings 合併——那次實跑是 5+5 共 10 筆。 - Synthesize 站:10 筆 JSON 被塞進第三個 agent 的 prompt,它產出一段 150 字摘要;
return summary就把摘要送回你的對話。
對話指揮和腳本編排的差別就在這裡:對話裡每一步都是模型即興發揮;workflow 裡站與站之間傳的是驗證過形狀的資料,控制流寫死在腳本——所以跑一百次,結構都一樣。
- 同一套多 agent 流程會重複跑:每個 MR 都要「平行審兩個檔再彙整」,第二次起就該是
/review-flow,不是重新描述。 - 平行度是重點:十個檔案各審一次,
parallel()/pipeline()一次派出去,比對話裡逐一派快得多。 - 需要確定性的控制流:「找到 0 個問題就跳過驗證」「兩輪沒有新發現就停」這種 if/while,寫在腳本裡比寫在提示詞裡可靠。
- 做事的人不當閱卷的人:上一章講過對抗式審查;workflow 讓它變成固定結構——「實作站」的產出交給「驗證站」的全新 agent 檢查,每個 finding 都再派人驗一次,不會因為誰偷懶就漏掉。
反過來,一次性的、需要你隨時介入改方向的工作留在對話裡就好。workflow 跑起來中途不能問你問題(只有權限提示能暫停它),要人簽核的節點,就把流程切成兩支 workflow,中間回對話。
先確認你的環境跑得動 workflow,再往下:
claude --version是 2.1.246 或更新。- 若你用的是 Pro 方案,在
claude裡打/config,找到 Dynamic workflows 那一列,確認是開的(其他付費方案預設就是開的)。 - 參考訊號:在提示符輸入
/,清單裡通常會有內建的/deep-research。但它另外依賴 WebSearch 工具——你的環境若不能用 WebSearch,它會消失,不代表 workflow 被關掉;看不到它先別下結論。 - 最後判準是你自己的 workflow 載不載得到。做完下面練習的步驟 3、4 之後,輸入
/看清單有沒有/review-flow:- 有 → 環境支援,照練習做下去。
- 沒有 → 照練習的「故障排除」逐項排除:檔案路徑對不對、有沒有
/exit重開。 - 都排除了還是沒有 → 打
/status看 Setting sources:有 managed 來源的話,可能是組織用管理設定關閉了 workflows,問管理者確認。確認之前這一章走閱讀模式——讀完本章與附錄的實跑輸出,自檢第 3 項勾「環境不支援,已閱讀」。
拆站:把流程切成站
Section titled “拆站:把流程切成站”寫腳本之前先在紙上拆站。每一站回答四個問題:
| 問題 | 例子(review-flow) |
|---|---|
| 輸入是什麼 | Review 站:兩個檔案路徑 |
| 輸出是什麼 | Review 站:每個檔一份 findings(檔名、行號、說明) |
| 誰確認 | Review 站不用人確認;Synthesize 站的摘要由你在主對話看 |
| 失敗怎麼辦 | 某個 agent 回 null(被中止或 API 出錯)就略過它,摘要照做 |
拆完會發現三種東西:該進 workflow 的(重複、平行、不需要人介入的站)、該留在對話的(需要你判斷、改方向的部分)、以及站與站之間的資料形狀(上一站回什麼、下一站吃什麼)。資料形狀就是你等一下要寫的 schema。
實務案例:五站的後端開發流程
Section titled “實務案例:五站的後端開發流程”一個團隊把後端開發流程寫成五站(上一章提過它——每站就是一個 subagent):ROUTE(判斷任務型態、驗分支)→ SPEC(產規格草稿,硬停等人在主對話回 OK;workflow 絕不自己把草稿翻成「已確認」)→ BUILD(依 spec 分批實作)→ VERIFY(跑測試,失敗就進 fix 迴圈,有上限)→ DONE(只產 MR 草稿,永不 push)。 值得學的形狀有三個:每站有明確的 schema;需要人簽核的地方用「停站、回對話、帶參數續跑」而不是在腳本裡問人;有上限的迴圈(fix 最多幾輪)而不是跑到收斂為止。
腳本長什麼樣
Section titled “腳本長什麼樣”| 元素 | 作用 |
|---|---|
export const meta = { name, description, phases } |
meta 必填,裡面 name、description 必填;name 就是之後的 /name。phases 選填,有給的話進度畫面才會照它分組 |
phase('Review') |
之後的 agent() 都歸在這一組顯示 |
agent(prompt, { label, phase, schema }) |
開一個 subagent。prompt 要自足(它看不到主對話);給 schema 就回驗證過的物件,不用自己 parse |
parallel([() => agent(...), () => agent(...)]) |
同時跑、全部回來才繼續;任一個失敗會變 null,記得 .filter(Boolean) |
pipeline(items, stage1, stage2) |
每個 item 各自走完所有 stage,不互等;多數多階段工作用這個 |
log('...') |
在進度畫面印一行給人看 |
return value |
腳本的回傳值就是 workflow 的結果,會回到你的對話 |
兩種叫法,別搞混
Section titled “兩種叫法,別搞混”- 叫 Claude 幫你寫一份:在提示詞裡說「use a workflow」或加
ultracode關鍵字,Claude 會另外寫一支腳本來做這件事。這是「請它現場設計」,不是跑你存的那支。 - 跑你自己存的:檔案放在
.claude/workflows/review-flow.js,重開claude後打/review-flow。這才是本章要練的。
練習寫一支 review-flow,用 /review-flow 跑完
-
起點:把 repo 重置回乾淨的
main(在 repo 根目錄執行)。Terminal window git merge --abort 2>/dev/nullgit checkout -f main && git reset --hard origin/maingit clean -fd && git worktree prune白話:這段=把 repo 恢復成剛 clone 完的乾淨狀態。它會丟掉未 commit 的改動與未追蹤檔(含你自己貼進
.claude/的檔案),但不會動被 gitignore 的.todo.json(只影響list顯示的項目,想清就rm -f .todo.json)與.claude/settings.local.json(存的是你核准過的權限,留著沒關係)。 -
信任資料夾:如果這個目錄還沒接受過信任對話,先
claude接受再/exit。 -
**在終端機(不是 Claude 裡)**貼這個檔案到
.claude/workflows/review-flow.js。內容與練習 repo 的solutions/ch3-workflow/.claude/workflows/review-flow.js完全相同:Terminal window cat > .claude/workflows/review-flow.js <<'EOF'// review-flow:平行審兩個檔,再把 findings 彙整成一段 zh-TW 摘要。// 放到 .claude/workflows/review-flow.js,/exit 重開 claude 後用 /review-flow 執行。export const meta = {name: 'review-flow',description: '平行審兩個檔再彙整',phases: [{ title: 'Review' }, { title: 'Synthesize' }],}// 每個審查 agent 都必須回這個形狀:findings[] 每筆帶 file / line / noteconst FINDINGS = {type: 'object',required: ['findings'],properties: {findings: {type: 'array',items: {type: 'object',required: ['file', 'line', 'note'],properties: { file: { type: 'string' }, line: { type: 'integer' }, note: { type: 'string' } },},},},}// prompt 要自足:subagent 看不到主對話,檔名、要看什麼、回什麼都得寫在這裡const reviewPrompt = (file) =>`你是唯讀的程式碼審查員。用 Read 工具讀取 ${file},找出可讀性、錯誤處理、測試涵蓋上的問題。` +`每筆都要附行號(line 為整數)。最多 5 筆;沒有問題就回空陣列。不要修改任何檔案。`phase('Review')const [todo, tests] = await parallel([() => agent(reviewPrompt('src/todo.py'), { label: 'review:todo', phase: 'Review', schema: FINDINGS }),() => agent(reviewPrompt('tests/test_todo.py'), { label: 'review:tests', phase: 'Review', schema: FINDINGS }),])// agent() 被中止或 API 出錯會回 null,先濾掉再彙整const findings = [todo, tests].filter(Boolean).flatMap((r) => r.findings)log(`Review 完成:共 ${findings.length} 筆 findings`)phase('Synthesize')const summary = await agent('以下是兩個檔案的程式碼審查結果(JSON)。請用繁體中文寫一段 150 字以內的摘要:' +'先講整體狀況,再列最值得先處理的 3 點(每點附 檔名:行號)。不要修改任何檔案,直接回摘要文字。\n\n' +JSON.stringify(findings, null, 2),{ label: 'synthesize', phase: 'Synthesize' },)return summaryEOF -
建完任何
.claude/檔案都要重開:如果claude還開著先/exit,然後:Terminal window claude -
打
/review-flow。第一次會出現核准提示(列出 Review、Synthesize 兩個 phase),選 Yes, run it;auto 模式且之前核准過會直接開始。 -
跑的時候打
/workflows,選這一筆按 Enter 看進度。
❯ /review-flow review-flow 平行審兩個檔再彙整 3 agents · 52,494 tokens · 81s · completed ├─ Review │ ├─ review:todo done 21,309 tokens 5 tool calls │ └─ review:tests done 16,163 tokens 3 tool calls └─ Synthesize └─ synthesize done 15,022 tokens 0 tool calls 整體來看,src/todo.py 功能正確但缺乏防禦性……(繁體中文摘要,3 點各附 檔名:行號)
兩組 phase、三個 agent 的進度樹+跑完回到對話的摘要。數字是一次實跑的值,你的會不同。
/review-flow送出後,任務面板出現review-flow的一行進度;/workflows進去看得到 Review(兩個 agent:review:todo、review:tests)與 Synthesize(一個 agent)兩組。- 跑完後對話裡出現一段繁體中文摘要:先講兩個檔的整體狀況,再列 3 點附
檔名:行號。 git status --porcelain只有?? .claude/workflows/review-flow.js——workflow 是唯讀審查,沒有改任何檔。
- 輸入
/沒有/review-flow:先ls .claude/workflows/確認檔案在、檔名對;再確認你有/exit重開claude(workflow 在 session 啟動時載入)。都對還是沒有,回「前置檢查」第 4 步的分流。 - 核准提示長得不一樣或沒出現:manual/accept edits 模式每次都會問;auto 模式只問第一次。無頭
-p模式不會問、預設拒絕自訂 workflow,要加--allowedTools Workflow(互動模式不需要)。 - 跑到一半停住:
/workflows進去看是哪個 agent 在等權限(不在 allowlist 的 shell 指令會問你);本章的 agent 只用 Read,不該發生,發生了多半是 prompt 被改過。
附錄:閱讀模式用的實跑輸出
Section titled “附錄:閱讀模式用的實跑輸出”環境不支援 workflow 的人看這裡。下面是同一支 review-flow.js 在練習 repo 上實跑一次的結果(無頭模式:claude -p "/review-flow" --permission-mode acceptEdits --allowedTools Workflow;互動模式直接打 /review-flow 就好,不需要這些旗標):
/review-flow 送出後,任務面板的一行進度與 /workflows 進度畫面(依實跑的進度事件整理,數字是那一次的實際值):
review-flow 平行審兩個檔再彙整 3 agents · 52,494 tokens · 81s · completed├─ Review│ ├─ review:todo done 21,309 tokens 5 tool calls 72.6s│ └─ review:tests done 16,163 tokens 3 tool calls 56.9s└─ Synthesize └─ synthesize done 15,022 tokens 0 tool calls 8.5s腳本 log() 印的那一行:Review 完成:共 10 筆 findings。
workflow 回傳(Synthesize 站的輸出,也就是你會在對話裡看到的摘要):
整體來看,src/todo.py 功能正確但缺乏防禦性:狀態檔損毀或寫入中斷都會噴 traceback 甚至遺失資料;tests/test_todo.py 只覆蓋 happy path,錯誤分支與參數驗證幾乎沒測,且測試環境清理不完整。建議優先處理:
1. src/todo.py:28 — save_items() 直接覆寫,寫入中斷會清空待辦;改為暫存檔 + os.replace() 原子寫入。2. src/todo.py:24 — load_items() 未處理壞 JSON/非 list/缺鍵,應捕捉例外並回報「狀態檔損毀」。3. tests/test_todo.py:18 — tearDown 未刪暫存目錄、環境變數未還原;改用 TemporaryDirectory 與 mock.patch.dict。跑完 git status --porcelain 只有 ?? .claude/workflows/review-flow.js,兩個檔案都沒被改。
自檢(勾選會存在你的瀏覽器)0/3
以 Claude Code 2.1.246(2026-08-26)為準