屬性測試聽起來很合理,但實際上該怎麼決定「哪些屬性值得寫成斷言」?如果屬性寫得太少,會不會漏掉重要的錯誤;寫得太多,會不會又變成另一種形式的過度精確匹配?
決定屬性測試該涵蓋哪些屬性,核心思路是「回頭問這個輸出如果錯了,實際會造成什麼傷害」,用傷害的嚴重程度來排優先順序,而不是想著要窮舉所有可能的屬性。
優先寫成斷言的屬性類型:直接影響決策正確性的核心欄位(例如前面提到的信心分數是否落在合理範圍、狀態欄位是否是預期選項之一),這些屬性的錯誤,會直接導致下游做出錯誤決策,值得優先覆蓋;涉及安全邊界的屬性(例如「Agent 絕對不應該在信心分數低於某個門檻時,仍然回傳 execute: true」這種邊界條件),這類屬性的斷言,是在測試「防禦機制是否真的生效」,值得優先覆蓋;格式和內容一致性的屬性(例如前面文章提到的,如果推理依據裡宣稱引用了某個具體數字,這個數字應該真的出現在依據的文字裡,而不是空泛的描述),這類斷言能捕捉「格式正確但內容空洞」這種前面文章討論過的隱藏風險。
不需要寫成斷言、可以放寬的部分:純粹措辭上的差異(例如「這個機會值得執行」和「建議執行這筆交易」這兩種說法,語意相同但用詞不同),不需要斷言精確的文字內容;非核心的補充說明欄位,即使內容有一定的表達彈性,對整體決策品質的影響有限,測試優先度可以放低。
判斷「寫太多還是寫太少」的實務訊號:如果你發現測試套件經常因為 LLM 輸出的措辭差異而失敗(即使實際決策邏輯是對的),這通常代表斷言寫得太精確、太接近字面匹配,該放寬成更寬鬆的屬性斷言;如果你發現生產環境出現了測試套件完全沒有攔截到的錯誤(例如信心分數明顯不合理卻通過了所有測試),這代表覆蓋的屬性維度不夠,該回頭補上對應的斷言。這是一個需要根據實際運作經驗持續調整的動態過程,不是一次寫完就不用再碰的靜態清單。
如果我的 Agent 系統會頻繁更新使用的 LLM 模型版本(例如從一個模型換成另一個更新的模型),前面提到的這套測試框架,需要每次換模型都重新設計嗎?
不需要重新設計整套框架,但需要重新執行,而且模型版本更新後的測試,恰好是屬性測試設計思路最能發揮價值的場景之一,原因如下。
測試框架本身(四個層次的劃分、屬性斷言的設計思路)是模型無關的。確定性邏輯層的測試,和用什麼 LLM 完全無關,不需要因為換模型而調整;LLM 推理層的屬性斷言,是針對「輸出該滿足什麼合理範圍」設計的,這個合理範圍本身,理論上不該因為換了一個模型就改變(如果換模型後,「信心分數該落在合理範圍」這個業務邏輯需求變了,那是業務邏輯本身的問題,不是測試框架的問題)。
該做的事是:換模型後,重新執行整套測試套件,而不是重新設計。這正是屬性測試相對於精確匹配測試的優勢所在——如果測試斷言寫的是「精確匹配某個字串」,換一個模型,措辭風格改變,這類斷言會大量失敗,但失敗原因只是措辭不同,不是邏輯真的錯了,這種測試在換模型時反而會製造大量「假警報」,浪費排查時間;如果測試斷言寫的是屬性層級的(前面 Q1 討論過的那種),換一個模型後重新執行,如果新模型的推理品質沒有退化,這些屬性斷言應該還是能通過,因為屬性斷言本來就不依賴特定模型的措辭風格。
換模型後特別值得關注的測試面向:即使測試框架不用重新設計,換模型後,建議特別關注端到端測試裡「邊界情況」這一類案例的結果——不同模型在處理邊界情況、模糊情境時的表現差異,通常比處理明確情境時的差異更大,這是換模型後最可能出現「新模型看起來還不錯,但邊界情況處理變差了」這種隱蔽退化的地方,值得優先復查。
額外建議:保留舊模型的測試結果作為基準線。如果測試框架有記錄「這次測試,各項屬性斷言的通過率、信心分數的分佈狀況」這類統計資訊,換模型後,可以拿新模型的測試結果和舊模型的基準線做比較,而不只是看「新模型有沒有通過測試」這個二元結果——例如新模型可能所有斷言都通過,但信心分數的整體分佈,和舊模型比起來明顯偏移(變得普遍更保守或更激進),這種趨勢性的變化,單看「通過/不通過」不容易發現,但透過和基準線比較就能看出來,值得在正式切換模型前納入評估。
文章提到端到端測試該包含「異常或缺失的市場數據」這種情境,具體該怎麼設計這類測試案例?直接故意輸入亂七八糟的假數據就可以了嗎?
刻意輸入完全隨機的亂數據,測試價值有限,因為這種資料在真實生產環境裡出現的機率很低,也不容易對應到具體的防禦邏輯要驗證什麼;更有價值的做法,是針對「真實世界裡真的可能發生的異常模式」設計對應的測試案例,這樣測試結果才能直接對應到「這個防禦邏輯有沒有處理好這個真實會遇到的情境」。
具體的異常模式分類與對應測試設計:
部分資料缺失(而不是完全隨機)。例如模擬「查詢某個協議的利率時,API 回傳成功,但其中某個欄位是 null 或空字串」這種真實可能發生的情境(第三方 API 偶爾會有欄位缺失的情況),測試 Agent 面對這種部分缺失的資料,是不是有妥善的處理邏輯(例如標記這筆資料不完整、觸發前面提到的結構性失敗處理,而不是直接把 null 值當成 0 或某個預設值硬做後續計算,那樣可能導致基於錯誤資料做出決策)。
格式正確但數值異常。例如模擬「查詢到的利率是 -50%」或「TVL 突然變成負數」這種格式上合法(是一個數字),但業務邏輯上明顯不合理的數值,測試 Agent 是不是有對這類「格式合法但語意異常」的數值做合理性檢查,而不是照單全收直接拿去做計算。
時間戳記異常。例如模擬「查詢到的市場數據,時間戳記顯示是很久以前的舊資料,而不是即時資料」,測試 Agent 是不是會檢查資料的時效性,而不是不管資料多舊都直接當成即時資料使用——這呼應了前面滑點容忍度文章討論過的,即時性敏感的資訊需要有效期概念。
部分服務不可用。例如模擬「查詢多個協議的利率時,其中一個協議的 API 逾時或回傳錯誤,其他協議正常」,測試 Agent 是不是能在部分資料來源失敗的情況下,依然基於還能取得的資料做出合理判斷(或明確標記「這次的判斷基礎不完整」),而不是因為一個來源失敗,整個判斷邏輯就崩潰或卡住。
設計原則:每一種異常測試案例,最好都能對應到「這是真實世界裡實際發生過、或有合理理由相信會發生的情境」,而不是憑空想像的極端情況——可以參考過去實際遇到的生產環境問題(如果有的話)、或參考類似系統常見的故障模式,把這些真實可能發生的異常模式,轉換成具體的測試案例,這樣測試結果才有實際的參考價值,而不是為了測試而測試。
在多 Agent 系統裡,測試框架該怎麼調整才能涵蓋 Sub-agent 之間的 Handoff 邏輯,尤其是前面 Handoff 詞條提到的循環偵測和全域上限這類機制?
多 Agent 系統的測試,除了前面提到的四層測試框架各自套用到每個 Sub-agent 身上,還需要額外針對「協調層面」的邏輯設計專門的測試案例,具體可以拆成幾個方向。
針對循環偵測機制的測試:前面 Handoff 詞條提到,如果任務在幾個 Agent 之間來回打轉(A 交給 B,B 又交回 A),應該被循環偵測機制攔截。測試這個機制的方法是,刻意構造一個會觸發循環的模擬情境(例如讓測試用的 Sub-agent B,故意判斷「這個任務我處理不了,交回給 A」,而 A 收到後又判斷「這個任務應該給 B 處理」),驗證循環偵測機制是不是真的在偵測到重複交接對象時,正確中止了這個循環,而不是放任它無限循環下去。這類測試需要能夠精確控制 Sub-agent 的判斷邏輯(例如透過測試用的 mock 版本,強制讓 Sub-agent 在測試情境下,回傳特定的判斷結果),而不是依賴真實 LLM 推理的隨機性去「碰巧」觸發循環情境。
針對全域上限機制的測試:模擬「任務被交接超過上限次數」這個情境,驗證系統是不是真的在達到上限時強制中止任務、標記需要人工介入,而不是讓交接次數的計數器本身有 bug(例如計數器沒有正確地在每一層之間累加傳遞,前面 Handoff 詞條提到的「多層重試次數被意外放大成乘積而非加總」的風險,也適用於交接次數的測試,值得專門設計一個涉及三層以上 Handoff 的測試情境,驗證全域計數器有沒有正確運作)。
針對交接資訊完整性的測試:模擬「Sub-agent A 交接給 Sub-agent B 時,交接封包缺少某個關鍵欄位」這種情境,驗證 Sub-agent B 面對不完整的交接資訊時,是不是有妥善的處理邏輯(例如觸發前面提到的重新查詢或要求補充資訊的機制),而不是直接假設交接封包一定完整,拿不完整的資訊硬做後續判斷。
針對執行前重新驗證機制的測試:模擬「Sub-agent A 完成判斷後、Sub-agent B 實際執行前,市場條件已經發生變化」這個情境(例如前面滑點容忍度文章的案例),驗證 Sub-agent B 是不是真的有重新查詢並比對,而不是盲目沿用 A 給的舊資料直接執行。
這些測試的共同特徵:它們測試的都不是單一 Sub-agent 的推理品質(那是前面提到的 LLM 推理層屬性測試該負責的),而是測試「多個 Agent 協調運作時,前面文章反覆討論過的那些安全機制(循環偵測、全域上限、交接完整性、執行前驗證)是不是真的按照設計生效」,這是多 Agent 系統特有、單一 Agent 測試框架不會涵蓋到的測試面向,值得作為一個獨立的測試類別,額外投入資源設計。
傳統軟體的單元測試,核心假設是「同樣的輸入,應該產生同樣的輸出」——這個假設讓測試變得可預測、可自動化:寫一個測試案例,斷言某個函式在特定輸入下該回傳什麼結果,跑一次測試就能確認邏輯是否正確。這個假設在 Agent 系統上直接失效,因為 LLM 的推理過程本身帶有一定的不確定性,同樣的輸入,可能因為模型的隨機性,在不同次執行時走出不完全相同的推理路徑,得到略有差異的結果。這代表 Agent 測試需要一套不完全依賴「同輸入同輸出」假設的測試框架,這篇文章要拆解的正是這套框架該長什麼樣子。
對 Agent 系統而言,把測試拆成三個層次分別處理,比想著「寫一套萬用測試」更務實:確定性邏輯層、LLM 推理層、端到端整合層,每一層適合的測試方法完全不同。
Agent 系統裡,不是所有東西都涉及不確定的 LLM 推理——工具函式本身的邏輯(例如「查詢利率」這個工具,給定一個協議名稱,回傳對應的 API 查詢結果)、狀態物件的更新邏輯、前面文章提到的重試次數計算、成本累加計算,這些都是確定性的程式邏輯,同樣的輸入必然產生同樣的輸出。這一層完全適用傳統的單元測試方法——寫斷言、跑測試、驗證通過與否,不需要任何特殊的 Agent 測試技巧。這一層測試的價值容易被低估,因為大家的注意力都放在「LLM 推理準不準」這個更顯眼的問題上,但如果連確定性邏輯本身都有 bug(例如重試計數器忘記遞增、成本累加算錯),再準確的 LLM 推理也救不回一個底層邏輯有缺陷的系統。
對於涉及 LLM 推理的部分,不能斷言「輸入 X 一定要得到輸出 Y」(因為輸出可能因為模型的隨機性而有所變化),但可以斷言「輸出必須滿足某些屬性」,這是屬性測試(property-based testing)的核心思路,套用到 Agent 場景。
具體來說,與其寫「給定這個市場情境,Agent 一定要回傳 `execute: true`」這種精確匹配的斷言,改寫成「給定這個市場情境(一個明顯有利可圖的套利機會),Agent 回傳的 `confidence_score` 必須高於 0.7」「Agent 的推理依據裡,必須提到具體的利率數字,不能只有籠統的文字描述」「如果 Agent 判斷不執行,`status` 欄位必須是 `insufficient_opportunity` 而不是留空」這類屬性斷言。這些斷言容許 LLM 輸出的措辭、細節有一定變化空間,但依然能捕捉「這個輸出是否在合理範圍內」這個核心問題。
前兩層測試各自獨立運作良好,不代表整個系統串起來也沒問題——狀態在節點之間傳遞是否正確、Handoff 的資訊是否完整、多 Agent 協調的流程是否符合預期,這些只有在端到端的整合測試裡才能被驗證。
端到端測試的關鍵設計是「用受控的模擬市場情境,而不是直接對接真實的生產環境數據」。具體做法是建立一組具代表性的測試情境(例如「明顯有利可圖的套利機會」「利差很小、接近盈虧平衡點的邊界情況」「流動性極差、交易很可能失敗的情境」「市場數據本身有異常或缺失的情境」),對每一種情境,用模擬或歷史快照的市場數據,跑一次完整的 Agent 流程,驗證整個系統在這些不同情境下的行為是否符合預期。這裡特別要包含前面提到的邊界情況和異常情境,因為多數 bug 不會出現在「一切正常」的情境裡,而是出現在邊界和異常狀況的處理邏輯上。
前三層測試假設輸入是「正常」的(即使包含邊界情況,也是自然發生的邊界情況),但前面 Prompt Injection 詞條和文章討論過的攻擊面,需要用專門的對抗性測試去驗證——刻意構造包含惡意指令的輸入(例如在模擬的鏈上 metadata 裡塞入試圖操縱 Agent 行為的文字),驗證 Agent 的防禦機制(輸入隔離、最小權限)是否真的有效攔截了這類攻擊,而不是假設「防禦機制寫了就一定有效」。這一層測試的成本較高(需要專門設計攻擊案例),但對於處理實際資金的 Agent,這是上線前不該省略的一步。
如果你正在開發準備上線的 Agent 系統,測試策略不該只停留在「手動跑幾次看起來正常就上線」這個階段。把測試拆成確定性邏輯、LLM 推理屬性、端到端整合、對抗性測試這四層,針對每一層用適合的測試方法(傳統單元測試、屬性測試、模擬情境整合測試、刻意構造的攻擊測試),能大幅提升在真正上線前抓到問題的機率。特別值得投入資源的是端到端測試裡的邊界情況和對抗性測試,因為這兩類問題,最容易在「一切正常」的手動測試裡被漏掉,卻是實際運營中最可能造成重大損失的情境。