- Published on
Wave-Based Parallel Execution:讓多個 Agent 平行拆任務的系統設計
Table of Contents
如果你看過 GSD (get-shit-done) 的架構分析,你可能對「Wave-Based Parallel Execution (波次平行執行)」這個概念留下深刻印象。但是「它怎麼知道哪些任務能並行、哪些要依序?」的底層邏輯值得深入說清楚。
這篇就是把 GSD 原始碼中的 gsd-planner.md Dependency Graph 演算法完整拆解出來,讓你可以把這個思路移植到自己的工作流中,不需要整套 GSD。
Wave 不是手動設定的,是算出來的
這是最重要的一點。GSD 不需要你手動說「這個任務放 Wave 1、那個放 Wave 2」。一切由 Dependency Graph (相依性圖) 自動決定。
整個流程分三步:
- Step 1: 每個 Plan 聲明自己「觸碰了哪些檔案」(
files_modified) - Step 2: Planner 建立相依性圖,對每個任務問:「它需要什麼?它產出什麼?」
- Step 3: 用拓撲排序自動計算 Wave,沒有相依的任務同一個 Wave 並行
Step 1:每個 Plan 聲明自己的檔案所有權
GSD 的 PLAN.md 有一個 YAML Frontmatter,最關鍵的欄位是 wave、depends_on、files_modified:
# plan-01.md
wave: 1
depends_on: []
files_modified:
- src/models/user.ts
- src/api/users.ts
# plan-02.md
wave: 1
depends_on: []
files_modified:
- src/models/product.ts
- src/api/products.ts
# plan-03.md
wave: 2
depends_on: [plan-01, plan-02]
files_modified:
- src/components/Dashboard.tsx
規則很簡單:files_modified 沒有重疊 → 兩個 Plan 就能平行。有重疊 → 後者必須等前者完成。
這個機制自動防止了「兩個 Agent 同時修改同一個檔案」的衝突問題。
Step 2:Planner 建立相依性圖的思路
Planner 在決定 Wave 前,對每個 Task 問三個問題:
Task A: needs → nothing | creates → user.ts
Task B: needs → nothing | creates → product.ts
Task C: needs → Task A | creates → users-api.ts
Task D: needs → Task B | creates → products-api.ts
Task E: needs → C + D | creates → Dashboard.tsx
然後畫出有向無環圖 (DAG):
A ──→ C ──┐
├──→ E
B ──→ D ──┘
Step 3:拓撲排序自動計算 Wave
根據上面的圖,拓撲排序的邏輯是:
- Wave 1:A, B(沒有任何
needs,根節點 → 同時執行) - Wave 2:C, D(只依賴 Wave 1 → Wave 1 全部完成後同時執行)
- Wave 3:E(依賴 Wave 2 全部 → 最後執行)
換算成執行時間,如果每個任務需要 10 分鐘:
- 傳統序列跑法: A → B → C → D → E = 50 分鐘
- Wave 並行跑法: [A+B] → [C+D] → E = 30 分鐘(節省 40%)
如果系列任務更長,節省比例會更高。
決定能不能並行的關鍵:Vertical Slice vs Horizontal Layer
這是 GSD Planner 強調的最核心的拆法選擇。
水平分層(Horizontal Layers)→ 天然序列,不能並行:
Plan 01: 建所有 Model (User, Product, Order)
Plan 02: 建所有 API ← 必須等 Plan 01
Plan 03: 建所有 UI ← 必須等 Plan 02
每個 Wave 只有一個 Plan,完全沒法並行。
垂直切片(Vertical Slices)→ 天然並行:
Plan 01: User 功能(Model + API + UI)
Plan 02: Product 功能(Model + API + UI)← 和 Plan 01 互不相依
Plan 03: Checkout UI ← 依賴 Plan 01 + 02
Plan 01 和 Plan 02 可以放同一個 Wave 同時跑,Wave 2 才是 Plan 03。
只有在「真正必須先有共用基礎」的時候(例如 Auth 服務必須先建),才需要水平拆法。其他情況都優先考慮垂直切片。
怎麼把這套思路移植到自己的 task.md
你不需要整套 GSD。最小化落地只需要兩件事:
1. 在 task.md 加入 depends_on 與 files 欄位
## Task: 使用者登入功能
- wave: 1
- depends_on: []
- files: [auth.ts, login-page.tsx]
## Task: 商品列表功能
- wave: 1
- depends_on: []
- files: [products.ts, product-list.tsx] ← 和上面沒有重疊,同一 Wave
## Task: 結帳流程
- wave: 2
- depends_on: [使用者登入功能, 商品列表功能]
- files: [checkout.tsx] ← Wave 2 才跑
2. 給 Orchestrator Claude 一個簡單的調度規則
在你的 CLAUDE.md 或 session prompt 裡說:
1. 讀取 task.md,找出 depends_on 為空的任務 → 這些是 Wave 1
2. 用 Task() 同時 spawn 多個 Sub-Agent,每個只拿對應的任務描述去執行
3. 等所有 Wave 1 完成後,找出 depends_on 只包含已完成任務的 → Wave 2
4. 每個 Sub-Agent 完成後立刻 Commit,不等其他 Agent
5. 重複直到所有任務完成
關鍵:每個 Sub-Agent 只看自己的任務,不讀整個 task.md。這樣每個 Agent 的 Context 是乾淨的,不會互相汙染。
總結:適合移植的三個核心思路
從 GSD 的 Wave Execution 設計中,最值得移植到自己工作流的有三點:
- 垂直切片拆任務:功能為單位,而不是技術層為單位,天然適合並行
files_modified宣告所有權:誰碰哪些檔案事先說清楚,讓並行排程算法可以自動避開衝突- Sub-Agent Context 隔離:每個執行 Agent 只帶最小必要的 Context,Orchestrator 負責彙整而不是執行