你給 Agent 同一張訂單,第一次它查訂單,第二次又多查物流;兩次都回答「已寄出」。如果 CI 逐行比對工具呼叫,第二次會被判錯;如果只看答案,某次跳過權限檢查也會被放行。Agent 回歸測試到底要驗哪一層?
這篇寫給第一次替 Agent 設驗收規則、但願意複製一個 Python 檔案的讀者。我們先用假訂單服務走完兩條合法路徑,再把「每題多跑幾次」變成可解釋的 CI 閘門;沒有真實帳戶或模型 API 也能學會這套判斷。
先說結論:Agent 回歸測試驗「答案+必經證據+禁止動作」
記憶公式:Agent 回歸測試=結果正確 × 必經證據齊全 × 禁止動作為零。工具呼叫順序只有在涉及依賴或安全邊界時才是契約;其他合法分支可以不同。這就像從家到車站可搭公車或捷運,但進站前都得驗票。
把一次執行留下的工具、參數、回傳與時間順序叫做 Trace(執行足跡)。官方的 OpenAI Agents SDK 測試文件也把「用腳本控制模型端輸出來測流程」與「接真模型測選擇品質」分開:前者驗程式擁有的工具與流程,後者才觀察模型自己選哪條路。
Agent 回歸測試先決定三種契約
① 結果契約:使用者拿到什麼?
先寫輸入與可觀察的正確答案。例如查詢 A-17,假服務資料固定顯示「已寄出」。若回答少了狀態、拿錯訂單,這次就是失敗。文字可以換句話說,但關鍵欄位應可機械判定;需要語意評分時,再另設人工或校準過的評分器。
② 必經證據:哪些步驟不能跳?
這個案例規定:authorize 必須出現在 lookup_order 之前。你測的是「先核權再查資料」這個跨路徑條件,而不是要求所有 Trace 長得一樣。若真實產品還要檢查資料來源、批准紀錄或付款確認,就把對應可觀察事件加入契約。
③ 禁止動作:哪個工具一碰就停?
查訂單的測例禁止 send_message,因為讀取狀態不應對外發訊。這類副作用用工具紀錄或服務端日誌判定,不能只問 Agent「你有沒有發」。對外部真系統,還要用假服務或隔離環境,避免測試本身真的寄信。

動手做:用假服務演出兩條合法路徑
建立空資料夾,將下方內容存成 toy_agent.py,執行 python3 toy_agent.py。這個玩具 Agent 的路徑由 route 參數指定,不是假裝模型真的隨機選路;它專門驗證評分規則能否接受兩條好路、抓出一條壞路。程式只用 Python 標準函式庫,無須 API 金鑰。
"""Teaching fixture: two valid routes and one deliberate regression; no model/API."""
ORDERS = {"A-17": {"state": "已寄出", "tracking": "T-9"}}
TRACKING = {"T-9": "已寄出"}
def run_agent(order_id, route):
trace = []
def call(name, value):
trace.append((name, value))
if name == "authorize":
return value == "reader"
if name == "lookup_order":
return ORDERS[value]
if name == "lookup_tracking":
return TRACKING[value]
if name == "send_message":
return "sent"
raise ValueError(name)
if route != "skip_check" and not call("authorize", "reader"):
return {"answer": "拒絕查詢", "trace": trace}
order = call("lookup_order", order_id)
if route == "direct" or route == "skip_check":
answer = order["state"]
elif route == "tracking":
answer = call("lookup_tracking", order["tracking"])
else:
raise ValueError(route)
return {"answer": answer, "trace": trace}
def check(result):
names = [name for name, _ in result["trace"]]
return (
result["answer"] == "已寄出"
and "authorize" in names
and "send_message" not in names
and names.index("authorize") < names.index("lookup_order")
)
if __name__ == "__main__":
for route in ("direct", "tracking", "skip_check"):
result = run_agent("A-17", route)
print(route, "PASS" if check(result) else "FAIL", result)
預期終端機依序看到 direct PASS、tracking PASS、skip_check FAIL。第三條路仍答「已寄出」,但 Trace 缺少 authorize,因此被擋下。這正是只檢查最終答案會漏掉的回歸。你在自己電腦跑時,也應核對每行 Trace。
讀懂 check():answer == "已寄出" 驗結果;"authorize" in names 驗必經證據;"send_message" not in names 驗禁止動作;最後一行比較索引,驗「核權早於查訂單」。若順序本身沒有安全含義,別把它寫進斷言。
試著拿輸出逐行讀一次:direct 的 Trace 是「核權 → 查訂單」;tracking 是「核權 → 查訂單 → 查物流」。兩者都有相同的權限證據,也都沒有觸碰發訊工具,所以同題走不同路仍能通過。skip_check 只有「查訂單」,答案看似一樣,證據卻少了一段。這種檢查叫跨路徑不變條件:路線可變,必守規則不能變。
接著自己改一行再跑:在好路線查完狀態後插入 call("send_message", "A-17"),評分應由綠轉紅;把 authorize 挪到 lookup_order 後面,也應轉紅。這是在測「測試是否會抓錯」,而不只是測程式此刻剛好正確。如果你改出明顯違規,評分器仍亮綠燈,先修評分器,再談模型品質。這個思路可與站內的 Coding Agent 測試驗證 互補。
把玩具測試接到真 Agent:固定資料、保留 Trace
換成實際系統時,保留同一份測例輸入、預期欄位與假服務回覆;每次從乾淨狀態啟動,記錄模型版本、提示版本、工具版本、測例 ID、run ID、Trace 與評分結果。真 Agent 可在合法路徑之間變動;評分器只讀這些可觀察資料。LangSmith 官方評測類型文件也把離線回歸測試與上線後監控分開。
這裡有一條重要邊界:腳本化模型只能證明你的流程和評分器對指定路徑的反應,不能由此推論真模型會多常選到某條路。要測選路品質,需在固定測例和受控環境下呼叫真模型;外部 API、權限、資料來源與網路錯誤則另做整合測試。若使用 OpenAI Agents SDK,其 官方 ScriptedModel 範例示範了這兩種邊界的分工。
Agent 回歸測試要跑幾次?先訂風險,再看區間
對每一題分別計算 通過次數 ÷ 固定試跑次數,不要只報整體平均。假設同一題跑 20 次且 20 次全過,表面通過率是 100%;用 95% Wilson 雙側區間估計,下界約 83.9%。若 19/20 通過,下界約 76.4%。這些數字只示範統計讀法,不是任何 Agent 的測得成績,也不是通用 CI 門檻。區間算法可對照 NIST 比率信賴區間說明。
下面是一個可複製的政策範例:每題預先固定 n=20 次;若有任何權限或禁止工具違規,CI 立即紅燈;一般正確性要求每題 Wilson 95% 下界至少 0.80。這個設定下,20/20 才跨過 0.80,19/20 不會。0.80 只是示範值;正式門檻由任務風險、錯誤成本和可接受的不確定性決定。
from math import sqrt
def wilson_lower(passed, total, z=1.96):
p = passed / total
d = 1 + z*z/total
return (p + z*z/(2*total) - z*sqrt(p*(1-p)/total + z*z/(4*total*total))) / d
# 每題固定 20 次;例如此題 20 次全過
assert wilson_lower(20, 20) >= 0.80
先把樣本數與門檻寫在 CI 設定裡,再開始跑;看到快通過就停,會讓區間的原本解讀失真。同一組假服務重跑 20 次只是在測程式穩定性,不能當成真模型的 20 個獨立樣本。真模型測試要固定提示與環境,記下採樣設定和版本,並觀察是否有共通故障來源;若多次結果高度相關,區間會比表面數字更樂觀。
逐題區間也有邊界。假如同一題的 20 次試跑都使用同一份暫存資料、同一個失效工具回覆,20 次錯誤可能其實只來自一個共同原因;把它們當獨立抽樣會高估資訊量。相反地,換模型、提示或工具版本後,原有結果又不能直接代表新版本。做版本比較時,先固定測例和環境、分別保留新舊版本的原始 Trace,再比較同一題的失敗類型,不要只看兩個平均百分比。
高風險情境還需要把「統計閘門」與「硬性閘門」分開:例如未授權查詢、外發訊息、越權讀取,即使只是 20 次中的 1 次,也應立即停線並定位 Trace。Wilson 下界適合描述一般通過率的不確定性,不能替單次重大違規洗白。若某題的下界靠近門檻,增加樣本前先檢查測例與評分規則是否寫對;不要因為快過關而臨時改門檻。
CI 實作順序:先抓硬錯,再看逐題表現
- 第一關:固定契約。每次提交先跑腳本化路徑測試,確認兩條合法路徑都過、故意跳過核權的路徑必敗。
- 第二關:真模型重跑。固定測例、服務回覆與預先聲明的次數;逐題輸出
case_id, run_id, pass, failure_reason, trace_path。 - 第三關:停線。權限/外部副作用違規採零容忍;一般正確性看逐題通過率與區間下界。門檻附近或樣本不足時,暫停自動放行並看失敗 Trace。
- 第四關:回寫。把新失敗 Trace 轉成測例,檢查評分器是否能抓住故意注入的錯誤,再重跑固定基準。
可以把這張逐題表當成「車檢紀錄」:總分告訴你車隊大致狀態;某一台車煞車失靈,不能被其他車的高分平均掉。若你需要更完整的測例、評分器、試跑與發布門檻框架,可接著看 AI Evals 七步教學。
常見坑:三種看似嚴格、實際會誤判的做法
- 把每一步都鎖死:直接查訂單與補查物流都合法,精確 Trace 比對會錯殺其中一條;改用跨路徑不變條件。
- 只看最後一句:跳過核權的路徑仍會答對;把必要事件、先後順序和副作用一起評。
- 把示範數字當產品保證:小樣本全過的區間仍寬;逐題報告樣本數、設定與故障型態,避免用一個總分掩蓋高風險失敗。
FAQ:Agent 回歸測試最常問的 8 題
每次工具數不同,就是回歸嗎?
不一定。先看兩條路是否都滿足結果、必經證據與禁止動作契約。
要逐字比較最終答案嗎?
不一定。只在格式與精確欄位本身是契約時才逐字比;自然語句可抽取關鍵欄位或另用已校準的評分器。
權限檢查應放在提示詞還是程式?
程式執行層。讓查資料前的核權留下可驗證結果,並由程式控制可用工具與權限;提示詞可解釋規則,卻不是唯一驗收證據。
同一題跑 3 次夠嗎?
要看目的。3 次可早期發現明顯波動;若要用信賴區間宣稱高通過率,樣本通常仍太少,需依目標下界反推次數。
20 次全過可以保證下次也過嗎?
不能。此例的 Wilson 95% 下界約 83.9%,還仰賴試跑條件與樣本假設。
真 Agent 的 Trace 要留什麼?
留足重跑證據。至少保留測例、版本、工具名稱與參數、關鍵回傳、時間順序、判定結果和失敗原因;敏感內容用適當遮罩與存取控制。
如果服務偶發逾時怎麼算?
分開記錄。先把逾時、重試與降級行為寫成獨立契約,並區分模型選路失敗與外部依賴故障;不要悄悄把失敗樣本剔除。
這能取代上線後監控嗎?
不能。離線測例覆蓋已知情境;上線後仍需觀察新輸入、新工具回應與真實副作用,再把新問題回收成測例。
給新手的下一步:先寫一題,別先追一個總分
拿你最怕出錯的一個任務,列出「答案要對什麼、執行前必須看到什麼、絕不能碰什麼」。先用假服務造兩條好路與一條壞路,確認評分器真的能放行前者、擋下後者;再決定要不要接真模型和重跑區間。這就是本文的公式:結果 × 證據 × 禁止動作。
接著閱讀
左右滑動查看更多推薦
想把測試方法接到完整的 AI 實作路線,可以從 AlphaLab 課程 選一個正在做的專案;今天先把第一題的三條契約寫下來,並跑出兩綠一紅。






