當修改一行給 AI Agent 的 prompt 後,Agent 出錯時不一定會報錯,即使中間出錯,Agent 還是會把整個流程照樣跑完,而它不會告訴你哪些細節沒做。
以昨天那張優惠碼功能為例,用 AI 處理原始 ticket 為例,、一張結構化 ticket 出來,這隻需求釐清 agent 看起來會動了。今天要講的是「看起來會動」有多脆。
改的是哪一行不重要,這裡也不比哪個 prompt 寫得比較好。重要的只有一件事:同一張卡進去,出來的東西換了樣子。
會換樣子的改動通常很小,小到 code review 沒人會停下來。像是把 AC 檢查那句指示從「每一條 AC 對三類問題各檢查一次」收成「檢查 AC 有沒有明顯問題」;或者連檢查那段都沒動,只在結尾補一句「輸出簡潔為要」。需求方的原話往往只是「它寫得太囉唆了」。
這種改動會怎麼影響那張優惠碼的卡?
這幾條都是「會」跟「可能」,是這種改動允許發生的事,不是量出來的數字。
真正最棘手的不是 Agent 變了,是 Agent 變了以後看起來一模一樣。
昨天那份我寫的期望結果裡有這一行:
3. 優惠碼可以無限次使用 → 彼此衝突(與 2)、與描述不符(description 說不要被亂用)
箭頭後面那兩個就是挑出來的問題,掛在這一條 AC 上,它同時中了兩類。把「與描述不符」拿掉、再數一遍:行還在,後半段短了十幾個字,行數一個沒少,四個區塊(Goal、Scope in、Scope out、AC 標註)一個都沒變空。
整張卡挑出來的問題從 4 個掉到 3 個。但這個「4」沒有被寫在任何地方,所以掉成 3 的時候,沒有東西會不一致。
攤開來對比,只有一個數字不一樣:
Goal:有 → 有
四個區塊:都在 → 都在
行數:一行沒少
挑出來的問題:4 個 → 3 個
四項裡只有最後一項不一樣,少的那個是「與描述不符」。
實際發生的事是:程式沒有拋 Exception,流程跑完,卡片被更新,post_comment 這個 Tool 照樣 tag 了相關的人。打開卡片的人看到 Goal 清楚、Scope 分好、有問題的 AC 也挑出來了,第一個反應是「喔,它有做事,看起來沒問題!」。要看出少了一條,前提是手上有另一份同一張卡的舊輸出可以擺在旁邊,而那舊紀錄份東西沒有人留著。
漏掉一條沒挑出來,agent 這邊不會有任何事,代價落在後面接手的人身上。
「結帳流程要順」那條不可驗證的 AC 沒被挑出來,就照原文進了 sprint。工程師只能自己解釋什麼叫順,做了個 loading 動畫;驗收當天需求方說不是這個意思,來回兩輪。活動日期也不能延後,變成要砍掉別的功能來爭取重做的時間。
「順便看一下結帳頁很慢」沒被劃到範圍外,就留在這張卡裡,有人花兩天去追效能;優惠碼的過期處理反而沒人做,而那條是原本 7 條 AC 裡寫得最完整的。
真正的代價是需求方那一句「這張已經釐清過了」。他相信這隻 agent 看過了,所以自己不再看第二遍;工程師相信需求方看過了,所以照 AC 做。兩邊都以為有人檢查過,實際上檢查那一步在某一次 prompt 改動之後就縮水了,而那次改動的 commit message 寫的是「讓 agent 的輸出簡潔一點」。
手動再跑一次,看到的是一份看起來合理的輸出,於是那次 prompt 改動就上線了,這正是 Day 1 那個場景。手動跑抓得到「壞掉」,抓不到「變差」。要抓到變差,得知道上一次同一張卡的輸出長什麼樣,而那份東西一開始就沒有被留下來。
這個動作到系列後面會再做一次 —— 同一張卡、同一行 prompt 改動,但那時候手上已經有東西可以比,改完當場就看得出來這次有沒有弄壞什麼。
到了這裡,大家會想說:那我寫測試不就好了?明天會講到這個:為什麼對一次呼叫寫 assert,抓不到這種問題。