Action is proposed as structured data (tool + args).
Проблема (з реального продакшену)
Ти хочеш writes. Агент хоче writes. Клієнти хочуть, щоб writes були правильні.
У демо це звучить легко: “агент просто закриє тікет”. У проді “просто” — це місце, де ти потім пояснюєш, чому 200 речей зламались.
Approvals — не “enterprise фіча”. Це safety valve для системи, яка приймає рішення ймовірнісно.
Чому це ламається в продакшені
1) “Write tool” — не одна категорія
Writes бувають різні:
- reversible vs irreversible
- low impact vs high impact
- idempotent vs “oops, duplicate”
Якщо ставитись однаково, отримаєш або:
- надто багато approvals (UX помирає)
- або надто мало (прод помирає)
2) Без approval path ти стаєш автономним випадково
Якщо write tool у allowlist і нічого його не зупиняє — він буде використаний. Не “може”. А коли це найкоротший шлях.
3) Approval без контексту — марний
“Approve цей tool call?” — недостатньо. Потрібне evidence:
- запропонована дія
- args (або diff)
- чому агент думає, що це правильно
- причина policy / ризик
Приклад реалізації (реальний код)
Мінімальний approval gate:
- write tools створюють approval request
- approval підтверджується id/key
- tool gateway виконує тільки якщо approval валідний
from dataclasses import dataclass
from typing import Any
WRITE_TOOLS = {"ticket.close", "db.write", "email.send"}
@dataclass(frozen=True)
class Approval:
approval_id: str
approved: bool
approved_by: str
class ApprovalRequired(RuntimeError):
pass
def requires_approval(tool: str) -> bool:
return tool in WRITE_TOOLS
def tool_gateway_call(tool: str, args: dict[str, Any], *, approval: Approval | None) -> Any:
if requires_approval(tool):
if not approval or not approval.approved:
raise ApprovalRequired(f"approval_required:{tool}")
return call_tool(tool, args) # (pseudo)const WRITE_TOOLS = new Set(["ticket.close", "db.write", "email.send"]);
export class ApprovalRequired extends Error {}
export function requiresApproval(tool) {
return WRITE_TOOLS.has(tool);
}
export function toolGatewayCall(tool, args, { approval } = {}) {
if (requiresApproval(tool)) {
if (!approval || approval.approved !== true) throw new ApprovalRequired("approval_required:" + tool);
}
return callTool(tool, args); // (pseudo)
}Реальний інцидент (з цифрами)
У нас був агент для “прибирання” support тікетів. Зміна prompt’а інтерпретувала “cleanup” як “close”.
Імпакт:
- 63 тікети закрито помилково
- ~2 години на reopen + вибачення
- ми перевели агента в read-only на тиждень, поки не відновили довіру
Fix:
- approvals для high-impact writes
- approval UI з diff/evidence (не просто “approve?”)
- idempotency keys, щоб approval не виконувався двічі
Компроміси
- Approvals додають latency. Це ціна safety.
- Забагато approvals вбиває UX → потрібні risk tiers.
- Approval система без audit logs залишає тебе без доказів в інциденті.
Коли НЕ варто
- Для read-only tools approvals не потрібні.
- Для low-risk і повністю reversible writes можна auto-approve під жорсткими budgets.
- Якщо люди не review реально — це не approval, це театр.
Чекліст (можна копіювати)
- [ ] Ідентифікувати write tools (capability-based)
- [ ] Approvals для irreversible/high-impact дій
- [ ] Evidence в UI (args diff, why, provenance)
- [ ] Enforce в tool gateway (не тільки в UI)
- [ ] Idempotency keys для approved actions
- [ ] Audit logs (who approved what)
Безпечний дефолтний конфіг (JSON/YAML)
approvals:
required_for:
- "db.write"
- "email.send"
- "ticket.close"
evidence:
include_args: true
include_diff: true
enforce_in: "tool_gateway"
FAQ (3–5)
Використовується в патернах
Пов’язані відмови
Q: Коли approvals реально потрібні?
A: Коли дія дорога/незворотна або змінює клієнтські дані. Default: writes → approval.
Q: Як уникнути approval-spam?
A: Risk tiers + ескалювати тільки high-risk. І тримати write tools маленькими (без “універсальних” RPC).
Q: Можна автоматизувати approvals?
A: Частково: auto-approve low-risk під tight budgets. Але логувати як “auto-approved”.
Q: Де enforce approvals?
A: У tool gateway. Якщо тільки в UI — це обійдуть.
Пов’язані сторінки (3–6 лінків)
- Foundations: What makes an agent production-ready · How agents use tools
- Failure: Prompt injection attacks · Cascading tool failures
- Governance: Tool permissions · Kill switch design
- Production stack: Production agent stack