第二週我們開始處理這個系列的第二個主題:Eval。
第一週的 Trace 讓我們可以回答:
Agent 執行過程中發生了什麼?
第二週的 Eval 則開始回答:
Agent 到底有沒有完成任務?
到 Day 13 為止,目前平台已經可以:
exact_match 評分。contains 評分。json_exact 檢查 JSON 格式與欄位值。今天是第二週回顧。
我們會整理目前的 eval dataset、batch runner 結果,並產出第一份 baseline report。
Day 14 是回顧與分析篇,不新增大型功能。
會完成:
今天先不做:
今天的重點是:
先看懂目前的 baseline,而不是急著修掉所有失敗。
第二週主要完成 Evaluation 的最小流程。
| 天數 | 主題 | 完成內容 |
|---|---|---|
| Day 8 | Agent Evaluation 概念 | 定義 eval dataset、test case、success rate、format accuracy |
| Day 9 | Test Case 格式 | 設計 id、input、expected、grading_method、task_type |
| Day 10 | Eval Dataset | 建立 15 筆測試案例 |
| Day 11 | Batch Runner | 批次執行 test cases,保存 output 與 trace |
| Day 12 | 基本自動評分 | 實作 exact_match 和 contains |
| Day 13 | JSON 格式驗證 | 實作 json_exact |
這些模組串起來後,目前 Evaluation 流程長這樣:
evals/cases.json
-> evals.runner
-> SimpleAgent.run()
-> AgentResult
-> save_trace()
-> evaluate()
-> eval_run_*.json
這表示我們已經從手動測試,前進到可以批次測試並保存結果。
目前 evals/cases.json 有 15 筆 test cases。
任務類型分布如下:
| task_type | 數量 |
|---|---|
calculation |
4 |
keyword_qa |
3 |
general_qa |
3 |
instruction_following |
2 |
json_output |
3 |
評分方式分布如下:
| grading_method | 數量 |
|---|---|
contains |
10 |
exact_match |
2 |
json_exact |
3 |
這份 dataset 不大,但已經能測出幾種不同問題:
這對 MVP 階段已經足夠。
在專案根目錄執行:
python3 -m evals.runner
執行完成後,會在 data/eval_runs/ 產生一份結果檔。
例如:
data/eval_runs/eval_run_20260906_042553.json
可以用以下指令檢查 JSON:
python3 -m json.tool data/eval_runs/eval_run_20260906_042553.json
實際檔名請換成你自己產生的檔名。
以目前使用 FakeLLMClient 的版本為例,一次 eval run 可能得到類似結果:
Total cases: 15
Passed: 5
Failed: 10
Success rate: 33.3%
這個數字不高,但它是合理的。
因為目前的 Agent 還不是完整 LLM Agent,而是使用 fake client 模擬最小流程。
目前 FakeLLMClient 主要只會處理一種情境:
看到「計算」兩個字
-> 嘗試抽取算式
-> 產生 calculator tool call
其他任務大多會回傳:
Fake response for: 原始問題
所以這份 baseline 的用途不是證明 Agent 很強,而是提供後續改善的對照組。
目前比較容易通過的是 calculation。
例如:
{
"case_id": "case_001",
"input": "請計算 135 * 28",
"expected": "3780",
"grading_method": "contains",
"task_type": "calculation",
"actual": "The result is 3780",
"passed": true
}
這題會通過,原因是:
FakeLLMClient 偵測到「計算」。135 * 28。SimpleAgent 呼叫 calculator。3780。contains evaluator 判定通過。這表示 calculation 任務目前在 Agent 能力範圍內。
看一筆 keyword 問答失敗案例。
{
"case_id": "case_006",
"input": "請回答 HTTP 狀態碼 404 通常代表什麼",
"expected": "找不到",
"grading_method": "contains",
"task_type": "keyword_qa",
"status": "completed",
"actual": "Fake response for: 請回答 HTTP 狀態碼 404 通常代表什麼",
"passed": false,
"failure_reason": "Expected output to contain '找不到', but got 'Fake response for: 請回答 HTTP 狀態碼 404 通常代表什麼'",
"trace_session_id": "...",
"error": null
}
這筆結果可以拆成兩層看。
第一,status 是:
completed
代表 Agent 執行流程沒有 crash。
第二,passed 是:
false
代表 Agent 雖然有回覆,但回答沒有符合 expected。
這是 Evaluation 中很重要的區分:
status 看執行是否成功
passed 看任務是否通過
這題失敗的原因不是程式錯誤,而是目前 FakeLLMClient 不具備真正回答知識問題的能力。
再看一筆指令遵循失敗案例。
{
"case_id": "case_011",
"input": "請只回覆 OK",
"expected": "OK",
"grading_method": "exact_match",
"task_type": "instruction_following",
"actual": "Fake response for: 請只回覆 OK",
"passed": false,
"failure_reason": "Expected exactly 'OK', but got 'Fake response for: 請只回覆 OK'"
}
這題使用 exact_match。
也就是說,Agent 必須剛好輸出:
OK
不能多任何字。
目前它失敗很合理,因為 fake client 的預設行為是把原始問題包進:
Fake response for: ...
這類案例後面很適合用來測試:
Day 13 加入 json_exact 後,JSON 題的失敗原因變得更清楚。
例如:
{
"case_id": "case_013",
"input": "請用 JSON 格式回傳 10 + 5 的答案,欄位名稱使用 answer",
"expected": {
"answer": 15
},
"grading_method": "json_exact",
"task_type": "json_output",
"actual": "Fake response for: 請用 JSON 格式回傳 10 + 5 的答案,欄位名稱使用 answer",
"passed": false,
"failure_reason": "Output is not valid JSON: Expecting value"
}
Day 12 時,這類題目失敗原因是:
Unsupported grading method: json_exact
Day 13 後,失敗原因變成:
Output is not valid JSON: Expecting value
這是一個實用的進展。
雖然這題還是失敗,但平台已經能指出更準確的問題:
不是 evaluator 不支援,而是 Agent 輸出不是合法 JSON。
這就是 Evaluation 派上用場的地方。
它不一定立刻提高成功率,但它能提高我們理解失敗的能力。
目前結果裡有一個很好的反例。
{
"case_id": "case_007",
"input": "請回答 Python 中 list 是可變還是不可變資料型別",
"expected": "可變",
"grading_method": "contains",
"task_type": "keyword_qa",
"actual": "Fake response for: 請回答 Python 中 list 是可變還是不可變資料型別",
"passed": true
}
這題表面上通過,因為 actual 裡包含:
可變
但這個「可變」其實來自原始問題:
請回答 Python 中 list 是可變還是不可變資料型別
也就是說,Agent 不是真的回答了「可變」,而是 fake client 把題目原樣回傳,剛好包含 expected。
這是 contains evaluator 的 false positive。
這個案例很重要,因為它提醒我們:
Evaluator 本身也需要被檢查,不能只相信 pass / fail。
也就是說,Evaluation 平台不只會測出 Agent 的問題,也會暴露評測方法本身的限制。
如果以這次 baseline 結果來看:
Passed: 5
Total: 15
Success rate: 33.3%
這個數字可以作為第一份 baseline。
但解讀時要注意兩件事。
第一,目前 Agent 使用的是 FakeLLMClient。
所以低成功率不是意外,而是合理結果。
第二,其中有一題可能是 false positive。
也就是說,表面上的 5 題通過不代表全部都是真正答對。
如果把 case_007 視為誤判,實際可靠通過可能更接近:
4 / 15 = 26.7%
這不是壞事。
因為 baseline 的目的不是漂亮,而是誠實。
我們需要先知道目前系統在哪些地方弱,後面才有辦法設計改善實驗。
每筆 evaluation result 都有 trace_session_id。
例如:
{
"case_id": "case_006",
"trace_session_id": "aa36ea41-557e-40f9-8fc0-af94ea7bc7dc"
}
這表示如果某一題失敗,我們可以回到 Trace Viewer 查看它的執行過程。
啟動 Trace Viewer:
streamlit run trace_viewer_app.py
接著選擇對應的 session。
這樣可以檢查:
這就是第一週 Trace 和第二週 Eval 串起來後能做到的事。
Eval 告訴我們:
哪一題失敗。
Trace 幫助我們回答:
為什麼失敗。
第二週結束後,平台已經可以做基本評測,但還有幾個明顯限制。
目前大部分失敗來自 fake client 能力不足。
它不是一個真正會理解任務的 LLM,只是用來測平台流程。
後面應該接上真正的 LLM client,讓 evaluation 結果更接近真實 Agent 行為。
contains 很容易實作,但也容易誤判。
例如 case_007 就是典型例子。
這表示後面需要更細的評分方式,或至少在 Failure Analysis 階段標記 evaluator limitation。
Day 13 的 JSON validation 只會判斷 output 是否合格。
它不會自動讓 Agent 重試,也不會修復 JSON。
這要等到第四週的 retry 和 schema validation guardrail。
目前只有 failure_reason。
例如:
Expected output to contain '找不到'
Output is not valid JSON
但還沒有更結構化的分類:
wrong_answer
format_error
instruction_error
tool_error
evaluator_false_positive
這會是第三週的重點。
看到 15 題只通過 5 題,很自然會想馬上修 Agent。
但這個系列的主題不是「刷高測試分數」,而是建立 Agent 測試驗證流程。
如果我們現在直接把 FakeLLMClient 寫成:
看到 case_006 就回答找不到
看到 case_011 就回答 OK
看到 case_013 就回答 {"answer": 15}
成功率可以很快變高。
但這沒有太大意義,因為它只是 hard-code 測試答案。
比較好的做法是:
這樣最後得到的數據才有說服力。
第三週會先做一個重要轉換:把前兩週用來驗證平台流程的 FakeLLMClient,換成真正的 LLM。
原因是第二週的 baseline 雖然有用,但它主要反映 fake client 的限制。接上 Gemini Flash 之後,我們才會得到更接近真實 Agent 的 evaluation result。
接著,我們會進入 Failure Analysis。
第二週的結果已經告訴我們哪些案例失敗,第三週接下來要進一步回答:
這些失敗分別是哪一種失敗?
目前可以先觀察到幾種可能分類:
| Failure Type | 例子 |
|---|---|
wrong_answer |
keyword_qa 沒有包含預期答案 |
format_error |
JSON output 不是合法 JSON |
instruction_error |
要求只回覆 OK,但輸出多餘文字 |
evaluator_false_positive |
contains 因為題目文字被複製而誤判 |
execution_error |
Agent 執行時發生 exception |
第三週會先接上真正的 LLM client,建立新的 baseline,再定義這些失敗類型,並把它們加入 evaluation result。
這樣我們就不只知道:
Failed: 10
而是能進一步知道:
format_error: 3
wrong_answer: 5
instruction_error: 2
這會讓後面的改善更有方向。
第二週完成了最小 Evaluation 流程。
目前平台已經可以:
exact_match、contains、json_exact 評分。passed 和 failure_reason。第一份 baseline report 顯示:
contains evaluator 可能出現 false positive。這些結果不是失敗,而是平台開始產生有用訊號。
Day 15 會先把 Agent 從 fake client 換成真正的 LLM client。
下一篇會使用 Gemini API 建立 GeminiLLMClient,讓同一套 eval dataset 可以分別測:
FakeLLMClient
GeminiLLMClient
這樣我們會有兩份 baseline:一份用來驗證平台流程,一份更接近真實 Agent 行為。
Day 16 之後,才會正式進入 Failure Analysis,把 wrong_answer、format_error、tool_error、instruction_error、execution_error 等分類加入 evaluation result,讓平台不只知道哪一題錯,也知道錯誤屬於哪一類。