第四部|走向 Production · 第 8 章
測試、Observability 與安全邊界
用 deterministic fixtures 與 run events 提升成功率,同時劃清 MCP、sandbox 與多租戶責任。
這章證明迴圈值得信任——先用測試證明它平常做得對,再用observability證明它正在做得對,最後劃清安全邊界,證明「信任」本身有極限。你需要分開評估結果是否正確、過程為何失敗、成本花在哪裡,這三件事各自對應測試與observability;但無論這三件事看起來多漂亮,都不能取代第三步:它是否越過了授權邊界。
三層測試
| 層級 | 驗證對象 | 是否呼叫真實模型 |
|---|---|---|
| Unit / contract | tools、provider normalization、Session reducer/planner | 否 |
| Conformance / integration | 四語言、MCP loopback、CLI behavior | 否 |
| Capability eval | Agent能否完成真實coding task | 是 |
一般CI必須免費且deterministic。公開MCP endpoints只適合optional compatibility smoke,不能成為release gate。
Outcome 是分數,Trace 是解釋
Repo內最小eval會複製fixture到temp workspace,執行四個CLI,再跑hidden verifier:
make eval
AGENT=tiny-rs TASK=fix-bug make eval
主要分數只有:
verifier exit 0 → PASS
otherwise → FAIL
Duration、tokens與tool count獨立報告,不能補償錯誤結果。Agent少用兩個tools卻修錯bug,不應取得較高分。
Structured Run Events
TypeScript提供one-shot machine mode:
tiny-ts --json "調查問題"
stdout只輸出JSONL:
{"type":"run.started","sessionId":"...","model":"..."}
{"type":"model.completed","durationMs":1589.31,"usage":{"input":157,"output":31,"cacheRead":1536,"cacheWrite":0,"cacheHitRate":90.72}}
{"type":"tool.started","toolCallId":"call_1","tool":"read"}
{"type":"tool.completed","toolCallId":"call_1","tool":"read","durationMs":12.4,"ok":true}
{"type":"run.completed","result":{"status":"succeeded","answer":"..."}}
Events 預設不含 prompt、tool arguments 與 results,避免 monitoring pipeline 複製企業資料。完整證據仍保存在 job 的
Session;Session 本身不會自動提供 tenant isolation。Go、Python、Rust 目前沒有 --json;不要把 TS
reference 能力寫成四語言共有。
觀察 Cache 崩潰
每個model.completed記錄當次request的cache hit rate,因此可以看到:
turn 1 91.2%
turn 2 90.8%
turn 3 0.0% ← prompt prefix改變或cache失效
turn 4 89.7%
Run result中的rate由整個run累計counters計算。兩種scope必須分開,才能最佳化成本而不誤判。
安全邊界:誰信任誰
還記得第1章的責任表嗎?這裡把「介面與部署」那一欄攤開,順便把「產生副作用」與「持續與恢復」兩欄裡「不該負責什麼」的部分也交代清楚。
- Agent implementation與 server operator是trusted code。
- Model output、prompt、repository與 dependency scripts視為untrusted。
- Tool registration是trusted deployment action;tool arguments是untrusted model input。
- MCP是Tool adapter,不是authorization或sandbox。
--plugin是capability selector,不是tenant ACL。
多租戶需要 Execution Capsule
若外部客戶能提供repository或prompt,Fence或path check都不是完整租戶邊界。每個job至少需要獨立container;hostile native code、高敏感資料、GPU或nested container應升級microVM/VM。部署層還要負責:
- CPU、RAM、PID、wall-clock與disk/inode limits。
- Network egress、SSRF與metadata protection。
- Tenant-scoped credentials、audit與retention。
- TERM → grace → KILL與descendant cleanup。
這些責任不放進 tiny-agent core。Tiny-agent 保持一般 CLI,由外層 trusted job controller 建立 execution capsule、綁定 tenant identity、配置 credential,並決定 Session 與 artifact 的儲存位置及存取權限。換句話說,Session 只保存 durable facts;tenant scope 是外層控制器的責任。
企業查詢 Tool 的原則
不要提供萬用internal_search(query)或讓模型直接寫 SQL。使用少量typed operations,並讓trusted
context注入tenant、actor與 credential。每筆結果帶 source、observedAt、retrievedAt、partial與
conflicts,才能組成可審查的evidence packet。
親手驗證
先跑全部離線 gate,確認測試、MCP loopback、安全界線與教學書都不需要真實 OpenRouter:
make test
make check
make build
npm --prefix book test
接著查看 capability eval 的 verifier,而不是直接花模型額度:
sed -n '1,220p' eval/run.ts
find eval/tasks -name 'verify.mjs' -o -name 'test.sh' | sort
最後做一個安全界線練習:列出哪些資料由 tiny-agent Session 保存,哪些必須由 job controller 提供。至少應把 tenant
identity、credential、network egress、CPU/RAM/PID/disk limits 與 retention 放在外層;若答案把它們交給 MCP arguments
或 --plugin,就跨錯邊界了。
你現在有一個能正確處理失敗、可觀測、並劃清邊界的迴圈——這是這本書目前教到的地方。