Day 9 我們設計了第一版 test case 格式,並建立了前 5 筆測試案例。
一筆最小 test case 目前包含:
id
input
expected
grading_method
task_type
這個格式讓我們可以清楚描述:
但 5 筆案例還太少,還不適合拿來比較 Agent 表現。
所以 Day 10 要把 evals/cases.json 擴充成第一組比較完整的 Eval Dataset。
今天的重點不是寫 runner,也不是寫 evaluator,而是先把測試資料準備好。
會完成:
evals/cases.json。cases.json 是否為合法 JSON。今天先不做:
這些從 Day 11 開始處理。
Agent Evaluation 的品質,很大一部分取決於 eval dataset 的品質。
如果測試資料太少,結果就不穩。
如果測試資料太簡單,所有版本都會通過,看不出改善效果。
如果測試資料太模糊,Agent 失敗時也很難判斷是 Agent 的問題,還是題目本身設計不好。
所以在寫 batch runner 之前,先把 dataset 設計清楚是值得的。
今天先建立一組小型但有代表性的資料集。
它不需要像正式 benchmark 那麼大,但至少要能覆蓋幾種常見任務:
今天只會修改 Day 9 建立的 evals/cases.json。
agent-testing-platform/
evals/
__init__.py
cases.json
修改:
| 檔案 | 修改內容 |
|---|---|
evals/cases.json |
將 5 筆 test cases 擴充成 15 筆 |
今天不新增 Python 檔案。
因為 Day 10 的重點是資料集本身,不是程式邏輯。
今天先把任務分成四類。
| task_type | 說明 | 主要測什麼 |
|---|---|---|
calculation |
計算任務 | Agent 是否會使用 calculator 並得到正確結果 |
keyword_qa |
關鍵字問答 | 回答是否包含必要資訊 |
json_output |
JSON 格式輸出 | 輸出是否符合結構化格式 |
general_qa |
一般問答 | 是否能回答基本概念 |
其中 calculation 和 json_output 會特別重要。
因為這兩類任務可以幫助我們觀察後面幾個問題:
修改 evals/cases.json:
[
{
"id": "case_001",
"input": "請計算 135 * 28",
"expected": "3780",
"grading_method": "contains",
"task_type": "calculation"
},
{
"id": "case_002",
"input": "請計算 72 + 19",
"expected": "91",
"grading_method": "contains",
"task_type": "calculation"
},
{
"id": "case_003",
"input": "請計算 500 - 123",
"expected": "377",
"grading_method": "contains",
"task_type": "calculation"
},
{
"id": "case_004",
"input": "請計算 144 / 12",
"expected": "12",
"grading_method": "contains",
"task_type": "calculation"
},
{
"id": "case_005",
"input": "請回答台灣的首都是哪裡",
"expected": "台北",
"grading_method": "contains",
"task_type": "keyword_qa"
},
{
"id": "case_006",
"input": "請回答 HTTP 狀態碼 404 通常代表什麼",
"expected": "找不到",
"grading_method": "contains",
"task_type": "keyword_qa"
},
{
"id": "case_007",
"input": "請回答 Python 中 list 是可變還是不可變資料型別",
"expected": "可變",
"grading_method": "contains",
"task_type": "keyword_qa"
},
{
"id": "case_008",
"input": "請用一句話說明什麼是 AI Agent",
"expected": "任務",
"grading_method": "contains",
"task_type": "general_qa"
},
{
"id": "case_009",
"input": "請用一句話說明什麼是 Trace",
"expected": "紀錄",
"grading_method": "contains",
"task_type": "general_qa"
},
{
"id": "case_010",
"input": "請用一句話說明為什麼 Agent 需要測試",
"expected": "可靠",
"grading_method": "contains",
"task_type": "general_qa"
},
{
"id": "case_011",
"input": "請只回覆 OK",
"expected": "OK",
"grading_method": "exact_match",
"task_type": "instruction_following"
},
{
"id": "case_012",
"input": "請只回覆 PASS",
"expected": "PASS",
"grading_method": "exact_match",
"task_type": "instruction_following"
},
{
"id": "case_013",
"input": "請用 JSON 格式回傳 10 + 5 的答案,欄位名稱使用 answer",
"expected": {
"answer": 15
},
"grading_method": "json_exact",
"task_type": "json_output"
},
{
"id": "case_014",
"input": "請用 JSON 格式回傳 8 * 7 的答案,欄位名稱使用 answer",
"expected": {
"answer": 56
},
"grading_method": "json_exact",
"task_type": "json_output"
},
{
"id": "case_015",
"input": "請用 JSON 格式回傳狀態 success,欄位名稱使用 status",
"expected": {
"status": "success"
},
"grading_method": "json_exact",
"task_type": "json_output"
}
]
這次從 5 筆擴充到 15 筆。
數量仍然不多,但已經足夠讓 Day 11 的 Batch Evaluation Runner 跑出第一份有意義的結果。
你可能會想,既然要做 Evaluation,為什麼不直接建立 100 題?
原因是目前還在 MVP 階段。
如果一開始就建立太多 test cases,會遇到幾個問題:
所以 Day 10 先用 15 筆。
這個數量剛好可以:
等平台架構穩定後,再增加更多測試案例。
今天的 dataset 分布如下:
| task_type | 數量 |
|---|---|
calculation |
4 |
keyword_qa |
3 |
general_qa |
3 |
instruction_following |
2 |
json_output |
3 |
這樣設計是為了讓 dataset 不只測一種能力。
例如,如果全部都是 calculation,成功率只能代表 Agent 在計算題上的表現。
但我們希望它也能測:
這樣後面做 task type accuracy 時才有意義。
今天使用三種 grading method。
| grading_method | 數量 | 用途 |
|---|---|---|
contains |
10 | 檢查回答是否包含預期文字 |
exact_match |
2 | 檢查回答是否完全等於預期字串 |
json_exact |
3 | 檢查 JSON 輸出是否符合預期 |
其中 contains 是目前最容易先實作的方式。
例如 Agent 回答:
The result is 3780
只要裡面包含:
3780
就可以先判定通過。
exact_match 比較嚴格。
例如題目要求:
請只回覆 OK
那回答必須剛好是:
OK
如果 Agent 回答:
OK!
或:
好的,OK
都應該視為失敗。
json_exact 會留到 Day 13 再完整處理。
目前先把這類案例放進 dataset,是為了讓後面能觀察格式錯誤。
這份 dataset 不是為了讓目前的 Agent 全部通過。
例如目前的 FakeLLMClient 只會對包含「計算」的任務產生 tool call。
所以這類任務比較可能得到合理結果:
請計算 135 * 28
但像這種任務:
請只回覆 OK
目前 fake client 可能會回:
Fake response for: 請只回覆 OK
這對 exact_match 來說會失敗。
另外 JSON output 任務也可能失敗。
例如:
請用 JSON 格式回傳 10 + 5 的答案,欄位名稱使用 answer
目前 Agent 可能回:
The result is 15
但這不是合法 JSON:
{
"answer": 15
}
這些失敗案例是刻意保留的。
如果 dataset 裡所有案例目前都會成功,後面就很難展示:
Evaluation dataset 應該同時包含容易成功和容易失敗的情境。
今天還不實作正式 dataset loader,但可以先用簡短的 Python one-liner 檢查每筆案例是否都有必要欄位。
在專案根目錄執行:
python3 -c "import json; cases=json.load(open('evals/cases.json', encoding='utf-8')); required={'id','input','expected','grading_method','task_type'}; print([case.get('id','<missing id>') for case in cases if not required.issubset(case)])"
如果輸出是:
[]
代表每筆案例都有必要欄位。
這個指令只是臨時檢查用。
Day 11 或 Day 12 之後,我們會把這類檢查整理成更正式的 Python function。
和 Day 9 一樣,修改完 evals/cases.json 後,應該確認它是合法 JSON。
在專案根目錄執行:
python3 -m json.tool evals/cases.json
如果 JSON 格式正確,終端機會印出排版後的 JSON。
如果格式錯誤,例如少了逗號,會看到類似:
Expecting ',' delimiter: line 12 column 3 (char 240)
這一步很重要,因為後面的 runner 會直接讀取 cases.json。
如果 JSON 本身壞掉,Evaluation 還沒開始就會失敗。
今天完成後,專案中的 evals/cases.json 已經從 5 筆擴充到 15 筆。
目前 dataset 包含:
目前還沒有:
但 dataset 已經可以作為 Day 11 的輸入。
今天重點是建立第一組小型 Eval Dataset。
一組好的 eval dataset 不只是問題集合,而是要能支援後續評測:
input。expected。grading_method。task_type。這樣後面才有辦法比較:
Day 11 會實作 Batch Evaluation Runner。
下一篇會新增 Python 程式來讀取 evals/cases.json,並讓 Agent 逐筆執行測試案例。
Day 11 還不會做完整評分,重點會放在:
也就是先讓整個 Evaluation 流程跑起來,再於 Day 12 加入自動評分。