同一顆模型,為什麼有人用起來又穩又可靠,有人卻一直失憶、亂跑、燒錢?差別常常在「Harness」——包在模型外面的執行框架,像廚房的水電,平常看不見,卻決定了整間店跑不跑得動。它不讓 AI 更聰明,而是讓 AI 值得信任。
flowchart TB
L5["🧠 Brain / 模型 —— 負責推理(Claude · GPT · 本地)"]
L4["🤖 Agent —— 真正在做事的(Claude Code · aider)"]
L3["🔄 Workflow —— 編排與品質驗證"]
L2["🔧 Harness —— 不失憶 · 自動帶規則 · 接管工具層"]
L1["📋 SPEC —— 把需求變成可驗證的規則"]
L5 --- L4
L4 --- L3
L3 --- L2
L2 --- L1
style L5 fill:#f3e8ff,stroke:#9333ea,stroke-width:2px,color:#581c87
style L4 fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d
style L3 fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#17335e
style L2 fill:#0ea5e9,stroke:#0369a1,stroke-width:3px,color:#04121f
style L1 fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f
Harness 夾在「Agent 要用的工具」和「Workflow 的編排」之間。上面幾層決定「要做什麼」,Harness 管的是「做的時候不出事」。
flowchart TB H1["① 對話開始 · SessionStart
自動載入規則 + 過去踩過的坑"]:::proc BRAIN{{"🧠 模型
帶著規則做決定"}}:::model DECIDE["Agent 決定要用工具
例如 npm test"]:::proc PRE{"② PreToolUse
這指令要改寫或擋下嗎?"} REWRITE["橋接腳本改寫
npm test → 只留 FAIL 那幾行"]:::proc BLOCK(["危險動作 → 直接擋下"]):::result TOOL["③ 實際執行工具"]:::infra POST["④ PostToolUse
記錄教訓 + 記帳 + 稽核"]:::proc H1 --> BRAIN --> DECIDE --> PRE PRE -->|一般指令 · 改寫瘦身| REWRITE --> TOOL PRE -.->|太危險 · 不放行| BLOCK TOOL --> POST --> BRAIN class PRE decision classDef model fill:#f3e8ff,stroke:#9333ea,stroke-width:2px,color:#581c87 classDef proc fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#17335e classDef decision fill:#ede9fe,stroke:#7c3aed,stroke-width:2px,color:#4c1d95 classDef result fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d classDef infra fill:#e5e9f0,stroke:#475569,stroke-width:2px,color:#1e293b
關鍵在中間那道 PreToolUse:模型想跑什麼,先被攔下來。該瘦身的瘦身(例如一個測試吐一萬行,只留失敗那幾行,省 token 又不洗版),該擋的擋。原則:只動會洗版的噪音指令,一般讀寫別亂改,免得把 AI 真正需要的資料也砍掉。
剛剛只放大看 PreToolUse 一個。實際上一個通用 Agent 框架,在整條生命週期處處都留了 hook 點——Harness 就是靠掛在這些點上接管一切。照 agent 從開機到關機的順序看:
flowchart TB A(["▶ Session 開始"]):::result --> SS["SessionStart
載入規則 · 記憶 · 過去的坑"]:::hook SS --> UPS["UserPromptSubmit
你每送一則訊息 · 處理前"]:::hook UPS --> TH{{"模型思考
決定要不要用工具"}}:::model TH --> PRE["PreToolUse
工具執行前"]:::hook PRE --> TL["工具執行"]:::infra TL --> POST["PostToolUse
工具執行後"]:::hook POST -->|還要用更多工具| TH TH -->|這一輪回完| STOP["Stop · SubagentStop
主 / 子 agent 回完"]:::hook TH -.要權限或通知.-> NOTI["Notification"]:::hook STOP -->|下一則訊息 · 再一輪| UPS STOP --> PC["PreCompact
上下文壓縮前 · 存檔"]:::hook PC --> SE["SessionEnd
收尾 · 記帳 · 稽核"]:::hook SE --> EN(["■ Session 結束"]):::result classDef hook fill:#ccfbf1,stroke:#0d9488,stroke-width:2px,color:#134e4a classDef model fill:#f3e8ff,stroke:#9333ea,stroke-width:2px,color:#581c87 classDef infra fill:#e5e9f0,stroke:#475569,stroke-width:2px,color:#1e293b classDef result fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d
| Hook | 何時觸發 | Harness 常做的事 |
|---|---|---|
SessionStart | 開一個新對話 / session | 載入規則、記憶、過去踩過的坑 |
UserPromptSubmit | 你每送出一則訊息(處理前) | 補上下文、擋不該問的、加提醒 |
PreToolUse | 每次工具執行前 | 改寫吵雜指令、擋危險動作、記成本 |
PostToolUse | 每次工具執行後 | 記錄教訓、檢查結果、格式化輸出 |
Notification | agent 要通知你時(要權限 / 要你注意) | 轉發到手機 / Slack、決定要不要打擾你 |
Stop | 主 agent 回完一輪 | 驗收、稽核有沒有守規則、決定要不要繼續 |
SubagentStop | 子 agent 回完 | 收子代理結果、彙整 |
PreCompact | 上下文要壓縮前 | 把進度存檔,免得壓縮後失憶 |
SessionEnd | session 結束 | 記帳、稽核、把學到的寫回去 |
不同 Agent 框架的 hook 名稱會略有出入,但邏輯一樣:開場 → 每輪對話 → 每次工具 → 通知 → 收尾,每個轉折都留一個掛勾。你掛得越準,Harness 就越能在對的時機接管,而不用改動 Agent 或模型本身。