—— LLM 可以「建議」,但所有「執行」都必須經過閘道。
對象:平台工程 / 資安 / 內部應用負責人 / LLMOps
關鍵機制:Allowlist → JSON Schema → Justification → JIT 提權 → Dry‑run → 審計
多數事故不是「模型答錯」,而是工具被誤用:寄錯信、刪錯資料、把機密傳到外網。
今天把「工具閘道(Tool Gateway)」做成系統級控制點,讓 AI 只能在護欄內「動手」。
[Client / LLM]
│ function call
▼
[Tool Gateway / Proxy] ──► [Policy Engine (OPA/Cedar)]
│ │
│ ├─ Param Validator (JSON Schema / Regex / Allowlist)
│ ├─ Idempotency / Replay Guard / Budget / Rate
│ ├─ Risk Scoring → Justification → JIT 提權(雙人核可)
│ └─ Dry‑run / Diff Preview(雙階段:prepare → execute)
▼
[Connectors] ──► [Target APIs/DB]
▼
[Audit / SIEM / Metrics]
additionalProperties=false
justification
+ ticket_id
prepare
→ confirm
→ execute
idempotency_key
僅執行一次,防重放/重複提交{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "send_email",
"type": "object",
"properties": {
"to": { "type": "string", "format": "email" },
"cc": { "type": "array", "items": { "type": "string", "format": "email" }, "maxItems": 5 },
"subject": { "type": "string", "maxLength": 120 },
"body": { "type": "string", "maxLength": 5000 },
"attachments": { "type": "array", "items": { "type": "string", "pattern": "^file://[a-zA-Z0-9/_.-]+$" }, "maxItems": 3 }
},
"required": ["to", "subject", "body"],
"additionalProperties": false
}
type Verdict = "allow" | "ask" | "deny" | "elevate" | "dryrun";
export async function gateTool(user, tool, params, ctx) {
// 1) 白名單
if (!allowlist(user.role).includes(tool)) return { verdict: "deny", reason: "not_allowed" };
// 2) Schema 驗證
if (!validateJson(schemaOf(tool), params)) return { verdict: "deny", reason: "bad_params" };
// 3) 敏感分級與用途說明
if (isHighRisk(tool) && !ctx.justification) return { verdict: "ask", ask: "請填用途說明與工單編號" };
// 4) JIT 提權(需核可與到期)
if (needsJIT(tool) && !await approved(user, tool, ctx)) return { verdict: "elevate", next: "等待二人核可" };
// 5) 寫入類工具 → 先 Dry‑run
if (isWrite(tool)) {
const diff = await preview(tool, params);
return { verdict: "dryrun", diff };
}
return { verdict: "allow" };
}
run_sql.update
:回傳將更新的列數/條件/示例差異send_email.bulk
:回傳收件群組、主旨與前三行內文預覽create_ticket
:回傳標題/標籤/受理人,確認後才寫入預設不直接「一鍵送出」,而是可審核的變更提案。
egress-allowlist.yaml
註冊;未註冊一律擋# egress-allowlist.yaml
destinations:
- name: corp-mail
host: smtp.corp.local
protocol: smtps
- name: ticketing
host: jira.corp.local
protocol: https
to:"external@evil.com"
應被 Schema 擋下idempotency_key
,應僅執行一次run_sql.prod
應被「要求填寫用途」或「提升核可」send_email.bulk
應被拒並告警additionalProperties=false
)justification
與 ticket_id
為必填(中高風險)PM:可以幫我寄封信給所有客戶嗎?
你:可以,請先填「用途說明」與「受眾群組」,等核可通過我再按下執行鍵。
PM:……那我先把名單與工單號開好。
工具閘道的價值,不是限制,而是可控。
讓模型去「想」沒關係;要「動手」就過閘道。你的系統,才會既聰明又守規。
把「外部介接」這條命脈做厚:簽章驗證、時間窗、nonce、租戶邊界。