Action is proposed as structured data (tool + args).
Проблема (з реального продакшену)
Починаєш з blocklist, бо це швидко: “забороняємо небезпечне”.
Потім додаєш новий tool. Забуваєш оновити blocklist. Агент його знаходить.
І тепер ти on-call, бо “забороняємо небезпечне” було радше історією, ніж контролем.
Blocklist здається контролем. У агентних системах це майже завжди пастка. І вона швидко гниє: через два тижні ніхто не пам’ятає, що саме цей список “захищав”.
Чому це ламається в продакшені
1) Неможливо наперед перелічити “всі небезпечні tools”
Сьогодні це db.delete_user.
Завтра — crm.merge_accounts.
Наступного тижня — tickets.close_all.
Tool, який ти забудеш заблокувати, і стане проблемою.
2) Blocklist ламається на назвах і непрямості
Блокуєш db.write — хтось релізить db.patch.
Блокуєш email.send — з’являється email.send_bulk.
Гірше: wrapper’и.
Агент викликає workflow.run("close_ticket"), і твій “blocklist” навіть не бачить реальний side effect.
3) Default-allow тихо збільшує blast radius
Якщо policy каже “дозволено все, окрім…”, то кожен новий tool — це розширення прав за замовчуванням. Це інцидент, який просто чекає моменту.
4) Allowlist змушує приймати явне рішення
Allowlist дратує. Добре. Вона змушує сказати: “так, агент може викликати цей tool за цих умов”.
Приклад реалізації (реальний код)
Маленький policy evaluator:
- default-deny
- опційний deny list для incident mode (екстрене гальмо)
- “write tools → approval”
from dataclasses import dataclass
WRITE_TOOLS = {"email.send", "db.write", "ticket.close"}
@dataclass(frozen=True)
class Policy:
allow: set[str]
deny: set[str] = None # for incident mode
require_approval_for_writes: bool = True
class Denied(RuntimeError):
pass
def evaluate(policy: Policy, tool: str) -> str:
deny = policy.deny or set()
if tool in deny:
raise Denied(f"denied: {tool} (incident mode)")
if tool not in policy.allow:
raise Denied(f"not allowed: {tool}")
if policy.require_approval_for_writes and tool in WRITE_TOOLS:
return "approve"
return "allow"const WRITE_TOOLS = new Set(["email.send", "db.write", "ticket.close"]);
export class Denied extends Error {}
export function evaluate(policy, tool) {
const deny = new Set(policy.deny || []);
if (deny.has(tool)) throw new Denied("denied: " + tool + " (incident mode)");
if (!policy.allow.includes(tool)) throw new Denied("not allowed: " + tool);
if (policy.requireApprovalForWrites && WRITE_TOOLS.has(tool)) return "approve";
return "allow";
}Реальний інцидент (з цифрами)
Ми бачили команду, яка запустила агента з policy: “deny dangerous tools”.
Потім вони додали tool: ticket.close_bulk.
Його не було в deny list.
Агент використав його, бо це був найкоротший шлях до “resolve”.
Імпакт:
- ~200 тікетів закрито помилково
- ~5 годин інженерного часу на reopen/пояснення/патч
- агента вимкнули на тиждень, бо ніхто йому не довіряв
Fix:
- default-deny allowlist
- write tools за approvals
- deny list — тільки для incident mode (тимчасово)
Blocklist — ок як аварійне гальмо. Як кермо — погано.
Компроміси
- Allowlist сповільнює “just ship it”. У проді це фіча.
- Потрібні інструменти для менеджменту allowlist (не хардкод у 12 місцях).
- Люди спробують обійти через wrapper’и. Не давай.
Коли НЕ варто
- Для локальних прототипів можна стартувати ліберальніше — але не тягни це в прод.
- Навіть якщо tools read-only, маленький allowlist зменшує “випадкові” експозиції.
- Якщо твій “tool” — це універсальний RPC “зроби будь-що”, виправ tool: розбий за capabilities.
Чекліст (можна копіювати)
- [ ] Default-deny allowlist (явні назви tools)
- [ ] Розділення tools за capability (read vs write)
- [ ] Approvals для незворотних write’ів
- [ ] Deny list тільки для incident mode (тимчасово)
- [ ] Логувати denied спроби (це сигнал)
- [ ] Не експонувати “do anything” tools
Безпечний дефолтний конфіг (JSON/YAML)
policy:
default: "deny"
allow: ["search.read", "kb.read", "http.get"]
require_approval_for_writes: true
incident_mode:
deny: ["browser.run"] # тимчасове гальмо
logging:
log_denies: true
FAQ (3–5)
Використовується в патернах
Пов’язані відмови
Q: Blocklist взагалі корисний?
A: Так: для incident mode і тимчасових відключень. Це гальмо, а не модель permission’ів.
Q: Можна wildcard allowlist?
A: Обережно. Wildcards майже завжди дрейфують у default-allow. Якщо треба — роби вузько і регулярно рев’ю.
Q: А якщо є tool типу ‘workflow.run’?
A: Він ховає side effects. Краще мати явні tools для явних дій, щоб policy могла міркувати.
Q: Яка мінімальна safe policy?
A: Default-deny allowlist + read-only tools. Writes додавати пізніше, за approvals.
Пов’язані сторінки (3–6 лінків)
- Foundations: How agents use tools · Planning vs reactive agents
- Failure: Prompt injection attacks · Tool spam loops
- Governance: Tool permissions · Kill switch design
- Production stack: Production agent stack