把 AI 功能寫成可測試的程式:假模型與快照測試
每次跑測試都真的呼叫模型,既慢又貴又不穩定。有更好的做法。
AI 功能的測試困境是:輸出不確定、呼叫要花錢、速度慢。解法是把「呼叫模型」隔離成一個可替換的介面。
抽出介面
from typing import Protocol
class LLM(Protocol):
def complete(self, prompt: str, **kw) -> str: ...
class FakeLLM:
def __init__(self, responses): self.responses, self.calls = responses, []
def complete(self, prompt, **kw):
self.calls.append(prompt)
return self.responses.pop(0)
def classify(text, llm: LLM):
raw = llm.complete(PROMPT.format(text=text))
return json.loads(raw)三層測試
- 單元測試(假模型):測解析、驗證、錯誤處理、重試邏輯。快、免費、可在 CI 跑。
- 快照測試(錄製回放):第一次真的呼叫並存下回應,之後回放。介面改動時能發現差異。
- 線上評測(真呼叫):只在發布前跑,用固定評測集,數量控制在幾十題。
假模型要涵蓋失敗情境
def test_handles_bad_json():
llm = FakeLLM(['這不是 JSON', '{"type":"oom"}'])
assert classify('...', llm)['type'] == 'oom' # 第一次失敗後重試成功
assert len(llm.calls) == 2實務上最常出問題的不是模型,而是「模型回了奇怪東西時你的程式怎麼辦」。這一層完全可以用假模型測到。
CI 不要打真 API
把真呼叫的測試標記起來,只在特定情況執行。CI 每次 push 都打模型,帳單與不穩定的失敗會很快讓團隊關掉測試。
把每次線上評測的結果存成檔案並提交。程式碼審查時能直接看到「這次改動讓分數如何變化」。