iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
AI Engineering

AI 寫的測試,誰來測?系列 第 25 篇

【Day23】它準確地打在邊界上,然後把期望寫反了

  • 分享至 

  • xImage
  •  

TL;DR

  • 把二十份 AI 腳本裡真正餵進去的數字全部抽出來數:年齡有整數偏好,金額沒有,而且反過來
  • 規格寫明邊界之後,AI 打邊界打得比人寫的對照組還準
  • 但同一份產物裡,一條測試知道那個點非法,另一條把它當合法用。打到邊界,不等於測對邊界

https://ithelp.ithome.com.tw/upload/images/20260924/20103826fkl4qLS5x5.jpg


前言

昨天量的是「規格細度決定測試的天花板」。今天換一個角度看同一批腳本:它們挑的數字長什麼樣?

這件事Day 12 在文件層做過:把「規格寫了小於等於,就該有前一格、剛好、後一格」這個 QA 直覺寫成 bva_checker.py。當時量的是自然語言案例集。今天量的是可執行的 pytest 腳本。

判定的部分直接重用那支檢核器,同一條尺量兩層,數字才可比。新寫的只有取樣器。

怎麼分辨「輸入」和「期望值」

這是整件事最麻煩的一步。

p = Params(current_age=30, retirement_age=65, life_expectancy=85, ...)
assert calculate(p).target_fund == 22938825.0

第一行的 30、65、85 是輸入,第二行的 22938825.0 是期望值。把後者算進「金額的分佈」,會得到一堆七位數,結論整個歪掉。

規則寫死兩條:

  1. 落在 assert 節點底下的數值 → 期望值,排除
  2. 綁在已知欄位名的關鍵字引數上的數值 → 輸入

第二條刻意只認欄位名,不認函式名。產物幾乎都會包一層自己的 helper(_p(...)、get_valid_params(...)),認 Params( 會整批漏掉。

還有一個老問題又出現了。產物常寫成 get_valid_params(retirement_age=65),呼叫點只看得到一個欄位,其餘來自預設值。Day 12 在文件層撞過同一件事(「同上」「其餘同 baseline」),到了代碼層它換個樣子又出現一次。處理方式是把欄位最多的那一處當基準情境併進去,並且把這個動作印出來:它是推論,不是讀到的事實。


年齡有整數偏好,金額沒有

事前假設是 Round-Number Bias:AI 會集中在 30、60、65、85 這種年齡,金額集中在一萬的倍數。

年齡是 5 的倍數 金額是一萬的倍數
一句話組(Pro) 100%(n=93) 57%
一句話組(Flash) 99%(n=155) 72%
PRD 組(Pro) 80%(n=179) 36%
PRD 組(Flash) 95%(n=316) 56%
golden set(人寫) 72%(n=82) 67%

年齡那一欄假設成立,而且比想像中極端:一句話組九十三個年齡,沒有一個不是 5 的倍數。 人寫的是 72%。

金額那一欄則相反。PRD 組只有 36% 是整萬,比人寫的 67% 還低。

這點可以從取值清單看出差異:

最常出現的金額
AI 1000、100、10000、50000
人 30000、800000、19391、24000

那個 19391 是真實情境裡抄下來的數字。二十份 AI 產物,一個都沒有。

AI 挑的是好手算的數:它要自己推出期望值,1000 比 19391 好乘。人挑的是像真的數,因為人寫 golden set 的目的是鎖住真實行為。

「AI 偏好整數」這個講法不夠精確。兩邊都有偏誤,只是方向不同:AI 挑好算的數,人挑像真的數。


規格寫明邊界,它打得比人準

事前假設的後半是「邊界 ±1 幾乎為零」,這個假設被數據推翻了。

把同一格五份的案例池在一起,看四條年齡關係式有沒有被打到(需要同時出現界內一步、剛好、界外一步,才算滿分):

現齡 < 退休年齡 退休 < 壽命 大筆支出不早於退休 大筆支出不晚於壽命
一句話組(Pro) 0% 0% 0% 0%
一句話組(Flash) 67% 33% 0% 33%
PRD 組(Pro) 67% 67% 100% 67%
PRD 組(Flash) 67% 67% 67% 100%
golden set(人寫) 0% 67% 33% 33%
行為測試(人寫) 0% 0% 0% 0%

PRD 組的命中率高於人寫的兩份對照。

一句話組四條全 0,它連有邊界這件事都不知道。而 PRD 組開始出現 61、62 這種年齡,那是為了構造相鄰年份硬擠出來的。規格寫了「現齡小於退休年齡小於預期壽命」,它就真的去把兩個年齡貼在一起試。

這是今天最意外的結果。規格夠細的話,AI 的邊界意識不比人差。


打到邊界,不等於測對邊界

但若檢視那些命中邊界的案例,會發現這份 100% 命中率背後有隱患。

run-A-PRD-02 這份產物裡有這條測試:

def test_prd_04_validation():
    """PRD-04: 輸入校驗,不符規定時拋出例外"""
    invalid_params = [
        # A_r < A_d 不成立
        Params(current_age=30, retirement_age=60, life_expectancy=60, ...),
        ...
    ]

它把「退休年齡等於預期壽命」列進非法輸入清單,寫了一條測試驗這種輸入必須拋錯。判斷完全正確。

而同一個檔案往下十幾行:

def test_prd_05_lump_sum_inflation():
    p = Params(current_age=50, retirement_age=60, life_expectancy=60, ...)
    res = calculate(p)          # ← 期待拿到一個結果

同一組非法值,這次它期待算出一個數字。

同一份產物、同一個邊界點,一條知道它非法,另一條把它當作合法用。後面那條就是昨天讓它在基準線那一關掛掉的原因之一。

人寫的 golden set 也踩了同一個點:def_b_equal,壽命等於退休年齡,但期望欄寫的是「被拒絕」。同一個座標,差別只在期望值。

而我的取樣器對這兩種情況一律報「命中」。它看的是輸入落在哪裡,完全不看期望寫的是什麼。


這就是 Goodhart

若將「邊界命中率」設為團隊 KPI,結果會很荒謬。

AI 會準確打到每一個邊界,然後把期望寫反,報表上依然是一片綠。這反映在今天的實驗數據上:命中率高達 100%,但其中一半的期望是錯的。

第三幕蓋了四把尺,每一把都在同一個地方失手:

尺 量得到 量不到
斷言忠實性 斷言嚴不嚴 在驗什麼
行覆蓋率 程式碼跑過沒 跑過的有沒有被檢驗
變異分數 改壞了會不會叫 目錄外的錯法
邊界命中率 打到邊界沒 打到之後期望對不對

四把尺量的都是「測試做了什麼動作」,沒有一把量得到「那個動作的期望值對不對」。 因為要判斷期望值對不對,得有一個知道正確答案的東西,而那正是這整個系列一開始就沒有的:一個 oracle。


後記

取樣器寫完,第一個拿去測的是人寫的 test_golden_v2.py,結果印出的輸入數值是 0 個。

這是因為那 23 組案例全由 JSON 讀入,程式碼裡本來就沒有硬編數字。0 這個結果是正確的,但它帶來另一個風險:若忽略了這個實作細節,這個 0 極可能被過度解讀為「人寫的測試完全沒有硬編數值,品質極佳」。量測本身正確,解讀出的結論完全相反,且過程中沒有任何機制能攔截這個謬誤。

這種東西在工作上最常見的長相是掃描報告全綠。綠可能是真的沒問題,也可能是路徑設錯、掃描器根本沒讀到那幾個檔案。兩種報表長得一模一樣,而且都很好看,很容易就貼進週報了。

這個系列已經抓到兩次同型的了。Day 14 的校準器,在沒裝 pytest 的環境下會讓「必錯組」全部假通過;Day 17 的覆蓋率探針,測試沒跑完照樣印得出比值。三次都是同一句話:工具在什麼都沒做的時候,印出了一個看起來很合理的數字。

所以這支取樣器加了一條:全部加起來抽到零個輸入時直接中止。與其印一個 0%,不如什麼都不印。JSON 的讀法也是那天補的,對照組不該因為換了檔案格式就消失。


這些數字不能拿來做什麼

二十份腳本全部來自同一廠牌、同一組提示詞,不是獨立樣本。人寫的對照只有兩份,而且都是我自己寫的,沒有第三方基準,「AI 比人整」這句話的「人」只有一個。

基準情境的併入是推論,不是讀到的事實,它會影響邊界命中的分母。取樣器把這個動作印在報表上,但推論就是推論。

「邊界命中率不看期望值」是這支工具的設計缺陷,也是今天要示範的東西。這不是 bug,是刻意留著的展示品。真要修,得讓它去讀 assert 那一側,而那又是另一把新尺。

逐格的分佈、取值清單與四條限制在 manifest.yaml。


只帶走一件事
所有量測測試品質的指標,量的都是「測試做了什麼」。
沒有一個量得到「它以為的正確答案對不對」。


第三幕到此為止。四把尺全部失手在同一個地方:沒有 oracle,就判不了期望值的對錯。

這引出了第四幕的核心問題:測試時,有沒有可能根本不需要知道正確答案?


今天的 repo:https://github.com/eyelash500/2026_ironman_test_ai/tree/day23


上一篇
【Day22】規格只給一句話,寫出來的測試殺掉 0 個變異體
下一篇
【Day24】AI 寫下那個數字的時候,沒有人在旁邊
系列文
AI 寫的測試,誰來測? 共 26 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言