Postmortem Agent 實作筆記 · 第 5 篇

不是 prompt 寫得不夠好,是答案不在輸入裡

五次修改 prompt、換模型甚至重做架構後,我才發現:當判斷依賴文字以外的組織脈絡,再好的 prompt 也找不到不存在於輸入中的答案。

前面幾篇,我講了關於後續優化領域以及 postmortem note 的評分方式,接下來就踏上了正式修 prompt 之旅。我心想,最艱難的時刻終於過去了,接下來應該一切都會很順利吧⋯⋯但結果並沒有。有一個後續優化領域的 prompt,不管怎麼改都改不好,它的分數一直趴在那,不見起色。到底是關於這個標籤要不要掛的定義太模糊?還是 agent 需要更多資訊?

我前後嘗試了五種不同的寫法,也換過模型,甚至換了個架構,但都沒有解決核心問題。最後我才發現,問題可能並不是因為 prompt 寫得不夠好。
有些問題 agent 答得很好,因為答案就在它讀得到的文字裡;有些問題它答不好,是因為答案依賴它沒有讀到的團隊背景脈絡。
不過,在講這些 agent 教不會的事之前,先來聊聊教得會的事吧。

修得好的事

在第三篇文章《多選題怎麼訂正?關於我如何修正後續優化領域的 prompt》中曾提到,當時我修了三個版本的後續優化領域 prompt。
在這之後,prompt 又陸續地修了好幾輪。不過在這好幾輪之中,我都專注於後續優化領域,關於 postmortem note 的 prompt 則是一個字也沒有動過。後來後續優化領域修得有點卡關,於是我決定先從 note 下手。

在第四篇文章《申論題怎麼評分?關於我如何驗證 AI 閱卷老師》中,note 的分數不是還不錯嗎?為什麼還要修?
目前 note 的分數看起來還算可以,只是偶爾會出現幻覺、或者是偶爾會把未確認的事寫得像是確認過一樣。但當 issue 的數量超過 1,000 筆,即使是偶發事件,長期的累積也會造成 note 的品質不佳。所以我的目標只有一個:要讓 note 變得更誠實。
在 55 筆資料、跑三輪的結果中,根因正確率從 87.3% 上升到 94.4%,幻覺率(例如:討論串中沒有提及問題發生的時間,但 note 自己編了一個)從 10.9% 下降到 7.4%,危險幻覺(例如:編造根因)也清零。

但是在這過程中,發生了另一件更奇怪的事:我明明只有改 note 的 prompt,但後續優化領域的 recall 分數卻莫名其妙地崩了。

拆解原因

一開始,我懷疑會不會是後續優化領域跟 note 的 prompt 在多次的版本更迭之中產生了神奇的化學效應,互相影響,導致 recall 分數降低。
為了找出原因,我採取一次只改一個變因的方式——我把標籤區還原到除了一句 Playbook 判斷之外,其他內容都與 v3 相同。但 note 的 prompt 則不變,維持在目前的狀態。跑分後我發現,即使標籤區已經幾乎與 v3 相同,跟當時跑 v3 的分數還是差了一截。

在拆解原因後,剩下的嫌疑犯,就是我在 note 的 prompt 中加上的「更誠實」的謹慎原則:

資訊不足判準:「根因只有猜測、沒查出原因就請客戶自行處理、自然恢復沒人追、不了了之——請只寫『資訊不足,無法完整分析。』」
觸發條件陷阱:「『有人指出觸發條件、當事人回對』不等於根因被確認」
錨點要求:「PR 編號、模組或指令名稱、錯誤訊息、關鍵數字,必須是 thread 中實際出現過的原文」

我懷疑是這個段落造成後續優化領域的 recall 分數異動,所以我在這段 prompt 之後加上了個防火牆:

標籤判斷獨立於 note:上面關於 note 的謹慎原則(根因需被確認、資訊不足要明說)只適用於 postmortem note,不適用於標籤。即使這筆的 note 是「資訊不足」,標籤照樣依下列定義判斷;掛標籤看的是 thread 中的合理跡象,不需要「根因被確認」等級的證據。

啊哈!recall 分數就這樣回來了。
即使我在 prompt 中有明確區分後續優化領域以及 postmortem note 的區塊,但 agent 還是不小心讓 postmortem note 的 prompt 影響到其他區域了。
同時,我觀察到標籤的 exact match 從 41.8% 降到 38.2%,但這個差距接近隨機波動,無法確定是這次修改造成的。
這次修改過後,agent 寫出來的 note 可信度更高。同時我也了解到,這個誠實化是教得會的。因為根因是否被證實、PR 編號是否出現,都是在討論串中就有的內容。

修不好的事

講完 note 修得好的事,接下來就來講講修不好的事吧。
後續優化領域裡面有個類別,叫做「產品技術改善」。要不要掛這個標籤,取決於一個判斷:這個問題是不是已經被修好、或是已經有人在接手處理了?已經有人接手處理的,就不掛;懸著沒有人認領的,才掛。它是六個領域裡唯一完全依賴這個判斷的,也是標籤多掛的大戶——49 個多掛的標籤裡,有 32 個是它。
第一次,我把規則寫成散文,試著告訴 agent 修完的、有人接手的不掛。

⋯⋯且 thread 結束時該改善還懸著、沒有人處理。兩種常見的「不掛」情境:(1) 修復已在討論串內完成(hotfix/PR 已合併部署、資料已修正、設定已調整)且沒有人提出剩餘的改善事項——完成的修復不是 follow-up;(2) 改善事項已有人接手——明確派工且被接下、已開 PR 處理、或已另開單追蹤⋯⋯只有「被指出但沒人認領」的改善空間才掛。

結果是雙輸——「產品技術改善」的 recall 直接從 76% 掉到 40%。而且更糟的是,這篇散文的影響力不容小覷,連我沒有動的「監控」領域,recall 也從 86% 掉到 43%。為了讓 agent 在一個特定領域忍著不要掛標籤,結果它連其他領域都跟著一起縮手了。

第二次嘗試,我試著把規則說明得更精準,還加上了反向保險。

但注意:主要修復完成不代表不掛——修復之外,討論串中還有被指出、而沒有人認領的結構性改善空間的,照掛。

會加上這條規則,主要是因為有些 issue 有急迫性,需要先靠一些暫時解法來解決當下的問題,但是後續還是要把其他東西撿回來修。在第一次嘗試裡,recall 掉得這麼誇張,有一部分就是這種「先應急」的 issue 沒有掛上這個標籤。
這次的結果算是半贏,「產品技術改善」算是有回穩,但這種「已修復就縮手」的指示,還是滲透到其他領域去了。

第三次嘗試,我放棄跟 agent 講道理,直接給四個判斷範例讓它參考。以下是其中兩個判斷範例:

修復 PR 已合併上線、確認沒問題後結案,沒有人提出其他改善事項 → 不掛(完成的修復不是 follow-up)
主要修復已完成,但另有人指出結構性問題而沒有人認領 → 掛

結果這次連「增加 Log」這個領域都被拖下水。

文章一開始,我提到我總共進行了五次修改,「產品技術改善」就佔了三次。另外兩次,我改教「可加入 Playbook」什麼時候不要掛。
模型確實讀懂了要找什麼,recall 從 30% 上升到 80%;但誤掛也從 1 筆增加到 61 筆。
下一版加上三條煞車後,我拿 15 筆 issue 試跑。上一版誤掛的 8 筆中,這次仍然誤掛了 7 筆。
它看到了煞車,卻沒有踩下去。

五次修改,表面上改的是不同規則,最後卻以同一種方式失敗:模型無法選擇性地忍住。有時抑制會外溢到其他標籤,有時煞車寫在 prompt 裡,卻根本沒有被執行。

那換個模型呢?

既然這個模型怎麼答題都答不好,會不會是這個題目對它來說太困難了?那就換個更新的模型試試吧。
我回到第二次嘗試教會「產品技術改善」的那個版本,請了新的模型來掛標籤,結果沒有更好。原本 49 個多掛,結果直線上升到 68 個多掛,而且 exact match 掉到 27%。

這一次嘗試,讓我了解到換新的模型並不一定會更好。
也有可能新的模型適合其他的描述方式,但這樣等於要從頭開始修 prompt,因此在現階段我還是決定採用原先的模型。

既然講不聽,那就換個問法吧——兩段式架構

不論怎麼修改 prompt,agent 與人類的判斷都還是會有差異。那如果直接換個問法呢?

我決定試著不讓 agent 直接判斷掛不掛,而是拆成兩段式的架構:
第一段先讓它從討論紀錄裡抽出結構化的事實:問題修了沒、有沒有人接手、有沒有 PR。
第二段則是交給 Python 的規則處理,來決定要不要掛這個標籤。第二段的判斷一旦寫成程式碼,同樣的輸入就會得到固定結果,而且還可以寫測試。

這套規則裡,有一個我認為蠻有把握的依據:23 個測試案例,每一個都是我審核 golden dataset 時的真實裁決。舉例來說,有一筆我實際做過判斷的 issue:在討論串中同意「之後再討論」,算是懸著的問題,這個 issue 應該要掛上「產品技術改善」。
在修改問法的同時,它成為一個 pytest 的案例,判斷標準改動的時候,我只要跑一次測試就知道過去的自己跟現在的自己判斷標準是否有不同。這也是我的判斷邊界第一次有了可執行、可回歸的形式。

但跑出來的結果,分數還是不太理想。
真正失守的不是 Python 規則,而是第一段的事實抽取。以「可加入 Playbook」為例,golden dataset 裡只有 10 筆案例被掛上這個標籤,模型卻在 101 筆中找出 54 筆它認為「可複用的處理方式」;在「產品技術改善」上,它也會把懸著與已接手的狀態雙向判錯。第二段規則雖然穩定,吃到的卻是不可靠的事實。換較新的模型擔任抽取器,結果反而更差。

在我後續覆盤 agent 的答案時,我發現,「產品技術改善」以及「可加入 Playbook」這兩個標籤需要的,像是「這個問題有沒有人接手」、「這件事情值不值得寫進指引裡」,這些資訊存在於團隊工作流程、另一個 ticket 系統,以及線下討論中。

為什麼 note 可以教得會?因為 note 是個總結的過程,它的答案都在內文裡。但是後續優化領域還需要許多額外的背景脈絡,像是這個問題對團隊來說值不值得修、討論過後決定要不要修,而這些資訊都是單一則討論串裡面不會出現的內容。

postmortem note 是有錨點的轉述;標籤則是無錨點的裁決。

最後,我沒有把這套兩段式架構放進正式流程裡,而是退回到原本的 v9 prompt。不過這些判斷規則以及 23 個測試案例被我保留了下來,也許未來還有機會能夠讓它重出江湖。

意外的貢獻:兩個架構抓出標準答案的問題

在修 prompt 上受挫,讓我開始懷疑,會不會其實是標準答案從一開始就錯了?
抱持著這樣的懷疑,我在整理上面的兩段式架構結果時,發現到一個奇怪的現象:v9 prompt 和上面的兩段式架構,在某些題目上給出了相同的答案,但這個答案卻跟 golden dataset 不合。
這個發現加重了我對標準答案的疑心。

重新審查一次

我把這些有嫌疑的題目挑出來,大約 10 筆,遮住過往我判斷過的標籤,重新盲審一次。這一次盲審,揭露了兩種不同的問題:
第一種問題出在 golden dataset 中。10 筆盲審案例中,有 5 筆的標籤跟我最初的判斷並不一致。重新比對我的判斷理由之後,我決定更新 golden dataset 中原本的內容。修正後,exact match 上升了約 3 個百分點——只改 5 筆資料,就足以明顯移動整體分數,也顯現出標籤層的指標非常依賴 golden dataset 的品質。

不過,這仍不足以解釋前面實驗的整體失敗。

第二種問題則出在規則文字。在這 10 筆案例中,另外有 5 筆集中在「操作體驗」。盲審時,我認為「客戶用錯我方介面」本身就是值得追蹤的改善訊號,因此應該掛上標籤。翻回一個月前建立 golden dataset 時留下的判斷理由,當時的判斷與現在一致。
也就是說,這次沒有發生第二篇《用 100 筆維運資料,建立 Golden Dataset》提到的「我比自己想像中更不一致」。我的判準前後一致,真正產生矛盾的,是 prompt 裡的規則文字沒有準確描述我的想法。
所以,我把「操作體驗」的規則改成:

確認為「客戶對我方介面、API、設定的使用方式錯誤或認知落差」所致的 issue,即使討論串內無人提出改善、客戶已自行修正,仍掛——此類落差本身即系統側(文件、提示、防呆)的改善訊號。

這次之後,我也決定往後每次判斷標籤時,都附上判斷理由。標籤本身只有答案,理由才能夠留下當時的判斷邊界。

既然修不好,還是乾脆直接移除?

在我覆盤後,我得到了「答案不在討論串裡」的這個體悟。於是我在想,既然 agent 判不準「產品技術改善」這個標籤要不要掛,那是不是乾脆把這個標籤從後續優化領域中移除就好了?
我先拿出之前的結果進行離線計算,在計分時忽略「產品技術改善」這個標籤,結果 exact match 從 43.6% 直升到 71.3%。

最直覺的方式:直接刪掉

第一次實作時,我用了最直覺的方式:直接把這個後續優化領域從 prompt 中移除。
結果並沒有如剛剛計算的那麼美好。原本多掛「產品技術改善」的傾向沒有消失,只是搬家了。
在進行離線試算時,誤掛應該只會剩下 16 個標籤,但這次實際移除後,誤掛卻一路噴到 47 個。「監控」也從原本的 5 筆誤掛變成 15 筆,連「可加入 Playbook」都冒出 11 個誤掛。
看來 agent 想掛標籤的衝動並不會因為少了一個選項而消失,而是轉移到其他標籤身上。

最後的解法可能聽起來有點怪:我把「產品技術改善」的定義留在 prompt 裡,agent 想掛就讓它掛,但是最後寫回 Notion 時,程式會默默地把這個標籤丟掉。如果我無法影響 agent 的行為,那就在系統層處理它的輸出吧。
這個解法讓誤掛從 47 降回 22 個,exact match 為 66.3%。最後我決定,就用這個版本上線吧。這個版本的配置正好就是離線模擬測量的配置,而 66.3% 也落在預測的 65%~70% 之間。離線模擬、直接刪除選項的反例,以及重新放回定義後的復原,三次都支持了「掛標壓力恆定」這個推測。

最終的正式跑分

最後的正式跑分,101 筆 golden dataset、跑三輪,exact match 平均為 67.3%,每一輪波動約正負 4.7 個百分點。
不過,這一次的 exact match 並不能跟第三篇的 48% 直接相比。因為跟第三篇時比起來,golden dataset 從 27 筆增加到 101 筆,而且又默默移除一個領域。真正能夠在相同條件下直接比較的,只有前述離線模擬中的 43.6% 與 71.3%。

以下是每個後續優化領域以及 postmortem note 最終跑分的結果:

後續優化領域PR
可加入 Playbook56%45%
增加 Log29%50%
監控79%85%
外部資料規格72%86%
操作體驗60%38%
postmortem note 指標結果
格式相符率100%
Q1:問題描述正確率99%
Q2:根因正確率94.9%
Q3:解法描述正確率99%
Q4:幻覺率6.1%
資訊不足誠實率97%

註:除了幻覺率越低越好,其餘指標皆是越高越好。

目前仍有幾項已知限制:

  • 「操作體驗」的 recall 只有 38%。我曾試著把「操作體驗」的定義放寬,但它的 recall 上升時,「外部資料規格」卻從 81% 降到 57%。連續兩版都出現類似的領域互相排擠,最後只好撤回修改,先接受這個限制。
  • 「增加 Log」在 golden dataset 中只有 4 筆,分數本身並不穩定。

考量這套工作流程本來就是「AI 建議、人類裁決」,即使仍有這些限制,現階段的結果已足以協助我找出值得檢查的案例。在總共修了 17 個版本的 prompt 之後,我決定正式上線,開跑真實資料。

回到起點

一路走完這五篇文章,第一篇文章《為什麼我讓 AI 只做建議、不做決定》的標題,對我來說又多了一層意思。不只是整個專案的走向要交由人類決定,連「哪些問題該交給 AI」,本身也是需要人類做出的決定。
就我在寫這篇文章的同時,AI 仍在不停地發展。也許半年後、一年後,我曾經遇到的困難、或是我們原本認為很難達成的事,都能夠迎刃而解。
在我建立這個 agent 時,AI 依舊比較適合處理有錨點的工作:轉述、抽取文中有明確證據的資訊,以及能從輸入中找到證據的初步判斷。我想,在人類能夠把組織脈絡文件化之前,這類的裁決,仍然比較適合由人類來做決定。
而 prompt 也不是什麼神奇的萬靈丹,能夠讓 AI 得到不存在於它的脈絡之中的東西。

一路走到這裡,終於可以正式開跑過去的 issue 了。關於這個 postmortem agent 的系列文章,也差不多到了尾聲。
下一篇想分享這個 agent 正式上線後,我們做了些什麼。

Comments