Claude Code 動態工作流程實戰:一個對話塞不下時,就讓上千個 subagent 一起上

環境與前置條件
- Claude Code v2.1.154 以上claude --version 先確認)
- 付費方案(Pro / Max / Team / Enterprise)或 API / Amazon Bedrock / Google Vertex AI / Microsoft Foundry
- Pro 方案要先到 /config 把「Dynamic workflows」那一列打開
- 功能狀態:research preview(2026-05-28 隨 Claude Opus 4.8 一起發布),行為可能還會變
- 預設知識:你已經用過 Claude Code、知道 subagent 跟 slash command 大概是什麼

我第一次認真用 /deep-research 是想查一個 Node.js 權限模型的版本差異。我按下 Enter,然後——畫面沒有一行一行慢慢吐字,而是冒出一個背景任務面板,裡面有十幾個 agent 各自跑去搜不同角度、互相打架、把站不住腳的論點砍掉,最後丟回一份標好引用的報告。我那條對話從頭到尾還是空的、隨時能繼續打字。

那一刻我才意識到,Claude Code 的玩法被改寫了。過去 subagent 是「Claude 一輪一輪決定要派誰」,現在 Dynamic Workflows(動態工作流程) 把整個編排計畫搬進一段 JavaScript 腳本,由一個獨立 runtime 在背景跑,規模直接拉到單次最多 1,000 個 subagent。這次更新(細節見 The New Stack 的報導 )給我的整體感覺就一句話:過去那種「一次小心翼翼的單一 pass」,不再是預設玩法了。

這篇文章我會把它從頭講到尾——它到底是什麼、怎麼觸發、腳本長怎樣、pipelineparallel 到底差在哪(這裡超多人寫錯),還有那些一不小心就會燒掉一堆 token 的雷。

中央編排節點向外輻射出大量 subagent 節點的示意圖

Workflow 到底是什麼?跟 subagent、skill 差在哪

先把名詞釐清,不然後面會一直打結。Claude Code 裡能跑「多步驟任務」的東西其實有三種:subagent、skill、workflow。它們最核心的差別,官方文件 講得很精準——差在「誰持有那個計畫(who holds the plan)」。

Subagents Skills Workflows
本質 Claude 生出來的 worker Claude 遵循的一組指示 runtime 執行的腳本
誰決定下一步 Claude,一輪一輪 Claude,照著 prompt 腳本本身
中間結果放哪 Claude 的 context Claude 的 context 腳本變數
可重複的是什麼 worker 定義 那組指示 編排邏輯本身
規模 每輪幾個 同 subagent 每次 run 數十到數百個 agent
中斷怎麼辦 重跑那一輪 重跑那一輪 同 session 內可 resume

用 subagent 跟 skill 的時候,Claude 自己是那個指揮官:它一輪一輪決定接下來生什麼,而且每個結果都會掉回它的 context 裡。問題是 context 是有限的,當你要掃 300 個檔案、每個都吐一份報告回來,Claude 的腦袋很快就塞爆了,夭壽。

Workflow 的解法是把那個指揮計畫——迴圈、分支、中間結果——全部寫進程式碼。腳本自己持有狀態,Claude 的 context 最後只拿到一份濃縮過的答案。這不只是「能跑更多 agent」而已,它還讓你能套用可重複的品質模式:讓幾個獨立 agent 互相對抗審查彼此的發現、或從好幾個角度各自起草一份方案再拿來比,最後得到的結果比單次 pass 可信得多。

一句話記法,幫你以後不會選錯工具:

一兩個獨立調查     → 用 subagent
know-how 要重用    → 用 skill
編排本身要重複     → 用 workflow(fan out、比較發現、重啟失敗的 agent、存起來當命令)

workflow 與 subagent 編排架構對比流程圖

到底有多大規模?開發者社群流傳過一個會嚇到的個案:有人用動態工作流程把 Bun 從 Zig 移植到 Rust,據說原測試套件 99.8% 通過、約 75 萬行 Rust、十一天就 merge。這類數字我沒辦法逐一查證、純粹當茶餘飯後看,但那個量級感受得到。比較踏實、官方自己列出來的標準場景是:全庫 bug sweep、500 個檔案的大遷移、需要跨來源交叉驗證的研究、以及「值得從好幾個角度各自起草再擇一」的困難計畫。

三種觸發方式:關鍵字、deep-research、ultracode

要讓 Claude 幫你寫並跑一個 workflow,總共三條路。

第一條:prompt 裡丟 workflow 這個字。 最直接,單次觸發、不改 session 的設定。

Run a workflow to audit every API endpoint under src/routes/ for missing auth checks

Claude Code 會把那個字反白,然後改成「幫你寫一段 workflow 腳本」而不是一輪一輪手動做。如果你只是剛好打到 workflow 這個字、根本沒想觸發,按 alt+w 就能忽略這一輪。

第二條:內建的 /deep-research 這是目前唯一的 bundled workflow,也是最好的入門體驗:

/deep-research What changed in the Node.js permission model between v20 and v22?

它會對問題從好幾個角度 fan out 網路搜尋、抓回來源互相 cross-check、對每個論點投票,最後給你一份引用完整、而且已經把站不住腳的論點濾掉的報告。

第三條:ultracode 模式。 這是把油門踩到底的玩法:

/effort ultracode

Ultracode = xhigh 推理強度 + 自動 workflow 編排。開了之後,Claude 每個實質任務都會自己判斷要不要編成 workflow,一個請求甚至會拆成好幾個連續的 workflow(一個負責理解程式碼、一個負責改、一個負責驗證)。代價就是每個任務都更燒 token、更慢,而且只在當前 session 有效,回到例行工作記得用 /effort high 降回來,不然帳單會讓你傻眼。

順帶一提,啟動時會不會跳權限確認,取決於你的 permission mode:Default / acceptEdits 每次都問(除非你選過「不再詢問這個 workflow」);Auto 只問第一次;Bypass、claude -p、Agent SDK 則直接開跑不問。有個細節很重要——workflow 生出來的 subagent 一律以 acceptEdits 模式跑、檔案編輯自動核准,但不在你 allowlist 裡的 shell / web / MCP 工具還是會在執行中跳提示。所以長 run 之前,先把 agent 會用到的指令加進 allowlist,不然跑到一半被一個權限框卡住,超雷。

腳本長怎樣:meta 加五個 API(順便校正坊間的簽名錯誤)

這一段是整篇的重點。我看過好幾篇教學把 API 簽名寫錯,照著抄一定跑不起來,所以這裡用 Claude Code 實際工具規格來校正。

先講清楚:官方那份 workflows 文件 主要描述「使用者怎麼操作」(觸發、權限、resume、成本),腳本層的 API 細節(agent / parallel / pipeline / budget / 程式化 resume 等)目前較少公開。下面這些簽名取自 Claude Code 的 workflow 執行環境(也就是 Claude 寫腳本時實際看到的規格),會隨版本演進——照抄前請以你自己 Claude Code 版本的實際行為為準。

每個 workflow 腳本必須export const meta = {...} 開頭,而且 meta 必須是純字面量——不能用變數、函式呼叫、展開運算子或樣板字串插值,runtime 會在你還沒跑之前就要讀它:

export const meta = {
  name: 'review-changes',                              // 必填
  description: 'Review changed files and verify findings', // 必填,權限對話會顯示這句
  whenToUse: 'after a large diff',                     // 選填,列在 workflow 清單裡
  phases: [                                            // 選填但建議,每個 phase() 對應一筆
    { title: 'Review' },
    { title: 'Verify' },
  ],
}

// 腳本本體從這裡開始,是 async context,可以直接 await
phase('Review')
const result = await agent('review src/auth.ts for bugs', { schema: FINDINGS_SCHEMA })

接著是五個核心 API,請務必看清楚參數型別:

API 簽名 重點
agent agent(prompt, opts?) 啟一個 subagent。沒給 schema 回傳純文字字串;給了 schema 回傳驗證過的物件optslabelphaseschemamodelisolation:'worktree'agentType。使用者中途略過會回傳 null
pipeline pipeline(items, stage1, stage2, ...) 每個 item 獨立流過所有 stage,stage 之間沒有 barrier。stage callback 收到 (prevResult, originalItem, index)。某個 item 在某 stage 拋錯就變 null、跳過後續。
parallel parallel(thunks) thunks函式陣列 Array<() => Promise<any>>不是直接傳 agent。這是個 barrier,等全部完成才回傳。某個 thunk 拋錯那一格變 null(呼叫本身不會 reject),用之前先 .filter(Boolean)
phase phase(title) 開一個新階段,之後的 agent() 在進度面板會歸到這個標題下。
log log(message) 對使用者吐一行進度訊息。

最容易寫錯的就是 parallel 我看過不少文章寫成 parallel(agent(...), agent(...)),直接把 agent 的回傳值丟進去——這樣等於在呼叫 parallel 之前就已經把 agent 啟動了,整個並行的意義就沒了。正確寫法是傳還沒被呼叫的函式(thunk):

// ❌ 錯:直接傳 agent 的結果(簽名也錯)
const r = await parallel(agent('check a'), agent('check b'))

// ✅ 對:傳一個函式陣列,每個函式回傳一個 Promise
const r = await parallel([
  () => agent('check a'),
  () => agent('check b'),
])
const usable = r.filter(Boolean)  // 拋錯的會是 null,先濾掉

還有一個我大力推薦的東西:schema 結構化輸出。給 agent() 傳一個 JSON Schema,subagent 會被強制呼叫 StructuredOutput 工具回傳已驗證的物件,不符合還會自動重試。從此不用自己解析一坨文字、不用祈禱它格式對:

const FINDINGS = {
  type: 'object',
  properties: {
    findings: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          title:    { type: 'string' },
          file:     { type: 'string' },
          severity: { type: 'string' },
        },
        required: ['title', 'file'],
      },
    },
  },
  required: ['findings'],
}

const r = await agent('find bugs in src/auth.ts', { schema: FINDINGS })
// r.findings 直接是陣列,拿了就用

最後幾個會踩到的環境限制,先講免得你撞牆:

  • 腳本是純 JavaScript,不是 TypeScript。型別註記 : string[]、interface、generics 一律解析失敗。
  • Date.now()Math.random()、無參數的 new Date() 會直接丟錯(因為它們會破壞 resume 機制)。要時間戳就從 args 傳進去,要亂數就靠 index 去變化 prompt。
  • 腳本本身沒有檔案系統跟 shell 存取權。讀檔、寫檔、跑指令都是 agent 在做,腳本只負責調度。

左側同步並行撞上 barrier、右側獨立交錯流動的資料流對比圖

pipeline 還是 parallel?這是最常踩的雷

這題我特別拉一段出來講,因為選錯的代價是白白浪費牆鐘時間

原則一句話:預設用 pipeline,只有當「下一階段真的需要上一階段的全部結果」時才用 parallel 的 barrier。

pipeline 沒有 barrier。item A 可以已經在第三 stage,同時 item B 還卡在第一 stage。整段的牆鐘時間 = 最慢的那一條單一 item 的鏈,而不是「每個階段最慢的那個」加總。這個差異在 agent 速度落差大的時候非常明顯:

const DIMENSIONS = [
  { key: 'bugs', prompt: 'review for correctness bugs' },
  { key: 'perf', prompt: 'review for performance issues' },
]

const results = await pipeline(
  DIMENSIONS,
  // stage 1:審查
  d => agent(d.prompt, { label: `review:${d.key}`, phase: 'Review', schema: FINDINGS }),
  // stage 2:每個發現派人對抗驗證(不用等別的維度審完)
  review => parallel(
    review.findings.map(f => () =>
      agent(`Adversarially verify: ${f.title}`, { phase: 'Verify', schema: VERDICT })
        .then(v => ({ ...f, verdict: v }))
    )
  ),
)
// 'bugs' 的發現在驗證的同時,'perf' 還在審——牆鐘時間不浪費

那什麼時候才真的該用 parallel 的 barrier?只有這幾種:下一階段要拿到全集合才能動——例如在昂貴的下游工作前先去重 / 合併全部結果、或是要提早退出(「0 個 bug 就整段跳過驗證」)、或是下一階段的 prompt 要「跟其他所有發現比較」。

// barrier 真正合理的場景:去重後才驗證
const all = await parallel(DIMENSIONS.map(d => () => agent(d.prompt, { schema: FINDINGS })))
const deduped = dedupe(all.filter(Boolean).flatMap(r => r.findings))  // 真的需要全集合
const verified = await parallel(deduped.map(f => () => agent(verifyPrompt(f), { schema: VERDICT })))

有個嗅探測試很好用:如果你寫出來的是「parallel → 一段純資料轉換(flatten / map / filter,沒有跨 item 依賴)→ parallel」,那中間那個 barrier 根本不需要,把轉換塞進 pipeline 的 stage 裡就好。猶豫的時候,選 pipeline 準沒錯。

進階品質模式:讓 agent 互相打臉

Workflow 真正比「叫一個 agent 做事」強的地方,是它能套用結構化的品質模式。這也是 Anthropic 官方部落格 一直強調的賣點。

對抗驗證(Adversarial Verify) 是其中最實用的。發現一個問題後,不是直接信它,而是派 N 個獨立的懷疑者、prompt 明確要他們反駁,多數反駁就否決。據 MarkTechPost 的報導 ,這個「其他 agent 專門去反駁前面發現」的機制是內建在 run 裡的,只有撐過反駁的論點才會送到你面前。舉個我自己常遇到的情境:當一個 subagent 宣稱某函式有 race condition,你就讓另一個 subagent 的唯一任務是想辦法推翻它——推不翻,這個 bug 才算數。

const votes = await parallel(
  Array.from({ length: 3 }, () => () =>
    agent(`Try to refute this claim: ${claim}. Default to refuted=true if uncertain.`,
          { schema: VERDICT }))
)
const survives = votes.filter(Boolean).filter(v => !v.refuted).length >= 2

兩個霓虹綠 agent 對峙、綠色箭頭互相碰撞如辯論的示意圖

Judge panel(法官小組) 適合解空間很寬的問題:從不同角度(MVP 優先、風險優先、使用者優先)各生一份獨立方案,並行 judge 評分,最後從贏家綜合、再把亞軍的好點子嫁接進來。比「單一方案反覆迭代」常常好得多。

Loop-until-dry(跑到枯竭) 用在未知規模的發現任務(bug、edge case):持續派 finder 直到連續 K 輪沒有新東西。這裡有個經典的坑——去重要對著 seen 集合,不是對著 confirmed,否則被否決掉的發現每一輪都會重新冒出來,永遠收斂不了:

const seen = new Set(), confirmed = []
let dry = 0
while (dry < 2) {                                  // 連續兩輪沒新東西才停
  const found = (await parallel(FINDERS.map(f => () =>
    agent(f.prompt, { phase: 'Find', schema: BUGS })))).filter(Boolean).flatMap(r => r.bugs)
  const fresh = found.filter(b => !seen.has(key(b)))
  if (!fresh.length) { dry++; continue }
  dry = 0
  fresh.forEach(b => seen.add(key(b)))             // 對 seen 去重,不是 confirmed
  // ……對 fresh 做多 lens 評審後,把通過的 push 進 confirmed
}

還有 multi-modal sweep(多個 agent 各用不同方式搜:by-container、by-content、by-entity、by-time,補單一角度的盲區)跟 completeness critic(最後派一個 agent 專門問「漏了什麼?哪個模態沒跑、哪個論點沒驗證」)。這些模式可以自由組合,不是互斥的。

並發、budget、resume:跑大 run 之前要懂的事

規模拉大之後,有三件事一定要搞清楚,不然不是燒錢就是白做。

並發上限。 官方文件講的是「最多 16 個並行 agent,CPU 核心少的機器會更少」(確切公式官方沒講明,大致隨可用核心數遞減),超過的會排隊等空位。單次 run 的總量上限是 1,000 個 agent——這是防失控迴圈的後盾,遠高於正常用量。你還是可以丟 100 個 item 給 parallel / pipeline,它們全部都會完成,只是同一時間大概只有十個左右在跑。

Token budget。 一次 workflow run 可能比用對話做同一件事燒多很多 token,而且照樣計入你方案的用量跟速率限制。每個 agent 預設用你 session 的模型,除非腳本用 opts.model 把某個 stage 導去別的模型。所以大 run 之前先 /model 確認一下、把不需要最強腦的 stage 換成小模型。腳本裡還有個 budget 物件可以拿來動態縮放深度:

const bugs = []
while (budget.total && budget.remaining() > 50_000) {
  const r = await agent('Find bugs in this codebase.', { schema: BUGS })
  bugs.push(...r.bugs)
  log(`${bugs.length} found, ${Math.round(budget.remaining() / 1000)}k remaining`)
}

注意那個 budget.total && 的 guard 不能省——沒設預算時 remaining() 會回 Infinity,這個迴圈會一路跑到 1,000 agent 上限才停,爆肝燒錢就是這樣來的。

Resume(暫停與恢復)。/workflows 裡選中一個 run 按 p 就能暫停 / 恢復,已完成的 agent 會回傳快取結果、其餘的繼續跑。據 digitalapplied 的整理 ,workflow 可以從先前的 runId 恢復,所以你改了腳本之後,沒被動到的 agent 不會重跑——最長未變動的呼叫前綴會秒回快取,從第一個被改或新增的呼叫起才實跑。對應的程式化重跑形式(帶 scriptPathresumeFromRunId)屬於前面提過的腳本層 API,官方公開文件還沒完整載明,以你的版本實際行為為準。

但有一個硬限制要記住:resume 只在同一個 Claude Code session 內有效。你只要退出 Claude Code,下次啟動時 workflow 就從頭來過。所以多日的大工程,盡量別關。

霓虹綠進度環與暫停/恢復控制、周圍漂浮快取資料塊的示意圖

最佳實踐與踩坑清單

把前面散落的雷集中起來,這張表存著,下次寫 workflow 前掃一遍:

你做的事 後果 正確做法
parallel(agent(), agent()) 簽名錯、根本沒並行 parallel函式陣列 [() => agent(...)]
動不動就用 barrier 快的 agent 空等慢的,浪費牆鐘 預設 pipeline,barrier 要有正當理由
loop 去重對 confirmed 被否決的發現每輪重現,永不收斂 seen 去重
budget 迴圈沒 guard budget.total 沒設預算時 Infinity,跑到 1,000 上限 while (budget.total && remaining() > X)
腳本裡用 Date.now() / Math.random() 直接丟錯、破壞 resume 時間從 args 傳、亂數靠 index
期待 subagent 自己整合彼此的工作 重複跟衝突一堆 整合交給最後的綜合階段或你自己
退出 session 後想 resume 從頭跑、白燒一輪 resume 僅限同 session;或用 scriptPath + resumeFromRunId
沒設明確終止條件的迴圈 逼近 1,000 agent 上限 設品質閾值或 dry-count 退出

幾個正面的習慣也一起列:任務邊界要講清楚(「audit src/routes/ 每個 endpoint 缺漏的 auth check,附 severity」遠勝於「improve the codebase」);用 schema 拿結構化輸出免解析;subagent 任務要可獨立驗證、輸入輸出明確、只碰有界的檔案集;規模化之前先小跑(/deep-research 或先掃 5~10 個檔)確認成本;成功的 run 記得在 /workflows 裡按 s 存成 /<name> 命令重複用——專案級存 .claude/workflows/(可進 git、整隊共用),個人級存 ~/.claude/workflows/

至於底層模型,這次 Claude Opus 4.8 的數字 也撐得起這套玩法——據報導 SWE-bench Pro 拿到 69.2%、長上下文檢索(GraphWalks long-context F1,1M tokens)達 68.1%,誠實度也明顯提升(官方主打未回報的程式碼瑕疵大幅減少)。對於要派上百個 agent 互相審查的場景來說,底層模型誠不誠實、看不看得懂長 context,直接決定了最後那份報告能不能信。

什麼時候該用、什麼時候別用

最後給個明確的判斷,免得你拿大砲打蚊子。

該用 workflow 的場景:全庫 bug sweep 或安全審計(100+ 檔)、大規模遷移(500+ 檔)、需要多來源交叉驗證的研究、值得從多角度起草再擇一的困難計畫。據 explainx.ai 的整理 ,這類「過去要一季、現在幾天」的大工程正是它的主場。

別用的場景:單檔編輯、快速 bug 修復、簡單問答——這些用對話或一兩個 subagent 就好,動 workflow 純粹是浪費 token。know-how 想重用就寫成 skill。token 預算緊張的時候也先忍著。

說穿了,動態工作流程不是要取代你跟 Claude 的對話,而是給你一個「當任務大到一個 context 裝不下」時的逃生艙。先從 /deep-research 玩起,習慣那個背景任務面板的節奏,再試著在 prompt 裡丟一個 workflow 讓 Claude 幫你寫第一支腳本。跑順了、覺得這流程每條 branch 都會重複,就按 s 存起來——下次它就是你自己的 / 命令了。

要不要現在就開一個小的試?挑你手邊一個「想掃但一直懶得掃」的資料夾,丟一句 Run a workflow to ...,看看十幾個 agent 同時上工是什麼感覺。跑完不妨在留言區分享你燒了多少 token、值不值得。


參考資料

沒有留言:

張貼留言

TTS 文字轉語音完全指南:技術原理、聲音克隆與 2026 選型實戰

前陣子我在測 Qwen3-TTS,官方技術報告寫著首包延遲 97ms,聽起來就是為即時對話而生的。結果我照官方範例跑起來,按下生成、等了整整 10 秒才聽到第一個字。乾,差了一百倍。 這不是我環境爛,而是 TTS 這個領域的日常:論文數字、行銷頁數字、你實際跑出來的數字,是三個...