skill 也要有考卷:我給 skill 上了回歸測試
寫程式改壞了,編譯器、型別檢查或單元測試會立刻叫;但改壞一份 skill,多半沒人發現。
skill、command、規則檔這類檔案,都是會被 AI 當成執行依據讀的:寫的是 agent 的行為規則,本體卻是純文字。改壞了只會產生語意偏差,不會噴出任何語法錯誤。而且這些檔案日常多半是 agent 在我授意下改的,改壞了它照樣面不改色回報「改好了」,直到系統在某個任務默默翻車。
給 skill 出份考卷
做法其實很直覺:給 skill 配一份回歸考卷。一份考卷包含一組真實場景的輸入提示,加上一組判定準則。每次改完就跑一輪:把場景輸入餵給 agent 實際跑一遍,再把過程紀錄交給另一個模型,逐條對著判定準則打分。
這套機制確實抓到過缺陷。有一個負責整理並存檔工作狀態的 command,在同一組判定準則下兩度被判不合格,失分的原因很具體:使用者交代要保留的內容沒有進「必留」段落,該保留的非機密欄位也連同機密一起被刪掉了。
追查發現,根因是該 command 檔內建的自我檢查條款彼此打架。這裡有個要小心的地方:那兩次失分之後考卷都曾轉綠,但轉綠與失分之間其實沒有任何對應的檔案修改,真正的修正是在十一天後改了自我檢查的條款文字,並補上對應的測試。所以重跑轉綠不能當成修好了:那兩次失分是真的,沒有修改就轉綠,反而說明綠燈自己也會漏掉問題。也別把這種情況跟後面要講的誤報搞混:這兩次失分有具體的規格違規內容可以指認,跟「整批零分」那種環境抖動是完全不同的形狀。
出題的四條硬原則
幫 skill 出考卷,摸索出四條原則:
- 題目從真實踩坑案例來:考題直接取材自已經發生過的真實事故,不做憑空想像的假題目。
- 判定準則斷言行為,不斷言字面:判定時看「有沒有做這個動作」,例如有沒有呼叫外部查證動作、有沒有擅自派工,而不是死抓有沒有出現某個特定字串,替等價的做法保留彈性。
- 要能辨識無效執行的形狀:
- 整批零分、緊接著重跑全綠:多半是執行層環境抖動。
- 一題都沒跑、得分整欄空白:考卷設定根本沒接上。
- 整份考卷全數失分、紀錄裡卻完全沒有該 skill 的動作:嚴格說不是誤報而是考卷過時,題目沒把被測對象帶進場,考的變成「模型會不會主動叫用它」。這種的修法是改題目輸入,不是動判定準則。
- 加嚴要防照著考卷教:同一批修正裡,不得靠放寬判定準則把失敗洗成通過;自動的改動只允許加嚴,真要放寬就把差異獨立攤出來,由人決定。
這四條其實都指向同一個提醒:考卷的覆蓋範圍,是人出的題決定的。考卷會綠,不代表剛剛新改的東西真的有被測到。而題目怎麼出只是第一關;改檔的人找不找得到考卷,是下一關。
查不到考卷的兩層盲點
我原本以為規則寫著「改檔前先查有沒有考卷」就萬無一失,後來才發現同一天連撞兩層盲點。
第一層盲點是搜尋規則只涵蓋了一種存放位置。考卷有的收在集中的測試目錄、有的跟著 skill 住在自己的目錄裡,存放位置不只一種。
把第二種位置補進搜尋規則後,依然查回零筆。事後追查,那份考卷就躺在剛補上的位置裡;查不到,是因為 skill 自己的考卷目錄根本不會寫上自己的名字,按名稱去搜尋永遠是零。別人的測試讀我的檔能按名字搜,但自己專屬的考卷只能查路徑存不存在。
把入口直接搬到編輯點
存放位置的清單只會越長越多,靠記憶或搜尋清單去列舉,根本追不完。
解法是把入口直接搬到編輯的當下。在有考卷的檔案檔頭加上一行「本檔有考卷」與執行指令,不管動手的是我還是 agent,一開檔第一眼就看到,搜尋降級成補充手段。
不過這是一套還在鋪設的慣例,目前覆蓋未滿。所以規則還留著後半句:開檔沒看到標記,不代表沒考卷,這時候補充查法還是得跑。標記行省下的是有標記那些檔的搜尋工夫,不是讓搜尋退場。
勸告管不住已經發生的事
第二個意外,是改檔當下的提醒只是勸告,實際上會被繞過。
這個提醒是掛在編輯動作上的一段自動檢查:agent 改到有考卷的檔,它就出聲要求「改完要跑考卷」,同時把這個檔記進一份「待跑考卷」的待辦清單。提醒雖然發了,agent 仍在沒有考卷紀錄時直接結案。
問題不在措辭不夠兇,在結構限制:提醒是在改動已經發生後插話,但最後結案那一刻根本不經過它,管不到改完有沒有跑。
結案時查收據才放行
既然勸告管不住,就把閘門移到結案那一刻。所謂結案,就是 agent 宣告「這輪工作做完了」、準備收尾的那個動作。
結案時多一道檢查:翻出前面那份「待跑考卷」的待辦清單,逐項去檔案系統重算收據,看有沒有一輪在「改動時間之後」才產生的考卷執行紀錄。口頭宣稱一律不採信,收據比改動舊也不算數。閘門知道要查哪些檔,靠的正是改檔提醒登記的那份待辦:提醒與閘門是一組的。
真實阻擋現場很乾脆:agent 改完 skill,同一個對話中想結案直接被打回,因為當時最新的收據是在改檔前產生的,不算數。補跑完新的一輪考卷後再結案,閘門才放行。被擋下的樣子也不神祕,就是收尾動作被退回,附一句「先跑考卷再來」。
把這輪真實案例攤平,就是一份考卷的最小樣子:場景,整理工作狀態、使用者指定哪些內容必須保留;判定,指定內容必須進「必留」段落、非機密欄位不得被一併刪掉;執行,改完 command 後讓 agent 跑一遍場景、留下過程紀錄;收據,考卷執行時間必須晚於該 command 的修改時間。
上線後的五次真實攔截
這道閘門上線後一週多的觀察窗裡,真實擋下了 5 次結案。每一次都是真的缺收據,零誤擋。被擋下後,補跑考卷就都順利放行,沒有一次卡死。順帶說明數字口徑:一次結案會對每個待跑項目各留一列紀錄,按列數算會膨脹,按「結案被擋幾次」算就是這 5 次。
也先講清楚「零誤擋」量的是什麼:它只計算收據檢查的錯攔;考卷及不及格,留到下一節再談。
兩道逃生門在整段紀錄裡都是 0 次。人工豁免是 0 次,而且要老實說,這個豁免字串 agent 自己就寫得出來,設計上接受這件事;豁免會留下紀錄,但抽驗目前沒有固定週期。連擋兩次後的自動放行也是 0 次,那是防止 agent 被卡死在無限重試的保險,放行會留下紀錄供事後對帳。
證明範圍其實比想像中窄
考卷、標記行、收據閘三件套到位之後,能證明的事仍比直覺以為的窄了一階:
- 考卷全綠,最多只表示這一輪、在現有題目與判定準則下,沒有觀察到既有行為退化:它不證明先前的缺陷已修好,也不證明新改的東西被覆蓋。曾經全綠,但仔細看判定準則,根本沒有一條在斷言剛加進去的新機制。
- 上一輪綠不等於這一輪綠:改動後憑著「後面改的只是文字描述」沒重跑,結案時才被抓到。這個藉口本身,恰好就犯了本文開頭說的那個錯。
- 閘門放行只證明改動後有跑過,不證明考卷會過:曾經有一次放行採用的收據本身就是判不合格的那一輪,而且那個不合格狀態在放行後維持了好一段時間才被處理。
繞行面也要老實列出來,跟上一節的逃生門對起來看:人工豁免可以自行核發,只靠事後抽驗,這是逃生門一;連擋兩次後自動放行並留紀錄,這是逃生門二;第三條不是逃生門而是盲點,直接用終端指令改檔不會觸發改檔提醒,也就不會進待辦,閘門根本看不到這筆改動。
skill 測試三件套
skill 測試的三件套就是:考卷(測行為)+ 檔頭標記行(把入口放在編輯點)+ 結案收據閘(改了不跑就走不掉;只保證改後有跑,不保證考卷通過)。三件各自堵前面出現過的一個失效點,少了哪件,對應的洞就還在。
選型判準很單純:清單追不完的用慣例,提醒管不住的用閘門。