iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
AI Engineering

它說得頭頭是道系列 第 11 篇

# Day 11 — 我一開始就選錯了 source of truth

  • 分享至 

  • xImage
  •  

前面兩天講的都是「它為什麼會錯」。

Day 9 說錯誤發生在接線,不在零件。Day 10 說那是因為資料缺了一個維度。

今天要講一件更難堪的事:

那個維度之所以缺,不是因為誰漏寫了一欄。是因為我從一開始,就拿錯了那份資料。


我原本的作法

一句話就講得完:

我把票卡餵給 AI,要它產出一份「這個系統現在長什麼樣子」的說明書。

現在寫出來看起來很蠢,但我想誠實說明為什麼當時覺得這是對的——因為那個理由到今天都還很有說服力。

第一,卡片是現成的。 一百多張,已經在那裡了,不需要任何人再寫一次。

第二,卡片是文字。 它是 AI 最擅長處理的東西:自然語言、有上下文、有前因後果。

第三,卡片裡有「為什麼」。 程式碼只會告訴你現在是什麼樣子,不會告訴你為什麼當初這樣決定。而那些理由,全部躺在卡片的描述和留言裡。

第四,那是唯一有人在維護的地方。 沒有人在寫規格文件,但所有人都會開卡。

四個理由都是真的。我到現在都不覺得它們錯。

錯的是我從這四點推出來的結論。


票卡到底是什麼東西

我後來花了一點時間,才把這件事想清楚。

票卡不是現況的描述,它是意圖的紀錄。

而且是一種很特別的意圖紀錄——會互相覆蓋的。

一張卡開出來的時候,它說的是「我希望它變成這樣」。這句話在那個時間點是有效的。但三個月後,同一個地方被另一張卡改掉了,第一張卡那句話沒有被刪掉,也沒有被標記過期——它就靜靜地躺在那裡,長得跟昨天剛開的卡一模一樣。

我想了一個比喻:

票卡是航海日誌,不是地圖。

日誌上的每一行都是真的:某天往東航行、某天遇到暴風、某天改道往北。

但你沒有辦法從日誌上讀出「現在船在哪裡」——除非你把每一行都按順序讀完,而且每一行都正確理解了它對前一行的修改。

而我做的事情,就是把一整本航海日誌交出去,然後說:「幫我畫一張地圖。」


我其實是同時要求了三件事

把這個任務拆開來看,它不是一件事,是三件:

第一件:排序。
哪一張卡在前、哪一張在後?時間戳是有的,但時間戳只告訴你「開卡時間」。一張三個月前開的卡,可能上週才被實作;一張上週開的卡,可能還沒動工。「寫下來的順序」不等於「生效的順序」。

第二件:覆蓋判斷。
就算順序對了,你還得知道後面那張卡跟前面那張是什麼關係——是推翻、是補充、還是根本在講不同的地方?這個判斷需要理解兩張卡實際改動的範圍有沒有重疊。而那個範圍,卡片上通常不寫。

第三件:頁面歸屬。
就是 Day 10 講的那個缺掉的維度——這件事到底發生在哪一頁。

三件事,沒有一件的答案在那批資料裡。

而我當時的作法是:把三件事一起丟出去,要一個乾淨的結果。

它給了我一份乾淨的結果。那份結果裡有 8 處錯。

我到現在都覺得,那 8 處其實算少的。


那正確的做法是什麼?

而真正該問的,其實是一個很簡單的問題:

「這個功能現在長什麼樣子」——這個答案,到底住在哪裡?

不在卡片裡。卡片講的是「我們想讓它變成什麼樣子」,而且是好幾個時間點的版本疊在一起。

答案住在兩個地方:

  • 程式碼 —— 它就是現在正在跑的東西,沒有歷史包袱,沒有「上一版也這樣說」的問題
  • 實際畫面 —— 打開測試環境,那一頁長什麼樣就是什麼樣

這兩個東西有一個共同的性質,而這個性質正是卡片缺的:

它們是可觀測的。它們只有一個版本,就是現在這個。

程式碼不會同時存在三種寫法讓你猜哪個是現行的。你 checkout 下來,看到什麼就是什麼。

所以我把整件事翻過來:

原本 修正後
「現在長什麼樣」 從卡片推論 從 code / 畫面直接讀
「為什麼是這樣」 混在同一份輸出裡 從卡片查,而且只查這個

這不是把卡片丟掉。卡片還是不可取代的——因為「為什麼當初決定不做另一個方案」這種事,程式碼裡一個字都沒有。Day 3 我寫過,那些被否決的決定是最容易消失的知識。

差別在於分工:

可觀測的事實,去可觀測的地方拿。
不可觀測的理由,才去卡片裡挖。

但我要先承認 code 也有一個很大的洞

我不想把「以 code 為準」講得太漂亮,因為它有一個很硬的限制,而我是撞上去之後才知道的。

程式碼會告訴你現在是什麼樣子,但它不會告訴你「為什麼還沒做完」。

這個網站有些功能是沒有做完善的。有些地方看起來像 bug,其實是刻意留的缺口;有些欄位空著,是因為在等另一個系統;有些流程只做了一半,是因為當初的決定就是先做一半。

而這些事情,程式碼上通常不會有註解。你讀到的就是一段「看起來沒寫完」的實作,而它沒有任何地方告訴你:這是還沒做,還是不打算做,還是做不了。

所以完整的圖其實是三層,不是兩層:

你想知道的 去哪裡拿
現在長什麼樣 code / 畫面——可觀測,只有一個版本
為什麼當初這樣決定 卡片——理由與否決紀錄
為什麼這裡還沒做完 常常兩邊都沒有

第三行是最難的,而且它沒有便宜的解法。

我後來的處理方式很土:遇到這種地方,就標記起來,不猜。 寧可在文件上留一句「這一段目前的狀態需要確認」,也不要讓 AI 去填一個合理的解釋——因為那正是 Day 10 講的,它會把空格填滿。

一個誠實的「不知道」,比一個通順的猜測有價值太多了。


而對不上的地方,才是真正的產出

這是我後來覺得整個專案最有價值的一個副作用。

當你把兩邊分開抓,一定會遇到對不上的情況:卡片說它應該是 A,但畫面上它是 B。

我一開始把這當成雜訊,想辦法要消掉它。

後來才發現:那才是我真正該交出去的東西。

那份清單上的每一條,都是一個「規格與實作已經分岔」的位置。而它們的成因不只一種:

  • 卡片開了但沒做完
  • 做了但跟卡片講的不一樣
  • 後來被另一張卡改掉,而原卡沒有更新
  • 或者——卡片的敘述本來就寫錯了

最後那一條特別值得停一下。Day 9 那個退單的案例,收尾是「回頭修改那張票卡的敘述」——那就是一條規格漂移,而且方向跟直覺相反:錯的是規格,不是實作。

我想強調一件事:

這份清單不是 bug list。

Bug list 的預設是「實作錯了,去修」。但這份清單上有一半的條目,正確的處理方式是去修卡片,不是去修程式。

所以我後來叫它規格漂移清單——它只回答一個問題:「這兩邊在哪裡對不上」,至於哪一邊該改,那是下一步的判斷,不是它的工作。

而這個東西,原本的做法永遠產不出來。因為當你只用卡片當唯一來源,你連「對不上」這件事都看不見——你沒有第二個來源可以對。


諷刺的地方

寫到這裡,我必須承認一件有點好笑的事。

Day 7 和 Day 8 我花了兩篇在講,我怎麼驗證那 18 張統整卡:把每一條宣稱拆出來,去程式碼裡比對,判準二元,每一項都要附證據。

那就是我上面講的方法。

一模一樣:以 code 為準、逐條比對、對不上的列出來。

我當時以為我在做「品管」——一個產出之後的檢查步驟。

我沒有意識到,我手上那個檢查方法,其實就是正確的生產方法。

換句話說:我花了很大的力氣,建了一條產線,做出一批有瑕疵的成品;然後又花了很大的力氣,建了一套檢驗流程去抓瑕疵。

而那套檢驗流程如果一開始就拿來當產線,那批瑕疵根本不會產生。

我後來想過為什麼會這樣。我的結論是:

驗證和生產,在我腦袋裡本來是兩件事。

生產是「從無到有」,我直覺覺得那需要創造力,所以交給 AI;驗證是「對答案」,我覺得那是苦工,所以我盯著。

但在這個任務上,「從無到有」根本是個假象——答案已經在程式碼裡了,沒有人需要創造它。需要的只是把它讀出來、放對位置。

那從頭到尾就是一件對答案的工作。


說出來有點好笑:其實沒有那個「轉念的瞬間」

我原本想把這一段寫成一個頓悟的故事。但我回頭看,發現事實比那個無聊,也比那個誠實。

因為有兩件事從頭到尾同時成立,而且從來沒有和解過:

我一開始就希望 AI 可以把所有事情都包下來。

而我一開始就不信任它。

所以最早的版本其實是反過來的——AI 負責整理,而檢查是我自己做的。

不是因為我想得多深,單純就是不信任而已。

而不信任的結果,是一套追問法

因為不放心,我發展出一個習慣:同一件事,用不同的方式問它三次。

舉個具體的例子。假設它跟我說:

「這張卡上的 A 頁面有一個新功能,所以 B 頁面也有這個功能。」

我不會接受這個答案。我會反問回去:

  • 你幫我查查,B 頁面有沒有自己的卡片?
  • A 那張票卡,有沒有寫明說 B 頁面要有這個功能?
  • 你是怎麼認定的?
  • 這是最新的資訊嗎?
  • 有沒有其他票卡有附註?

當時我以為我在做的事情是「防止它唬爛我」。

但現在回頭看,這五個問題全部在問同一件事:

你這個說法,是從哪裡來的?而那個來源,是不是現在的?

那就是 source of truth。我只是不知道我在做這件事。

我以為我在防守。實際上我在做的,是逼它把結論和來源分開放——而一旦分開放,哪些結論站得住、哪些站不住,自己就浮出來了。

這也解釋了上一節那個諷刺:我手上一直有正確的方法,只是我把它當成防禦手段在用,沒有意識到它可以當成建構方法。

真正出事的那一次,剛好是我沒有這樣問的那一次

而事情真正翻船,是在順序被打亂的時候。

新人的那批 error 回報進來,我被其中一條帶偏了。那一次,我沒有自己去查,我請 AI 去查。

Day 9 那條鏈的中段就是這件事——它給了我一個支持錯誤方向的答案,然後我開了一張不該開的單。

現在把兩件事並排,那個好笑的地方就出來了:

我怎麼做的 結果
平常 不信任 → 自己檢查 → 追問到底 沒出事
那一次 被帶偏 → 交給它查 → 沒有追問 退單、RD 白做工

我唯一一次把查證交出去,就是唯一一次出事。

但我想很小心地講清楚一件事:這不是「AI 不可信」的故事。

真正的問題不在它身上,在於我那次帶著一個已經有答案的問題去問。我沒有問「B 頁面到底有沒有這個功能」,我問的是「是不是少了前期數字」——問題裡就已經藏好了我要的答案。

那五個追問之所以有效,不是因為它們很聰明,是因為它們逼的是來源,不是結論。 而我那次問的,是結論。

一個我有點不好意思寫出來的感覺

最後講一件很小、但我想留在這裡的事。

這樣問久了,我心裡其實有一點過意不去——我很怕一直這樣追問下去,會打擊到它的自信心。

我知道這句話不精確。它沒有自信心可以被打擊,那只是我的投射。

但那個感覺是真的,而且我覺得它透露了一件事:我已經在用對待同事的方式對待它了。 這件事後面還會再出現一次(Day 14)。

而感覺歸感覺,做法沒有變過。為了確保每一條都是對的,我只能繼續這樣問。

我後來對這件事的結論是:追問不是不信任的表現,它是把工作交出去的前提。


明天:於是我開始決定什麼不給它

選對來源之後,下一個問題馬上就來了。

如果事實要從 code 和畫面抓,那意味著我得讓 AI 接觸到更多東西——更多程式碼、更多環境、更多實際資料。

而這裡有一條線我不打算跨過去。

有些資料,我看過之後的結論是:這個不能餵。

明天那篇要講的是,那條線畫在哪裡,以及我是怎麼決定的。


上一篇
# Day 10 — 它不是抓錯,是資料缺了一個維度
下一篇
# Day 12 — 有些資料我決定不餵給 AI
系列文
它說得頭頭是道 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言