網站要讓 agent 做事,先把按鈕背後的承諾寫清楚
人類看到「確認預約」按鈕時,讀到的從來不只四個字。
畫面上還有日期、時段、價格、取消規則、登入身分,以及一段提醒:按下去之後,預約才會正式成立。使用者通常能從這些線索理解自己正在承諾什麼,也知道哪一步還能返回修改。
如果 agent 只拿到一個叫做 confirm 的動作,這些線索可能全都不見了。它或許知道怎麼呼叫,卻不知道目前替誰操作、價格是否已更新、重複呼叫會不會建立兩筆預約,也不確定逾時代表失敗,還是伺服器其實已經完成處理。
讓 agent 按下按鈕並不難。產品缺少的是一份 action contract,把按鈕背後的承諾說清楚。
可讀的網站,還不一定能安全地被操作
過去幾年,網站花了很多力氣讓內容更容易被理解。語意化 HTML、結構化資料、canonical URL、清楚的標題與穩定的物件識別,都能幫助搜尋引擎和 agent 知道頁面上有什麼。
這一層處理的是 readable 與 discoverable。它很重要,但和 callable 是兩回事。
一篇服務介紹可以被完整讀懂,不代表任何讀到它的 agent 都能替使用者下單。一份 llms.txt 可以說明網站內容,也不等於授權。Schema markup 能描述價格與營業時間,仍無法回答誰可以改動帳戶、哪個步驟需要本人確認,以及操作失敗後該相信哪一份狀態。
當網站開始提供 WebMCP-style page actions,或透過其他 agent-facing tool surface 暴露操作能力,前端多了一項工作:不能再把關鍵語意只留在畫面裡。那些原本靠按鈕位置、警告文字、disabled state 與登入流程傳達的限制,需要成為機器也能核對的產品契約。
單純把 DOM 換一種格式輸出,仍然沒有交代產品承諾。DOM 描述畫面如何組成,action contract 描述一次動作對使用者代表什麼。
從使用者目標開始,不要翻譯每一次 click
最直接、也最危險的做法,是把現有介面上的按鈕逐一變成 agent tools。搜尋、展開、選取、下一步、送出,各自對應一個可呼叫動作。清單看起來很完整,操作介面的細節卻全轉嫁給 agent。
人類可以在畫面上邊看邊修正。Agent 面對一串低階動作時,必須猜測頁面狀態、步驟順序與失敗後的恢復方式。產品一改版,這套隱含流程也跟著改變。
如果是小型產品,我會先選一件邊界清楚、對使用者真的有價值的事。
以預約服務為例,第一版不必公開所有畫面操作。可以只整理出三個概念動作:
查詢可預約時段。
準備預約摘要。
確認建立預約。
查詢只讀取資料。準備摘要時,系統驗證服務項目、時段、價格、取消條件與目前身分,但不建立預約。最後的確認才產生外部副作用,而且必須讓使用者看見精確目標,再給出相稱的授權。
這種切法保留了人的決定權,也減少 agent 必須猜測的狀態。更實際的是,畫面改版時,使用者目標通常不會跟著改名,action contract 因此能比 DOM click 穩定得多。
一份 action contract 至少要回答六件事
Action contract 不必先變成龐大的新規格。對小型產品來說,一份放在 repository、能跟著程式碼 review 的文件就能起步。先寫清楚下面六件事就夠了。
1. 這個動作用來完成什麼
先寫使用者目標,也寫不該呼叫的情況。search_slots 是查詢可用時段,不保留名額;prepare_booking 是產生待確認摘要,不代表價格已鎖定。名稱若無法表達差異,說明文件也很難補救一個過度寬泛的動作。
2. 需要哪些輸入與前置狀態
列出必要識別碼、格式、目前資源狀態與驗證規則。不要要求 agent 從上一個頁面猜出隱藏欄位,也不要讓伺服器默默沿用某次瀏覽留下的暫存值。
同一個「下午兩點」可能屬於不同門市、不同時區或不同服務人員。契約應讓這些條件出現在輸入或可驗證的 context 裡,而不是依賴人類看得見、agent 卻拿不到的畫面。
3. 誰有權呼叫,哪一步仍要人確認
登入狀態只是起點。系統還要知道 agent 代表哪個使用者、取得什麼 scope,以及這份授權能否建立訂單、付款、取消或讀取個人資料。
準備摘要可以由 agent 自動完成,正式建立預約則可能需要一次明確確認。確認畫面要顯示服務、時間、金額與對象,不能只問「是否繼續」。使用者核准的是一個具體操作,不是一張可以重複使用的空白支票。
4. 它會改變什麼狀態
契約要說明動作是 read-only、準備中,還是會正式提交。若呼叫可能重送,也要定義 idempotency key 或其他去重方式。
網路一斷,agent 常會嘗試重試。若產品沒有穩定的去重語意,一次正常的恢復動作可能變成兩筆訂單。不能把「模型應該知道不要重按」當成資料一致性策略。
5. 成功與失敗各自代表什麼
「出錯了」對人和 agent 都沒有幫助。輸入驗證不通過、權限不足、需要再次確認、系統逾時與部分完成,應該是不同結果。
最麻煩的是 unknown state:呼叫端逾時,但伺服器可能已經建立預約。這時回傳再試一次並不安全。產品需要提供可查詢的 operation ID,讓 agent 先核對結果,再決定是否重送或交回人工處理。
6. 事後留下什麼收據
一次可操作的 agent session,至少應留下動作名稱、目標物件、代表的使用者、確認內容、執行結果與錯誤類別。這份收據同時服務工程偵錯與使用者查核,也要能對應到使用者看得到的正式資源,例如同一筆 canonical booking。
如此一來,使用者問「剛才到底有沒有預約成功」,系統不必從模型對話猜答案。產品可以直接拿出可核對的狀態。
人看到的 UI,不必等於 agent 能呼叫的 surface
Agent-ready 常被誤解成另外做一套 AI 版網站。其實人類介面可以繼續保留熟悉的表單、按鈕與導覽,agent 只需要看到經過挑選的動作,以及完成動作所需的最小 context。
兩者甚至不該完全相同。
畫面上的「複製地址」按鈕對人很方便,卻沒有必要成為公開 tool。相反地,prepare_booking 可能在 UI 裡分散成好幾個畫面,對 agent 卻適合被整理成一個不產生副作用的準備動作。介面負責幫人理解,tool surface 負責讓機器在明確邊界內行動。
近期 agent runtime 逐漸把 typed tool context、approval、timeout、sandbox 與 telemetry 分開處理,方向是合理的。但 runtime controls 只能執行產品已定義的邊界。它無法替團隊決定哪些任務值得公開、誰必須確認,以及哪一份狀態才算正式結果。
MCP 的無狀態核心、routing、authorization 與版本政策,也說明 agent capability 一旦離開 demo,就會遇到部署與治理問題。不過協定解決的是能力如何被連接與呼叫,網站仍要自己定義操作的產品語意。WebMCP-style page actions 與 MCP 不該被混成同一份規格。
衡量完成,而不是只數 agent 流量
網站若真的開放 agent 操作,最容易取得的數字可能是 agent visits、tool calls 或 token usage。這些數字能反映活動量,卻不代表使用者的事有辦完。
預約流程更值得看的,是成功完成率、輸入驗證被拒絕的原因、確認階段的放棄率、重複呼叫、unknown state,以及最後交回人工的情況。這些資料會直接指出 action contract 哪裡仍然模糊。
如果很多 agent 都卡在價格確認,先檢查準備摘要與正式提交之間是否有穩定的價格版本。重試經常產生重複資源時,該修的是 idempotency,繼續調 prompt 只會掩蓋問題。
Agent 流量可以很高,完成率仍然很差。對產品團隊來說,前者是新型態的 page view,後者才接近使用者價值。
網站為 agent 做準備,不必從一場全面改造開始。先挑一條高價值流程,把讀取、準備與提交拆開,定義權限、確認、狀態與失敗,再讓人和 agent 都能核對同一份結果。
按鈕可以繼續留在畫面上。它背後的承諾,則不能再只靠人類自己領會。