iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0

前言

Day 9 我們設計了第一版 test case 格式,並建立了前 5 筆測試案例。

一筆最小 test case 目前包含:

id
input
expected
grading_method
task_type

這個格式讓我們可以清楚描述:

  • 要交給 Agent 的任務是什麼。
  • 預期答案或條件是什麼。
  • 要用哪種方式評分。
  • 這題屬於哪一類任務。

但 5 筆案例還太少,還不適合拿來比較 Agent 表現。

所以 Day 10 要把 evals/cases.json 擴充成第一組比較完整的 Eval Dataset。

今天的重點不是寫 runner,也不是寫 evaluator,而是先把測試資料準備好。


今天要完成什麼?

會完成:

  1. 擴充 evals/cases.json。
  2. 加入 exact answer 任務。
  3. 加入 keyword 任務。
  4. 加入 calculator 任務。
  5. 加入 JSON output 任務。
  6. 確認每題都有預期結果。
  7. 檢查 cases.json 是否為合法 JSON。

今天先不做:

  • Batch Evaluation Runner。
  • 自動評分器。
  • 評測結果儲存。
  • 成功率統計。
  • Dashboard。

這些從 Day 11 開始處理。


為什麼要先重視 Dataset?

Agent Evaluation 的品質,很大一部分取決於 eval dataset 的品質。

如果測試資料太少,結果就不穩。

如果測試資料太簡單,所有版本都會通過,看不出改善效果。

如果測試資料太模糊,Agent 失敗時也很難判斷是 Agent 的問題,還是題目本身設計不好。

所以在寫 batch runner 之前,先把 dataset 設計清楚是值得的。

今天先建立一組小型但有代表性的資料集。

它不需要像正式 benchmark 那麼大,但至少要能覆蓋幾種常見任務:

  • 明確答案。
  • 關鍵字回答。
  • 計算任務。
  • 工具使用。
  • JSON 格式輸出。

今天的專案結構

今天只會修改 Day 9 建立的 evals/cases.json。

agent-testing-platform/
  evals/
    __init__.py
    cases.json

修改:

檔案 修改內容
evals/cases.json 將 5 筆 test cases 擴充成 15 筆

今天不新增 Python 檔案。

因為 Day 10 的重點是資料集本身,不是程式邏輯。


Dataset 要包含哪些任務?

今天先把任務分成四類。

task_type 說明 主要測什麼
calculation 計算任務 Agent 是否會使用 calculator 並得到正確結果
keyword_qa 關鍵字問答 回答是否包含必要資訊
json_output JSON 格式輸出 輸出是否符合結構化格式
general_qa 一般問答 是否能回答基本概念

其中 calculation 和 json_output 會特別重要。

因為這兩類任務可以幫助我們觀察後面幾個問題:

  • Agent 會不會正確使用工具?
  • Agent 能不能穩定輸出固定格式?
  • 格式錯誤和答案錯誤要如何區分?

修改 cases.json

修改 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,會遇到幾個問題:

  • 題目品質不容易控管。
  • 錯誤會變多,但還沒有工具分析原因。
  • 每次修改 dataset 都很花時間。
  • 文章會變成資料整理,而不是系統設計。

所以 Day 10 先用 15 筆。

這個數量剛好可以:

  • 涵蓋不同任務類型。
  • 手動檢查每題是否合理。
  • 讓 Day 11 runner 可以快速跑完。
  • 讓 Day 12 evaluator 可以先處理基本評分。

等平台架構穩定後,再增加更多測試案例。


任務類型分布

今天的 dataset 分布如下:

task_type 數量
calculation 4
keyword_qa 3
general_qa 3
instruction_following 2
json_output 3

這樣設計是為了讓 dataset 不只測一種能力。

例如,如果全部都是 calculation,成功率只能代表 Agent 在計算題上的表現。

但我們希望它也能測:

  • 是否能遵守簡短指令。
  • 是否能包含必要關鍵字。
  • 是否能輸出 JSON。
  • 是否能使用 calculator。

這樣後面做 task type accuracy 時才有意義。


grading_method 分布

今天使用三種 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 裡所有案例目前都會成功,後面就很難展示:

  • format accuracy。
  • failure type。
  • retry。
  • schema validation。
  • guardrails。

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。


檢查 JSON 格式

和 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 包含:

  • 4 筆 calculation。
  • 3 筆 keyword_qa。
  • 3 筆 general_qa。
  • 2 筆 instruction_following。
  • 3 筆 json_output。

目前還沒有:

  • dataset loader。
  • Batch Evaluation Runner。
  • evaluator。
  • pass / fail 統計。
  • evaluation dashboard。

但 dataset 已經可以作為 Day 11 的輸入。


今天的重點整理

今天重點是建立第一組小型 Eval Dataset。

一組好的 eval dataset 不只是問題集合,而是要能支援後續評測:

  • 每題都有明確 input。
  • 每題都有 expected。
  • 每題都有 grading_method。
  • 每題都有 task_type。
  • 題目類型要有基本分布。
  • 要保留一些目前可能失敗的案例。

這樣後面才有辦法比較:

  • 哪些 task type 表現比較差。
  • 哪些 grading method 最常失敗。
  • 改 prompt 後成功率是否提升。
  • 加 retry 或 schema validation 是否有幫助。

下一步

Day 11 會實作 Batch Evaluation Runner。

下一篇會新增 Python 程式來讀取 evals/cases.json,並讓 Agent 逐筆執行測試案例。

Day 11 還不會做完整評分,重點會放在:

  • 讀取 eval dataset。
  • 逐筆呼叫 Agent。
  • 儲存 Agent output。
  • 綁定 trace session id。
  • 輸出簡單測試結果表。

也就是先讓整個 Evaluation 流程跑起來,再於 Day 12 加入自動評分。


上一篇
Day 9|設計 Test Case 格式
下一篇
Day 11|實作 Batch Evaluation Runner
系列文
從黑盒到可驗證:30 天打造 AI Agent 的 Trace、Eval 與 Guardrails 系統 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言