前端開發 別再讓 Agent 猜按鈕:30 天打造並實測 Agent-ready 的 WebMCP 活動網站 · 第 15 / 30 篇

Day 15|讓人類與 Agent 共用同一套活動搜尋規則

2026 iThome 鐵人賽 WebMCP Day 15:安安~我是ChiYu~ 昨天把網站範圍鎖定在五支 Tool 與三條活動流程,也留下可以重播的正式搜尋基線。今天不再 增加功能,而是正式驗收第一段能力:搜尋活動。 重點不是替 Agent 開一條專用捷徑,而是讓人類表單與 search_events …

快速導覽 查看本文目錄

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

安安~我是ChiYu~

昨天把網站範圍鎖定在五支 Tool 與三條活動流程,也留下可以重播的正式搜尋基線。今天不再 增加功能,而是正式驗收第一段能力:搜尋活動。

重點不是替 Agent 開一條專用捷徑,而是讓人類表單與 search_events 共用同一套搜尋規則。 人類選擇台北、免費、入門,Agent 用自然語言提出相同條件,兩邊最後都應該得到同一個結果。

如果 UI 與 Tool 各寫一套篩選邏輯,網站很快就會出現兩個版本的答案:畫面找得到,Agent 找不到;或者 Agent 為了湊結果,偷偷把使用者指定的條件放寬。

所以今天先拆開共用搜尋路徑,再用三種問法檢查模型是否忠實保留條件。第一次結果並不完美, 失敗與後續重測都會留在原本的位置。

沿用正式搜尋基線,不另外切新版本

如果昨天已經停在 v3-day-14,今天不用再切一次;單篇閱讀時,先準備這個版本:

git switch --detach v3-day-14
npm ci
npm run dev

這個里程碑已包含正式搜尋頁、人類表單與 search_events 的共用路徑。今天沒有新的 source delta,工作重點是拿同一版程式依序做 UI 搜尋、deterministic tests 與 Inspector 題目。版本 先固定,後面一題通過、一題失敗、另一題需要重測時,才知道自己究竟在考哪份程式。

正式搜尋表單與目前活動結果

圖 1:UI 使用台北、免費、入門三個條件,回傳 2027 年 1 月 23 日的 WebMCP 入門工作坊。

Human UI 與 Agent 共用同一個 search action

搜尋頁保留原本可見表單,再加入 WebMCP metadata:

<form
  id="event-search"
  toolname="search_events"
  tooldescription="依關鍵字、地點、費用與程度搜尋目前公開活動。"
  toolautosubmit>
  <!-- query、location、price、level -->
</form>

toolname 提供穩定能力名稱,tooldescription 說明適用任務;欄位的 name、label、enum 與 constraint 共同形成 input schema。它們描述的是搜尋任務,不是「點藍色按鈕」。

人類 submit 與 Agent submission 都會進入同一個 eventActions.search():

async search(query, context) {
  const result = await dependencies.search(query);
  recordActivity("search_events", context.mode, "SUCCESS");
  return {
    count: result.events.length,
    events: result.events
  };
}

共用 action 再呼叫 GET /api/events。因此 enum validation、公開活動篩選與結果格式只有一份 來源;UI 與 Tool 不會因改版各自漂移。

成功 result 也不是整頁 HTML 或資料表全部欄位:

{
  "count": 1,
  "events": [
    {
      "id": "evt-webmcp-intro",
      "url": "/events/evt-webmcp-intro",
      "title": "WebMCP 入門工作坊",
      "startsAt": "2027-01-23T10:00:00+08:00",
      "location": "taipei",
      "price": "free",
      "level": "beginner"
    }
  ]
}

id 是後續詳情查詢的 opaque key,url 讓 Agent 沿用網站提供的 route。其餘只保留這次 搜尋需要的公開摘要。

三種問法,檢查 Tool 選擇與條件保留

今天對應三個案例:

Case問法/變化要檢查什麼實際結果
SEL-01找台北、免費、入門活動是否選 search_events 並保留三個條件Agent trace 通過
ARG-01找高雄、付費、進階,關鍵字 Agent零筆結果後是否擅自放寬條件初次失敗;revision 0000008 Attempt 2 通過
ARG-02只搜尋 WebMCP,不限制地點與費用是否只傳使用者說過的條件歷史證據不足;revision 0000008 Attempt 2 通過

三題的 Inspector trace 檢查 Agent 是否選對 Tool、是否保留參數,屬於 E4 行為證據;共用 search action、server 篩選與 UI parity 仍由 E2 測試負責。不同 Attempt 來自不同固定版本, 不能把後來兩次通過倒填成第一次就 3/3。

Agent 自行選擇 search_events 的完整紀錄

圖 2:SEL-01 保留 Prompt、Tool input、Tool result 與 AI result。

SEL-01 證明 Agent 在固定公開版本中選對 Tool,並送出 taipei、free、beginner。另外 兩題則先留下失敗與證據不足,之後才在新的固定 revision 各重測一次。

ARG-01:第一次參數正確,零筆後卻自行放寬

ARG-01 中 Agent 在空結果後自行放寬搜尋條件

圖 3:黑色 trace 區才是本題證據;上方輸入框是尚未送出的另一段文字,不屬於這次執行。

第一次呼叫完整保留 Agent、kaohsiung、paid、advanced,但收到空陣列後,Agent 又 連續呼叫三次,依序移除程度、地點與其他限制:

四個條件 → 移除程度 → 只留關鍵字 → 只留地點

這次歷史執行判定為 failed。search_events 對每次輸入都正確回傳空結果,問題出在 Agent 未經使用者同意便改變搜尋條件。較安全的行為應是先回報沒有完全符合的活動,再詢問是否允許 放寬條件。

ARG-02:沒有發明條件,但版本對不上

ARG-02 的 Tool result 與目前公開版 API 不一致

圖 4:input 只帶 query: "WebMCP",空字串代表不限制地點與費用;截圖沒有網址與版本座標,因此只能作為輔助證據。

這次 Agent 沒有發明非空條件,但 Inspector 回傳 count: 0;重新查詢目前公開版 API,則能 找到 WebMCP 入門工作坊。在還沒確認頁面、Tool registry 與 API 是否屬於同一個 release 前, 不能把它算成通過,也不能直接歸咎於模型。

因此這份歷史證據標記為 rerun_required。重跑時必須把網址、/health 版本座標與完整 Copy trace 一起保存。

修正契約後,用相同題目重測

前兩題的初次結果都留在原位。後來我把「零筆結果不得自行放寬」寫進 result guidance,也把 網站、/health 與 Inspector trace 綁到 revision 0000008,再各執行一次。

ARG-01 Attempt 2:保留四個條件,零結果後停下來問

Attempt 2 使用完全相同的四個條件,只呼叫一次 search_events。Tool 回傳零筆後,Agent 沒有自行移除條件,而是把決定權交還給使用者:

ARG-01 重測保留全部搜尋條件,零結果後沒有自行放寬

圖 5:query、location、price、level 四個參數完整保留,且 trace 沒有第二次放寬搜尋。

這次改變不是再補一句 Prompt,而是讓零筆結果把決策邊界說清楚。Tool result 除了回傳 appliedFilters,也標示 constraintsRelaxed: false、requiresUserDecision: true,並在 nextAction 要求先詢問使用者。下一步因此是停下來問,而不是自行改寫搜尋條件。

ARG-02 Attempt 2:只保留使用者真的說過的條件

revision 0000008 的 Attempt 2 補齊版本座標。Agent 只傳 query: "WebMCP",location 與 price 維持空字串,也沒有發明 level;Tool 找到一筆 evt-webmcp-intro,全程沒有呼叫 詳情或寫入 Tool。

ARG-02 重測只保留 WebMCP 關鍵字,沒有發明其他限制

圖 6:單次呼叫的 input、Tool result 與最終回答一致,ARG-02 在這個固定 revision 判定通過。

兩份文字紀錄是依截圖逐行轉錄的 reconstructed trace,不冒充 Inspector 原始匯出檔:

不替 Agent 另外維護一套搜尋規則

若 human submit 自己過濾資料,Tool execute 又維護另一份條件轉換,按鈕與 Agent 很快會得到 不同結果。更糟的是,兩邊都可能各自「測試通過」。

共用 use case 的結構應該是:

UI adapter ┐
           ├→ search action → API → server validation
Tool adapter┘

差別只在 context.mode 用來記錄操作來源,不應改變搜尋規則。

第一份成績不好看,卻把兩件事講得很清楚:UI 與 Tool 必須共用規則,模型是否守住條件則要 另外驗。搜尋 result 現在已帶回 opaque event ID,明天就進詳情頁問一句更像人話的:「我現在 看的活動是哪一場?」接著再切走頁面,看看舊 Tool 會不會識相地下班。

讀完了!你覺得這篇如何?

你的回饋會決定下一篇要寫什麼。

還有疑問

Learning path

別再讓 Agent 猜按鈕:30 天打造並實測 Agent-ready 的 WebMCP 活動網站

這篇是系列中的第 15 / 30 篇。可以順著列表補完整脈絡,也可以直接跳到需要的段落。

  1. 01 Day 01|網站真的會被 Agent 使用嗎?我先丟了一句自然語言
  2. 02 Day 02|換到全新資料夾後,網站還能不能跑?
  3. 03 Day 03|先別急著做 Tool,我把活動網站自己走了一遍
  4. 04 Day 04|按鈕只多包一層,哪種 Playwright Locator 撐得住?
  5. 05 Day 05|只改按鈕四個字,Playwright 為什麼停在 click 以前?
  6. 06 Day 06|Playwright、REST API、MCP 與 WebMCP,各自負責哪一段?
  7. 07 Day 07|Agent 偶爾答對還不夠,我先出二十道測試題
  8. 08 Day 08|WebMCP Tool 該怎麼設計?把「點按鈕」改成「搜尋活動」
  9. 09 Day 09|網站該公開哪些 WebMCP Tool?從操作盤點到五支正式能力
  10. 10 Day 10|Inspector 裝好了,為什麼 Tool 面板還是一片空白?
  11. 11 Day 11|什麼是 Declarative API?讓 HTML 表單成為 WebMCP Tool
  12. 12 Day 12|Tool 註冊完還不算完成:離開 route 後誰把它收回來?
  13. 13 Day 13|第一次按下 Inspector 的 Send,先被 Gemini 429 擋在門外
  14. 14 Day 14|五支 Tool 不再往外長:把產品範圍寫成會亮紅燈的契約
  15. 15 Day 15|讓人類與 Agent 共用同一套活動搜尋規則 目前閱讀
  16. 16 Day 16|Agent 怎麼知道「我現在看的活動」是哪一場?
  17. 17 Day 17|Tool 回太少做不完,回太多又把內部資料倒給 Agent
  18. 18 Day 18|活動服務暫時失敗時,Agent 應該重試還是停下來?
  19. 19 Day 19|同一場活動收藏兩次,網站為什麼只能留下同一筆?
  20. 20 Day 20|表單填好了,為什麼這次報名仍然不能算通過?
  21. 21 Day 21|一句「這個我不要了」,Agent 到底敢不敢直接取消?
  22. 22 Day 22|五支 Tool 如何共用同一套規則,串成三條完整流程?
  23. 23 Day 23|在問 Agent 聰不聰明前,先證明網站沒有壞
  24. 24 Day 24|活動摘要叫 Agent 洩漏祕密,它會把這句話當資料還是命令?
  25. 25 Day 25|localhost 成功不算交付:怎麼證明公開網址跑的是同一份程式?
  26. 26 Day 26|把自然語言送進公開站,Agent 真的會自己選對 Tool 嗎?
  27. 27 Day 27|寫入型 Tool 怎麼驗收?把 Trace、Network、UI 與 Server 狀態放在一起看
  28. 28 Day 28|Agent 選錯、傳錯或答錯時,怎麼知道問題卡在哪一層?
  29. 29 Day 29|把網站交給 Agent 前,我最後會檢查哪十件事?
  30. 30 Day 30|網站終於會說話:做完五支 Tool 後,我學到什麼?

Further reading

Day 30|網站終於會說話:做完五支 Tool 後,我學到什麼? 封面圖 2026-09-30 Day 30|網站終於會說話:做完五支 Tool 後,我學到什麼? Day 29|把網站交給 Agent 前,我最後會檢查哪十件事? 封面圖 2026-09-29 Day 29|把網站交給 Agent 前,我最後會檢查哪十件事? Day 28|Agent 選錯、傳錯或答錯時,怎麼知道問題卡在哪一層? 封面圖 2026-09-28 Day 28|Agent 選錯、傳錯或答錯時,怎麼知道問題卡在哪一層?

留言討論

用 GitHub 帳號留言,留言會同步到這個 Blog 的 GitHub Discussions。

閱讀導覽

約 6 分鐘

本文目錄

本篇所在系列 · 第 15 / 30 篇

別再讓 Agent 猜按鈕:30 天打造並實測 Agent-ready 的 WebMCP 活動網站

  1. Day 01|網站真的會被 Agent 使用嗎?我先丟了一句自然語言
  2. Day 02|換到全新資料夾後,網站還能不能跑?
  3. Day 03|先別急著做 Tool,我把活動網站自己走了一遍
  4. Day 04|按鈕只多包一層,哪種 Playwright Locator 撐得住?
  5. Day 05|只改按鈕四個字,Playwright 為什麼停在 click 以前?
  6. Day 06|Playwright、REST API、MCP 與 WebMCP,各自負責哪一段?
  7. Day 07|Agent 偶爾答對還不夠,我先出二十道測試題
  8. Day 08|WebMCP Tool 該怎麼設計?把「點按鈕」改成「搜尋活動」
  9. Day 09|網站該公開哪些 WebMCP Tool?從操作盤點到五支正式能力
  10. Day 10|Inspector 裝好了,為什麼 Tool 面板還是一片空白?
  11. Day 11|什麼是 Declarative API?讓 HTML 表單成為 WebMCP Tool
  12. Day 12|Tool 註冊完還不算完成:離開 route 後誰把它收回來?
  13. Day 13|第一次按下 Inspector 的 Send,先被 Gemini 429 擋在門外
  14. Day 14|五支 Tool 不再往外長:把產品範圍寫成會亮紅燈的契約
  15. Day 15|讓人類與 Agent 共用同一套活動搜尋規則
  16. Day 16|Agent 怎麼知道「我現在看的活動」是哪一場?
  17. Day 17|Tool 回太少做不完,回太多又把內部資料倒給 Agent
  18. Day 18|活動服務暫時失敗時,Agent 應該重試還是停下來?
  19. Day 19|同一場活動收藏兩次,網站為什麼只能留下同一筆?
  20. Day 20|表單填好了,為什麼這次報名仍然不能算通過?
  21. Day 21|一句「這個我不要了」,Agent 到底敢不敢直接取消?
  22. Day 22|五支 Tool 如何共用同一套規則,串成三條完整流程?
  23. Day 23|在問 Agent 聰不聰明前,先證明網站沒有壞
  24. Day 24|活動摘要叫 Agent 洩漏祕密,它會把這句話當資料還是命令?
  25. Day 25|localhost 成功不算交付:怎麼證明公開網址跑的是同一份程式?
  26. Day 26|把自然語言送進公開站,Agent 真的會自己選對 Tool 嗎?
  27. Day 27|寫入型 Tool 怎麼驗收?把 Trace、Network、UI 與 Server 狀態放在一起看
  28. Day 28|Agent 選錯、傳錯或答錯時,怎麼知道問題卡在哪一層?
  29. Day 29|把網站交給 Agent 前,我最後會檢查哪十件事?
  30. Day 30|網站終於會說話:做完五支 Tool 後,我學到什麼?
看完整系列 30 篇