iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0

前言

Day 8 我們先定義了 Agent Evaluation 的概念。

Evaluation 的主要想法是:

不要只手動測幾題,而是用固定測試集重複測量 Agent 表現。

但在真的開始批次測試之前,我們需要先回答一個很基本的問題:

一筆測試案例應該長什麼樣子?

如果 test case 格式沒有先設計好,後面會很容易混亂。

例如有些任務是精確答案,有些任務只要包含關鍵字,有些任務要求 JSON 格式,有些任務需要使用工具。這些任務的評分方式不一樣,所以不能只存一個 input。

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


今天要完成什麼?

會完成:

  1. 設計 test case JSON 格式。
  2. 定義 id。
  3. 定義 input。
  4. 定義 expected。
  5. 定義 grading_method。
  6. 定義 task_type。
  7. 建立前 5 筆測試案例。

今天先不做:

  • Batch Evaluation Runner。
  • 自動評分器。
  • 評測結果儲存。
  • Dashboard。

今天只先把測試資料格式定下來。Day 10 會再把 5 筆案例擴充成第一組完整 eval dataset。


為什麼 Test Case 格式很重要?

Evaluation 不是只有「把問題丟給 Agent」而已。

如果我們只準備這樣的資料:

{
  "input": "請計算 135 * 28"
}

那系統其實不知道該怎麼判斷 Agent 是否成功。

它不知道:

  • 預期答案是什麼?
  • 要用完全相同判斷,還是只要包含某個關鍵字?
  • 這題是哪種類型?
  • 這題是否需要工具?
  • 這題是否要求固定輸出格式?

所以一筆好的 test case 至少要包含「任務」和「驗證方式」。

也就是:

input:要 Agent 做什麼
expected:期待看到什麼結果
grading_method:要怎麼判斷是否通過
task_type:這是哪一類任務

有了這些欄位,後面才能實作 evaluator。


今天的專案結構

今天新增 evals/ 資料夾,用來放 evaluation 相關檔案。

agent-testing-platform/
  app.py
  trace_viewer_app.py
  agents/
    __init__.py
    simple_agent.py
    prompts.py
    fake_llm.py
  tools/
    __init__.py
    calculator.py
  tracing/
    __init__.py
    models.py
  storage/
    __init__.py
    database.py
    schema.sql
  ui/
    __init__.py
    trace_viewer.py
  evals/
    __init__.py
    cases.json
  data/
    agent_traces.db

新增:

檔案 用途
evals/__init__.py 讓 evals 成為 Python package
evals/cases.json 存放 eval test cases

設計第一版 Test Case Schema

今天先採用這個最小格式:

{
  "id": "case_001",
  "input": "請計算 135 * 28",
  "expected": "3780",
  "grading_method": "contains",
  "task_type": "calculation"
}

這個 schema 不複雜,但已經足夠支援第二週的基本評測。

欄位說明如下。

欄位 說明
id 測試案例的唯一編號
input 要交給 Agent 的任務
expected 預期答案或預期條件
grading_method 評分方式
task_type 任務類型

接著逐一說明這些欄位。


欄位一:id

id 是每一筆 test case 的唯一編號。

例如:

{
  "id": "case_001"
}

它的用途是讓後面的 evaluation result 可以對應回原本的測試案例。

未來一筆結果可能會長這樣:

{
  "case_id": "case_001",
  "passed": true,
  "actual": "The result is 3780"
}

如果沒有 id,當測試案例變多時,就很難追蹤是哪一題失敗。


欄位二:input

input 是要交給 Agent 的任務。

例如:

{
  "input": "請計算 135 * 28"
}

它會在後面的 Batch Evaluation Runner 中被傳進:

agent.run(test_case["input"])

也就是說,input 就是使用者平常會輸入給 Agent 的內容。

設計 input 時要注意一件事:任務描述要足夠明確。

例如這樣比較好:

請計算 135 * 28

這樣比較不穩:

算一下這個

Evaluation 的目標是測 Agent,不是測 test case 本身是否寫得模糊。所以早期測試案例應該盡量清楚。


欄位三:expected

expected 是預期答案或預期條件。

例如計算題:

{
  "expected": "3780"
}

如果是關鍵字問答:

{
  "expected": "台北"
}

如果是 JSON 格式任務,expected 也可以先用物件表示:

{
  "expected": {
    "answer": 15
  }
}

這表示 expected 不一定只能是字串。

不同任務可能需要不同型態:

  • 字串
  • 數字
  • JSON object
  • 關鍵字列表

第二週先保持簡單,等 Day 13 做 JSON 格式驗證時,再處理比較完整的 structured output。


欄位四:grading_method

grading_method 用來描述這題要怎麼評分。

今天先定義三種:

grading_method 說明 適合情境
exact_match 實際輸出必須和預期答案完全相同 很短且固定的答案
contains 實際輸出只要包含預期文字即可 Agent 回答可能有補充說明
json_exact 實際輸出必須是 JSON,且欄位值符合預期 structured output 任務

例如計算任務目前比較適合用 contains:

{
  "input": "請計算 135 * 28",
  "expected": "3780",
  "grading_method": "contains"
}

因為目前 Agent 回答會是:

The result is 3780

如果使用 exact_match,就會失敗,因為它不等於單純的:

3780

所以 test case 的評分方式要和 Agent 目前的輸出型態搭配。


欄位五:task_type

task_type 用來標記任務類型。

例如:

{
  "task_type": "calculation"
}

它不一定會影響單題評分,但對後續統計很重要。

未來我們會想看:

calculation 任務成功率是多少?
keyword_qa 任務成功率是多少?
json_output 任務成功率是多少?
tool_use 任務成功率是多少?

如果沒有 task_type,就只能看整體成功率,看不出 Agent 哪一類任務最弱。

第二週先定義幾種簡單類型:

task_type 說明
calculation 計算任務
keyword_qa 關鍵字問答
json_output JSON 格式輸出
general_qa 一般問答
tool_use 需要使用工具的任務

建立 evals package

新增 evals/__init__.py:

這個檔案今天可以先留空,不需要放任何程式碼。

它的作用是讓 Python 把 evals 資料夾視為 package。雖然今天還不會匯入它,但 Day 11 實作 Batch Evaluation Runner 時會用到。


建立前 5 筆 Test Cases

新增 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": "請回答台灣的首都是哪裡",
    "expected": "台北",
    "grading_method": "contains",
    "task_type": "keyword_qa"
  },
  {
    "id": "case_004",
    "input": "請用 JSON 格式回傳 10 + 5 的答案,欄位名稱使用 answer",
    "expected": {
      "answer": 15
    },
    "grading_method": "json_exact",
    "task_type": "json_output"
  },
  {
    "id": "case_005",
    "input": "請用一句話說明什麼是 AI Agent",
    "expected": "任務",
    "grading_method": "contains",
    "task_type": "general_qa"
  }
]

這 5 筆案例先涵蓋幾種不同任務。

id 類型 目的
case_001 calculation 測試乘法計算
case_002 calculation 測試加法計算
case_003 keyword_qa 測試答案是否包含關鍵字
case_004 json_output 測試 structured output
case_005 general_qa 測試一般概念回答

其中 case_004 目前很可能不會通過。

原因是現在的 SimpleAgent 還沒有能力穩定輸出 JSON:

The result is 15

不一定會變成:

{
  "answer": 15
}

這是正常的。

Evaluation dataset 不只需要成功案例,也應該包含可能失敗的案例。這樣後面做 format accuracy、schema validation 和 Guardrails 時,才有東西可以比較。


檢查 cases.json 是否為合法 JSON

今天還沒有寫 Python 程式,但可以先確認 evals/cases.json 的格式正確。

如果你的電腦有 python3,可以在專案根目錄執行:

python3 -m json.tool evals/cases.json

如果 JSON 格式正確,終端機會印出排版後的 JSON。

如果格式錯誤,例如少了一個逗號或括號,會看到類似:

Expecting ',' delimiter: line 10 column 3 (char 180)

這個檢查很重要。

因為 Day 11 的 Batch Evaluation Runner 會讀取 evals/cases.json。如果 JSON 檔案本身格式錯誤,runner 還沒開始測 Agent 就會失敗。


今天先不建立 Python TestCase class

你可能會想,既然已經有 test case schema,是不是應該直接建立 Python class?

例如:

@dataclass
class TestCase:
    id: str
    input: str
    expected: str
    grading_method: str
    task_type: str

這件事可以做,但今天先不做。

原因是 Day 9 的重點是資料格式,而不是程式載入流程。

如果今天同時做:

  • JSON schema
  • TestCase class
  • dataset loader
  • evaluator

範圍會太大,也會和 Day 10、Day 11 重疊。

所以今天先把 cases.json 設計好,後面再逐步把它讀進 Python。


Test Case 設計原則

整理一下今天的設計原則。

1. 每筆案例都要有明確預期

不建議寫這種案例:

{
  "input": "請介紹 AI"
}

因為它沒有 expected,後面無法自動判斷通過或失敗。

比較好的寫法是:

{
  "input": "請用一句話說明什麼是 AI Agent",
  "expected": "任務",
  "grading_method": "contains"
}

雖然這不是完美評分,但至少可以先檢查回答是否包含主要概念。

2. 評分方式要和輸出型態搭配

如果 Agent 會回答:

The result is 3780

就不適合用:

{
  "grading_method": "exact_match",
  "expected": "3780"
}

因為字串不會完全相同。

這時可以先用:

{
  "grading_method": "contains",
  "expected": "3780"
}

3. 要刻意保留一些會失敗的案例

Evaluation 不是要讓所有題目都通過。

如果測試集太簡單,所有版本都 100%,就很難看出改善效果。

例如 case_004 的 JSON output 任務,就是為了後面觀察 format error。

4. task_type 要能支援後續統計

task_type 不只是分類好看而已。

後面 Reliability Dashboard 會需要它來統計不同任務類型的成功率。

所以一開始就加上 task_type,可以避免之後補資料。


今天完成後的系統狀態

今天完成後,專案新增了:

  • evals/__init__.py
  • evals/cases.json

cases.json 目前有 5 筆 test cases,包含:

  • calculation
  • keyword_qa
  • json_output
  • general_qa

目前還沒有:

  • 讀取 dataset 的 Python function。
  • Batch Evaluation Runner。
  • 自動評分器。
  • Evaluation result storage。
  • Evaluation Dashboard。

今天的重點整理

今天重點是設計 test case 格式。

一筆最小 test case 包含:

id
input
expected
grading_method
task_type

其中:

  • input 是要交給 Agent 的任務。
  • expected 是預期答案或條件。
  • grading_method 決定如何評分。
  • task_type 用來支援後續統計。

有了這個格式,第二週後面的工作才有基礎。


下一步

Day 10 會把今天的 5 筆案例擴充成第一組 Eval Dataset。

下一篇會新增更多測試案例,讓 dataset 覆蓋:

  • exact answer 任務。
  • keyword 任務。
  • calculator 任務。
  • JSON output 任務。

Day 10 還不會實作 batch runner,重點會放在讓測試資料更完整,並確認每一題都有合理的預期結果。


上一篇
Day 8|什麼是 Agent Evaluation?
下一篇
Day 10|建立第一組 Eval Dataset
系列文
從黑盒到可驗證:30 天打造 AI Agent 的 Trace、Eval 與 Guardrails 系統 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言