---
title: "09 - 實戰 A：刷題網站 Taiwan_IM_board"
type: note
specialty: Programming
tags: [herdr從0開始使用教學, 09-實戰, taiwan-im-board, 詳解量產]
created: 2026-08-04
updated: "2026"
modified: 2026-08-04
---

# 09 - 實戰 A：刷題網站 Taiwan_IM_board

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

---

## 這個專案現在的痛點

[`Taiwan_IM_board`](https://github.com/dirtywolf1213/Taiwan_IM_board) 是 104–114 年共 2000 題的內專考古題刷題網站。它的 `CLAUDE.md` §5 有一段非常誠實的實戰紀錄：

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

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

| 痛點 | 根因 | Herdr 怎麼解 |
|---|---|---|
| stream idle timeout | subagent 走的是**串流連線**，長時間沒輸出就斷 | 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` 冒上來提醒你。

建立：

```bash
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 準備任務

```bash
# 在 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`，或直接：

```bash
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：等待與驗收

```bash
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：機器驗證

```bash
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**：

```bash
# 一個 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 驗證：

```bash
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 講。**兩個畫面同時看得到**，比切來切去快很多。

---

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

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

之後 Claude 可以隨時：

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

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

---

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

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

`CLAUDE.md` §2 是硬規則：任何使用者看得到的更動，必須同步改 `src/data/changelog.json`、`src/components/UserManual.jsx`、`README.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」。所以合併前一定要：

```bash
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`。**這個架構省的是時間和額度，不是品質關卡。**

---

## 🔗 相關筆記

- [[08-Claude當大腦-Codex當雙手]] — 上一步：架構與腳本
- [[10-實戰-Obsidian醫學筆記庫]] — 下一步：套在 vault 上
- [[06-CLI與自動化基礎]] — 指令細節
- [[Programming/Python/07-實戰自動化範例|Python 07 - 實戰自動化範例]] — 這個專案 `tools/` pipeline 的說明
- [[Programming/Git-GitHub/09-實戰工作流範例|Git 09 - 實戰工作流範例]] — 多 AI 推同一個 repo 的 reconcile 紀律

---

*最後更新：2026-08-04*
