Math.max(0, ...) 抄進了規格書
昨天畫好及格線,今天資料到手:兩個模型各跑五次,產出十份測試案例集。(第一輪因隔離不完全作廢,這裡用的是第二輪的乾淨資料)
程式碼寫錯,編譯器和 linter 會立刻噴紅字。但規格書和測試案例都是文字,文件層沒有編譯器,也沒有紅綠燈。面對排版整齊的測試案例,靠肉眼核對到底漏了什麼、有沒有偷渡不存在的規格,五分鐘就累了。所以需要一把尺:RTM(Requirement Traceability Matrix,需求追溯矩陣)。 它做的事只有一件:建立「規格」與「案例」互相指認的對映。過去它常被當成冗長的交付文件,但如果只當作每次生成案例後的輕量檢查,它其實非常有效。
RTM 只檢查兩個方向:
它不判斷案例寫得好不好,只判斷「有沒有對到」。在沒有編譯器的地方,這是少數機器能代勞的事。
這兩個方向我都跑了,兩邊都是空的(沒有漏測也沒有幻覺)。所以今天真正的工作是這三件:用眼睛讀完抽驗的 74 條案例、處理 AI 多附的一張清單,然後承認一件我造成的事。
先看測試規模:
| 環境 | 案例數 | 合計 | |
|---|---|---|---|
| 模型 A | Gemini 3.1 Pro/AI Studio | 五次皆 13 條 | 65 |
| 模型 B | Claude Opus 5/Console Workbench | 62–84 條(中位 74) | 359 |
兩邊皆在開發者控制台手動輸入,總計產出 424 條案例。(開發層沒有記憶或範本可以掛——不過這句是從「那一層沒有這些功能」推出來的,我沒有真的去問過它載入了什麼。)
正向掃描非常快:每條案例都按規定自報了覆蓋的 PRD 編號,我只要把編號全抓出來,直接對 PRD-01 到 PRD-10 進行比對即可。結果出乎意料:兩個模型、十份案例集,每一份的覆蓋率都是 10/10,裸需求為零。
我原本準備好大寫特寫「AI 漏測了哪幾條」,結果完全沒有素材可寫。
反向的機器掃描也是空的:每一條案例都掛著至少一個 PRD-xx 編號,沒有一條「無家可歸」。
但這結果其實很虛,因為機器只驗「有沒有掛編號」,不驗「掛得對不對」。若案例宣稱覆蓋 PRD-02,斷言卻在測 PRD-06,它依然會通過檢查。這就是藏起來的孤兒。要得知真實的反向追溯結果只能靠肉眼。我全查了 run-B-01 的 74 條案例,先訂標準再套用:
| 判定 | 定義 |
|---|---|
| 屬實 | 斷言確實在測它宣稱那條 PRD 的內容或邊界處置 |
| 不實 | 斷言測的是別條,或引入 PRD 沒寫的規則或常數 |
| 阻塞 | 明確標示規格未定義,因此不給預期值 |
結果:
| 判定 | 條數 | 占比 |
|---|---|---|
| 屬實 | 61 | 82% |
| 阻塞 | 12 | 16% |
| 歸屬存疑 | 1 | 1% |
| 不實/幻覺 | 0 | 0% |
存疑的是 TC-01-07:宣稱掛 PRD-01,斷言卻點名「PRD-04 未定義報酬率是否允許負值」。歸屬有爭議,故判存疑。
我原本預期 AI 會從預訓練知識憑空捏造「所得替代率要達 70%」或「二代健保」等 PRD 沒提過的案例,但在這 74 條中完全沒有。可能跟昨天提示詞第 3 條有關:「規格沒寫的不要自行補上」——那原本是測幻覺的餌,結果沒有魚上鉤。但我沒跑過「不加那句話」的版本,所以這只是猜測。
更值得注意的是,74 條中能用單一算式獨立驗證的預期值僅有 9 條。我實算後 9 條全中(誤差 ≤ 1 元):
TC-01-02 1,000,000 × 1.005^12 實算 1,061,677.81 它寫 1,061,678
TC-02-03 600,000 × 1.02^25 實算 984,363.60 它寫 984,364
TC-08-01 720,000 × 1.02^25 實算 1,181,236.32 它寫 1,181,236
TC-03-01 900,000 × 1.03 實算 927,000.00 它寫 927,000
其餘 52 條屬實案例的斷言是結構性的,如「相鄰期比值恆為 1.02」或「軌跡不得出現 0 截斷」。要驗收這些得實際跑受測物,無法光靠讀文件。
我沒有判定 Duplicate(同分支、換輸入),因為這得等案例變成腳本後追蹤程式路徑。昨天預設它是最大宗,今天無從驗證。
那 12 條阻塞案例更麻煩:它們不屬於任何一類。
它們有對應的 PRD、未發明新規則,但也不是 Valid,因為它們沒有預期值。
TC-01-06 阻塞:規格未載明月存為期初或期末投入。
期末 = 123,356;期初 = 123,972。兩值皆不得作為預期值,須先補條款
昨天的四個分類完全無法涵蓋它們。依照「跑完不改」的規則,我不回頭改分類,只記為「依 v1 規則無法分類,計 12 條」。這正是「先畫及格線」壓力測試未通過的結果。
另外,模型 A 每條規格只掛 1–3 條案例(PRD-08 只有 1 條);模型 B 則是 4–20 條。由於單一案例無法同時測正常與邊界值,A 的「覆蓋 10/10」只是蜻蜓點水。這只是表面指標,真正的深度明天用邊界命中率來量。
該亮紅燈的地方全都是綠的。若就此收工,結論會是:
十次獨立生成 PRD 覆蓋率 100%;抽驗 74 條屬實 82%、幻覺 0。文件層品質達可用水準。
這結論有數據支撐,卻很糟——因為 82% 只證明「編號掛對了」,我能在文件層驗證的僅有 9 條。剩下 52 條對不對,得等變成腳本才知道。
數量不等於品質,品質得看明天的邊界命中率。
模型 B 的五份輸出,每一份最後都多了一個章節。五份的標題各不相同:
編號前綴雖不同(GAP-、D-、G-),做的事卻一樣:列出規格未定義之處,並註明哪些案例因此寫不出預期值。
五份的項數分別為 10、10、11、10、12,而我的提示詞根本沒要求做這個。
而那份 PRD v1.0 是 Day 8 挖出十個歧義現場、Day 9 一條一條裁決出來的。我以為那件事做完了。
跨五次生成比對,有三個缺口是五次全部指到同一處:
| 缺口 | 五份的說法 |
|---|---|
| 月存投入時點(期初/期末)未定義 | 5/5 |
退休後收入的通膨基準年是 A_c 還是 A_r |
5/5 |
| 大筆支出是否隨通膨調增 | 5/5 |
另外「PRD-02 折現用的是 r 還是 r_p」有 4/5 指認。
我去翻條款原文。PRD-01 寫的是:
本金與月存統一採名目月利率
r/12,同為月複利
它規定了利率,沒有規定月存什麼時候進去。
而模型 A 那五份,這個章節一項都沒有。零。
把兩件事放在一起看,形狀就出來了。
面對規格未明處,兩個模型反應不同。
「月存期初期末」兩邊都撞牆了。模型 B 將其列為缺口,將案例改為區間或不變量斷言;run-B-05 更直接把 r_p 設成 0 來迴避,並明文告知。
而模型 A 的 run-a-1 直接在 TC-005 給了公式:
本金=100萬, 月存=1萬, r=6%
預期輸出:退休本金為 100萬×(1.005)^12 + 1萬×[((1.005)^12-1)/0.005]
後面這項是期末年金因子。它默默替我選了期末,沒說它做了選擇。而另外四份則直接繞開不處理。
繞開沒錯,B 也在繞,但 B 會明說,A 不會。最糟的是 run-a-1:它不僅擅自決定,還固化為預期值。一旦進了測試套件,就會去保護一個無人裁決過的行為。
這病很眼熟。Day 4 探討的 (1+r) 就是受測物擅自選擇期初期末的問題。我在 Day 9 釘死了提領端,卻忘了累積端有同樣的漏洞。
但對於規格寫得像「修正」的地方,兩者反應卻一致——這是我的問題。
run-B-01 中有 4 條斷言直接指向受測物的真實錯誤:
TC-01-02 必須不等於 1,060,000(年複利) ← 受測物本金確實走年複利
TC-02-03 必須不等於 600,000(誤鎖 A_r 為基準) ← 通膨基準
TC-03-01 必須不等於 930,000(期末扣款) ← 缺陷 A
這是在完全隔離環境下產出的。但這並非「AI 好厲害」,而是效度威脅:我在深知缺陷後才寫下 PRD,部分措辭已帶有修正語氣(如統一採、須回傳明確錯誤)。最明顯的是 PRD-03:
期初扣款,索引與 PRD-02 對齊 | 禁止
Math.max(0, ...)截斷真實餘額
我直接把缺陷程式碼 Math.max(0, ...) 抄進了規格書。
AI 不是猜到的,是我洩漏的。未來比較生成品質時必須記住:部分成績是我餵出來的,不能全算規格的功勞。
(順帶一提,首輪作廢的資料裡也有類似斷言。乾淨重跑證明了 PRD 的措辭就足以產生它們——但這不代表首輪的記憶完全沒出力,只是它不再是唯一的解釋。)
昨天的驗收標準有這麼一行:
human_audit_ratio: 0.30 # 每 run 至少人工覆核 30%
寫這行時,我還不知道會拿到幾條案例。現在手上有 424 條,30% 是 127 條。但我實際只全查了 run-B-01 的 74 條(占 17%)。按規定,我跳票了。
而另一半標準要求 [Omission, Hallucination] 須 100% 覆核,實測因為這兩類為 0 而無事可做。這標準一半做不到、一半沒得做。
閘門 3 在此失守。先定門檻是對的,但它未考慮工作量,也沒預料到紅燈類別會是空的。訂標準不僅要早,還要確認可行性,我昨天只做到前者。
解法不是下修 0.30 來迎合現況(這是照結果配標準),而是要把「人工核對」轉交給程式自動化。這是明天的任務。
而抽驗完要有去向,不然抽驗只是感想:
| 掃描結果 | 本輪實測 | 處置 | 閘門 |
|---|---|---|---|
| 宣稱屬實 | 61 條(run-B-01) |
進明天的技法檢核 | — |
| 宣稱不實 | 0 條 | 記錯改率;降級或剔除 | 4 |
| 裸需求 | 0 條 | 補寫針對性案例(本輪無,流程保留) | — |
| 孤兒 case | 0 條 | 合理補充 → PRD v1.1;幻覺 → 剔除 | 4 |
| 無法分類(阻塞) | 12 條 | 記「依 v1 規則無法分類」;判真缺口 → PRD v1.1 | 4 |
| 未抽驗 | 350 條 | 標記未抽驗,不進任何結論 | — |
三個「0」是本輪的實測值,不是流程沒寫。而最後一列那 350 條是今天所有結論的邊界——「零幻覺」只在 run-B-01 的 74 條之內成立,另外九份我只做了機器掃描。
GAP 清單那 53 項要不要進 PRD v1.1,是閘門 4 的判讀。今天只登記,不裁決——PRD 本日不動。
還有一筆紀錄缺失要認:temperature 兩邊皆未記錄,當次沒截圖,事後不可復原。同一個提示詞在不同溫度下的分佈本來就不一樣,所以今天的分佈數字只能對照,不能當基準。
Day 9 我證明了「歧義不是 bug,而是未定的決策」,並將其裁決為十條 PRD,以為規格層已結案。
今天 AI 讀完後卻告訴我:還有十個。指出的三個共同缺口都不是刁鑽邊角(月存時點、通膨起算、大筆支出通膨),它們都會直接改變最終數字,而我的十條條款全漏了。我原以為規格考古是一次性工程。實則不然:每裁決一個歧義,就會在更精確的層次衍生新歧義。 如同 PRD-01 釘死了利率,才讓「時點」問題浮現。
規格永遠沒有寫完的一天。
更諷刺的是,今天發揮作用的,是我未曾要求 AI 做的章節。我設計的正反向追溯皆空手而回,反而是模型自附的缺口清單抓出了問題。
那我今天的角色是什麼?我想,它能列出「未定義」,卻無權裁決。要不要改、如何改、代價誰扛,是閘門 1 負責的,而那裡只能站人。AI 會指出漏洞。但它不會替你做決定。
只帶走一件事
覆蓋率 100% 只證明它標得齊,不證明它測得對。而真正值錢的那一欄,往往是你沒有要求它填的。
今天的 repo:https://github.com/eyelash500/2026_ironman_test_ai/tree/day11