wen aidev
Published on

四級驗證與自動修復:拆解 GSD Verifier 的 Agent 設計與工程實作

上一篇 Wave 執行策略中,我們拆解了 GSD 如何透過 Dependency Graph 把任務分波次平行跑。但平行跑完之後,誰來確認產出是完整的?Agent A 寫了元件、Agent B 寫了 API,如果沒人驗證它們有沒有串起來,那只是「看起來寫完」。

這篇直接進 GSD 原始碼,拆解 Verifier 的三個面向:Agent Prompt 怎麼寫的、背後的工具是什麼、Orchestrator 怎麼呼叫它和路由結果

Orchestrator 呼叫 Verifier 的完整流程

先看全局。在 execute-phase.md 工作流中,所有 Wave 跑完後,Orchestrator 會執行 verify_phase_goal 步驟:

// execute-phase.md 中的 verify_phase_goal 步驟
Task(
  prompt="Verify phase {phase_number} goal achievement.
  Phase directory: {phase_dir}
  Phase goal: {goal from ROADMAP.md}
  Check must_haves against actual codebase.
  Create VERIFICATION.md.",
  subagent_type="gsd-verifier",
  model="{verifier_model}"
)

Verifier 跑完後,Orchestrator 用一行 grep 讀取結果:

grep "^status:" "$PHASE_DIR"/*-VERIFICATION.md | cut -d: -f2 | tr -d ' '

然後根據 status 做硬性路由:

  • passed → 標記 Phase 完成,更新 ROADMAP.md
  • gaps_found → 把 Gaps 餵給 Planner 產 Fix Plan,再派 Executor 修,修完重跑 Verifier
  • human_needed → 自動化查核全過,但有些東西必須人眼確認(UI 外觀、即時互動等)
Orchestrator 呼叫 Verifier 的完整通訊流程

圖說:Verifier 是獨立的 Sub-agent,在自己的 Context 中跑完查核後回傳 status,由 Orchestrator 做路由決策。gaps_found 會觸發自動修復閉環。

重點:Verifier 自己不能修程式碼。它的 Prompt 裡明確限制工具只有 Read, Bash, Grep, Glob,沒有 WriteEdit。它只能產出 VERIFICATION.md 報告,然後交還主控權。修復是 Planner + Executor 的事。

gsd-verifier 的 Prompt 怎麼寫的

GSD 的 Verifier Agent Prompt 在 agents/gsd-verifier.md,總共 700 行。它不是叫 AI「請幫我檢查一下程式碼有沒有問題」,而是把整個驗證 SOP 寫死在 Prompt 裡,包含具體的 Bash 指令。

Prompt 的核心結構:

<role>
You are a GSD phase verifier.
CRITICAL: Do NOT trust SUMMARY.md claims.
SUMMARYs document what Claude SAID it did.
You verify what ACTUALLY exists in the code.
</role>

<verification_process>
  Step 0: 有沒有之前的 VERIFICATION.md?有的話進 RE-VERIFICATION 模式
  Step 1: 載入 Context(PLAN.md、ROADMAP.md)
  Step 2: 建立 must_haves(從 PLAN frontmatter 或 ROADMAP 推導)
  Step 3: 驗證 Observable Truths
  Step 4: 驗證 Artifacts(四級)
  Step 5: 驗證 Key Links(串接)
  Step 6: 需求覆蓋率
  Step 7: Anti-Pattern 掃描
  Step 8: 哪些東西需要人類測
  Step 9: 判定整體 status
  Step 10: 結構化 Gaps 輸出(YAML)
</verification_process>

<stub_detection_patterns>
  // 直接在 Prompt 裡教 AI 認 Stub:
  return <div>Placeholder</div>     // RED FLAG
  onClick={() => {}}                // RED FLAG
  return Response.json([])          // RED FLAG(沒有 DB 查詢的空回傳)
</stub_detection_patterns>

=> Verifier 的 Prompt 本質上是一份「Code Review Checklist 的自動化執行手冊」,AI 只是負責跑手冊上的指令然後填表。

四級驗證是工具強制還是 Prompt 驅動?

這是最關鍵的區別。答案:兩者合用,Level 1-2 是 Node.js 工具強制,Level 3-4 是 Prompt 驅動 AI 跑 grep。

四級驗證的實際運作流程

圖說:Level 1-2 由 gsd-tools.cjs 硬邏輯執行(確定性高),Level 3-4 由 Prompt 模板驅動 AI 跑 grep(覆蓋面廣但依賴 AI 判斷)。

Level 1-2:gsd-tools.cjs 硬邏輯

Verifier Agent 的 Prompt 裡要求它必須呼叫 gsd-tools.cjs verify artifacts。這支 Node.js 腳本(在 get-shit-done/bin/lib/verify.cjs)做的事情是:

// verify.cjs 中的 cmdVerifyArtifacts 函數(簡化)
function cmdVerifyArtifacts(cwd, planFilePath) {
  // 從 PLAN.md 的 frontmatter 解析 must_haves.artifacts
  const artifacts = parseMustHavesBlock(content, 'artifacts');

  for (const artifact of artifacts) {
    // Level 1: 檔案存在嗎?
    const exists = fs.existsSync(artFullPath);

    if (exists) {
      const lineCount = fileContent.split('\n').length;

      // Level 2: 行數夠嗎?(防 Stub)
      if (artifact.min_lines && lineCount < artifact.min_lines) {
        check.issues.push(
          `Only ${lineCount} lines, need ${artifact.min_lines}`,
        );
      }
      // Level 2: 有沒有包含指定的程式碼模式?
      if (artifact.contains && !fileContent.includes(artifact.contains)) {
        check.issues.push(`Missing pattern: ${artifact.contains}`);
      }
      // Level 2: 有沒有導出指定的函式?
      if (artifact.exports) {
        for (const exp of exports) {
          if (!fileContent.includes(exp))
            check.issues.push(`Missing export: ${exp}`);
        }
      }
    }
  }
  // 回傳 JSON:{ all_passed, passed, total, artifacts: [...] }
}

這些檢查是確定性的。不管 AI 怎麼想,fs.existsSync 就是存在或不存在,行數就是行數。AI 不可能跳過這步,因為 Prompt 規定了它必須呼叫這個 CLI。

Level 3:Prompt 驅動的 grep 模板

Level 3 (Wiring) 沒有對應的 CLI 工具。Verifier 的 Prompt 裡直接提供了 grep 指令模板,讓 AI 動態代入變數:

# Prompt 裡教 Verifier 怎麼檢查「有沒有被 Import 且被使用」
# Import check
grep -r "import.*$artifact_name" src/ --include="*.ts" --include="*.tsx" | wc -l

# Usage check (beyond imports)
grep -r "$artifact_name" src/ --include="*.ts" --include="*.tsx" | grep -v "import" | wc -l

AI 需要自己判斷 $artifact_name 是什麼、在哪個目錄搜、以及怎麼解讀結果。

Level 4:完全依賴 AI 追溯

Level 4 (Data-Flow Trace) 最複雜,Prompt 給了方向但細節靠 AI:

# Prompt 教 Verifier 追溯資料流:
# 1. 找到 state 變數
grep -n -E "useState|useQuery|useSWR" "$artifact"

# 2. 找到資料來源
grep -n -A 5 "set${STATE_VAR}" "$artifact" | grep -E "fetch|axios|query"

# 3. 驗證來源是否有真實查詢
grep -n -E "prisma\.|db\.|findMany|select|FROM" "$source_file"

# 4. 抓空包彈:回傳寫死空陣列
grep -n -E "return.*json\(\s*\[\]|return.*json\(\s*\{\}" "$source_file"

must_haves 機制:驗收契約從哪來

Verifier 不是隨便猜「應該驗什麼」。每個 PLAN.md 的 YAML frontmatter 裡有一個 must_haves 欄位,這是 Planner Agent 在規劃階段就已經定義好的「驗收契約」:

# PLAN.md frontmatter 範例
must_haves:
  truths:
    - 'User can see existing messages'
    - 'User can send a message'
  artifacts:
    - path: 'src/components/Chat.tsx'
      provides: 'Message list rendering'
      min_lines: 50
      contains: 'useEffect'
      exports: ['ChatComponent']
  key_links:
    - from: 'Chat.tsx'
      to: 'api/chat'
      via: 'fetch in useEffect'
      pattern: 'fetch.*api/chat'

=> min_lines: 50 就是 Level 2 的閾值。如果 AI 生出來的元件不到 50 行,gsd-tools.cjs 會直接報 STUB。pattern: "fetch.*api/chat" 是 key-links 驗證的 regex,工具會去 source 檔案裡跑這個 regex,有匹配才算 WIRED。

Gaps 的結構化輸出與自動修復

當 Verifier 發現問題,它不是寫一段自然語言抱怨,而是產出結構化的 YAML:

# VERIFICATION.md 的 frontmatter
status: gaps_found
score: 3/5 must-haves verified
gaps:
  - truth: 'User can send a message'
    status: failed
    reason: 'Chat.tsx onSubmit handler only calls e.preventDefault()'
    artifacts:
      - path: 'src/components/Chat.tsx'
        issue: 'Form handler is stub - no API call'
    missing:
      - 'fetch POST to /api/chat in onSubmit handler'

Orchestrator 讀到 status: gaps_found 後,自動觸發:

  1. Planner AgentVERIFICATION.mdgaps 欄位 → 產出精準的 Fix Plan(只修壞掉的,不重新規劃整個 Phase)
  2. Executor Agent 按 Fix Plan 修好 → Commit
  3. 再跑一次 Verifier → 如果還有 Gaps,重複循環

這就是自動修復閉環。整個過程不需要人類介入 Debug。

把 Verifier 搬到你自己的框架

GSD 的 Verifier 機制不依賴特定的 AI 工具。核心就是三件事:

1. 讓 Planner/Executor 產出驗收契約

不管你用什麼框架,讓 Plan 階段強制產出一份「這個任務做完後,磁碟上應該要有什麼」的清單。格式隨你,但至少要有:

  • artifacts:預期產出的檔案路徑 + 最低行數
  • key_links:哪些檔案之間應該要有 import 關係

2. 用腳本做 Level 1-2(不要靠 AI)

寫一支簡單的 shell script 或 Node.js 腳本,做兩件事:檔案存不存在、行數夠不夠。這部分必須是確定性的工具,不能讓 AI 自己判斷。

#!/bin/bash
# 最小化 Verifier 腳本
for file in $EXPECTED_FILES; do
  if [ ! -f "$file" ]; then
    echo "MISSING: $file"
  elif [ $(wc -l < "$file") -lt 20 ]; then
    echo "STUB: $file ($(wc -l < "$file") lines)"
  else
    echo "OK: $file"
  fi
done

3. 用 Prompt 驅動 Level 3-4

在你的 Verifier Agent 的 Prompt 裡,塞入 grep 模板讓 AI 做 Wiring 檢查。這部分可以直接抄 GSD 的模板:

對於每個通過 Level 1-2 的檔案:
1. 跑 grep -r "import.*{file_name}" src/ | wc -l → 沒人 import 就是 ORPHANED
2. 跑 grep -r "{file_name}" src/ | grep -v "import" | wc -l → import 了但沒用也是問題
3. 如果是渲染元件,追溯 useState 的資料來源,確認不是寫死的假資料

4. 限制 Verifier 的工具權限

GSD 設計中最聰明的一個決定:Verifier 不能 Write。這能避免它偷改程式碼來讓自己的檢查通過。驗證者跟執行者必須是分開的角色。

延伸閱讀

留言討論