每次接到一個 bug,你會做一樣的事:讀 issue、看附件、追前端按鈕呼叫哪個 API、追到 PHP、追到 SQL、看資料庫實際資料、寫個診斷腳本驗證、最後寫一份「根因在哪」的報告。
這套流程每次都一樣,但每次都要重新跟 Claude Code 講一遍。
我把它做成一個 Skill:170 行 markdown,把整套調查流程標準化成 7 步固定流程,串接一個 issue 讀取 skill + 3 個專屬 subagent。這篇完整解構這個 vibe-research skill 的設計。
TL;DR
| 項目 | 內容 |
|---|---|
| Skill 名稱 | vibe-research |
| 行數 | 170 行 SKILL.md |
| 核心流程 | 7 步:讀單 → 全棧定位 → 資料模型 → 看 DB → 實測 → 報告 → 等確認 |
| 鐵則 | 報告/計畫產出前絕不改任何專案程式碼 |
| 串接 | read-redmine-issue skill + 3 個 subagent(field-finder / consistency-checker / php74-pro) |
| 驗證手法 | 拋棄式診斷腳本 + rollback transaction + 端點實打 |
| 附帶 | 報告固定格式模板 + 環境踩雷小抄 |
為什麼把調查流程做成 Skill
Claude Code 的 Skill 機制讓你把「一套固定的工作流程」封裝起來,用關鍵字觸發。
調查 bug 的痛點:
- 每次都要重講步驟:「先讀 issue、然後追呼叫鏈、記得看前端 JS、然後看 DB⋯⋯」
- AI 容易跳步:直接猜根因、跳過驗證、或太早改 code
- 環境陷阱重複踩:docker exec 怎麼下、MySQL 中文編碼、Grep timeout——每個新 session 都要重新踩一次
- 報告格式不一致:每次調查的產出格式都不一樣,難比較
Skill 解決這些——把流程、鐵則、踩雷經驗一次寫進去,每次觸發都套用。
觸發設計:description 決定何時啟用
Skill 的 frontmatter description 決定 Claude Code 何時自動套用:
---
name: vibe-research
description: 對某個 Redmine issue 做深入的「全棧定位調查」(不只是讀單)。bug 查根因;客製/新功能則定位現有實作 + 出實作計畫。當使用者說「vibe research / 對 issue N 做 research / 查根因 / 定位問題 / 深入調查 issue N / 這個 bug 為什麼會發生 / 問題出在哪 / 要客製或新增某功能先幫我定位評估」時使用。流程:讀單 → 全棧追呼叫鏈 → 看 DB → 實測/驗證 → 產出固定格式報告...
---
設計重點:
1. 列出大量觸發詞
「查根因」「定位問題」「深入調查」「這個 bug 為什麼會發生」「問題出在哪」——自然語言的各種說法都列進去。使用者不用記特定指令,講人話就會觸發。
2. 明確劃清界線
description 結尾特別寫:「與 read-redmine-issue 區隔:那個只讀 issue 內容,這個做定位與驗證」。
兩個 skill 容易混淆——read-redmine-issue 只讀單、vibe-research 做完整調查。明確劃界讓 Claude Code 選對 skill。
3. 涵蓋兩種任務型態
description 同時涵蓋「bug 查根因」和「客製/新功能定位」——同一套流程,兩種產出(根因報告 vs 實作計畫)。
7 步固定流程
Step 0:讀單(含截圖 / Office 附件)
python "...\read-redmine-issue\read_issue.py" <id> --download
呼叫另一個 skill read-redmine-issue 讀 issue。關鍵:規格常畫在附件裡,一定要 --download。
- 圖片:印出本機路徑,餵 Read 工具看圖
- Office 附件(.pptx / .xlsx / .docx):Read 工具開不了,用 PowerShell 解壓:
Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory('檔.pptx','解壓夾')- pptx 截圖:
ppt\media\*.png - pptx 文字:
ppt\slides\slideN.xml抓<a:t>...</a:t> - xlsx 表格:
xl\sharedStrings.xml+xl\worksheets\sheet1.xml還原
- pptx 截圖:
為什麼 Step 0 這麼細:企業系統的需求規格經常是 PowerPoint 簡報或 Excel 範例檔。漏看附件 = 漏看一半需求。把「怎麼讀 Office 檔」寫進 skill,AI 不會卡在「我開不了 pptx」。
Step 1:全棧定位(不略過前端 JS)
前端按鈕/事件 (form*.php 裡的 onclick / $.ajax)
→ ajax url (class/*.php 或 ajax/*.php 的 action)
→ 路由 ?mod={mod}&func={func} → module/{mod}/{func}.php
→ class 封裝 (DBPDO / DB / DB2)
→ SQL → 資料表
關鍵指示:JS 那段不要跳過。很多根因要靠完整呼叫鏈才追得到。
特別標出機構客製分支:$_SESSION['mOrgGroup']、$_SESSION['mOrgID']、in_array($_SESSION['mOrgID'], [...])。多租戶系統的 bug 常常是「只有某機構會中」——找出客製分支才知道為什麼。
Step 2:資料模型 / SQL 來源
關鍵指示:追到 SQL,不要只看畫面或函式名。
最重要的一條洞察:
釐清「編輯版 / 列印版 / 匯出版 / 申報版」各自讀哪張表——客製機構常把某欄位存在不同表/欄位(例:某機構把「追蹤評值」存在
case_form_24_item.assess,標準機構存case_form_24_assess)。
這是 Legacy 多租戶系統的經典陷阱——同一個欄位,不同機構存不同地方。不追到 SQL 永遠不知道。
Step 3:看 DB(實際資料)
docker exec mysql mysql -udbuser -pdbpass --default-character-set=utf8 'demo-org-2' -e "SHOW COLUMNS FROM xxx;"
幾個關鍵知識寫進 skill:
- 中文一定要
--default-character-set=utf8,否則變問號 - 共用表在中央庫
demo-org-1:機構資訊、選單權限、機構↔群組對照都在這裡,不在各租戶庫——找不到表先往中央庫找 - 本地租戶庫對照:
demo-org-2本地機構 /demo-org-81本地 ServiceTypeB /demo-org-136本地 ServiceTypeA /demo-org-338本地 ServiceTypeC - 分清本機 vs 線上:中央庫是線上資料副本,但某些租戶庫可能不在本機。先
SHOW DATABASES LIKE 'demo-org-XXX'確認
這些是踩過坑才知道的環境知識。寫進 skill 後 AI 不會「找不到表就放棄」或「中文亂碼不知道為什麼」。
Step 4:實測 / 驗證(有條件)
這步最精彩,依任務型態分三種驗證法:
A. bug 根因 → 拋棄式診斷腳本
// 放在 AppSystem/module_a/_diag_<id>.php
// 用 PowerShell 跑:docker exec php-fpm php /var/www/html/module_a/_diag_<id>.php
三個原則:
- 對照法:同一段邏輯跑「乾淨資料」vs「觸發條件的資料」,證明只有後者會壞
- 不留痕跡:寫入測試包在
beginTransaction()…rollBack() - 跑完一定刪掉診斷檔
B. 功能/端點 → 實打端點驗證
# 用 _dev_login.php 建測試 session
curl -s -c cookies.txt "http://localhost/_dev_login.php?db=demo-org-136&oid=ORG-T1&ogid=GRP-004&uid=<uid>&hc=2"
# 帶 cookie curl 端點,看 HTTP 碼 / headers / 輸出
C. 無法重現時
真測不了:明確寫「假設根因/結論」+「程式佐證(檔案:行)」+「要怎樣才能 100% 確認」。不要假裝證明了。
最後這句是整個 skill 的精神——不要假裝證明了。測不到就老實說測不到、需要什麼條件才能確認。
Step 5:產出固定格式報告
用固定模板(後面詳述)。重點:「可執行」——指到 檔案:行,講清楚為什麼。
Step 6:停,提計畫,等確認
提出修法/實作計畫(可附多方案 + 取捨 + 風險),等使用者確認。
Step 7:(可選)確認後續做實作
使用者確認計畫後,可在同一流程續做實作(確認前仍絕不動 code)。
鐵則:報告前絕不改 Code
整個 skill 開頭第一條就是:
鐵則:報告/計畫產出前絕不改任何專案程式碼。 先把「定位 + 計畫」講清楚、等使用者確認。
為什麼這條這麼重要?
Claude Code 的天性是直接動手。但調查 bug 時,太早動手是災難:
- 還沒搞懂根因就改 → 改錯地方
- 改了 A 機構 → 壞了 B 機構(多租戶)
- 沒驗證就改 → 改完不知道有沒有效
這條鐵則強制「先理解、再動手」。診斷腳本可以寫(拋棄式、會刪掉)、但正式 code 在使用者確認計畫前一律不動。
這跟我另一個 偵測複雜任務的 Hook 是同個哲學——讓 AI 慢下來,先確認方向。
Skill + Subagent 的協作架構
vibe-research 不是單打獨鬥,它串接一個 skill + 3 個專屬 subagent:
vibe-research (skill, 編排者)
│
├─ Step 0 → read-redmine-issue (skill)
│ 讀 issue + 下載附件
│
├─ Step 2 → AppSystem-field-finder (subagent)
│ 給欄位名,找它在 form/print/export/申報/ajax/class/sql 哪些檔案出現
│
├─ Step 2 → AppSystem-consistency-checker (subagent)
│ 改完 form 後比對列印/匯出/客製/申報是否同步
│
└─ Step 7 → AppSystem-php74-pro (subagent)
需要實作/除錯/效能時的 PHP 7.4 工程師
為什麼拆成 skill + 多個 subagent
| 角色 | 為什麼獨立 |
|---|---|
vibe-research | 編排整個流程的主 skill |
read-redmine-issue | 讀單是獨立能力,單獨也常用(不一定每次都要全棧調查) |
field-finder | 「欄位影響範圍」是高頻獨立查詢,且需要掃大量檔案(適合丟給 subagent 隔離 context) |
consistency-checker | 「多檔一致性」是另一個獨立查詢 |
php74-pro | 實作階段才用(Step 7),且有自己的 PHP 7.4 專屬知識 |
主 skill 負責流程編排,subagent 負責耗 context 的查詢——這是 Claude Code 多 agent 協作的典型分工。
特別注意 skill 中的時序控制:
AppSystem-php74-pro— 需要實作/除錯/效能時的 PHP 7.4 工程師(等確認修法後才用)。
php74-pro 是實作 agent,呼應「報告前不改 code」鐵則——它只在 Step 7(確認後)才登場。
報告固定格式模板
## issue-<id> 定位報告 ← 客製模式可改「現有實作定位報告」
### 問題位置 / 涉及位置
| 層 | 檔案:行 | 角色 |
(前端 JS / ajax / PHP / class / SQL)
### 全棧呼叫鏈
按鈕/事件 → ajax url → ?mod=&func= → PHP → class → SQL → 資料表
### 資料模型
涉及哪些表、關鍵欄位存哪;機構客製差異
### 根因 / 現況
- 根因模式:出問題的程式(檔案:行)+ 為什麼會壞 + 實測對照
- 客製模式:有無既有實作、可沿用的範例功能
### 為何只在某情境 / 某機構發生
(解釋 DEMO/一般情況正常、特定機構或資料才中招的原因)
### 建議修法 / 實作計畫
- 根因模式:修法(多方案 / 取捨 / 風險 / 影響範圍)
- 客製模式:要新增/改哪些檔 + DB migration + gating
模板的價值
- 「問題位置」用表格 + 檔案:行——可點擊、可驗證,不是抽象描述
- 「全棧呼叫鏈」固定畫出來——下次看報告一眼懂資料怎麼流
- 「為何只在某機構發生」獨立一節——多租戶系統最重要的問題,強制回答
- 根因 / 客製雙模式共用——同一個模板,兩種任務
固定格式讓每次調查的產出可以橫向比較,也讓 Claude Code 知道「報告要長這樣」。
環境踩雷小抄
Skill 結尾有一份「踩雷小抄」,把過去踩過的環境坑寫成清單:
| 踩雷 | 教訓 |
|---|---|
docker exec 一律走 PowerShell 工具 | Bash 工具會把 /var/www/html/... 改寫成 Windows 路徑而失敗 |
| MCP MySQL 工具別用 | 連的不是 Docker 的 MySQL,資料不準 |
| MySQL 中文 | 加 --default-character-set=utf8 |
| Grep 在大 repo 會 timeout | 限縮路徑或改用 rg --max-filesize 800K |
DBPDO::insert() 後不能用 last_insert_id() | insert() 內部會再寫一筆 log,洗掉 lastInsertId |
date_format(col,...) 在 WHERE 停用索引 | 改 PHP 組日期範圍比較 |
| 未綁參數的字串內插 SQL | 遇單引號等特殊字元會炸(常見根因) |
為什麼踩雷小抄要寫進 Skill
這些坑的共同點:每個新 session 的 Claude Code 都不知道。
- 它不知道你的 docker exec 要走 PowerShell
- 它不知道你的 MCP MySQL 連錯資料庫
- 它不知道
DBPDO::insert()有 lastInsertId 陷阱
每次重新踩 = 每次浪費時間。寫進 skill 一次,永久解決。
這也是 skill 跟一次性 prompt 的最大差別——累積的環境知識可以沉澱。
驗證手法的精髓:拋棄式診斷 + rollback
Step 4 的拋棄式診斷腳本值得單獨講:
// _diag_1234.php(跑完就刪)
$pdo = new PDO('mysql:host=mysql;dbname=demo-org-136', 'dbuser', 'dbpass',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$pdo->beginTransaction();
try {
// 對照組:乾淨資料
$clean = testLogic($pdo, $cleanData);
// 實驗組:觸發條件的資料(例如含單引號)
$broken = testLogic($pdo, $triggerData);
// 證明只有後者會壞
} finally {
$pdo->rollBack(); // 不留痕跡
}
三個設計原則:
- 對照法:乾淨資料 vs 觸發資料,科學化地證明根因——不是「我覺得是這個」,是「只有觸發條件會壞」
- Transaction rollback:診斷會寫測試資料,但包在 transaction 裡,跑完 rollback——不污染資料庫
- 拋棄式:診斷檔跑完一定刪,不留在 codebase
這是把「除錯」變成「實驗」——有對照組、可重現、不留副作用。
給其他人的借鑑:怎麼把調查流程做成 Skill
Step 1:先觀察自己重複講什麼
連續查幾個 bug,記下你每次都跟 Claude Code 講的話:
- 「記得先讀 issue 附件」
- 「追呼叫鏈不要跳過前端」
- 「docker exec 要走 PowerShell」
- 「報告前不要先改 code」
這些重複的指示,就是 skill 的內容。
Step 2:固定流程化
把零散的指示組織成有編號的步驟(Step 0 → Step N)。編號讓 Claude Code 知道順序、不跳步。
Step 3:寫進「鐵則」
最重要的原則放在最前面,標成鐵則。例如「報告前絕不改 code」——這種容易被 AI 違反的原則,要明確、要放開頭。
Step 4:累積踩雷小抄
每踩一個環境坑,寫進 skill 的踩雷小抄。下次不用重踩。
Step 5:設計觸發詞
description 列出大量自然語言觸發詞——讓使用者講人話就能啟動,不用記指令。
Step 6:拆出 subagent
耗 context 的查詢(掃大量檔案、跨檔比對)拆成 subagent,主 skill 只負責編排。
結語
一次性的 prompt 用完就忘。Skill 把「流程 + 鐵則 + 踩雷經驗」沉澱下來,每次觸發都套用。
vibe-research 的價值不是 170 行 markdown——是它把資深工程師查 bug 的思考方式具象化:
- 先讀懂需求(含附件)
- 追完整呼叫鏈(不跳步)
- 追到 SQL(不只看畫面)
- 用實驗驗證(對照組 + rollback)
- 先報告、後動手(鐵則)
新進工程師要幾個月才養成的調查直覺,寫進 skill 後 Claude Code 第一次就具備。
從你最常重複的調查步驟開始寫。不用一次寫到 170 行——先把「固定 5 步流程 + 1 條鐵則 + 3 條踩雷」寫進去,就比每次重講有效得多。需求來了再長。
最有價值的 skill 不是別人寫好的通用模板——是你自己工作流程的具象化。