強迫 Claude Code 停下來問清楚:用 UserPromptSubmit Hook 觸發 Interview Mode


我:「重構這個模組的權限檢查邏輯」

Claude Code 直接動手,把權限檢查全抽成 middleware,改了 8 個檔案。但我其實只想把重複的部分抽出來、保留 inline 的快速檢查。重做 30 分鐘。

它沒問我範圍,我也忘了講。這不是技術錯誤,是「沒問清楚就動手」的方向錯誤——而方向錯誤的代價遠大於技術 bug。

我寫了一個 UserPromptSubmit Hook:偵測到複雜任務時,自動注入「先回答四個問題」的提示,強迫 Claude Code 在動手前先確認方向。這篇拆解它的設計。


TL;DR

項目設計
Hook 類型UserPromptSubmit(無 matcher,所有訊息觸發)
語言Python,~160 行
判斷27 條 regex(17 簡單 + 10 複雜)+ 訊息長度
三段邏輯先判簡單 → 再判複雜 → 不確定看長度(偏觸發)
提問Why / What / How / Edge cases 四個
觸發方式approve + message 注入(不阻擋送出)
誤判處理結尾寫「若是簡單任務忽略此提示」給 AI 台階

為什麼 Claude Code 不會自己問

Claude Code 的訓練目標是「有用」——直接動手解決比反覆確認更「有用」,這在簡單任務是對的。但複雜任務的失敗成本高:

任務失敗成本
改 typo / 加 log一行回滾
重構 / 優化30+ 分鐘重做
新功能漏掉模組、上線後客戶回報

我踩過的四種方向錯,根因都是「沒講清楚 + 沒問清楚」:

  • 重構權限檢查:沒講範圍(What)→ 它全抽成 middleware,但我只要抽重複的
  • 優化查詢:沒講目的(Why)→ 它加了 Redis cache + 改 schema,但我只想看 EXPLAIN
  • 加欄位:沒講範圍精確版 → 它改了表單/列印/匯出,漏了 API 上傳等 4 個模組
  • 整合表單:沒講做法(How)→ 它一次性統一三個表單,但歷史資料相容性壞了

「簡單任務直接做、複雜任務先問」這個偏好,需要一個外部訊號告訴 Claude Code。Hook 就是那個訊號。


UserPromptSubmit:注入 context 的最佳時機

使用者送出訊息


[UserPromptSubmit Hook]  ← 本文主角
    │ ├─ 可注入 message 到 context
    │ └─ 可阻擋訊息送出

Claude Code 看到使用者訊息(+ hook 注入的 message)→ 推理 → 動作

它在使用者按 Enter、Claude Code 開始處理之前觸發——可以根據訊息內容動態修改 Claude Code 的行為

Hook 的輸入輸出

// Input
{ "prompt": "重構這個模組的權限邏輯" }

// Output
{ "decision": "approve", "message": "💡 偵測到複雜任務..." }

我用 approve + message不阻擋使用者(不像 block 要重打訊息),但在 Claude Code 看到使用者訊息之前先讓它看到我的提示。誤判也沒關係——頂多它多讀一段文字。


三段判斷的決策邏輯

prompt 進來

    ├─ is_simple_task()? ──Yes──→ 不觸發(「輸入太短」/「符合簡單模式」)

    └─ No → is_complex_task()? ──Yes──→ 觸發(「符合複雜模式」)

              └─ No(不確定)→ len > 30 ? 觸發 : 不觸發

順序很重要。「重構這個變數命名」這句命中「重構」(複雜),但其實是小改動——如果先檢查複雜模式會誤判。所以先檢查簡單模式

SIMPLE_TASK_PATTERNS = [r".{0,3}(變數名|函數名|檔名)", ...]  # 命中「改變數命名」,優先放行

簡單模式的優先級高於複雜模式,這個順序消除大量誤判。不確定時(中等長度、沒明確關鍵字)偏向觸發——問四個問題的成本遠低於做錯方向。


Pattern 設計

簡單任務(17 條)

SIMPLE_TASK_PATTERNS = [
    r"(||).{0,5}(typo|錯字|拼錯|顏色|文字|標題)",   # 小修改
    r".{0,3}(log|console|print|註解|comment)",
    r"^(怎麼|如何|為什麼|什麼是|哪裡|哪個|是否|有沒有)",   # 詢問類
    r"^(|||搜尋|列出|顯示)",                          # 查看類
    r"^(執行||run|test|build|commit)",                    # 簡單指令
    r"^git\s+(status|log|diff|branch)",
    r"^(ok||確認|||可以|繼續|next|yes|y)$",           # 確認/繼續
    # ... 共 17 條
]

邏輯:訊息短、以疑問詞開頭、明確說「改 typo / 加 log」、或是確認回應 → 簡單。

複雜任務(10 條)

COMPLEX_TASK_PATTERNS = [
    r"(重構|refactor|整合|migrate|遷移)",                  # 大型改動
    r"(優化|改善|提升).{0,5}(效能|performance|速度)",
    r"(所有|全部|每個|整個).{0,5}(檔案||模組|頁面)",      # 多檔案
    r"(批次|批量|大量)",
    r"(架構|設計|規劃|方案)",                              # 架構層級
    r"(資料庫|db|schema|table).{0,5}(設計|變更||新增)",
    r"(不確定|可能|或者|也許|應該)",                        # 不確定性
    r"(怎麼做比較好|你覺得|建議)",
    # ... 共 10 條
]

邏輯:「重構/優化/整合」、「所有/整個/批次」、「不確定/可能/應該」→ 複雜。

Regex 設計的三個陷阱

陷阱一:過廣的 OR

r"(改|修|換)"                                  # ❌ 命中「我改了密碼後忘了 commit」
r"(改|修|換).{0,5}(typo|錯字|拼錯|顏色|文字)"   # ✅ 用 {0,5} 限制 context 距離

陷阱二:忘記錨點

r"(ok|好|確認)"      # ❌ 命中「請確認 ok 後執行重構」
r"^(ok|好|確認)$"    # ✅ 整句就是 ok / 好 / 確認

陷阱三:中英混用

r"(重構|refactor|整合|migrate|遷移)"  # 中英都要涵蓋(「幫我 refactor 這段」很常見)

為什麼只有 27 條

多 pattern 不等於更準:太多會互相干擾、誤判機率上升、維護成本高。從少開始、用 log 校正、慢慢加(見後面校正章節)。

短訊息預設不觸發

if len(prompt) < 15:
    return True, "輸入太短"   # 視為簡單

我統計過自己半年的 prompt 長度:確認/指令多在 15 字內、真正的複雜任務幾乎都 > 30 字。< 15 字幾乎沒有複雜任務,這個閾值過濾掉大部分噪音。


Interview Mode 的四個問題

判定為複雜任務時,注入這段:

💡 偵測到複雜任務({reason}),建議使用 Interview Mode

🎤 請先回答(可簡答,我會追問細節):
1. Why - 為什麼要做?想解決什麼問題?
2. What - 具體做什麼?範圍包含哪些?
3. How - 有偏好的做法或技術限制嗎?
4. Edge cases - 有需要特別處理的例外情況嗎?

💡 如果這是簡單任務,忽略此提示直接說明需求即可。

四個問題正好對應我四次踩坑的根因:

問題防止對應踩坑
Why「我以為你要 X,結果你要 Y」優化查詢(其實只想看 EXPLAIN)
What範圍誤判(1 個檔案還是 50 個)重構權限(全抽 vs 抽重複)、加欄位漏模組
How技術選擇踩到「不要碰」的雷整合表單該分階段
Edge cases邊緣情況的 bug通用

少於四個容易漏,多於四個會煩。

「可簡答」:複雜任務不該變成填表單,每題兩句話就夠——重點是「有想過」。結尾「忽略此提示」:誤判難免,給 Claude Code 一個明確台階,否則它會努力把「改個變數名」也套 Interview 流程。


效能

純函式,只看 prompt 字串、不依賴外部狀態:

操作時間
Python 啟動30-50ms
27 條 regex< 15ms
總計30-65ms / 次提示

safety_guard hook 快——後者要跑 git rev-parse 拿分支名 + 讀寫 state.json。這個 Hook 沒有狀態,所以更快。


Edge Cases

情境行為
「幫我 refactor 這段」命中複雜模式(中英混用已涵蓋)
「改個變數名稱」簡單模式優先,不觸發
「重構這個變數命名」命中「重構」→ 觸發,但有「忽略提示」台階
git status命中簡單模式,不觸發
「優化縮排」因「優化」加了 `.{0,5}(效能

誤判成本 = Claude Code 多讀一段提示,零實質影響。漏接(複雜沒觸發)的成本才高,所以保守策略偏向誤判。


替代方案比較

方案缺點
不處理持續踩方向錯誤的坑
CLAUDE.md 寫規則Claude Code 不一定遵守、複雜任務還是直接動手
Slash Command /interview要使用者主動下指令、會忘記用
regex Hook(本文)會誤判、要寫程式——但自動觸發、無感、可調
LLM 判斷複雜度準但慢又貴(每個 prompt 一次 API 呼叫)

準確性不是 Hook 的最高目標——速度才是。regex 的成本是偶爾誤判,而誤判成本接近零。實務上我組合用:Hook 自動觸發 + /interview 手動 + CLAUDE.md 通則備援。


除錯與測試

Hook 沒觸發時的檢查:.claude/settings.json 路徑是絕對路徑嗎?Python 在 PATH 嗎?注意 UserPromptSubmit 不需要 matcher

獨立測試(不用起 Claude Code):

echo '{"prompt": "git status"}' | python detect_complex_task.py        # 不該注入 message
echo '{"prompt": "重構整個權限模組"}' | python detect_complex_task.py   # 該注入 message

可以寫成 pytest 集合,把「短訊息不觸發」「重構觸發」「優化縮排不誤觸」變成回歸測試——尤其調 regex 後跑一遍確認沒影響其他 case。


漸進式演化:從 30 行開始

第一版只做兩件事——判斷長度、注入提示:

import sys, json

INTERVIEW = """💡 偵測到較複雜的任務,請先回答:
1. Why 為什麼做? 2. What 做什麼? 3. How 有限制嗎? 4. Edge cases 例外?"""

def main():
    data = json.loads(sys.stdin.read())
    prompt = data.get("prompt", "")
    if len(prompt) > 30:
        print(json.dumps({"decision": "approve", "message": INTERVIEW}))
    else:
        print(json.dumps({"decision": "approve"}))

main()

跑兩天看誤判/漏接,然後逐步加:

階段加什麼
Week 1長度 > 30 觸發
Week 2明確的簡單模式(過濾 git status 等)
Week 3明確的複雜模式(重構/優化必觸發)
Week 4「忽略此提示」台階
Week 5+依踩坑經驗加 pattern

規則是長出來的,不是設計出來的。


Pattern 校正:從 log 反推

每週看一次 log,逐筆判斷「觸發 / 沒觸發」是否正確。真實案例:

prompt: "優化這段 SQL 的縮排"
trigger: True (命中複雜模式「優化」)

「優化縮排」是格式整理,不是效能優化——誤判。修法是給「優化」加 context:

# Before: r"(優化|改善|提升)"
# After:  r"(優化|改善|提升).{0,5}(效能|performance|速度)"

只有「優化效能」「改善速度」才觸發,「優化縮排」不會。

另一個案例「批次提交剛改的檔案」也誤觸(命中「批次」),但我選擇不修——誤判成本(多讀一段提示)< 修 pattern 的維護成本。判斷要不要修,比修本身更重要


為什麼這個 Hook 比想像中重要

我原本以為 Claude Code 的「失敗」是技術錯誤——壞 SQL、空指標、邏輯錯。

用久了才發現,最大的失敗是「方向錯」:我要 A 它做 B、我以為它會問它直接做、我預期改 100 行它改了 500 行。這些不是 bug,是期望落差,根因是「沒問清楚就動手」。

Interview Mode Hook 不解決技術問題——它解決溝通問題。把「直接動手」變成「先問清楚再動手」,就避免了大部分方向錯誤。


結語

Claude Code 的速度是最大優勢,也是最大陷阱——速度太快,做錯方向時你來不及攔。這個 Hook 強制它慢下來:代價是複雜任務多花 1-2 分鐘問問題,回報是避免做錯方向的 30 分鐘重做。

如果你常用 Claude Code 處理複雜任務,從 30 行的最小版本開始,每週看 log 調 pattern。重點不是這 160 行 Python,是讓 AI 配合你的工作節奏

這個 Hook 跟攔截危險指令的 PreToolUse Hook 搭配——一個攔方向錯誤、一個攔危險操作,是我目前 Claude Code 體驗最大的兩個優化。