Action is proposed as structured data (tool + args).
Problem (aus der Praxis)
Du fängst mit einer Blocklist an, weil’s schnell geht: „Wir verbieten halt das gefährliche Zeug.“
Dann shippt jemand ein neues Tool. Du vergisst, die Blocklist zu updaten. Der Agent findet es.
Und plötzlich bist du on-call, weil „deny dangerous stuff“ nur eine Story war, die du dir selbst erzählt hast.
Blocklists fühlen sich nach Kontrolle an. In Agent-Systemen sind sie meistens eine Falle. Und sie verrotten schnell: zwei Wochen später weiß niemand mehr, wovor die Liste eigentlich „schützen“ sollte.
Warum das in Production bricht
1) Du kannst „alle gefährlichen Tools“ nicht vorab aufzählen
Heute ist es db.delete_user.
Morgen ist es crm.merge_accounts.
Nächste Woche ist es tickets.close_all.
Das Tool, das du vergisst zu blocken, ist das, das dich beißt.
2) Blocklists scheitern an Naming und Indirection
Wenn du db.write blockst, shippt jemand db.patch.
Wenn du email.send blockst, shippt jemand email.send_bulk.
Noch schlimmer: Wrapper.
Der Agent ruft workflow.run("close_ticket") auf und deine „Blocklist“ sieht den echten Side Effect nie.
3) Default-Allow lässt Blast Radius leise wachsen
Wenn die Policy sagt „alles erlaubt außer…“, shipst du Permission-Expansion by default. Jedes neue Tool ist ein Incident, der nur auf Timing wartet.
4) Allowlists erzwingen eine explizite Entscheidung
Allowlists sind nervig. Gut so. Sie zwingen dich zu sagen: „Ja, der Agent darf dieses Tool unter diesen Bedingungen callen.“
Implementierungsbeispiel (echter Code)
Ein kleiner Policy-Evaluator:
- default-deny
- optionale Blocklist für Notfälle (Incident Mode)
- „write tools brauchen 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";
}Echter Incident (mit Zahlen)
Wir haben ein Team gesehen, das einen Agenten mit „deny dangerous tools“-Policy shipped hat.
Dann kam ein neues Tool dazu: ticket.close_bulk.
Es stand nicht in der Deny-Liste.
Der Agent hat’s benutzt, weil es der kürzeste Weg zu „resolve“ war.
Impact:
- ~200 Tickets falsch geschlossen
- ~5 Engineer-Stunden zum Reopen, Erklären und Patchen
- Agent war eine Woche aus, weil niemand ihm vertraut hat
Fix:
- default-deny allowlist
- write tools hinter Approvals
- deny list nur für Incident Mode (temporär)
Blocklists sind okay als Notbremse. Sie sind schlecht als Lenkrad.
Abwägungen
- Allowlists bremsen „just ship it“-Velocity. In Prod ist das ein Feature.
- Du brauchst Tooling für Allowlists (nicht in 12 Stellen hardcoden).
- Devs werden versuchen, es mit Wrappern zu umgehen. Lass es nicht zu.
Wann du es NICHT nutzen solltest
- Für lokale Prototypen kannst du lockerer starten — aber nimm das nicht mit nach Prod.
- Auch wenn alle Tools read-only sind, lohnt sich eine kleine Allowlist (verhindert „versehentliche“ Exposure).
- Wenn dein „Tool“ ein generisches RPC ist, das alles kann: fix das zuerst. Splitte Tools nach Capability.
Checkliste (Copy/Paste)
- [ ] Default-deny allowlist (explizite Tool-Namen)
- [ ] Tools nach Capability trennen (read vs write)
- [ ] Approvals für irreversible Writes
- [ ] Deny-Liste nur für Incident Mode (temporär)
- [ ] Denied Tool Attempts loggen (Signal)
- [ ] Keine generischen „do anything“-Tools exposen
Sicheres Default-Config-Snippet (JSON/YAML)
policy:
default: "deny"
allow: ["search.read", "kb.read", "http.get"]
require_approval_for_writes: true
incident_mode:
deny: ["browser.run"] # temporary brake
logging:
log_denies: true
FAQ (3–5)
Von Patterns genutzt
Verwandte Failures
Q: Sind Blocklists jemals sinnvoll?
A: Ja: für Incident Mode und temporäre Disables. Sie sind eine Bremse, kein Permission-Modell.
Q: Kann ich Wildcards in Allowlists nutzen?
A: Vorsicht. Wildcards driften schnell zu Default-Allow. Wenn du Patterns brauchst, mach sie eng und review regelmäßig.
Q: Was ist mit Tools wie ‘workflow.run’?
A: Sie verstecken Side Effects. Bevorzuge explizite Tools für explizite Actions, damit Policy darüber entscheiden kann.
Q: Was ist die minimal sichere Policy?
A: Default-deny allowlist + read-only Tools. Writes später hinter Approvals.
Verwandte Seiten (3–6 Links)
- 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