顯示具有 測試 標籤的文章。 顯示所有文章
顯示具有 測試 標籤的文章。 顯示所有文章

Agent 測試策略實作課:別拿真 API 測 Agent——模擬工具、錄製回放與軌跡比對

Agent 測試策略實作課:別拿真 API 測 Agent——模擬工具、錄製回放與軌跡比對

今日主題:Agent 測試策略:模擬工具、錄製回放與軌跡比對
2026-10-28 | 資料來源:LangChain 官方測試文件、LangGraph 測試實務、VCR 錄製回放、Agent VCR、軌跡評測

前言

一個 Agent 在本機跑得好好的,推上 CI 卻三天兩頭失敗;修了一個提示詞,舊功能悄悄壞掉,沒人發現;上線後某天模型換版,Agent 開始用錯工具順序,客服單才把問題帶回來。這些都不是「模型不夠聰明」,而是測試方法跟不上 Agent 的本質。

傳統軟體測試假設同一個輸入永遠得到同一個輸出。Agent 剛好違反這個假設,而且一次違反三個地方:模型輸出本來就有隨機性;工具會改變外部世界,寄信、扣款、寫資料庫都不能重來;外部世界自己也會變,檢索結果換了、第三方 API 回了 429、服務商換了回應欄位。拿真 API 跑測試,等於把這三種不確定性全部綁進測試裡,測試失敗時你永遠不知道是程式壞了,還是世界變了。

這篇文章給一套實作上可落地的測試策略:先分層,再針對每一層選對工具——模擬工具、錄製回放、軌跡比對、故障注入,最後用兩層 CI 把它們串起來。

一、先分層:三種測試各管一種不確定

LangChain 官方文件把 Agent 測試分成三層,這個分法值得直接拿來用。如圖一,最底層是單元測試:測 Agent 裡確定性的小零件,用記憶體內的假物件取代真工具,斷言行為精確且快速。第二層是整合測試:用真實網路呼叫確認元件能串起來、憑證與資料格式都對得上,但它本來就比較慢、比較貴。最上層是評測(Evals):用確定性比對或 LLM 評判,評估 Agent 整條執行軌跡的品質。

分層的意義在於,不要拿錯層的工具去解另一層的問題。想驗證「提示詞改了之後,Agent 還會不會先查庫存再下單」,那是軌跡問題;想驗證「重試邏輯在 API 超時時會不會重複扣款」,那是確定性邏輯,不該每次都花真錢呼叫模型來測。層次分清楚,測試才會又快又穩。

二、第一招:模擬工具——先給測試留一條縫

模擬工具是最便宜的一招:把工具背後的資料來源換成假的,Agent 的推理迴路不變,但世界變得完全可控。例如一個查業績的工具,測試時注入一筆假資料,就能斷言 Agent 回傳的數字是不是正確,而不需要真的有 CSV 檔、資料庫或網路。

關鍵不在於「寫一個假工具」,而在於「架構上有沒有留縫」。PydanticAI 的設計值得借鏡:它把依賴注入當成一等公民,測試時直接換掉依賴,Agent 程式碼一行都不用改。LangChain 的 Agent 則沒有內建的注入點,常見做法是工具閉包引用模組層級的資料來源,測試時用 unittest.mock.patch 去換那個模組變數。能跑,但換檔名、改匯入結構、平行測試共用全域狀態,都可能讓測試莫名其妙地時好時壞。選框架時,測試縫隙的設計應該跟推理能力一起評估——因為測試寫不下去的 Agent,最後都沒人敢改。

模擬工具還有一個附帶好處:它逼你把工具的「契約」寫清楚。假工具回傳的欄位,就是 Agent 真正依賴的欄位;如果真實 API 哪天拿掉其中一個欄位,契約測試會先叫,而不是等上線後 Agent 讀不到欄位才出事。

三、第二招:錄製回放——錄一次,回放無限次

模擬工具適合測邏輯,但測不出「真實世界長什麼樣子」。錄製回放(VCR cassette)補上這一塊:第一次跑測試時真的呼叫模型與工具,把每一對請求與回應存成檔案(通常是 JSONL,一行一次工具呼叫,附上參數摘要當查詢鍵);之後每次測試都從檔案回放,不再碰網路。

如圖二,錄製回放有三條紀律,缺一條就會變成安慰劑。第一,未命中要失敗:回放時找不到對應的請求,測試必須直接報錯,而不是悄悄改打真 API——否則提示詞一改,測試就默默變成花錢的整合測試,你還以為它在跑回放。第二,夾帶要定期重錄:提示詞、模型版本、工具介面一改,舊錄音就過期;重錄應該是由專門任務觸發、開出審查單,而不是在測試失敗時自動覆蓋。第三,祕密要過濾:錄音檔會進版本控管,API 金鑰、授權標頭必須在寫檔前就清掉,這是錄製層的基本功能,不是事後提醒。

錄製的範圍可以分兩種。只錄工具邊界:模型保持真實呼叫,只凍結工具回應,適合想觀察模型在新提示詞下行為的場景。連模型回應一起錄:整個迴路變成完全確定性的重播,適合測迴路本身的邏輯——解析工具呼叫、預算控管、重試、審批路由、最終答案格式化。實務上兩種都要:前者放夜間,後者放每次提交。

值得一提的是 MCP 場景的錄製。MCP 走 JSON-RPC 而非一般 HTTP,通用 HTTP 錄製工具套不上;Agent VCR 這類工具直接站在 MCP 客戶端與伺服器中間當透明代理,錄下每一次請求、回應、通知與延遲,還能對兩個版本的錄音做差異比對——刪了方法、改了欄位型別、換了錯誤碼,都算是破壞性變更。伺服器升級前先跑一次差異比對,比上線後才發現 Agent 讀不到欄位便宜得多。

四、第三招:評軌跡,不評文字

Agent 測完「會不會跑」,還要測「跑得對不對」。只看最終回答夠不夠像,是不夠的:一個 Agent 可能答對了答案,卻在過程中對同一張單退了兩次款;也可能答錯了,卻只是因為工具回傳的格式換了。軌跡評測(trajectory evaluation)看的是 Agent 實際做了什麼——工具名稱、呼叫順序、參數、副作用。

LangChain 的 AgentEvals 提供了現成的軌跡比對:把 Agent 的訊息與工具呼叫序列跟參考軌跡比對,支援嚴格、無序、子集、超集四種模式,參數比對的嚴格程度也可以調。沒有參考軌跡時,就用 LLM 當評判去評整條路徑。開源工具 Agentsnap 更進一步,把一次黃金執行的模型呼叫與工具呼叫錄下來,之後每次比對四個維度:結構(工具名稱與順序,用編輯距離算)、參數(深層比對,可忽略指定欄位)、模型呼叫(模型自己要求了哪些工具)、語意(最終輸出用相似度或 LLM 評判比對)。任何一個維度漂移超過閾值,就帶著結構化差異報告報錯。

軌跡評測還有一個常被忽略的用途:把生產事故變成測試。線上出過一次錯的軌跡,錄下來、寫清楚「應該要怎麼做」,就變成一條永遠有效的迴歸案例。業界把這個要求叫 fail-to-pass 契約:同一條案例在舊程式碼上必須失敗、在修復後必須通過,否則這條測試什麼都沒證明。

五、第四招:故障注入——先讓世界變壞,再看 Agent 怎麼辦

真實世界不會每次都回漂亮的 JSON。測試要主動把世界弄壞:工具超時、回 429、回分頁結果、第二次重試才成功、回傳惡意內容、重複執行副作用。這些場景用錄製回放特別好做——把錄音檔裡的回應改成超時或錯誤,Agent 的恢復邏輯立刻現形。

最值得測的三類故障是:重試後重複生效(有沒有冪等鍵)、工具回傳格式改變(Agent 讀不到欄位時會不會亂猜)、工具回傳不可信內容(檢索到的文件裡夾帶指令,Agent 會不會照做)。第三類特別重要:把惡意回應寫進錄音檔,斷言 Agent 會記錄錯誤、清理輸出或拒絕執行,而不是把文件裡的指令當成使用者的指令。在真 API 上很難穩定重現的場景,在錄音檔上改一行就好。

六、兩層 CI:提交時回放,夜間時跑真 API

四招湊起來,最後要排成兩層 CI,否則還是會互相拖累。

第一層跑在每次提交:全部用模擬工具與錄製回放,不需要 API 金鑰、不花 token、幾十秒跑完。它的任務是守住迴歸:提示詞、工具、路由邏輯一改,立刻知道有沒有壞掉。

第二層跑在夜間:用真實 API 跑同一批黃金案例,任務是抓模型漂移與外部變化。模型換版、第三方 API 改欄位、檢索結果分布改變,這些只有真呼叫才看得到。夜間失敗不擋提交,但要自動把失敗的軌跡收進案例庫,變成下一批迴歸測試的素材。

兩層之間還有一條迴路:線上監控抽樣真實流量評分,抓到新的失敗模式,就錄成案例、補進第一層。追蹤、黃金案例、CI 閘門、線上監控四個環節互相餵養,才是完整的評測迴圈;缺了任何一環,覆蓋率都會慢慢跟現實脫節。

結論

Agent 測試難,不是因為 Agent 太聰明,而是因為測試方法還停留在「輸入固定、輸出固定」的假設上。把不確定性一層一層剝開:確定性邏輯用模擬工具測,真實互動用錄製回放凍結,行為正確性用軌跡比對驗證,惡劣環境用故障注入預演,最後用兩層 CI 分開守迴歸與漂移。

起手清單只有四件事:第一,給 Agent 的工具層留一個測試可以換掉的縫;第二,挑一條黃金路徑錄成第一捲錄音檔,讓 CI 先跑起來;第三,把最近一次生產事故的軌跡寫成一條 fail-to-pass 案例;第四,排一個夜間任務跑真 API,專門抓模型漂移。做完這四件,Agent 才算是有測試護著的系統,而不是每次改動都要憑運氣的黑盒子。

資料來源

- LangChain 官方文件:Agent 測試策略(單元/整合/評測三層):https://docs.langchain.com/oss/python/langchain/test

- LangGraph Agent 測試實務(LangSmith、pytest、AgentEvals 軌跡比對):https://www.testmuai.com/blog/langgraph-agent-testing/

- VCR 風格的 LLM 測試錄製回放:https://dev.to/mukundakatta/vcr-style-recordreplay-for-llm-tests-make-your-agent-tests-deterministic-and-free-3o6d

- Agent VCR:錄製、回放與差異比對 MCP 互動:https://dev.to/pramodvoola/introducing-agent-vcr-record-replay-and-diff-mcp-server-interactions-2np0

- 錄製回放除錯 Agent 失敗(Cassette 模式解析):https://dev.to/ghostfactory/how-to-debug-ai-agent-failures-for-0-vcr-cassette-replay-explained-58ec

- 生產軌跡轉 CI 迴歸測試(Tracely、fail-to-pass 契約):https://dev.to/devtoaaron/ai-agent-regression-testing-production-traces-to-ci-25i0

- 通過評測卻在生產失敗的評測框架缺口(軌跡評測、五層框架):https://genai.qa/blog/ai-agent-evaluation-harness/

- PydanticAI 與 LangChain 的測試縫隙對照:https://dev.to/devasservice/pydanticai-vs-langchain-choosing-an-agent-framework-for-production-not-demos-3fj

Uploaded Image Uploaded Image

簡易的網頁 CodeTemplate 測試

  
class DylanHouse
{
    String message = "DylanHouse Welcome";
     
    public void printMessage() 
    {
        System.out.println(message);
    }

        /*  Bloger CodeStyle Example
        http://pydoing.blogspot.com/
        檔名:DylanHouse.java
        功能:簡單的 Java 範例
        作者:Dylan
        時間:西元 2014 年 7 月 */
}

  
 
class DylanHouse
{
    String message = "DylanHouse Welcome";
     
    public void printMessage() 
    {
        System.out.println(message);
    }
}
 
/*  Bloger CodeStyle Example
    http://pydoing.blogspot.com/
    檔名:DylanHouse.java
    功能:簡單的 Java 範例
    作者:Dylan
    時間:西元 2014 年 7 月 */