iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0
AI Engineering

30 天 AI eval 實戰:一隻 PM agent,改完之後怎麼確定它還是對的系列 第 29 篇

Day 29|兩行定義寫窄之前先寫好成功條件,跑完只成立一條,不併也不放行

  • 分享至 

  • xImage
  •  

早就決定要寫窄的 unverifiable、mismatch 兩行定義,照事先 commit 的三條成功條件跑完四批,答案是不能放行,precision 確實升到 45.65% 與 42.67%,卻沒到事先訂的 50%,recall 掉了仍守住七成,run-all 四批都輸出「擋下」,窄定義也不併進 prompt。

需求釐清 agent 整理 ticket 時,除了寫 Goal、Scope in、Scope out,還會在 AC 標註區塊替有問題的驗收條件 AC 標上類別,類別有三種,彼此衝突 conflict、無法驗證 unverifiable、與描述不符 mismatch。整份 dataset 跑下來,它標出的標註大約每三個才有一個人工也標了,115 個多標裡有 104 個落在後兩類,看卡的人碰到這種比例,已經在放棄整個區塊的邊緣。

於是決定往 precision 讓,把後兩類的定義寫窄,界線是 recall 不掉破七成。

改之前,先把怎樣算成功寫進 repo

改動只有兩行,放在 prompts/narrow-definitions.txt,其餘 23 行與 src/agent/prompt.ts 一字不差(原版第 18–19 行、窄定義第 13–14 行):

原版
- unverifiable:這條 AC 沒有寫清楚怎樣算做完
- mismatch:這條 AC 跟 description 說的不一樣

窄定義
- unverifiable:這條 AC 只用形容詞描述,寫不出做完時可以觀察的行為或數字;寫了具體行為的不算
- mismatch:這條 AC 跟 description 裡某一句講的相反或對不上;description 沒提到這條 AC 的事不算

跑任何一批之前,這份對照紀錄先寫下三條成功條件,k 是同一筆 case 重跑的次數:

  1. precision 要真的升,而且兩批窄定義都到 50% 以上,也就是看卡的人每讀兩個標註有一個是真的
  2. 兩批窄定義的 recall 都不掉破七成
  3. 結果門檻不變,16 筆 case 各跑 5 次、次次通過,pass^5 要 16/16,擋或放由 run-all 判定

單趟通過的意思是,最終卡片每個受評欄位都符合 goal state,也就是人工事先寫好的通過條件;AC 標註要和人工標的集合完全相同,多一個、少一個都不算。

成立之後怎麼處理也先寫好了:

1、2 都成立,叫做「診斷變好」,可以把窄定義併進 prompt.ts;3 成立才叫「放行」。

這段條件在 commit dd91f23 進版控,時間是 2026-10-01 10:37:13,判定擋或放的程式 ae1e54a 在 10:37:44 進版控,計入結果的四批 10:44 開跑。之後這份文件只多了結果那一段,條件一個字都沒動。

照比對步驟跑四批

原版與窄定義各跑兩批,每批 16 筆、k=5,模型 gpt-5.4-mini,開著 --fill-missing。A5 與 C5 是一組,A6 與 C6 是另一組,同組兩批相隔 2 秒開跑。

下表的計數單位是條號加類別。agent 標出、人工也事先標進 goal state 的叫對上,只有 agent 標的叫多標,只有人工標的叫漏標,precision 是對上占 agent 所有標註的比例,recall 是對上占人工標註的比例:

批次 prompt 完成/預定 通過趟數 對上/多標/漏標 precision recall
A5 原版 80/80 4 116/222/19 34.32% 85.93%
C5 窄定義 80/80 10 105/125/30 45.65% 77.78%
A6 原版 80/80 6 115/199/20 36.62% 85.19%
C6 窄定義 80/80 6 99/133/36 42.67% 73.33%

比對的第一步是核對每批完成幾趟,四批都是 80/80,其中 5 趟靠 --fill-missing 補上錄製時那句固定回答,agent 讀到的內容沒有變。

另有一個不是抖動的問題,第一次開跑時兩批在同一毫秒啟動,寫進同一個目錄互相覆蓋,那兩批作廢,接著要開的兩批也停掉;run-all.ts 第 33–35 行改成目錄已存在就直接停下。

precision 同一個 prompt 兩批之間差 2.30(原版)與 2.98(窄定義)個百分點,配對差距是 C5 高 11.33、C6 高 6.05,方向相同、都大於 2.98,照事先約定的判法算改動造成。recall 同 prompt 兩批差 0.74 與 4.44,配對差距是 -8.15 與 -11.85,同樣方向一致、都超過 4.44,照同一個判法算是掉了。

差值都由原始計數算出,例如 recall 的 4.44 是 (105−99)/135。這個判法是事先約定的工程規則,不是統計檢定。

多標按類別看,減少的主要是 unverifiable 類,從 117 到 123 個降到 36 到 44 個;mismatch 類沒有一致減少。兩行是在同一個變體裡一起改的,分不出每一行各自的效果。

三條條件逐條對

條件 1 不成立,precision 升了,兩批卻停在 45.65% 與 42.67%,都低於 50%。條件 2 成立,窄定義兩批的 recall 是 77.78% 與 73.33%,都還在七成以上,但這只是這個時段量到的,同一個原版前一晚跑的 recall 就比這次低約 7 點,73.33% 那批離七成只剩 3 點多。

條件 3 交給 run-all 判定,report-all.ts 的 releaseDecision 逐筆 case 分類:

for (const r of rows) {
  if (r.passes < r.completed) failed.push(r.caseId);
  else if (r.completed < r.k) incomplete.push(r.caseId);
  else passed.push(r.caseId);
}
return {
  release: failed.length === 0 && incomplete.length === 0,

完成的趟裡有一趟沒過,這筆 case 記為判定失敗;完成的都過、趟數卻不足 k,記評估未完成;兩邊都是 0 筆才放行。四批總表的放行判定(A5、C5、A6、C6 第 30–34 行)都是同樣兩行:

擋下 門檻 pass^5 16/16,這批 pass^5 0/16

判定失敗 16 筆 評估未完成 0 筆

照事先寫下的規則,條件 1 沒過,窄定義不併進 prompt.ts;條件 3 沒過,這一版不放行。假如 precision 到了 50%,窄定義可以併進去,但 pass^5 0/16 照樣擋下,併不併和放不放行分開判。

改完定義之後,第一個要看的是沒過的趟裡有多少只差在多標。每趟的 score.json 逐個欄位記下過或沒過,ac_flags 另記漏了哪些、多了哪些;沒過的欄位只有 ac_flags、人工標的又一個都沒漏,這一趟就是只差在多標(對照紀錄):

批次 沒過的趟 只差在多標 只有 ac_flags 沒過、但有漏標
A5 原版 76 55 11
C5 窄定義 70 38 16
A6 原版 74 54 9
C6 窄定義 74 38 21

原版沒過的趟約七成只差在多標,窄定義降到約一半。通過的趟數只從 4、6 變成 10、6,有漏標的失敗則從 11、9 變成 16、21。兩版跑的是不同的趟,這些是總數的變化,看不出同一趟從多標變成了什麼。

四批的 token 依 gpt-5.4-mini 單價、input 全按原價換算約 US$1.10,作廢與停掉的批次另計;每批從第一趟到最後一趟 4 分 14 秒到 5 分 47 秒。

總結

unverifiable、mismatch 兩行定義寫窄之前先 commit 三條成功條件,再讓原版與窄定義成對開跑四批。precision 升到 45.65% 與 42.67%,沒到事先訂的 50%;recall 掉了,仍守住七成;run-all 四批都輸出「擋下」,判定失敗 16 筆、評估未完成 0 筆,所以窄定義不併進 src/agent/prompt.ts,這一版也不放行。

多標減少的主要是 unverifiable 類,mismatch 類沒有一致減少。原版沒過的趟約七成只差在多標,窄定義降到約一半,通過的趟數卻沒多出多少,四批 pass^5 仍是 0/16,離結果門檻沒有更近。


上一篇
# Day 28|分數掉了三個百分點,先補齊缺趟,再成對比較
系列文
30 天 AI eval 實戰:一隻 PM agent,改完之後怎麼確定它還是對的 共 29 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言