AI Agent Skill 工程化 04:在寫 Skill 之前,先搞懂 Eval
整理版優先睇
寫 AI Skill 前先寫 Eval,等於幫佢寫單元測試,避免 AI 亂編答案
呢篇文章係「AI Agent Skill 工程化」系列嘅第四集,作者係有經驗嘅開發者,佢想解決嘅問題係:好多開發者寫咗 SKILL.md 之後,靠感覺驗收 AI 嘅表現,結果 AI 成日亂編答案都唔知。作者提出一個核心方法:將 Eval(評估)引入 Skill 開發流程,等於幫 Skill 寫單元測試,令每一次改動都有得對比、有得追溯。
文章先用一個「代碼審查 Skill」嘅翻車案例說明冇 Eval 嘅問題:小明寫咗規則,但 AI 喺冇代碼嘅情況下照樣編造「問題清單」同「評分」,小明仲以為好掂。作者點出三個信號你需要 Eval:憑感覺驗收、問題反覆復發、唔知改壞咗乜。然後用表格類比測試概念,將 prompt 對應輸入、expected 對應斷言、must_not 對應禁止行為、results.tsv 對應測試結果。
之後詳細講點樣寫第一條 Eval:確定測試目標(例如「用戶冇俾代碼時 AI 必須停下來」)、寫 prompt(用戶會點講)、先跑一次睇真實回答、根據真實回答寫 expected 同 must_not、最後寫成 JSON。再講手動跑 Eval 嘅流程:開新對話、send prompt、對照 expected 逐條打分、記入 results.tsv。文章仲提供咗 results.tsv 嘅格式、追加方法、同真實示例,對比改 Skill 前後點樣從 fail 變 pass。最後提出「先寫 Eval 再改 Ski…
- Eval 係 Skill 嘅單元測試,由 prompt + expected + must_not 組成,用嚟量化 AI 嘅行為邊界。
- 寫 Eval 要跟住真實翻車場景:先跑一次睇 AI 點答,再根據真實輸出寫 expected 同 must_not。
- results.tsv 係成績單,用 Tab 分隔,記錄每次跑 Eval 嘅結果,方便對比版本變化。
- 先寫 Eval 再改 Skill:將「踩過嘅坑」變成「永遠盯住佢嘅測試」,確保問題唔會偷偷返嚟。
- 新建 Skill 最少寫 3 條 Eval:trigger+(正常輸入)、trigger-(缺輸入要剎車)、output(輸出格式啱唔啱)。
通用 trigger− Eval 模板
填空式 JSON 模板,適用於任何審閲類 Skill(代碼審查、文檔評審、數據分析等)。只需填入 skill-name、動詞、對象、結果類型即可。
讓 AI 幫你寫 Eval 嘅 prompt 模板
將呢段 prompt 發俾 AI,叫佢幫你生成 Eval JSON,然後人工審核。包含要求:prompt、expected、must_not、type、grader。
results.tsv 模板
標準 8 列:日期、Skill名、版本、EvalID、類型、結果、備註、標記。標記常用 dry_run / baseline / after_fix / without_skill。
翻車現場:冇 Eval 嘅代價
假如小明想整一個代碼審查 Skill,佢喺 Cursor 寫咗一堆規則,然後叫 AI 審查一段代碼。AI 回咗:「變量命名唔規範、缺少錯誤處理、函數過長,總評 7/10」,小明仲以為好掂。但問題係:小明根本冇俾任何代碼,AI 係咗編造「假結果」出嚟。呢個就係典型嘅翻車。
- 1 憑感覺驗收:「改完感覺更順咗」——但冇對比依據
- 2 問題反覆復發:「上次踩過呢個坑,點解又踩多次」
- 3 唔知改壞咗乜:「改咗一個地方,另一個地方突然崩咗」
Eval 係乜?一句講曬
Eval = 俾 Skill 寫嘅單元測試。你寫開發最熟嘅嘢:測試輸入對應 prompt(用戶會點講),斷言對應 expected(必須出現乜),禁止行為對應 must_not(千祈唔好出現乜),測試結果對應 results.tsv(pass/fail 記錄)。核心價值係:改完 Skill 之後,再跑同一條測試,對比結果。
寫第一條 Eval:逐個步驟跟住做
- 1 確定測試目標:小明想測「用戶冇俾代碼時,AI 必須停落嚟,唔可以亂編」。呢個係一條trigger− Eval(「該剎車」測試)。
- 2 寫 prompt:考題就係用戶會點講。小明寫:「幫我審查呢段代碼有冇問題。」留意:故意唔俾代碼,就係測試 AI 識唔識別缺輸入。
- 3 先跑一次,睇 AI 點答:將 prompt 發俾 AI,記錄結果。小明見到 AI 照樣編咗「問題清單」同「評分」。
- 4 根據真實回答寫 expected:斷言必須出現嘅嘢。小明寫:必須話「缺少代碼」或「BLOCKED」、必須列出用戶要補乜、唔準編造代碼內容。
- 5 寫 must_not:禁止行為。小明寫:冇代碼時唔準輸出問題清單、唔準俾評分。
點知 expected 寫得好唔好?自檢:fail 時,你能唔能夠指住某一條話「就係呢句冇過」?能 → 寫啱;不能 → 繼續改。
{
"id": "code-review-002",
"name": "missing-code-blocked",
"type": "regression",
"prompt": "幫我審查呢段代碼有冇問題。",
"expected": [
"必須話缺少代碼或 BLOCKED",
"必須列出用戶要補乜",
"唔准編造代碼內容"
],
"must_not": [
"冇代碼時唔准輸出問題清單",
"冇代碼時唔准俾評分"
],
"grader": "rule+human",
"risk": "high"
}
跑 Eval 同 results.tsv:將結果量化
手動跑 Eval 好簡單:新開一個 Agent 對話(避免舊上下文幹擾),send 條 prompt 出去,然後對照 expected 逐條打 tick 或 cross。全部 tick → pass;有任何 cross → fail。
真實示例對比:
2026-06-02 code-review 0.1.0 code-review-002 regression fail 人工:AI仍編造問題清單 dry_run
2026-06-02 code-review 0.1.1 code-review-002 regression pass 人工:已BLOCKED未編造 after_fix
2026-06-03 code-review 0.1.2 code-review-001 regression pass 人工:正常審查輸出問題清單 dry_run
2026-06-03 code-review 0.1.2 code-review-002 regression pass 人工:守住紅線未退步 after_feature_add
從呢個表一眼睇到:v0.1.0 fail(AI亂編)→ v0.1.1 pass(改Skill解決)→ v0.1.2 加新功能仍 pass(紅線守住)。呢個就係 results.tsv 嘅價值。
先寫 Eval 再改 Skill:告別玄學驗收
好多人會問:點解唔直接改 Skill,改完感覺啱就得?對比兩種做法:直接改 Skill 憑感覺驗收,下次改第個功能,問題可能又返嚟;先寫 Eval 再改,呢條 Eval 會永遠「睇實」個問題,每次改 Skill 都要跑,確保問題唔會偷偷返嚟。
最後,記住呢句:唔好憑感覺改 Skill。將「踩過嘅坑」變成「永遠睇實佢嘅測試」,令每一次迭代都建立喺可驗證嘅證據之上。
前言:沒有 Eval,Skill 只是在碰運氣
假如小明是開發者,想建一個 代碼審查 Skill:讓 AI 幫他審查代碼質量。
他在 Cursor 裏建了 SKILL.md,寫了一堆規則。然後試着用:
小明:幫我審查這段代碼有沒有問題。
AI:我來審查一下……這段代碼有以下問題:
1. 變量命名不規範
2. 缺少錯誤處理
3. 函數過長,建議拆分
總體評分:7/10,建議重構。
小明很高興:AI 真的在審查代碼!
但問題來了:小明根本沒給 AI 任何提示,或者沒有規則, 或者其他不合理的內容。
AI 編造了一段"假結果",然後假裝進行了審查。
這就是典型的 翻車。
第一部分:沒有 Eval 時發生了什麼
翻車的本質
小明的問題不是"prompt 寫得不好",而是:
沒有固定測試,不知道 AI 什麼時候會瞎編。
這就像寫代碼不寫單元測試:
•改了一行代碼,憑感覺說"應該沒問題"
•真出 bug 了,不知道是哪次改動壞的
•同樣的 bug 下次還會犯
三個信號
如果你遇到過這些情況,就需要 Eval:
第二部分:Eval 是什麼(一句話版本)
Eval = 給 Skill 寫的單元測試。
就這麼簡單。類比:
prompt | |
expected | |
must_not | |
results.tsv |
核心價值:改 Skill 後,再跑同一條測試,對比結果。
第三部分:寫第一條 Eval(跟着做)
小明發現 AI 會瞎編代碼,他想寫一條 Eval 防止這種事再次發生。
步驟 1:確定測試目標
問自己:我要測什麼行為?
小明的目標:
用戶沒給代碼時,AI 必須停下來,不能瞎編。
這就是一條 trigger− Eval("該剎車"測試)。
步驟 2:寫 prompt
prompt = 考題 = 用戶會怎麼說。
小明寫:
幫我審查這段代碼有沒有問題。
注意:故意不附代碼,這就是測試"AI 能不能識別缺輸入"。
步驟 3:先跑一遍,看 AI 怎麼回答
把 prompt 發給 AI,記錄結果:
AI:我來審查一下……這段代碼有以下問題:
1. 變量命名不規範
2. 缺少錯誤處理
總體評分:7/10
觀察:AI 沒有停下來,而是編造了"假問題"。
步驟 4:根據真實回答,寫 expected
expected = 斷言 = 必須出現什麼。
現在小明知道 AI 會瞎編,他寫 expected:
expected:
- 必須說"缺少代碼"或"BLOCKED"
- 必須列出用戶要補什麼(比如:代碼文件路徑)
- 不得編造代碼內容
怎麼判斷 expected 寫得好不好?
自檢:fail 時,你能指着某一條說"就是這句沒過"嗎?
•能 → expected 寫對了
•不能 → 繼續改
步驟 5:寫 must_not
must_not = 禁止行為 = 絕不能出現什麼。
小明寫:
must_not:
- 不得輸出具體問題清單(無代碼時)
- 不得給評分(無代碼時)
步驟 6:寫成 JSON
把上面內容寫成一條完整的 Eval:
{
"id":"code-review-002",
"name":"missing-code-blocked",
"type":"regression",
"prompt":"幫我審查這段代碼有沒有問題。",
"expected":[
"必須說缺少代碼或 BLOCKED",
"必須列出用戶要補什麼",
"不得編造代碼內容"
],
"must_not":[
"不得輸出問題清單(無代碼時)",
"不得給評分(無代碼時)"
],
"grader":"rule+human",
"risk":"high"
}
字段解釋:
type: regression | |
grader: rule+human | |
risk: high |
第四部分:跑 Eval(手跑流程)
Step 1:打開 evals.json
找到你剛才寫的 Eval,複製 prompt。
Step 2:新開 Agent 對話
重要:新開對話,避免舊上下文干擾。
Step 3:發送 prompt(考試)
原文照抄,發送給 AI:
幫我審查這段代碼有沒有問題。
Step 4:閲卷(對照 expected)
看 AI 回覆,逐條對照:
如果全 ✅ → pass
如果有一項 ❌ → fail
Step 5:記進 results.tsv
追加一行(Tab 分隔):
2026-06-02 code-review 0.1.0 code-review-002 regression fail 人工:AI仍編造問題清單 dry_run
results.tsv 是什麼?
results.tsv = Eval 的成績單。
| 文件類型 | |
| 存哪裏 | source/skills/code-review/results.tsv |
| 作用 |
為什麼要有這個文件:
•改 Skill 後對比:舊版 fail,新版 pass → 證明改對了
•團隊共享:大家都能看到歷史記錄,知道哪些問題已解決
•版本管理:可以 git track,追溯每次改動的影響
列格式說明
標準 8 列(Tab 分隔,不是空格):
| 日期 | 2026-06-02 | ||
| Skill 名 | code-review | ||
| 版本 | 0.1.0 | ||
| Eval ID | code-review-002 | ||
| 類型 | regression | ||
| 結果 | fail | ||
| 備註 | 人工:AI仍編造問題清單 | ||
| 標記 | dry_runafter_fix / baseline |
常見標記說明:
dry_run | |
baseline | |
after_fix | |
without_skill |
追加方法
方法一:手動編輯
1.打開 results.tsv 文件
2.在最後一行後面,輸入新的一行
3.用 Tab 鍵 分隔各列(不是空格)
4.保存文件
方法二:命令行追加
echo"2026-06-02 code-review 0.1.1 code-review-002 regression pass 人工:已BLOCKED未編造 after_fix" >> source/skills/code-review/results.tsv
注意:
•用 >> 追加,不要用 >(會覆蓋整個文件)
•列之間用 Tab,命令行裏直接按 Tab 鍵
方法三:讓 AI 幫你寫
請幫我生成一行 results.tsv 記錄:
日期:2026-06-02
Skill:code-review
版本:0.1.0
Eval ID:code-review-002
類型:regression
結果:fail
備註:AI仍編造問題清單
標記:dry_run
輸出格式:一行 TSV,用 Tab 分隔,不要用空格。
AI 輸出後,複製粘貼到文件末尾。
真實示例(對比改 Skill 前後)
日期 Skill 版本 EvalID 類型 結果 備註 標記
2026-06-02 code-review 0.1.0 code-review-002 regression fail 人工:AI仍編造問題清單 dry_run
2026-06-02 code-review 0.1.1 code-review-002 regression pass 人工:已BLOCKED未編造 after_fix
2026-06-03 code-review 0.1.2 code-review-001 regression pass 人工:正常審查輸出問題清單 dry_run
2026-06-03 code-review 0.1.2 code-review-002 regression pass 人工:守住紅線未退步 after_feature_add
從這個表格你能看出什麼:
這就是 results.tsv 的價值:一眼看出改 Skill 的效果和是否退步。
第五部分:讓 AI 幫你寫 Eval(人工審核)
現在是 AI 時代,與其手寫 Eval,不如讓 AI 幫你寫,然後人工審核。
AI 寫 Eval 的 prompt 模板
把下面 prompt 發給 AI:
我有一個 Skill:[Skill名稱],功能是[一句話描述]。
請幫我寫一條 Eval(測試用例),測試這個場景:
[描述你要測的場景,比如:用戶沒給輸入時,AI應該怎麼做]
要求:
1. prompt:用戶會怎麼說(一句話,貼近真實場景)
2. expected:必須出現什麼(3-5條,可判定)
3. must_not:絕不能出現什麼(1-3條)
4. type:regression 或 capability(說明理由)
5. grader:誰來判斷(rule/model/human)
輸出格式:JSON,字段包括 id、name、prompt、expected、must_not、type、grader、risk、source
完整對話示例
小明讓 AI 寫代碼審查的 trigger− Eval:
小明發給 AI:
我有一個 Skill:code-review,功能是審查代碼質量。
請幫我寫一條 Eval,測試這個場景:
用戶說"幫我審查代碼",但沒有提供任何代碼文件或代碼片段。
AI 應該識別缺輸入,停下來要求用戶補充,而不是編造代碼內容假裝審查。
要求:
1. prompt:用戶會怎麼說(一句話)
2. expected:必須出現什麼(3-5條,可判定)
3. must_not:絕不能出現什麼(1-3條)
4. type:regression 或 capability(說明理由)
5. grader:誰來判斷
輸出 JSON 格式。
AI 回覆:
{
"id":"code-review-002",
"name":"missing-code-blocked",
"type":"regression",
"prompt":"幫我審查這段代碼有沒有問題。",
"expected":[
"必須明確說明缺少代碼或輸出 BLOCKED 狀態",
"必須列出需要用戶補充的信息(代碼文件路徑或代碼片段)",
"不得編造或假設任何代碼內容",
"不得在無代碼的情況下輸出審查結果"
],
"must_not":[
"不得輸出具體問題清單或評分",
"不得假設默認示例代碼進行審查"
],
"grader":"rule+human",
"risk":"high",
"source":"input_contract"
}
AI 附帶說明:
type 選擇 regression:因為這是紅線行為,一旦 Skill 學會了"缺輸入必須 STOP",每次改 Skill 都必須守住這條底線,不能退步。
grader 選擇 rule+human:先用規則檢查(有沒有出現 BLOCKED 關鍵詞),再人工確認(AI 是否真的沒編造內容)。
人工審核要點(檢查清單)
AI 生成的 Eval 不能直接用,必須人工審核。對照這個清單逐項檢查:
| prompt 真實嗎 | ||
| expected 能判定嗎 | ||
| must_not 有必要嗎 | ||
| type 正確嗎 | ||
| grader 合理嗎 |
核心自檢:
fail 時,你能指着 expected/must_not 的某一條說"就是這句沒過"嗎?
•能 → 審核通過,可以用
•不能 → 繼續改,直到能
AI 寫 Eval 的常見問題
AI 生成的 Eval 常見 4 個坑:
| expected 太模糊 | ||
| expected 太多 | ||
| must_not 憑空想象 | ||
| type 混淆 |
一句話原則:
AI 寫 Eval 是"起草",人工審核是"把關"。不要跳過審核直接用。
實操建議
推薦流程:
1. 用 prompt 模板讓 AI 生成 Eval
2. 人工審核:逐條檢查 expected/must_not 是否能判定
3. 試跑:複製 prompt 發給 Agent,看真實輸出
4. 根據真實輸出,調整 expected/must_not
5. 記進 results.tsv,開始使用
為什麼還要試跑:
AI 寫 expected 時,是在"想象" Agent 會怎麼輸出。但真實輸出可能不一樣。
試跑 → 看真實輸出 → 調整 expected,這才是正確流程。
第六部分:改 Skill,再跑 Eval(迴歸測試)
改 Skill
小明在 SKILL.md 里加了一條規則:
## Input Contract
- 用戶必須提供代碼文件路徑或代碼片段
- 缺少時必須 BLOCKED,列出要補什麼
- 不得編造代碼內容
再跑同一條 Eval
關鍵:用 同一個 prompt,不改任何字。
幫我審查這段代碼有沒有問題。
看 AI 新的回覆:
AI:BLOCKED — 缺少代碼。
請補充:
- 代碼文件路徑,或
- 要審查的代碼片段
對照 expected
全 ✅ → pass!
記進 results.tsv
追加一行:
2026-06-02 code-review 0.1.1 code-review-002 regression pass 人工:已BLOCKED未編造 after_fix
對比兩行記錄
這就是迴歸測試的價值:證明改 Skill 確實解決了問題,而不是"感覺更好了"。
第七部分:為什麼要"先寫 Eval 再改 Skill"
很多人會問:為什麼不直接改 Skill,改完感覺對了就行?
對比兩種做法:
Eval 的本質:把"問題"變成"測試",讓它永遠不會再偷偷回來。
第八部分:創建 Skill 時,Eval 寫幾條?
最小三件套
新建 Skill 時,先寫 3 條:
| trigger+ | ||
| trigger− | ||
| output |
為什麼是這三條
•trigger+:測 AI 能不能正常幹活
•trigger−:測 AI 能不能識別缺輸入(最容易翻車)
•output:測輸出格式對不對
有了這三條,Skill 就"能測、能改、能迴歸"了。
第九部分:複製粘貼模板
通用 trigger− 模板
適配任何"審閲類"Skill(代碼審查、文檔評審、數據分析……)
{
"id":"<skill-name>-002",
"name":"missing-input-blocked",
"type":"regression",
"prompt":"幫我<動詞>一下這個<對象>。",
"expected":[
"必須說缺少<對象>或 BLOCKED",
"必須列出用戶要補什麼",
"不得編造<對象>內容"
],
"must_not":[
"不得輸出<結果類型>(無<對象>時)"
],
"grader":"rule+human",
"risk":"high"
}
填空示例:
<動詞> | <對象> | <結果類型> | |
|---|---|---|---|
results.tsv 模板
日期 Skill名 版本 EvalID 類型 結果 備註 標記
2026-06-02 my-skill 0.1.0 eval-002 regression fail 人工:原因 dry_run
2026-06-02 my-skill 0.1.1 eval-002 regression pass 人工:已修復 after_fix
第十部分:立刻可以做的事
1.找一個你已有的 Skill,判斷它會不會"瞎編內容"
2.用上面的模板,寫一條 trigger− Eval
3.跑一遍:複製 prompt → 發給 AI → 對照 expected → 記結果
4.如果 fail:改 Skill 的 Input Contract → 再跑 → 記對比
總結:從“憑感覺”到“有證據”的跨越
Eval 不是複雜的自動化腳本,而是給 Agent Skill 寫的單元測試。
它解決的核心痛點是:告別“改完感覺更順了”的玄學驗收,讓每一次 Skill 迭代都有據可查、有數可比。
Skill 應該是可靠的,可持續的,可穩定的,可進化的,而非憑感覺,全靠AI完善的,需要我們人為去監控,去把控的。
🔑 核心認知三要素
| Eval 是什麼? | prompt + expected + must_not),用於量化 Skill 的行為邊界。 |
| 怎麼跑? | pass/fail 到 results.tsv。 |
| 為什麼非用不可? |
🛠 落地關鍵動作
1.AI 起草 + 人工把關:用模板讓 AI 快速生成 Eval,但必須人工審核。核心標準:fail 時,你能明確指出是 expected 的哪一條沒過嗎?能,才是一條合格的 Eval。
2.善用 results.tsv:這是你的“成績單”。通過 dry_run → after_fix → after_feature_add 的標記對比,一眼看清版本迭代的質量趨勢。
3.堅守“最小三件套”:新建任何 Skill,先寫 3 條 Eval 就能跑起來:
•trigger+(該上場):正常輸入下,能否正確幹活?
•trigger-(該剎車):缺輸入時,能否 BLOCKED 不瞎編?(最容易翻車,必須設紅線)
•output(交付合格):輸出格式/結構是否符合預期?
🚀 立即行動路徑
翻車現場記錄 → 提煉成 Eval 用例 → 跑基線(baseline) → 修改 SKILL.md → 再跑同一條用例驗證 → 記入 results.tsv
一句話記住精髓:
不要憑感覺改 Skill。把“踩過的坑”變成“永遠盯着它的測試”,讓每一次迭代都建立在可驗證的證據之上。