客服後台接了一個 Agent。客服人員打開一張退貨案件,請它「幫這位客人處理退款」,模型回傳一個工具呼叫:issue_refund,參數是一個訂單號和金額 29.90。JSON 格式完全正確,欄位一個不少。問題是,這個訂單號屬於另一位客人。
這是本刊虛構的示意情境;下面的工具名稱、欄位和授權表,也都是為了說明而設計的,不是測試結果,也不是任何公司的系統。不過,如果你剛接手客戶的 Agent 專案,要把「退款」「改訂單」這類工具接上模型,遲早得回答同一個問題:模型說要退款時,哪一層該把它擋下來?請主管按一下批准,夠不夠?
這篇不討論模型夠不夠聰明,只談模型提出呼叫之後,誰允許它執行。
模型只提出呼叫,真正執行的是你的程式
先把流程拆開。OpenAI 的 function calling 文件把工具呼叫寫成幾個步驟:模型回傳的是一個 tool call,接著由應用程式這一側,拿 tool call 裡的參數執行程式碼,再把執行結果送回模型。這段說的是開發者自己定義的 function tools;平台內建的工具,例如網頁搜尋或 MCP,由平台提供,不在這篇的範圍。
對寫後端的人來說,這代表模型送出的只是一個提議。真正去呼叫退款 API、改資料庫的,是你寫的那段程式。
OpenAI 文件沒有說授權要放在這一步。把檢查放在這裡,是我們根據後面 OWASP 的建議所做的設計:模型給什麼參數,程式都先查過再執行。既然執行在你手上,授權也就能、而且應該放在你手上。
strict schema 管得住格式,管不到權限
OpenAI 的 function 定義用 JSON schema 描述參數。文件說明,開啟 strict mode 後,模型產生的呼叫會可靠地符合 schema,而不是盡力而為,並建議一律開啟。
開頭那筆呼叫剛好劃出它的範圍。order_id 是字串、amount 是數字、必填欄位都在,這些 strict mode 能幫你顧好。但這張訂單是不是這位客人的、金額有沒有超過可退餘額、這筆訂單現在能不能退,schema 看不出來。
我們讀到的文件段落沒有談到授權或業務規則。「格式對了,不代表這個人有權限」是我們的判斷,不是 OpenAI 的說法。strict 仍然值得開,程式可以少處理缺欄位和型別錯誤;只是過了 schema 這關,真正的檢查才要開始。
用 OWASP 的三個根因,回頭檢查工具清單
OWASP 在 LLM Top 10 的 2025 版,把這類風險稱為 Excessive Agency:模型輸出出現意外、含糊,或被人操縱時,系統仍能執行造成損害的動作。它列出三個根因:功能給太多、權限給太多、自主空間給太多。
OWASP 舉了一個讀信助理的例子。使用者只需要它讀信,但接上的套件也能寄信;遇到藏在信件裡的間接提示注入,助理就可能被誘導掃描整個信箱,再把內容轉寄出去。OWASP 的解法正好一層補一個洞:只提供讀信功能、OAuth 只給唯讀 scope、寄信由使用者自己按送出。這是 OWASP 的假設情境,不是某家公司的事故,但很適合拿來對照手上的工具清單。
套到客服 Agent,可以這樣問:
- 功能:這個 Agent 真的需要退款嗎?還是查訂單就夠了?有沒有接上任意 shell 這種開放式工具?
- 權限:退款工具背後的服務帳號,能不能動到所有客人、所有租戶的訂單?
- 自主:哪些動作模型一提出就直接執行,中間沒有任何規則或人把關?
OWASP 的緩解建議也沿著這三條走:工具和功能盡量少、避免開放式工具、權限最小化、在下游系統以使用者的身分執行、高影響動作請使用者批准。其中一條對寫後端的人最直接:授權要在下游系統做完整檢查,不要依賴模型判斷這個動作該不該允許。至於記錄 log 和速率限制,OWASP 明說只能降低損害,防不了事情發生。
身分從登入狀態取得,不讓模型填
回到那筆別人的訂單。如果 issue_refund 的參數裡有 customer_id 或角色欄位,模型可能填錯,也可能被對話內容誘導填成別的值。
比較穩的做法是:客人是誰、屬於哪個租戶、操作的客服是什麼角色,都從登入 session 和案件資料取得,不開放成模型可以填的參數。模型只提出訂單號、金額和退款原因;服務端收到後逐項比對:訂單是否屬於這張案件的客人、是否在同一個租戶、金額有沒有超過可退餘額、訂單目前的狀態允不允許退款,以及這個角色能核准多少。
這就是 OWASP 說的「以使用者身分在下游執行」,再加上完整的下游授權。OWASP 另有一份交易授權指引,原則包括授權要在伺服器端執行並強制、交易資料要防竄改、控制允許的狀態轉換。一筆訂單能不能從「已出貨」轉成「已退款」、能退幾次,應該寫在服務端的規則裡,不是指望模型在對話中記得。
人工批准有用,前提是看得到後果
高影響的動作請人批准,OWASP 也這樣建議。問題在於,人會不會看漏。
Scale X 部落格的作者在 2026 年 8 月公開了一份統計,這篇在 Hacker News 上也引起不少討論。資料來自一個社群遊戲:玩家要判斷是否批准 AI agent 準備執行的指令,累積約 4 萬場、40.9 萬次批准或拒絕。作者算出平均準確率 66.3%,玩家大約漏掉三分之一的威脅。分類來看,外洩或執行類漏掉 33.4%,越界讀取漏掉 35.0%,明顯具破壞性的指令則是 11.7%。
被最多人誤批的是 npm run analyze,有 64.7% 的人按了批准。指令本身看起來無害,實際執行的卻是 package.json 裡寫的腳本。
這些數字要連同作者自己說明的限制一起看:遊戲中大約 34% 的指令是威脅,真實工作裡很少這麼密集;玩家有時間壓力;參與者也是自己選擇來玩的。這是作者公開的遊戲統計,不是對照實驗,不能說人在真實環境就有三分之一的失誤率。它比較能說明的是:批准的人看到的是指令名稱,不一定是指令實際會做的事。
客服退款也是一樣。批准畫面如果只寫「issue_refund,是否同意?」,主管很難判斷。OWASP 交易授權指引的原則之一,是讓使用者辨識並確認重要的交易資料。放到這裡,批准畫面至少要顯示退款金額、哪一張訂單、退給哪位客人;我們建議由服務端依實際要執行的內容產生這個畫面,而不是轉述模型的說法。人工批准是在服務端授權之後多加的一道,不能拿來取代它。
交付物:一張工具授權表
接手專案時,可以先把每個工具填成一張表。下面三列是本刊虛構的示意,欄位可以照抄,內容要換成客戶的實際規則:
| 工具 | 讀/寫 | 身分來源 | 服務端檢查 | 是否需批准 | 速率限制 |
|---|---|---|---|---|---|
lookup_order |
唯讀 | 登入 session 的客服角色、案件綁定的客人 | 訂單屬於該客人、同一租戶 | 不需要 | 依查詢量設上限 |
issue_refund |
寫入,動到金流 | 同上;模型不能傳客人 ID 或角色 | 訂單歸屬、租戶、可退餘額、狀態轉換、角色額度 | 超過角色額度時需主管批准,畫面顯示金額、訂單與客人 | 每位客服、每張訂單各設上限 |
send_email |
寫入,對外發送 | 同上 | 收件人只能是案件客人登記的信箱 | 客服確認內容後自己按送出 | 每張案件設上限 |
最常空白的是「身分來源」和「服務端檢查」兩欄。填不出來,通常表示這個工具目前只靠模型把參數給對。速率限制那欄則要記得 OWASP 的提醒:它只能讓出錯時的損失小一點。
先把每個工具的授權寫下來
接手客戶的 Agent 專案時,不必先急著判斷模型夠不夠聰明。模型負責提出呼叫,strict schema 確保呼叫的格式,你的服務端決定這個人能不能做這件事,人工批准則在高影響動作上再加一道,讓人看清楚將要發生什麼。三個檢查點各自防不同的漏洞,其中最不能省的,是服務端那一層。
開頭那筆格式全對的退款呼叫,在這套設計下會停在服務端:訂單不屬於這張案件的客人,程式直接拒絕,根本輪不到主管批准。
下一篇接著談批准之後的事:主管按了同意,怎麼確定實際執行的就是他看到的那一筆,重試時也不會重複退款。
