09 - 實戰 A:刷題網站 Taiwan_IM_board

08-Claude當大腦-Codex當雙手 | 00-Index | 下一篇 → 10-實戰-Obsidian醫學筆記庫


這個專案現在的痛點

Taiwan_IM_board 是 104–114 年共 2000 題的內專考古題刷題網站。它的 CLAUDE.md §5 有一段非常誠實的實戰紀錄:

「一個 agent 約 10–12 題 最穩;混合次專科(神經+精神+皮膚)或題目特長者,超過 ~16 題容易 stream idle timeout,切小批。」 「agent 常在 token / 每日滾動上限 / 週上限間擺盪;多數會「寫完檔案才回報上限」,所以失敗也先檢查 tmp_drafts/ 有沒有產出,再決定重派。」

這兩句話拆開來看,是四個具體的成本:

痛點根因Herdr 怎麼解
stream idle timeoutsubagent 走的是串流連線,長時間沒輸出就斷Codex 是本機 process,沒有 stream 這一層。慢就慢,不會被切斷
撞 Claude 用量上限產出量都算 Claude 帳單產出移到 Codex(ChatGPT Plus 額度)
「寫完檔案才回報上限」= 看不到進度subagent 是黑箱每個 worker 一個真實終端畫面,隨時看
要人工檢查 tmp_drafts/ 決定重派沒有狀態面板sidebar 直接顯示誰 working / 誰 done / 誰 blocked

建議的 Herdr 版面

Workspace w1  "刷題網站"   cwd=~/Taiwan_IM_board
├── Tab w1:t1  "brain"
│   └── w1:p1   ★ claude — 大腦(切批、驗收、合併、commit)
├── Tab w1:t2  "writers"
│   ├── w1:p2   ○ codex "w-心臟a"   114 心臟 001–012
│   ├── w1:p3   ○ codex "w-心臟b"   114 心臟 013–024
│   └── w1:p4   ○ codex "w-腎臟a"   114 腎臟 001–012
└── Tab w1:t3  "ops"
    ├── w1:p5   □ shell — validate / build_index
    └── w1:p6   □ shell — npm run dev(本機預覽)

用 tab 分「大腦 / 產出 / 維運」有個實際好處:你在 t1 跟 Claude 講話時,不會被 t2 那三個 Codex 的滾動輸出洗版,但 sidebar 仍然會把 t2 的 blocked 冒上來提醒你。

建立:

ws=$(herdr workspace create --cwd ~/Taiwan_IM_board --label "刷題網站" --no-focus)
wid=$(printf '%s\n' "$ws" | jq -r '.result.workspace.workspace_id')
 
herdr tab rename "$(printf '%s\n' "$ws" | jq -r '.result.tab.tab_id')" brain
herdr tab create --workspace "$wid" --label writers --no-focus
herdr tab create --workspace "$wid" --label ops --no-focus

應用一:詳解量產(主戰場)

流程總覽

Claude                         Codex ×N                    shell pane
  │                                                            │
  ├─ 1. export_questions.py 匯出待寫題 ────────────────────────▶│
  │◀── 拿到題目 JSON                                            │
  ├─ 2. 依「10–12 題一批」切成 N 個任務檔                        │
  ├─ 3. pane split ×N + agent start --kind codex ──▶ ○ ○ ○      │
  ├─ 4. agent prompt(不 --wait,平行跑)─────────▶ ○ ○ ○      │
  │                                              寫入 tmp_drafts/│
  ├─ 5. agent wait 逐一等 ◀────────────────────── ✓ ✓ ✓        │
  ├─ 6. 檢查檔案存在 + JSON 合法                                │
  ├─ 7. 合併進 explanations.<年>.json                           │
  ├─ 8. pane run "python3 tools/validate.py" ──────────────────▶│
  │◀── wait-output 抓結果 ──────────────────────────────────────│
  ├─ 9. 逐題查證 PMID / guideline(PubMed,不可外包)           │
  └─ 10. commit + push(唯一一次)

步驟 1–2:Claude 準備任務

# 在 w1:p5(ops pane)匯出待寫題
herdr pane run w1:p5 "python3 tools/export_questions.py --year 114 --subject 心臟血管"
herdr pane wait-output w1:p5 --regex "匯出|export|完成|Done" --timeout 120000
herdr pane read w1:p5 --source recent-unwrapped --lines 40

然後 Claude 把匯出結果切成每批 10–12 題的任務檔(沿用 CLAUDE.md §5 的實測數字)。

💡 為什麼還是維持 10–12 題? 這裡 timeout 不再是限制(Codex 是本機 process),但批次小仍然有價值:批次越小,出錯時要重做的成本越低,而且每個 pane 的 transcript 短一點,你巡場時比較好讀。改成 15–20 題也可以,值得自己試一次。

步驟 3–4:派工

08-Claude當大腦-Codex當雙手herdr-fanout.sh,或直接:

for batch in tmp_drafts/tasks/*.task.md; do
  name="w-$(basename "$batch" .task.md)"
  split=$(herdr pane split --current --direction right --cwd "$PWD" --no-focus)
  pane=$(printf '%s\n' "$split" | jq -r '.result.pane.pane_id')
  herdr agent start "$name" --kind codex --pane "$pane"
  herdr pane report-metadata "$pane" --source user:fanout \
    --display-agent "$name" --token summary="$(basename "$batch" .task.md)" --ttl-ms 86400000
  herdr agent prompt "$name" "$(cat "$batch")"
done

report-metadata 那一行讓 sidebar 直接顯示「114-心臟-001-012」這種標籤——手機巡場時這行超值得

步驟 5–6:等待與驗收

herdr agent wait w-心臟a --until idle --until done --timeout 1800000
 
# 驗收看檔案,不看狀態
test -f tmp_drafts/out/114-心臟-001-012.json || echo "❌ 沒產出,需重派"
jq empty tmp_drafts/out/114-心臟-001-012.json || echo "❌ JSON 不合法"
jq 'length' tmp_drafts/out/114-心臟-001-012.json   # 應該是 12

🔑 這一步直接取代了原本 CLAUDE.md 說的「失敗也先檢查 tmp_drafts/ 有沒有產出,再決定重派」——現在是腳本自動查,而且你在 sidebar 就看得到誰失敗

步驟 8:機器驗證

herdr pane run w1:p5 "python3 tools/validate.py"
herdr pane wait-output w1:p5 --regex "PASS|FAIL|Error|錯誤" --timeout 300000
herdr pane read w1:p5 --source recent-unwrapped --lines 80

validate.py 會檢查必要欄位、選項=5、answer/answerLetter 一致、科目合法、題號連續、image 存在、index 同步、詳解覆蓋率。這是零成本的第一道防線,一定要在 Claude 花 token 讀之前先跑。

步驟 9:查證(不可外包)⚠️

專案 CLAUDE.md §1 的最高原則是「零錯誤優先:醫療內容寧缺勿錯」,§5.4 要求 PMID/DOI 必須 WebSearch 查證為真,查不到就改原則性描述,不可捏造

Codex 在這個架構裡沒有 PubMed MCP,所以任務檔要求它把不確定的引用標成 [需查證](見 08-Claude當大腦-Codex當雙手 的任務檔範本)。Claude 收尾時:

  1. grep -rn "\[需查證\]" tmp_drafts/out/ 找出所有標記
  2. 逐條用 PubMed / WebSearch 查證
  3. 查得到 → 補上正確 PMID;查不到 → 改成原則性描述
  4. status 一律維持 draft(§1:AI 永遠不可自行改成 reviewed

應用二:考點分類與索引重建

classify_topic.py 那套「anchor 關鍵字權重 10 + 一般關鍵字權重 3」的計分規則,是很典型的「規格明確、需要大量試錯」的工作——很適合外包給 Codex

# 一個 codex pane 專職調關鍵字
herdr agent prompt w-topic "
讀 tools/classify_topic.py 的 TAXO 與計分規則。
目前 Other-* 有 6 題(見 --review 輸出)。
任務:優先「改關鍵字」讓這 6 題被正確分類,只有關鍵字無法涵蓋的個案才加 MANUAL。
每次改完跑 python3 tools/classify_topic.py --all --diff,確認沒有把原本對的題目改錯。
不要 commit。改完回覆 DONE 並列出你動了哪些關鍵字與理由。
"

重點是把 CLAUDE.md §4 的既有紀律寫進 prompt

  • 英文關鍵字一律用詞界比對(避免 all→ALL、ten→TEN 誤中)
  • 中文縮寫小心子字串( 會誤中「乾癬」,所以只寫 足癬/體癬/甲癬
  • 修分類時優先改關鍵字(一次修好同型題),MANUAL 是最後手段
  • 考點可跨科共用是刻意設計,不要「修正」成一科一考點

改完由 Claude 驗證:

herdr pane run w1:p5 "python3 tools/classify_topic.py --all --diff"
herdr pane run w1:p5 "python3 tools/build_index.py"
herdr pane run w1:p5 "python3 tools/validate.py"

應用三:附圖裁切(新增年份時)

extract_figures.py 要在 LAYOUT 補新年份的裁切座標——這是典型的「試 → 看圖 → 微調 → 再試」循環,很吃時間但不吃腦力。

Herdr 的價值在這裡是版面而不是編排:

w1:p2  codex — 調 LAYOUT 座標、跑 extract_figures.py
w1:p3  shell — 開一個圖片檢視器 / 或跑 npm run dev 看實際網頁效果

你在 p3 直接看切出來的圖對不對,不對就在 p2 跟 Codex 講。兩個畫面同時看得到,比切來切去快很多。


應用四:把預覽跟開發放在一起

herdr pane run w1:p6 "npm run dev"
herdr pane wait-output w1:p6 --regex "localhost:5173" --timeout 60000

之後 Claude 可以隨時:

herdr pane read w1:p6 --source recent-unwrapped --lines 50   # 看 Vite 有沒有噴錯

不用你複製貼上錯誤訊息。


⚠️ 這個專案特有的注意事項

1. 「改功能 = changelog + 使用說明 + README」三件事

CLAUDE.md §2 是硬規則:任何使用者看得到的更動,必須同步改 src/data/changelog.jsonsrc/components/UserManual.jsxREADME.md

這條規則不要外包——它需要判斷「這算不算使用者看得到的更動」,而且版本號要語意遞增。由 Claude 收尾時統一處理。

2. GitHub Actions 紅 ❌ 是預期行為

deploy.yml 因為 repo 是 private + 免費方案 Pages 不支援私有 repo,每次 push 都會在 configure-pages 失敗CLAUDE.md §8 明講這是預期行為、勿花時間修。

記得寫進給 Codex 的任務檔,否則它會很熱心地跑去「修」這個 workflow。

3. 有另一個 Codex 在持續推 main

CLAUDE.md §14:「另有 Codex 持續推 main,需先 reconcile」。所以合併前一定要:

git fetch origin main && git merge origin/main

這步由 Claude 做(它才知道怎麼判斷衝突該怎麼解)。

4. commit 作者固定

name=Claude / email=noreply@anthropic.com

Codex 不 commit,所以這條自然不會被違反——這也是「只有 Claude 能 commit」這條鐵律的另一個好理由


預期效益(保守估計)

以「一個年度、一科 24 題詳解」為單位:

現況(Claude subagent)Herdr + Codex
Claude 額度消耗全部只有切批 + 查證 + 合併(約 20–30%)
中途可見度每個 worker 一個畫面
失敗成本整批 timeout 重來單批重派,其他不受影響
可離開電腦✅ detach 走人,手機巡場
平行度受 Claude 額度限制受 Codex 額度 + 機器資源限制

⚠️ 不變的是:醫學正確性的責任沒有轉移。Codex 產出的東西仍然要 Claude 逐題查證status 仍然是 draft,仍然需要人工定稿才能變 reviewed這個架構省的是時間和額度,不是品質關卡。


🔗 相關筆記


最後更新:2026-08-04