系列:「邊做邊補:用一個 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。交付動詞在句子中段,而我的規則只看第一個字。
改成看整句,而且用途優先於動詞:
// 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%,跟判定一致。
問另一個問題:這台輸出的每一條,在最終答案裡有沒有一條對應得上?
|A∩B| / |A|),≥ 40% 算對應。確定性、離線、零成本、每次算都一樣。理由和前兩天拒絕 embedding 一樣:被量測的系統可以不確定,量它的尺不行。
這是做的時候唯一猶豫過的選擇,值得寫一段。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 才抓過的錯。它就是另一個角度,有自己的失效方式,如此而已。

task 9036e574。上面那行是舊尺,下面那行是剛上線的新尺。差四倍,錯的是新的。
子任務文字重用 2/2(100%)
條目對應 1/4(25%)
同一次合併。舊尺說 100%,新尺說 25%。角色分布那段寫著 3 未分類——剛剛才 9/9 的分類器。
原因:這一輪 planner 用簡體中文寫子任務。 原題是英文,前八輪也都是英文,這輪沒改任何設定就換了。
unknown。[a-z0-9],中文條目變成空集合,相似度恆為 0;那個 25% 是靠 fan-out、Incast 幾個拉丁片段僥倖對上的。我之所以知道新尺壞了,是因為舊尺還在旁邊。 如果做完新的就把舊的換掉,畫面上只剩一個 25%,看起來完全合理——合併重寫了嘛。我會把它當發現寫進文章,而它是個 bug。
修法很小(中日韓字元補上字元 bigram),重算之後兩把尺一致:4/4。分類器的中文我沒修——那是對一輪樣本過擬合,正解是 planner 自己標角色,我把它寫成契約備註交出去。
修好再跑,新尺回了一句我沒在找的話:
條目對應 2/2(100%)· 其中 3 條沒有任何一台的對應(合併時補的)
最終答案五條,三條沒有任何工作機寫過。追下去是三件事疊在一起:planner 這輪用了分配式拆法(第 1–2 條給一台、3–4 給一台、第 5 條給第三台);負責 3–4 的那台因 provider 額度用完失敗;合併模型把缺掉的條目自己補上,畫面上五條編號整齊。
前天我為了「任務卡住也拿得到答案」讓合併不等終止狀態,那是對的。它的副作用今天才看到:合併端拿到殘缺材料時不會說「缺了」,它會補。

task 8be06c2c。這張截圖裡寫 3 條——真值是 2,修正在下一段。留著這張,因為它就是「指標的第一個極端值先懷疑指標」的實例。
答案上方多一行警告:「⚠ 有 1 台失敗,而這份答案的 5 條裡有 3 條沒有任何一台的輸出支持——那幾條是合併模型自己補的,不是工作機的結果。」
然後我去對 task record,真值是 2 條,不是 3。負責第 5 名的那台只回了一行;splitItems 依規則不把一行當清單(這規則對「比率」是對的),於是那台從「支持者」集合裡整個消失,第 5 條被判成沒人寫。
修法:支持判定用寬鬆版切分(一行也是一項、散文算一項),比率仍用嚴格版。那一輪的原文變成測試 fixture。
一個為了抓別人錯而做的檢查,上線一小時內先抓到自己的錯。 這不是壞事,這正是它應該做的事——只要你願意去對原始資料。

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 會在同一題上自己在兩種之間跳。
也就是說,該看哪把尺,取決於 planner 這輪用了哪種拆法——而拆法不是我選的,是 planner 每輪自己決定的。這把「角色分布」和「拆法」都記進紀錄的理由又多了一條:不記,事後連該用哪把尺讀那一輪都不知道。
aie-book 第四章列了 AI-as-a-judge 的幾種已知偏差,我把它們拿來對照今天這兩個純函式,發現大部分都有對應版本:
把這張清單對到純函式上的收穫是:「評審偏差」不是模型的專利,是任何量測工具都有的性質。 教材把它寫在 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 未分類」這個刺眼的數字逼我去看原文。
今天兩次拒絕用模型來量測,不代表永遠不用。寫下我覺得可以用的條件,以後對照:
四條裡第一條最重要。便宜的方法沒用完之前,不要換貴的——不是因為省錢,是因為便宜的方法失效的方式你看得懂。
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 協作。