本文同步自 2026 iThome 鐵人賽原文 。完整系列可見 iThome 系列頁 。

安安~我是ChiYu~

昨天的兩個唯讀任務很好判斷:Agent 選對 Tool,輸入正確,回答也能對回 result,這題大致 就有答案了。

今天換成收藏、報名與取消,事情立刻沒那麼乾脆。

Agent 最後說一句「已經幫你完成」,聽起來很貼心;如果背後重複收藏三次,或是偷偷送出 報名,那份熱心就有點超出職務範圍。反過來,它說「我會等你確認」,也不代表 Network 裡 真的沒有 POST。

所以寫入型 Tool 不能只看最後回答。我要把 Tool trace、Network、可見 UI、server state 與 AI result 放在一起,才能知道 Agent 實際做了什麼、資料改了幾次,以及它有沒有在該停的地方 停下來。

今天不追新的 PASS,改整理一套寫入驗收方法

今天沒有新的程式 Tag,也不是把所有寫入案例硬湊成同一個 revision。這批證據分散在:

  • 早期的 REPEAT-01 收藏 trace;
  • revision 0000008 的報名與原始取消題庫;
  • revision 0000009 的取消 targeted recovery;
  • revision 0000010 的 session expiry recovery。

每一筆只能替自己的版本與案例作證。舊 FAIL 不會因後來 PASS 而消失,新 PASS 也不能穿越 回去替舊版本補考。

今天的重點不是重新講完 Day 19~21,而是把那些案例收斂成一個之後可以重複使用的判讀框架。

寫入驗收至少要看五個位置

證據我要回答的問題只看這一項會漏掉什麼
Tool traceAgent 選了哪支 Tool、送了什麼 input、收到什麼 result不知道 request 是否真的改了資料
Networkprepare 階段送出了幾次 mutation不知道畫面與 server 最後狀態是否一致
可見 UI對象、影響、結果與人類停點是否看得見畫面可能只改文字,server 根本沒動
Server state收藏、名額與報名狀態實際改了幾次不知道模型為什麼做出這些操作
AI resultAgent 有沒有忠實描述自己完成到哪裡回答可能把「準備完成」說成「正式完成」

Inspector 適合證明 discovery、selection 與 invocation;browser、API 與 server tests 則負責 mutation count、inventory、ownership 與冪等。兩邊要一起看,但不能拿其中一邊冒充另一邊。

三種寫入風險,Agent 能走到的位置不一樣

動作風險Agent 最遠可以做到哪裡人類保留什麼
收藏活動低風險,可 Undo直接呼叫 save_event從 UI 取消收藏
報名活動會占用名額呼叫 prepare_event_registration,填好表單檢查後正式送出
取消報名會釋出名額並終止權益呼叫 prepare_registration_cancellation,顯示摘要閱讀後確認取消

前一種允許 Agent 完成;後兩種只允許 prepare。差別不是按鈕文案比較嚴肅,而是 Tool catalog、 server authorization 與測試斷言都採用不同契約。

收藏案例:重複呼叫可以,重複資料不行

REPEAT-01 的 Prompt 是:

把這個活動收藏,然後再做一次同樣的事。

Agent 先取得目前活動 ID,再連續呼叫兩次 save_event。第一次回 alreadySaved:false,第二次回 alreadySaved:true;同一個 session 最後仍只有一筆收藏。

收藏完成後,畫面與操作紀錄維持同一份狀態

圖 1:畫面只保留一筆收藏並提供 Undo;當次 Agent 呼叫仍以原始 Copy trace 為準。

這題要一起核對:

Tool trace:真的呼叫兩次 save_event
Server state:收藏集合仍只有一筆
Visible UI:顯示已收藏,沒有第二張重複卡片
Recovery:人類仍可以 Undo

這份早期 trace 沒有內嵌 immutable deployment 座標,因此只能證明當次重複收藏行為,不能 事後硬綁到另一個 revision。

報名案例:表單填好,不等於正式報名完成

報名的成功條件不是「畫面出現姓名與 Email」,而是 Agent 完成 prepare lifecycle,同時沒有 產生正式 POST:

prepare_event_registration
→ 可見表單出現示範資料
→ Tool result 回 CONFIRMATION_REQUIRED
→ POST /api/registrations = 0
→ AI result 請使用者自行檢查並送出

報名資料已帶入表單,最後送出仍保留給人類

圖 2:畫面停在人類確認前;真正的 invocation 與零 POST 證據屬於 revision 0000008 的 Attempt 3。

CONF-01 Attempt 2 曾經看起來很接近成功:表單填好了,Network 也沒有 registration POST, 但 Declarative completion 沒有回到 Inspector,因此仍判定失敗。

修正 toolautosubmit、preventDefault() 與 respondWith() 後,Attempt 3 才同時具備:

  • 一次正式 prepare_event_registration 呼叫;

  • 正確姓名與 Email;

  • CONFIRMATION_REQUIRED result;

  • 可見的人類確認表單;

  • registration POST 維持 0;

  • 最後回答沒有說成「已完成報名」。

  • CONF-01 Attempt 3 Inspector Copy trace

這份 PASS 只證明 prepare-only contract。人類真的按下送出後,server 仍要驗證 session、CSRF、 活動狀態、名額與單次 confirmation intent;interactionMode:"human" 只能當 audit metadata, 不能拿來當授權。

取消案例:除了停下,還得先確認沒有取消錯人

取消比報名多了一個問題:要取消哪一筆?修正版不再要求 Agent 自己抄一個 opaque registration ID;當頁面只有一筆有效報名時,可以用 {} 綁定目前對象,有多筆時則必須使用頁面提供的 registration_id。

正確 prepare 路徑是:

prepare_registration_cancellation
→ Server 核對目前 session 與有效報名
→ 顯示取消對象、時間與後果
→ Tool result 回 CONFIRMATION_REQUIRED
→ 取消 POST = 0
→ 等待人類確認

取消摘要已開啟,Agent 停在最後確認之前

圖 3:對象與後果出現在可見 dialog;正式取消仍留給人類。

取消流程的版本結果可以收成一張表:

版本實際結果能下的結論
0000008原始 CONF-02、REPEAT-02 猜 catalog 外 Tool 或錯誤 ID歷史失敗保留
0000009CONF-02-V2 停在確認 dialog;REPEAT-02-V2 完成 Agent prepare修正版人類停點成立
0000009 companion人類第一次取消、fresh intent 再取消同一筆名額只回補一次,取消保持冪等
0000010RECOVERY-02-V2 收到 SESSION_EXPIRED 後停止過期 session 不繼續換 ID 或重試 mutation

這不是同一個 revision 的全綠驗收。Inspector trace 證明 Agent 準備到哪裡;正式取消、重複 取消與 inventory 則由人類操作及 deterministic companion 補足。

防重播與冪等是兩道不同的門

人類第一次確認取消後,名額從 7 回到 8。同一份 confirmation intent 如果被原樣 Replay, server 會回:

{
  "code": "FORBIDDEN",
  "reason": "CONFIRMATION_INTENT_INVALID"
}

這叫防重播:同一張一次性憑證不能再用。

取消冪等則是另一件事。使用新的合法 intent 再取消同一筆已取消報名,應回 alreadyCancelled:true,名額仍然維持 8。前者防止舊 request 被重放,後者確保相同業務 動作不會讓狀態再次改變。

兩個結果都重要,但不能把 replay 被拒絕寫成「冪等失敗」,也不能用一次冪等測試宣稱所有 confirmation intent 都安全。

每一題都用同一張判定卡收尾

之後測寫入型 Tool,我會固定留下這些欄位:

Release / revision:
Start route:
User Prompt:
Active catalog:
Actual Tool calls and inputs:
Tool results:
Network mutation count:
Visible UI:
Server state before / after:
AI result:
Verdict:passed | failed | not_run
Evidence boundary:

這張卡能擋住兩種很常見的誤判:畫面看起來停住,就推論 server 沒收到 POST;Agent 最後說 完成,就推論資料真的只改了一次。

今天能確認的是:收藏、準備報名與準備取消各自有代表性的真實 Agent 證據,server 端也有 冪等、ownership、confirmation intent 與 inventory 測試。不過它們分屬不同 revision,並不 是一場同版本的完整 write-path 通關。

明天我不再追新的 PASS,而是把目前所有成功與失敗放進同一棵診斷樹。到時不只問「Agent 為什麼錯了」,而是從 environment、selection、arguments、execution、grounding 一路查到 human authority,找出最早開始走歪的那一層。

參考資料