iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
AI Engineering

它說得頭頭是道系列 第 7

# Day 7 — 我決定去驗證 AI 幫我寫的東西:待測物是一份文件

  • 分享至 

  • xImage
  •  

昨天結尾我說:那 18 張統整卡好到我不安。

但「不安」不是證據。它不能拿去跟任何人講,也不能拿來決定要不要用這份文件。

所以我做了唯一能做的事:把它當成待測物。


第一個問題:一份文件,什麼叫「通過」?

這是整件事最卡的地方。

系統可以測——你點下去,它有反應,反應對或不對很清楚。文件呢?文件不會「執行」,它只是一段一段的句子。你要怎麼對一段句子說 FAIL?

所以第一步不是開始查,是先把這份文件拆成一條一條可以被判定的宣稱

而拆的時候我立刻發現:它們不是同一種東西。


兩種宣稱:能被否證的,和不能的

18 張卡上的內容,大致可以分成兩類。

第一類:可查證的宣稱。

  • 「這項功能由 PR #1234 實作」 → 那個 PR 存不存在?主旨對不對?什麼時候合併的?
  • 「由 feature flag XXX_ENABLED 控制」 → 程式裡有沒有這個旗標?
  • 「改動了 SomeClass::someMethod」 → grep 下去,有就是有,沒有就是沒有
  • 「這個頁面有九個分頁」 → 我自己數一次

這一類有一個共同點:它們在外面有一個對應的真實可以比對。 比對的結果是二元的,沒有解釋空間。

第二類:不可查證的宣稱。

  • 「這個設計是為了避免使用者誤解」
  • 「這個做法比較符合現有架構」
  • 「建議後續一併調整」

這一類不是錯的,也不是廢話——它們往往是最有價值的部分。但它們沒有辦法被否證。你沒有一個地方可以去查「這個設計是不是為了避免誤解」。

我只測第一類。

這是一個刻意的取捨,而不是偷懶:一個無法被否證的宣稱,測它是沒有意義的。 我唯一能做的是「讀起來合不合理」,而那正是 Day 1 那段假規則騙過我的方式。

但這裡要講一句很重要的話,因為它後來反咬我一口:

沒被測到的部分,不等於正確。它只是這次沒有被測到。

我當時知道這句話,但我低估了它。第二類裡面藏著一種錯誤,是我這套方法設計上就抓不到的

那是 Day 9 的事。


對照組:四個儲存庫的主幹

決定了要測什麼,接下來是:跟什麼比?

我用的是本機四個儲存庫的主線分支。

為什麼是程式碼,不是原始的票卡?

因為票卡會互相覆蓋。同一個地方被改過三次,就有三段描述,而且看起來一樣有自信——你拿卡片去驗卡片,只會得到一個循環。

程式碼不一樣。程式碼沒有「三個版本並存」這種事,它只有現在這一版。它是唯一可觀測、而且不會自我矛盾的東西。

(這句話後來變成我整個作法的轉折點。Day 11 會講。)


具體怎麼查

方法很土,但每一項都是二元判定:

宣稱類型 查法 結果
PR 編號 去對應儲存庫查這個號碼 存在 / 不存在
PR 主旨 存在的話,比對它到底做了什麼 相符 / 不符
合併時間 看 merge 日期 與卡片敘述一致 / 矛盾
類別、方法、路由、資料庫遷移的名稱 grep 找得到 / 找不到
feature flag 查程式與設定 有 / 沒有
計數宣稱(幾個分頁、幾條路由) 自己數一次 相符 / 不符

沒有任何一項需要判斷力。這是刻意的——我要的是一個事後可以重跑、而且換個人重跑會得到同樣結果的檢查。

如果一個檢查需要我「覺得」,那它就不是檢查,是意見。


而且動手查的不是我,是 AI

這裡必須講清楚,否則這篇文章會變成它自己在批評的東西。

而且我要先更正自己一句話。

Day 1 我是這樣寫的:「我把那十八張卡拿去對照程式碼,一項一項查。」

那句話不算假,但它不夠精確,而且在這個系列裡,不夠精確就是不夠好。準確的說法是:

實際去搜尋那些編號、grep 那些符號名、比對合併日期的,是 AI。我做的是下指令。

我知道這聽起來很像一個笑話:用 AI 去驗證 AI 寫的東西,那不是循環嗎?

我認為不是,而理由就在上面那張表。

一個檢查會不會變成循環,關鍵不在執行者是誰,在於這個檢查需不需要判斷力:

  • 「#1305 這個編號存不存在?」——不需要判斷。它存在或不存在,而答案在儲存庫裡,不在任何人的腦袋裡。
  • 「這個符號在程式碼裡找不找得到?」——不需要判斷。grep 的結果是零或不是零。
  • 「這個設計合不合理?」——需要判斷。所以我從一開始就把這類宣稱排除在測試範圍外。

換句話說:我把「需要判斷的部分」全部留給自己,把「只需要比對的部分」交出去。

而分工線就是昨天那張可查證 / 不可查證的分類表。它看起來是在決定「測什麼」,實際上它同時也決定了「誰來測」。

還有一個判準:誰定義「什麼叫錯」

這件事我覺得更重要。

整個流程裡有幾個環節是不能外包的:

  1. 定義待測物 —— 要驗的是這 18 張卡,不是別的
  2. 定義什麼算一條宣稱 —— 哪些句子要被拆出來測
  3. 定義判準 —— 什麼樣算相符、什麼樣算不符,而且必須是二元的
  4. 決定範圍 —— 全查,不抽樣
  5. 判定結果 —— 這 8 處到底算不算錯、嚴不嚴重

AI 負責的只有中間那段機械勞動:去搜、去比、把結果貼回來。

我負責的是「什麼叫通過」。

回想 Day 2 那張 32 則留言的卡——我當時也是把東西丟給 AI,結果是它幫我寫 AC、幫我測、幫我判讀,整條流程跑完而沒有人在驗證。

同樣是找 AI 幫忙,差別在哪?

差別不在有沒有用 AI,在於判準是誰定的、結果能不能被重驗。

Day 2 那次,判準是它給的,而我沒有能力重驗。這一次,判準是我定的,而且每一項都可以被重跑——如果你不相信結果,你可以自己再查一次那個編號。

那 AI 會不會在查核時也出錯?

會。這是這套作法真實存在的風險。

它可能搜錯儲存庫、看錯分支,然後回報一個假的「查無此項」——那樣我會得到一個假的錯誤,而不是漏掉一個真的錯誤。

防線有三條,而且都很土:

  • 每一項都要附證據 —— 不是「我查過了,不存在」,而是把查詢與結果貼出來
  • 判準二元 —— 沒有解釋空間的結果,才有辦法事後被挑戰
  • 可重跑 —— 任何一條我懷疑的,我自己再查一次就知道

這三條加起來,就是我敢把執行交出去的全部理由。


這就是 QA 的本行,只是方向反過來

我想在這裡停一下,因為這件事我做的時候才意識到,而它大概是這整個系列裡我最喜歡的一個觀察。

平常的 QA 是這樣運作的:

待測物是系統,測試依據是文件。

我拿著規格、票卡、驗收條件,去點那個系統,看它有沒有照文件說的做。文件是尺,系統是被量的東西。

而這一次:

待測物是文件,測試依據是系統。

這次是拿程式碼去量那份文件,看它有沒有照系統實際的樣子寫。

尺和被量的東西,對調了。

而方法完全沒有變——一樣是拆宣稱、定判準、逐項比對、記錄不符。

這件事之所以重要,是因為它說明了一件我原本沒想清楚的事:QA 的技能不是「會測軟體」,是「會驗證一個宣稱」。 待測物是什麼並不重要。

那也是為什麼我覺得這件事該由 QA 來做——不是因為我懂 AI,是因為我習慣不相信輸出。


全查,不抽樣

還有一個決定:我把所有可驗證的宣稱全部查完,沒有抽樣。

抽樣比較省時間,而且統計上完全站得住腳——抽 20% 估出錯誤率,是很正常的做法。

但我要的不是錯誤率。

我要的是「這份文件能不能直接給人用」。

如果我抽查了 20%、發現 3 處錯,那我得到的結論是「大概還有 12 處錯在裡面,只是我不知道在哪」。那份文件我還是不敢給新人。

錯誤率是給決策用的數字;而我要的是一份可以交付的東西。這兩件事需要的證據量不一樣。


我沒有定「幾處錯以內可以接受」

先誠實講:沒有。我沒有定過那個數字。

而且我當時就知道,自己不可能做到完美。

因為就算我把每一個 PR 號、每一個符號名都查完,那也只涵蓋得到硬事實。而這批卡片本身還帶著另一種陷阱,而且陷阱不是我設計的——是資料本身就有的:

  • 新版覆蓋舊版 —— 同一個地方改過好幾次,後面的把前面的蓋掉了,但兩段文字都還好好地待在那裡,看起來一樣正確
  • 票卡名稱不正確 —— 標題寫的,跟這張卡實際在做的,不是同一件事

這兩種東西,沒有一個 grep 抓得到。要判斷「哪一段才是現在有效的」,你得先知道整段歷史。

範圍這麼廣,我幾乎可以確定自己抓不完。

而且說一句實話:連商用的大模型都會給我錯誤資訊,我也沒什麼立場要求自己 100%。

所以我的標準就是四個字:盡力而為。

我知道這在 QA 的語彙裡是很不合格的答案。但我寧願誠實說我沒有定標準——也不要事後才定一個剛好及格的數字。 後者是這一行最常見的自我欺騙:測完再決定什麼叫通過,那個門檻一定會剛好落在你的結果下面一點點。

而且我現在回頭看,那一天其實不算「驗證」

當時我以為我在做驗收。

現在回頭看,那天真正發生的事情是:蒐集資料。

我查到的那 8 處錯誤,最後沒有變成一份「請修正以下項目」的清單。它們變成了證據——關於「這個做法會在哪裡失敗」的證據。

而那批證據,後來決定了整個知識庫的架構。

(真正拿問題去逼它回答、看它會不會亂牽拖,是一個多禮拜以後的事了。那部分在 Phase 5。)


這整件事,其實像在蓋一座圖書館

我後來想到一個比喻,它大概是我對這個專案最準確的描述。

這座圖書館裡的書,就是那些票卡。 有舊的,有新的。

而關鍵是:每一本在它被寫下來的那一天,都是正確的。

它們不是錯的書。沒有人寫錯什麼。只是時間一直在往前走,於是有了新的情況、新的決定、新的觀點——然後又多出一本新書,擺在舊書旁邊。

兩本書都在架上,都是真的,都很有自信。

那要怎麼知道哪一本才是「現在」?

目前為止,方法只有兩個:看出版日期,還有——

我的腦袋。

除了日期以外,判斷哪一本書才是最新版資料的唯一途徑,就是我記得。

而這句話,就是整個專案要解決的問題的全貌:

我是那個唯一的索引。而我會忘記、會漏看、會請假,而且總有一天會離開。


我真心希望現在是《攻殼機動隊》的世界。那樣我就可以去偷一顆愛因斯坦的腦,請它來當這座圖書館的管理員。

前提是它沒有失智。

但現實是,我手上只有一台沒有獨立顯卡的筆電。

所以這座圖書館真正需要的,不是一個更聰明的館員——

是一套不依賴館員記憶的排列方式。

(這個比喻在 Day 11 會回來,而且那時候我會發現:問題不只是書怎麼排,而是我一開始就走錯了房間。)


為什麼這一步不能省

最後講一件很實際的事。

這件事花掉的時間,比讓 AI 產出那 18 張卡多得多。產出是幾分鐘,驗證是好幾個小時。

那我為什麼不直接用?

因為這份文件的下游是人。它會被拿去當測試依據、被新人當教材、被引用到別的卡片裡。文件跟程式不一樣的地方在於:程式錯了會壞給你看,文件錯了會被相信。

而且錯誤在文件裡是會複製的。一條錯的敘述被引用三次,就變成三個地方都錯,而且後來的人會覺得「三個地方都這樣寫,那應該是對的」。

Day 1 那件事就是這樣發生的——那條錯的敘述沒有停在文件上,它走到了一個新人手上,變成一份假的測試失敗回報。

驗證的成本是一次性的,不驗證的成本是複利的。


明天

明天講結果:8 處錯誤。

包括一張卡上寫著「此問題尚未修復、尚未看到 PR 紀錄」——而那兩個問題都已經修好合併了,合併日期還早於卡片的更新日。

還有三個 PR 編號,在對應的儲存庫裡根本不存在。


上一篇
# Day 6 — 89 張卡變成 16 張:我讓 AI 幫我做一份「現在長怎樣」的說明書
系列文
它說得頭頭是道7
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言