這一課會完成什麼
- 替單一用途工具設計嚴格 schema
- 不依賴模型產生的參數,另外驗證呼叫端與租戶
- 回傳精簡有型別的觀察紀錄與可處理的錯誤碼
- 測錯誤的工具、偽造參數、逾時、超出大小限制的結果與重複呼叫
開始前先準備
- • 模組 01 的結果聯集型別與執行環境驗證模式
- • 基本授權概念與本機 TypeScript 測試執行器
先把定義說清楚
工具呼叫與權限
工具呼叫(function 呼叫)讓模型以結構化參數提出請求,例如查詢某個帳戶。收到請求後,主應用程式先驗格式與權限,再用受限身分執行,記錄結果,將有型別的觀察資料交給下一輪模型。參數合法只代表格式正確,並不授予資料存取權。使用者、租戶與角色都由可信的執行環境提供,模型不能自行填入身分來擴大權限。
工具 schema 只能限制參數形態。它無法證明目前使用者可讀這個帳戶、帳戶屬於登入中的租戶,或這個動作符合規則。這些條件必須從可信工作階段與伺服器端規則得到。
工具結果會變成模型脈絡。把整包 CRM 傳送資料、內部 UUID 或呼叫堆疊塞回去,既浪費 token,也讓模型更難復原。介面應回傳下一步真正用得到的語意欄位、大小限制與具名錯誤。
現場情境
Meridian account lookup
具名合成情境。Meridian CRM、租戶 ID、帳戶紀錄、執行軌跡與規則全是本實作虛構資料,不代表真實環境的系統。
- 負責人
- 你是 API 工程師,正在替研究助理加入唯讀帳戶查詢。
- 要做的決策
- 設計能回答分析師問題的最小工具介面,同時不暴露原始 SQL、任意 HTTP 或跨租戶資料。
- 目前狀態
- 助理會先摘要已核准的帳戶脈絡,再由分析師研究公司。兩個合成租戶共用同一個資料庫;早期原型直接相信模型提出的帳戶 ID,也允許模型傳 tenantId。這讓跨租戶讀取成為實際風險。
- 預期成果
- 授權查詢回傳精簡觀察紀錄;偽造租戶、unknown 紀錄、schema 錯誤與逾時各有不洩密的具名錯誤,也不回傳帳戶資料。
限制條件
- • 工具只能讀取公司 name、分類、已核准的筆記與紀錄版本。
- • 租戶身分來自已驗證身分的工作階段,絕不從模型參數取得。
- • 回應最多 5 則筆記、2,000 個 UTF-8 字元。
- • 每次請求記錄呼叫端、租戶、工具、已驗證的參數、結果碼、耗時與關聯 ID。
實作範例
帳戶 ID 合法,租戶檢查仍然擋下
證據類型: 具名模擬情境進行中的工作階段屬於 meridian-eu。使用者詢問 Northwind Labs,但檢索到的公開文字夾帶指令,要模型用 accountId acct_us_204 呼叫 lookup_account。這筆帳戶確實存在,卻屬於 meridian-us。模型看不到資料庫責任歸屬規則,因此輸出符合 schema 的呼叫。
主應用程式忽略自動產生的文字裡所有租戶值,從工作階段綁定 meridian-eu,驗證 accountId,再用租戶與帳戶兩個條件查詢。結果回傳 NOT_FOUND,不透露 acct_us_204 是否存在於別處。執行軌跡記為 denied_at_data_boundary,只保存已驗證的參數雜湊,不存原始帳戶筆記。
EU 呼叫端拿不到任何 US 欄位,稽核紀錄保留已驗證身分的租戶,模型只收到能處理的精簡錯誤,對抗測試資料才算通過。這是確定性的應用程式行為,不宣稱供應商有任何可靠度百分比。
主張限制
範例在記憶體中模擬資料儲存區,採簡單角色規則。正式系統可能還需資料列層級安全、委派 OAuth、依屬性授權、欄位遮蔽與法定保存規則。OpenAI 和 Anthropic 文件描述工具迴圈,實際授權仍由應用程式負責。
做法
照著做,每一步都有檢查點
現場情境
設計能回答分析師問題的最小工具介面,同時不暴露原始 SQL、任意 HTTP 或跨租戶資料。
- 01拆開身分與參數
- 02先驗證,再讀資料
- 03縮小觀察紀錄
驗收條件
工具 schema 協助模型提出有用請求;身分、授權、執行、傳送資料、重試與稽核仍由應用程式控制。跨租戶或未授權情境不會因模型參數看似合理而漏資料。
- 01
拆開身分與參數
工作階段由可信的中介層建立,Args 只來自模型呼叫。從工具 schema 移除 tenantId、userId、角色、URL 與查詢,模型不需要知道也不能控制。
檢查點 · 模型可見的 schema 只有 accountId;執行器必須由主應用程式額外傳入可信工作階段才能執行。
- 02
先驗證,再讀資料
依序做嚴格 schema、角色規則與限定租戶範圍的查詢,通過後才組回應。不存在與屬於其他租戶的帳戶一律用相同 NOT_FOUND 形態。
檢查點 · meridian-eu 查 acct_us_204 得到 NOT_FOUND,紀錄與工具結果都沒有任何資料列欄位。
- 03
縮小觀察紀錄
只回傳 name、最多 5 則筆記、穩定版本及受大小限制的資料。失敗用明確代號指出下一步,不回堆疊或內部查詢;另測超出上限時是否完整拒絕,避免洩漏部分筆記。
檢查點 · 正常結果少於 2,000 字元;超大結果回 RESULT_TOO_LARGE,不洩漏部分筆記。
- 04
控制逾時與重複呼叫
資料存取層呼叫到期限就取消;成功讀取依執行與已驗證的參數雜湊快取。暫時逾時最多再讀 1 次,之後直接轉交人工。
檢查點 · 逾時測試資料結束於 TIMEOUT;同一執行的 3 次相同呼叫,在成功後最多執行資料存取層 1 次。
- 05
寫對抗性測試與稽核斷言
涵蓋額外參數、格式錯誤 ID、缺漏角色、跨租戶 ID、unknown 紀錄、逾時、超出大小限制的回應與重複呼叫,逐一檢查稽核。
檢查點 · 每個測試都記關聯 ID、可信租戶、工具名稱、結果碼與耗時,不存帳戶筆記。
實務脈絡
展示版之後
實作拆解
工具宣告只描述模型可提出的請求,授權紀錄由應用程式建立。中介層從工作階段取得 userId、tenantId 與 roles,以伺服器端型別傳給執行器;模型不必自行填租戶。資料存取層只提供 findByTenantAndId,避免先做全域查詢再補權限判斷。CI 固定測跨租戶呼叫,回應、紀錄、快取鍵值和時間差都不得透露其他租戶是否有該帳戶。
只回傳下一個決策需要的欄位。分析師需要名稱(name)、分類(分類)、已核准筆記與版本時,就省去內部分數、負責人電子郵件、後端鍵值與完整變更歷史。5 則筆記及 2,000 UTF-8 字元的限制,要用多位元字元測清楚位元組與字元的算法。超出上限回完整錯誤碼,不能截掉後半段又讓模型當成完整資料。
驗證前建立關聯 ID,稽核紀錄最後補上可信租戶、已驗證參數雜湊、權限判定、資料存取結果碼、耗時與實作版本,省去原始帳戶筆記。逾時、取消與完成狀態未知分開處理;唯讀操作可依固定規則有限重試。未來若允許寫入,先設計冪等性與結果核對。模型收到可採取下一步的錯誤,操作人員取得可定位原因的軌跡,兩者都不直接暴露內部呼叫堆疊。
營運檢查
工具介面審查要從最壞情境開始:攻擊者能控制使用者文字、檢索內容與模型提出的參數,但不能控制已驗證的工作階段。沿著一次呼叫把信任來源標出來,帳號識別碼來自模型所以要驗格式;租戶、角色與使用者來自中介層所以仍要檢查有效期;資料列來自資料庫,但回傳前還要投影與遮蔽。接著用八個離線測試逐一證明,格式不正確時資料庫呼叫次數為零、跨租戶與不存在都回同一外觀、逾時不會無限重送、成功後相同輸入不重做查詢、過大結果不回半套資料。測試除了看回傳值,也要對稽核事件與儲存庫呼叫計數做斷言。
工具說明不是行銷文案,應明講用途、不能做的事與回傳限制。名稱要能和其他工具區分,參數使用穩定的業務語意,不暴露任意查詢、網址或後端欄位。錯誤結果也要設計:找不到不能透露其他租戶是否存在;權限不足不回內部政策;結果過大要告訴協調器改走分頁或縮小範圍,而不是讓模型自己猜。準備新增第二個工具前,先用保留的真實任務評估選擇正確率、無效參數、呼叫次數、回傳長度與失敗復原。若兩個工具功能重疊到工程師都難以說明差別,模型只會更難選。
驗證與上線
部署前的權限檢查由兩個人分工:一人只看公開工具格式,嘗試塞入額外租戶、任意網址、萬用查詢與過長字串;另一人只看執行器與資料存取,確認可信身分從哪裡來、每個拒絕發生在查詢前還是查詢後。兩邊的結果合併後,工具文件才算完整。這種分工能抓出常見落差:格式很嚴格,執行器卻先查全域資料;或資料層有租戶條件,工具回傳與稽核卻洩漏不該看的欄位。
工具升級也要保留相容性策略。欄位更名、結果增大、錯誤碼新增或權限收緊,都可能讓既有模型與協調器走錯路。先用版本化宣告與保留任務重跑,再決定是否同時支援舊版;不可默默放寬額外欄位,讓舊呼叫『先能跑再說』。移除工具前,搜尋所有提示、允許清單、測試、儀表板與操作手冊的引用,確認舊名稱呼叫會得到明確不可用狀態,而不是轉到一個權限更大的替代工具。
補強練習
再加一個工具登錄測試:程式啟動時逐一確認每個工具都有風險類別、參數格式、執行身分、逾時、回傳上限、負責人與稽核版本。缺一項就不註冊。這能避免後來加入的工具繞過本模組建立的嚴格邊界。
交付前最後檢查
最後用一個完全沒有帳號資料的工作階段執行正常查詢,確認拒絕發生在任何資料讀取之前;再以合法角色執行不存在與跨租戶識別碼,兩者外觀一致。這三筆稽核足以讓審查者確認格式、授權與防枚舉不是同一道檢查。
延伸實作
安全評審另做一張欄位級資料表,列每個回傳欄位的業務用途、原始資料位置、可見角色、遮蔽規則、最長保留與是否進模型。欄位沒有明確下一步用途就移除。工具錯誤也套同樣原則:模型只需要知道能否縮小查詢、稍後再試或交人工,內部堆疊、資料庫名稱與其他租戶線索不應回傳。這張表日後能直接用來審新增欄位,避免後端物件擴張時,模型介面跟著無意放寬。
動手實作
建立 Meridian 唯讀工具邊界
對兩個租戶的記憶體內測試資料實作 lookup_account,跑允許、denied、不合法、逾時與超出大小限制的回應。
準備項目
- • 沿用模組 01 的封閉結果模式。
- • 工作階段與工具參數使用不同型別,不能合併成模型可見的物件。
- • 帳戶筆記只用合成資料,不放姓名、Email 或機密。
- • 資料儲存模擬器回固定結果,驗收可離線執行。
本課產出
嚴格工具宣告、綁定工作階段的執行器、合成程式庫、精簡結果契約、稽核事件型別,以及至少 8 個離線測試。
起始模板: 有授權檢查的帳戶工具
TypeScripttype Session = { userId: string; tenantId: string; roles: string[] };
type Args = { accountId: string };
type ToolResult =
| { ok: true; account: { id: string; name: string; segment: string; notes: string[]; version: number } }
| { ok: false; code: "FORBIDDEN" | "NOT_FOUND" | "INVALID_ARGS" | "TIMEOUT" | "RESULT_TOO_LARGE" };
const TOOL = {
type: "function",
name: "lookup_account",
description: "Read one account in the authenticated tenant. Never searches other tenants or updates records.",
strict: true,
parameters: {
type: "object",
additionalProperties: false,
required: ["accountId"],
properties: { accountId: { type: "string", pattern: "^acct_[a-z0-9_]+$" } }
}
} as const;
export async function executeLookup(session: Session, args: Args): Promise<ToolResult> {
if (!session.roles.includes("account:read")) return { ok: false, code: "FORBIDDEN" };
if (!/^acct_[a-z0-9_]+$/.test(args.accountId)) return { ok: false, code: "INVALID_ARGS" };
const row = await repository.findByTenantAndId(session.tenantId, args.accountId);
if (!row) return { ok: false, code: "NOT_FOUND" };
const account = { ...row, notes: row.notes.slice(0, 5) };
if (JSON.stringify(account).length > 2000) return { ok: false, code: "RESULT_TOO_LARGE" };
return { ok: true, account };
}預期結果
工具 schema 協助模型提出有用請求;身分、授權、執行、傳送資料、重試與稽核仍由應用程式控制。跨租戶或未授權情境不會因模型參數看似合理而漏資料。
留給下一課
保留工作階段、ToolResult、稽核欄位與雙租戶測試資料。模組 05 會用同樣窄的讀取能力封裝成 MCP 伺服器。
驗收條件
- 01跨租戶與未授權測試資料不回帳戶欄位,錯誤形態也不能協助枚舉資料。
- 02額外或格式錯誤參數在程式庫呼叫前失敗。
- 03工具結果不超過文件列出的欄位與大小上限。
- 04同一執行的重複成功呼叫不重做程式庫工作,所有路徑都有稽核紀錄。
常見故障
故障診間
F1呼叫端只要提供 ID,就能讀到另一個租戶的帳戶。
- 先檢查
- 追查租戶身分來源,並確認程式庫判定條件同時包含 tenantId 與 accountId。
- 可能原因
- 授權由模型參數推測,或先做全域 ID 查詢才檢查租戶。
- 修復方式
- 租戶只能從已驗證身分的工作階段綁定,工具僅可使用限定租戶範圍的程式庫方法。
- 下次怎麼避免
- CI 保留跨租戶測試資料;資料儲存區支援時再加資料列規則。
F2模型一直呼叫正確工具,卻反覆帶入無效可選欄位。
- 先檢查
- 檢查原始呼叫、schema 嚴格程度、參數 name、工具說明與驗證錯誤。
- 可能原因
- Schema 太寬,或參數命名模糊,模型無法區分用途。
- 修復方式
- 移除沒用欄位,additionalProperties 設不成立,INVALID_ARGS 回一個可處理範例。
- 下次怎麼避免
- 擴大工具面前,先對保留評測貼近實際情況的集合評估工具選擇與參數。
F3查詢成功,原始 CRM 傳送資料卻塞滿脈絡,後續判斷變差。
- 先檢查
- 量測結果 token,找出下一步從未使用的欄位。
- 可能原因
- 工具直接鏡射後端回應,沒有設計 Agent 看到的觀察紀錄。
- 修復方式
- 改回精簡語意欄位選取;真的需要才透過分頁或詳細模式取更多資料。
- 下次怎麼避免
- 設定回應預算,評測報告要包含工具結果 token 總量。
F4逾時引發重複呼叫,執行軌跡不一致或後端過載。
- 先檢查
- 檢查期限、嘗試計數器、執行層級的快取鍵值與完成狀態未知處理。
- 可能原因
- 重試留給模型自由決定,沒有冪等性或去重邊界。
- 修復方式
- 用確定性的任務協調限制重試,成功結果依已驗證的輸入快取。
- 下次怎麼避免
- 工具串接測試主動注入緩慢、已遺漏的與重複回應。
展示版之後
正式上線前的邊界
- 01身分、租戶與角色從可信的中介層取得,不放在模型可見的參數。
- 02每個工具只做一件事,schema 嚴格且最小。
- 03能力逐一標成唯讀、可復原寫入、外部通訊或不可復原操作。
- 04使用短效、最小權限憑證,網路目的地明列允許清單。
- 05結果有大小限制、穩定錯誤碼,也不回內部呼叫堆疊。
- 06期限、重試、速率限制與去重全在模型外執行。
- 07稽核要有發起者、可信的租戶、參數雜湊、決策、結果、延遲與版本。
- 08加新工具前,先跑保留評測選擇測試與對抗性授權測試。
證據類型
資料來源與主張限制
資料來源只支撐本課標示的主張,不代表換一個系統也會得到相同結果。
- [1]Function callingstrict function schema · 平行呼叫 · tool-call 狀態機
OpenAI · 官方文件 · 2026-08-20
- [2]Claude tool use工具定義 · tool result 迴圈 · 使用者端執行責任
Anthropic · 官方文件 · 2026-08-20
- [3]為 agent 設計有效工具工具介面設計 · 保留集工具評測 · 節省 token 的結果格式
Anthropic · 官方文件 · 2026-08-20
- [4]建置 agent 的安全指引prompt injection · 結構化資料邊界 · MCP 核准
OpenAI · 官方文件 · 2026-08-20
延伸的 Tenten 資源