iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
AI Engineering

邊做邊補:用一個 AI 代理 mesh 專案,補齊 AI 工程師該有的能力系列 第 16

Day 15|測試|做:把串流假成功修成可驗證的失敗

  • 分享至 

  • xImage
  •  

系列:「邊做邊補:用一個 AI 代理 mesh 專案,補齊 AI 工程師該有的能力」— 第 15 篇(系列 ID:9765)
紀錄日期:2026-09-21

本機草稿,尚未貼到網站。本篇練習故障重現、測試前置條件、工具副作用與端到端驗證;不把本機候選修正當成發布完成。

今天要補的能力:定義失敗

上一篇用故障情境選 gateway,找到一個很適合拿來練習的問題:Spectyn 收到 partial content → error → [DONE],卻 exit 0。

這是一個協定與產品語意的差距。[DONE] 表示這條串流結束,並不表示工作完成。測試如果只看「HTTP 200」「有文字」「程序沒有 crash」,三個都可能通過,但任務仍失敗。

今天的驗收條件是:明確失敗的這一輪不能發成功 done,不能執行它的工具,也不能因前輪有成果就回報成功。

先把 RED 做到可觀察

先在既有 Rust 測試檔新增三項回歸:部分文字後 error、工具片段後 error、工具形狀文字後 error。工具案例包含完整與不完整參數。

原程式跑出既有兩項 PASS、新增三項 FAIL。這才是本次修正的 RED。工具測試在 gate 計數後拒絕真正執行,因此即使舊程式有 bug,也不會為了證明 bug 去任意寫入。

這裡要測的是「根本不該抵達工具 gate」,不是最後剛好沒有檔案。

為什麼不能只把 truncated 改成 true

truncated 是結果上的標記,但原本後續流程仍會解析、補救工具格式並執行工具。只改標記,並沒有阻止副作用。

直接讓底層 provider 回 Err 也不夠:上層原本有一段邏輯會保留先前已執行工具的成果,最後仍可能形成成功結果。

因此這次在串流回應內帶一個內部 terminal-error 標記,讓上層先記錄 usage,再在工具解析和本輪回答儲存之前返回失敗。它不是新的對外協定,也沒有新增另一套任務儲存。

補上的第四項 Rust 測試驗證 error 後的 usage 仍入帳,error 後的 LATE 文字不再發出。合成的 100 萬輸入+50 萬輸出 token 依 gpt-4o 價格表得到 $7.50,只是測試值,不是真正花費。

用真 CLI 檢查使用者看見的結果

單元層能證明某個分支,但 CLI 還有輸出格式、退出碼與對話檔案。於是另外建立本機 HTTP fixture,呼叫真的 Spectyn binary。

每個案例都有獨立 SPECTYN_HOME、工作目錄、合成設定與信任紀錄。七種情境分別跑 plain、JSON、quiet,共 21 組。

情境 必須看到的行為
健康回答 exit 0,JSON 有 done,對話存入 COMPLETE
文字後 error exit 1,JSON 無 done,不追加成功對話
完整/不完整工具後 error 同上,且失敗輪工具不執行
工具形狀文字後 error 不從失敗文字補救並執行工具
前輪工具已成功,下一輪 error 既有副作用保留、沒有重播,任務仍失敗
工具片段後 transport error 不執行收到的工具,不重試這一輪

JSON 模式可能已輸出部分 token 事件,最終失敗不能撤回這些事件;目前 plain/quiet 不直接印出串流 token。判準是退出碼、完成事件與副作用;不要把「有字」當成功。

驗收前,先驗證前置條件

測試過程出現兩個假訊號。

第一個是 workspace 不受信任,所以工具被拒絕。沒有寫檔看起來安全,但這根本沒測到「前輪工具真的執行過」。補上隔離信任後,要同時檢查檔案內容與第二輪請求裡的 tool_result。

第二個是 fixture 以 HTTP/1.0 傳 chunked,客戶端提早拒絕,根本沒有進入想測的串流階段。正確使用 HTTP/1.1,flush 工具片段後再截斷傳輸,才能驗證失敗輪中止。

兩次修正都沒有放寬產品斷言。測試失敗不能自動推論是產品失敗,測試通過也不能自動推論情境成立。

GREEN 分層看

最終 Rust 共 12 項通過,包括新錯誤回歸、既有純文字 transport/stall 契約與用量回歸。真 CLI 21 組通過。固定 FreeLLMAPI 的本機 HTTP/CLI 15 項判準通過,原本的串流假成功轉為 exit 1。

這些數字不能合成「整個產品 48 項全部安全」。各層有重疊,也有沒測到的地方:真實免費額度、模型任務品質、帳戶共享、多機競爭、跨平台、完整 gateway 背景啟動,都沒有因此獲得通過。

純文字 transport truncation 的原有行為保留;純 EOF 判斷、空錯誤串流的重試及用量、denied-tool 計數是後續獨立問題。這次也沒有提供 crash 後 exactly-once 保證。

把測試變成可以交棒的資料

保留 source diff、binary/harness 雜湊、原始請求與輸出、每項斷言、RED 和修正後結果。另一個 CLI 不需要相信這篇文章,可以直接重跑:

cargo test --locked --offline --manifest-path core/Cargo.toml --test stream_error_frame_failover --test stream_truncation_is_not_success --test p4_a_streamed_turn_reports_its_cost
python3 scripts/audit/stream-error-frame-check.py --binary core/target/debug/spectyn --out /tmp/spectyn-stream-check-new

輸出目錄必須是新目錄。隔離 SPECTYN_HOME 不等於作業系統 sandbox;這裡刻意只啟用合成 file_write。

證據位於 docs/specs/demo-monday-mesh/reports/evidence/m5/2026-09-21-stream-fix/。非作者審查另外記錄,timeout 或空回答不算核准。cargo check 通過;PowerShell 契約檢查缺 pwsh,因此 NOT_RUN。

今天練的是讓「完成」有可查的依據。這個能力會直接用在後面的自我開發迴圈:拆任務、做候選、測試、獨立評估,最後才討論部署和回復。

審查結果補記:Claude Code 最終 APPROVE with limits;OpenCode 最終 APPROVE,並更正初審的兩段誤讀。兩份非作者審查已齊,agy 無輸出不計入。這是本輪候選修正的審查,不是發布核准。


上一篇
Day 14|評估|做:用失敗情境挑選 LLM Gateway
下一篇
斷更說明:能力地圖還在補,只是忘了發——累積的先放出,補到 30 篇後改成每天一篇技術文
系列文
邊做邊補:用一個 AI 代理 mesh 專案,補齊 AI 工程師該有的能力17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言