iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
AI Engineering

邊做邊補:用一個 AI 代理 mesh 專案,補齊 AI 工程師該有的能力系列 第 6

Day 06|評估|做:量尺自己也要被量——兩把尺並排、讀不懂就說、抓到合併在補條目

  • 分享至 

  • xImage
  •  

系列:「邊做邊補:用一個 AI 代理 mesh 專案,補齊 AI 工程師該有的能力」— 第 6 天
紀錄日期:2026-09-19
能力區:第 9 區 評估(兼第 12 區 多 Agent 協作)| 類型:做

它是什麼

教材談評估的時候,幾乎都在講怎麼評估模型系統。aie-book 第三、四章把評估方法論攤開來講:指標怎麼選、資料集怎麼建、人工評分和自動評分的取捨。hello-agents 的評估章講 agent 的任務完成率、工具呼叫正確率。這些都是「拿一把尺去量系統」。

但這些章節裡有一小段常常被跳過:評估你的評估器。aie-book 直接說,用 AI 當評審(AI-as-a-judge)的時候,評審本身的偏差、一致性、對輸入分布的敏感度都要先量過,不然你不知道那個分數在講什麼。

昨天我做出來的東西不是 AI 評審,是兩個純函式:一個算逐字重用率,一個算子任務角色。它們比 AI 評審笨得多,但同樣的問題完全成立:這把尺在什麼輸入上會失效?它失效的時候會不會告訴你?

今天做的事,一句話:把量尺自己也放到被量的位置上。具體是三件:讓角色分布被記錄下來(這樣分類器才有成績單)、做第二把原理不同的尺並排、然後看兩把尺在真實輸入上會發生什麼事。

專案裡哪件事逼我用到

前天(Day 05)我做了角色分類器,讓複核型子任務退出重用率的分母。做完那天跑了一輪,三份子任務全是產出型,分類器「運作正常」。

問題是:我憑什麼說它正常? 它在那一輪什麼都不用分類就答對了。如果我想回答「這個分類器是不是裝飾品」,我需要的不是某一輪的畫面,是跨輪的分布——九份子任務裡,幾份被讀成產出、幾份複核、幾份根本讀不懂。這個比例就是分類器的成績單,而我當時沒有把它記下來。

這是第一件逼我的事。第二件是逐字重用率本身:合併模型把清單重寫一遍,每一條的意思都進去了,字元片段一個都對不上,指標讀成 0。9/17、9/18 各撞一次。我需要一把問不同問題的尺。

做之前我以為

我以為今天的順序會是:記角色分布 → 發現分類器準確率不高 → 改規則 → 準確率變高 → 做第二把尺 → 兩把尺互相印證 → 收工。

前四步確實照劇本走。分類器在九份真實子任務上從讀懂四份變成讀懂九份。第二把尺也做完了,測試全綠。

然後我按下送出,兩把尺同時壞掉

做完的證據

第一步:角色分布寫進每一行紀錄

export interface RunRecord {
  // …
  roles: { produce: number; review: number; unknown: number } | null;
}

null 和全零是兩件事:呼叫端沒傳子任務文字是「不知道」,planner 真的沒拆子任務是「知道是零」。舊紀錄沒有這個欄位,加總時要排除,不能當三個零算。

跑了 E1 解釋型、E2 清單型、E3 判斷型各一輪,分布出來:3 產出 / 1 複核 / 5 未分類。九份裡超過一半讀不懂。

去讀那五份原文,原因一致:planner 寫子任務的習慣是「先說去哪裡找材料,再說要交出什麼」——Read this project's source code … Then write a final plain-English explanation。交付動詞在句子中段,而我的規則只看第一個字。

第二步:分類器 v2

改成看整句,而且用途優先於動詞

// 1. 目的子句:這份輸出是要被拿去對照的 → review,不管句中有幾個 produce
// 2. 交付子句(任意位置):產出動詞 + 同一子句內的交付物名詞 → produce
// 3. 退回 v1 的開頭動詞規則
// 4. 其餘 unknown,仍計入指標

回歸樣本是那九份真實子任務,從 coordinator 的 task record 用簽章 API 撈下來、原文寫進測試檔,不是我編的:

produce review unknown
v1 3 1 5
v2 8 1 0

有一份特別值得講:Produce a short factual reference … This reference will be used to fact-check a plain-English explanation written by another teammate。它明寫 Produce,但它的產物是給別人對照用的,不該進答案。v1 判 unknown,v2 靠「用途優先」判 review。它實測的重用率是 1%,跟判定一致。

第三步:第二把尺——條目層級對應

問另一個問題:這台輸出的每一條,在最終答案裡有沒有一條對應得上?

  • 把兩邊切成條目(至少兩條才算清單,否則回報「不適用」,不是 0)。
  • 每條取內容詞集合,算 containment(|A∩B| / |A|),≥ 40% 算對應。
  • 每列除了「幾條對上」,也存下每一條的原始分數——門檻一定有爭議,原始分數是你用來質疑門檻的東西。

確定性、離線、零成本、每次算都一樣。理由和前兩天拒絕 embedding 一樣:被量測的系統可以不確定,量它的尺不行。

為什麼是 containment 不是 Jaccard

這是做的時候唯一猶豫過的選擇,值得寫一段。Jaccard 是 |A∩B| / |A∪B|,兩邊對稱;containment 是 |A∩B| / |A|,只問「A 的東西有多少在 B 裡」。

實測有一組是這樣:工作機寫「Straggler nodes dominating total latency: one overloaded or throttled host drags the whole fan-out to the slowest participant」,最終答案寫「Straggler nodes and resource exhaustion (CPU, memory, or I/O) on the slowest machines, causing aggregate timeouts」。人看了會說是同一條。用 Jaccard,兩句聯集很大、交集只有 straggler / nodes / slowest 幾個字,分數會被兩邊各自多出來的字拖到 0.2 以下。用 containment 以工作機那句為分母,拿到 0.36——還是沒過 0.4 的門檻,但至少落在「差一點」的區間,而不是「完全無關」。

這個 0.36 我刻意沒有去調門檻讓它過。門檻一調,13% 和 3% 那些真的沒對上的也會跟著跨線,那不是量到了,是把標準放寬。留著原始分數,讓看的人自己判斷,比一個剛好讓所有例子都通過的門檻誠實。

兩把尺各自會在哪裡錯

逐字重用(12 字元片段) 條目對應(內容詞 containment)
問的問題 這台的文字有多少活到答案裡 這台的每一條有沒有對應的一條
合併整段照抄
合併逐條改寫 嚴重低估(趨近 0) 大致準
合併換了題材 準(0) 準(0)
兩條剛好共用詞彙 不會誤中(片段太長) 會誤中
散文型答案 可用 不適用(回報 null)
語言 不分語言 修正前只認拉丁字母
是上界還是下界 下界 都不是

最後一列是我差點寫錯的地方。做第二把尺的時候,很自然會想說「逐字是下限、條目是上限,真值在中間」——這句話寫到一半我停了:條目對應會因為大幅改寫而漏掉(straggler 那組就是),所以它不是上界。給一個指標一個它撐不起來的名分,是 Day 20 才抓過的錯。它就是另一個角度,有自己的失效方式,如此而已。

然後:第一輪就壞了

同一次合併:逐字重用 100%,條目對應 25%

task 9036e574。上面那行是舊尺,下面那行是剛上線的新尺。差四倍,錯的是新的。

子任務文字重用 2/2(100%)
條目對應 1/4(25%)

同一次合併。舊尺說 100%,新尺說 25%。角色分布那段寫著 3 未分類——剛剛才 9/9 的分類器。

原因:這一輪 planner 用簡體中文寫子任務。 原題是英文,前八輪也都是英文,這輪沒改任何設定就換了。

  • 分類器的規則全是英文正則,中文一律 unknown
  • 新尺的分詞只留 [a-z0-9],中文條目變成空集合,相似度恆為 0;那個 25% 是靠 fan-outIncast 幾個拉丁片段僥倖對上的。
  • 舊尺沒事,因為它數的是字元片段,不看語言。12 個中文字的片段是一句很長的話,74% 表示合併真的整段照抄。它是對的。

我之所以知道新尺壞了,是因為舊尺還在旁邊。 如果做完新的就把舊的換掉,畫面上只剩一個 25%,看起來完全合理——合併重寫了嘛。我會把它當發現寫進文章,而它是個 bug。

修法很小(中日韓字元補上字元 bigram),重算之後兩把尺一致:4/4。分類器的中文我沒修——那是對一輪樣本過擬合,正解是 planner 自己標角色,我把它寫成契約備註交出去。

再然後:新尺抓到合併在補條目

修好再跑,新尺回了一句我沒在找的話:

條目對應 2/2(100%)· 其中 3 條沒有任何一台的對應(合併時補的)

最終答案五條,三條沒有任何工作機寫過。追下去是三件事疊在一起:planner 這輪用了分配式拆法(第 1–2 條給一台、3–4 給一台、第 5 條給第三台);負責 3–4 的那台因 provider 額度用完失敗;合併模型把缺掉的條目自己補上,畫面上五條編號整齊。

前天我為了「任務卡住也拿得到答案」讓合併不等終止狀態,那是對的。它的副作用今天才看到:合併端拿到殘缺材料時不會說「缺了」,它會補。

今天早上:把它做成看得見的,然後抓到自己的錯

答案上方的警告:有 1 台失敗,5 條裡有 3 條沒有任何一台的輸出支持

task 8be06c2c。這張截圖裡寫 3 條——真值是 2,修正在下一段。留著這張,因為它就是「指標的第一個極端值先懷疑指標」的實例。

答案上方多一行警告:「⚠ 有 1 台失敗,而這份答案的 5 條裡有 3 條沒有任何一台的輸出支持——那幾條是合併模型自己補的,不是工作機的結果。」

然後我去對 task record,真值是 2 條,不是 3。負責第 5 名的那台只回了一行;splitItems 依規則不把一行當清單(這規則對「比率」是對的),於是那台從「支持者」集合裡整個消失,第 5 條被判成沒人寫。

修法:支持判定用寬鬆版切分(一行也是一項、散文算一項),比率仍用嚴格版。那一輪的原文變成測試 fixture。

一個為了抓別人錯而做的檢查,上線一小時內先抓到自己的錯。 這不是壞事,這正是它應該做的事——只要你願意去對原始資料。

還有一個:nonce 被截斷

iPad 那台的輸出在第 4 條被截斷,nonce 沒有回傳

task e0d8db08。256 token 的上限 × planner 要 5–8 條 × nonce 必須在末尾 = 一個看起來像格式錯誤的截斷。

同一個早上,iPad 那台第二次出現 incomplete_receipt: nonce not echoed。這次原因看得清楚:手機端的推論上限是 256 token(9/14 為了共享 Groq 額度設的),planner 這輪要求「5 到 8 條候選」,輸出在第 4 條被截斷;而契約 §1.3 規定 nonce 必須在回覆末尾。截斷即遺失,coordinator 依規則拒收。

三個各自正確的決定(nonce 放末尾、輸出設上限、planner 多要幾條),相乘之後是一個看起來像「格式不合格」的失敗。這是今天交出去的第 20 條備註:回報應該帶 truncated 旗標,不然發起端分不出「被截斷」和「沒照規矩」。

順帶:兩種拆法,兩種容錯性質

這是第 12 區的東西,但它是被第 9 區的尺量出來的,所以寫在這裡。

ai-agent-book 講多 agent 拆題時分「平行同題」和「分工協作」,語氣上把後者當成比較進階的做法。今天的紀錄給了一個不在教材裡的補充:兩種拆法在「掛一台」時的行為完全不同,而 planner 會在同一題上自己在兩種之間跳。

  • 面向型(每台各寫一份完整清單,各自聚焦一個角度):掛一台,答案少一個觀點,其餘完整。合併時要去重、重排,逐字重用趨近 0,條目對應才看得出貢獻。
  • 分配式(第 1–2 條給一台、3–4 給一台、5 給一台):掛一台,答案缺一塊,而合併會自己補。逐字重用很高(各台的行被原樣搬進去),但「合併自補」的計數才是那一輪真正該看的數字。

也就是說,該看哪把尺,取決於 planner 這輪用了哪種拆法——而拆法不是我選的,是 planner 每輪自己決定的。這把「角色分布」和「拆法」都記進紀錄的理由又多了一條:不記,事後連該用哪把尺讀那一輪都不知道。

對照教材:AI 評審的偏差清單,我的兩把尺也逃不掉

aie-book 第四章列了 AI-as-a-judge 的幾種已知偏差,我把它們拿來對照今天這兩個純函式,發現大部分都有對應版本:

  • 對輸入分布的敏感度(評審在訓練分布外會亂答)→ 我的分類器在中文上直接歸零,新尺的分詞在中文上變成空集合。純函式沒有「訓練分布」,但有「作者想像過的輸入分布」,效果一樣。
  • 冗長偏好(評審偏愛比較長的回答)→ containment 以工作機那條為分母,最終答案那條越長、包含工作機用詞的機率越高,所以合併寫得越囉嗦,條目對應會越好看。這是今天才意識到的偏差,還沒量,先記下來。
  • 位置偏差(評審偏愛排在前面的候選)→ 目前沒有對應,因為兩把尺都不比較候選之間的順序。但「合併自補」的計數其實隱含一個順序假設:我假設最終答案的第 N 條對應某一台的第 N 條,這在分配式拆法下成立、在面向型拆法下不成立。
  • 自我偏好(評審偏愛自己生成的文字)→ 合併模型和 iPad 上的 app-local 執行器是同一個模型(qwen)。如果合併偏愛自己家的措辭,ipad-sim 的逐字重用會系統性偏高。今天十輪裡 ipad-sim 有 60%、75%、61% 三次高分,樣本太少,不能下結論,但這是一個該追的方向。

把這張清單對到純函式上的收穫是:「評審偏差」不是模型的專利,是任何量測工具都有的性質。 教材把它寫在 AI 評審那節,是因為那裡最明顯,不是因為別的尺沒有。

這十輪的數字

最近 10 次 · 重用率中位數 58%(10 次可量測)· 完成率 80%(24/30)· 條目對應中位數 86%(4 次適用)· 合併自補 3 條 · 子任務 7 產出 / 1 複核 / 13 未分類

完成率從 94% 掉到 80%:一台連續四輪額度失敗,一台兩次 nonce 截斷。未分類從 5 漲到 13:三份中文、加上幾份 v2 也還沒學會的英文寫法(identify … and return 5 to 8 numbered candidate failure modes)。

這行數字比前天難看,但它每一段都能指回一個原因。前天那行好看,是因為它還不知道自己看不見什麼。

十輪長什麼樣

# 日期 題型 planner 拆法 逐字 條目 事件
1 9/17 E2 1 產出 + 2 複核 0/3 指標誤判複核型
2 9/17 E2 3 產出各一面向 0/3 合併逐條改寫
3 9/18 E2 1 產出 + 1 複核 + 1 未分類 1/2 第一次非零
4 9/18 E1 1 產出 + 2 未分類 1/2(78%) 不適用 nonce 未回傳 ×1
5 9/18 E3 1 產出 + 2 未分類 1/3 不適用
6 9/19 E2 中文.分配式 2/2 1/4 → 4/4 新尺分詞壞掉;額度失敗 ×1
7 9/19 E2 分配式 2/2 2/2 合併自補 3 條;額度 ×1
8 9/19 E2 分配式(明寫 RANKS) 2/2 2/2 警告首現;多報 1 條;額度 ×1
9 9/19 E2 面向型(各 5–8 條候選) 1/1 5/7 nonce 截斷 ×1;額度 ×1

同一題 E2 跑了七輪,planner 給出至少五種結構。事件欄裡沒有一輪是「什麼都沒發生」。

我原本以為固定題組的價值是「讓數字可比」。跑到第十輪才看清楚它更大的價值:當題目固定,所有變化都來自系統,於是每一輪的意外都能直接指回系統的某個性質,不用先排除「是不是題目變了」。

今天真正學到的

一個指標在單元測試裡全綠,跟它在真實輸入上有意義,是兩件沒有關係的事。

新尺有九個測試,含真實 fixture、邊界情況、null 與 0 的分辨,全綠。它上線第一輪就給出一個錯得離譜的數字,而且沒有拋任何例外、沒有紅字、沒有 NaN——它非常有信心地回答了一個它其實看不懂的問題。

會抓到,只因為旁邊還站著一把原理不同的舊尺。這件事沒辦法靠寫更多測試解決,因為我測的永遠是我想得到的輸入;「planner 會用中文出題」這件事,我寫測試時一次都沒想過。

所以結論是策略性的:在同一件事上保留兩把原理不同的尺,並排顯示。 冗餘在這裡不是浪費,是唯一能在「程式沒壞、只是沒意義」時發出聲音的機制。

第二條,稍微反直覺:能說「我不知道」的笨工具,比永遠有答案的聰明工具更適合拿來量測。 我有一整排 CLI 可以叫來判斷子任務角色,中文也不怕。但那樣同一筆紀錄不同時間重算會得到不同角色,歷史就不可重現;量測會跟被量測的系統搶同一份額度;最要緊的是,模型會很有信心地給我三個 produce,我永遠不會知道那輪換了語言。正則讀不懂就是 unknown,於是「13 未分類」這個刺眼的數字逼我去看原文。

如果哪天真的要用 AI 當評審

今天兩次拒絕用模型來量測,不代表永遠不用。寫下我覺得可以用的條件,以後對照:

  1. 純函式的尺已經到頂了。 現在還沒有——條目對應才做一天,冗長偏差還沒量,分類器連英文的新句型都還在漏。
  2. 評審的判斷要被記錄成可重放的東西。 至少存下評審看到的輸入、它的完整輸出、模型版本。這樣歷史雖然不可重算,至少可以重讀。
  3. 評審和被評的系統不共用額度。 今天一台因額度掛掉,如果評審也在同一個池子裡,掛的會是紀錄。
  4. 評審要有「我不知道」的出口,而且那個出口要被統計。 這是最難的一條。模型天生傾向給答案;要它在不確定時說不確定,得在提示裡明確給它這個選項,然後在紀錄裡把這個選項的比例當成一個指標來看——就像今天的「13 未分類」。

四條裡第一條最重要。便宜的方法沒用完之前,不要換貴的——不是因為省錢,是因為便宜的方法失效的方式你看得懂。

如果你也在做 agent 系統的評估

  1. 量尺上線時,舊尺至少再留一輪並排。 兩把尺吵架的那一分鐘,比它們同意的一整週有價值。
  2. 讓「讀不懂」成為一個看得見的類別。 今天畫面上「13 未分類(其中 3 份非英文)」很醜,但它是唯一一個在事情不對時會自己變大的數字。任何「永遠有答案」的設計都會把這個訊號吃掉。
  3. 量尺要確定性、離線、零成本。 一旦量測會失敗、會排隊、會花錢、會隨模型版本漂移,歷史就不可信,而歷史是你唯一能拿來反駁自己的東西。
  4. 為抓別人的錯而做的檢查,先拿自己剛做的東西試。 今天的「合併自補」計數第一次報數就多報了一條,對原始資料才發現。指標的第一個極端值永遠先懷疑指標。

還缺什麼

  • 分類器對中文一無所知,我選擇不修。正解在 coordinator(子任務角色欄位),三條備註已交出去。
  • 條目對應對散文型答案不適用,這是設計不是缺陷,但表示 E1、E3 兩種題型只有一把尺。
  • truncated 旗標要 provider 層回傳 finish_reason,app 端還沒有。
  • 連續四輪額度失敗的那台仍然每輪被派工。凍結名單時應該能看到「上一輪為什麼失敗」。

明天

Day 07|多 Agent|讀。把這幾天觀察到的 planner 行為(同一題五種拆法、面向型 vs 分配式、語言切換)對照教材裡的規劃者設計,看哪些是「規劃者該有的自由」、哪些是「契約應該釘住的」。今天累積的十輪紀錄是材料。

參考:aie-book 第三章「Evaluation Methodology」、第四章「Evaluate AI Systems」(特別是 AI-as-a-judge 那節對評審本身偏差的討論);hello-agents 的評估章;ai-agent-book 第 10 章多 Agent 協作。


上一篇
Day 05|多 Agent|做:讓指標看懂「誰是產出、誰是複核」,然後它露出第二層問題
下一篇
Day 07|多 Agent|讀:我沒他們的時間和錢,所以把 55 個開源專案查了一遍,決定借哪一層
系列文
邊做邊補:用一個 AI 代理 mesh 專案,補齊 AI 工程師該有的能力7
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言