Idee in 30 Sekunden
Rate limiting ist eine Runtime-Kontrolle, die die Frequenz externer Agent-Aufrufe begrenzt, damit Request-Spitzen und Retry-Stürme in Production nicht eskalieren.
Wann das nötig ist: wenn ein Agent häufig model/tool APIs aufruft, Retry-Logik hat und unter Lastspitzen läuft.
Problem
Ohne Rate Limiting erzeugt ein instabiler Service schnell eine Kette: Retry → Aufruf → noch ein Retry.
Im Demo ist das kaum sichtbar. In Production führt dieses Verhalten zu Wellen von 429/5xx, Warteschlangen und Latenz.
Am schlimmsten: Der Incident skaliert sich selbst:
- ein überaktiver Nutzer zieht Quoten leer
- ein Tenant "erstickt" andere
- eine globale Spitze bricht Abhängigkeiten für alle gleichzeitig
Und jede Minute ohne Frequenzbegrenzung erzeugt neue Retries, Queues und Verzögerungen, bis sich das System effektiv selbst DDoS't.
Analogie: Das ist wie eine Auffahrtsregelung auf eine Autobahn. Ohne Dosierung wird selbst eine gute Straße in Minuten zum Stau.
Lösung
Die Lösung ist ein zentraler Rate-Limit-Policy-Layer in Runtime und Tool Gateway.
Jeder externe Agent-Aufruf wird gegen Limits per_user, per_tenant, global und burst_tokens geprüft.
Die Policy gibt eine technische Entscheidung zurück: allow oder stop mit explizitem Grund:
rate_limited_userrate_limited_tenantrate_limited_globalburst_limited
Wenn stop zurückkommt, sendet Runtime retry_after_ms an den Client und führt den Aufruf nicht aus.
Das ist eine eigene Systemschicht, nicht Teil von Prompt oder Modelllogik.
Rate limiting ≠ Step limits
Das sind unterschiedliche Kontrollstufen:
- Rate limiting begrenzt die Frequenz externer Aufrufe.
- Step limits begrenzen Länge und Verhalten der Runtime-Loop.
Eins ohne das andere reicht nicht:
- ohne Rate Limiting fallen externe APIs bei Spitzen und Retry-Stürmen aus
- ohne Step limits kann ein Run selbst bei moderater Aufruffrequenz zu lange kreisen
Beispiel:
- rate limiting: nicht mehr als
per_user=6Aufrufe pro 10 Sekunden - step limits:
max_steps=18,max_repeat_action=3
Komponenten der Rate-Limiting-Kontrolle
Diese Komponenten wirken bei jedem externen Agent-Aufruf zusammen.
| Komponente | Was sie kontrolliert | Zentrale Mechanik | Warum |
|---|---|---|---|
| Per-user limit | Verhalten eines einzelnen Nutzers | per_user quotasliding window | Verhindert, dass ein Nutzer alle Ressourcen verbraucht |
| Per-tenant limit | Last eines Tenants | per_tenant quotatenant-scoped keys | Isoliert Spitzen zwischen Kunden |
| Global limit | Gesamtsystemlast | global capshared limiter | Schützt externe Abhängigkeiten vor Massen-Spitzen |
| Burst control | Kurze Peak-"Spikes" | token bucket refill rate | Dämpft plötzliche Sprünge ohne Vollstopp des Systems |
| Rate-limit observability | Sichtbarkeit von Policy-Entscheidungen | audit logs alerts bei stop spikes | Begrenzt Aufrufe nicht direkt, hilft aber, Spike-Quellen schnell zu finden |
Beispiel-Alert:
Slack: 🛑 Support-Agent hit rate_limited_tenant. retry_after=1200ms, tenant=t_42.
Wie das in der Architektur aussieht
Der Rate-Limit-Policy-Layer steht zwischen Runtime und externen model/tool APIs.
Jede Entscheidung (allow oder stop) wird im Audit Log erfasst.
Jeder externe Agent-Aufruf läuft vor der Ausführung durch diesen Flow: Runtime führt Aufrufe nicht direkt aus, sondern lässt zuerst den Policy Layer entscheiden.
Flow kurz zusammengefasst:
- Runtime bildet einen externen Agent-Aufruf
- Policy prüft
per_user,per_tenant,globalundburst_tokens allow→ Aufruf wird ausgeführtstop→retry_after_msund partielle Antwort werden zurückgegeben- beide Entscheidungen werden ins Audit Log geschrieben
Beispiel
Ein Support-Agent verarbeitet viele Requests gleichzeitig und retried crm.search mehrfach.
Mit Rate Limiting:
per_user = 6 / 10sper_tenant = 120 / minglobal = 50 / sburst_tokens = 5
→ die Spitze wird auf Policy-Ebene gestoppt, bevor Abhängigkeiten und Queues ausfallen.
Rate Limiting stoppt den Incident direkt vor dem externen Aufruf, nicht erst nach einer Welle von 429.
Im Code sieht das so aus
Die vereinfachte Skizze oben zeigt den Haupt-Flow.
Kritisch: rate-limit check muss O(1) und atomar sein (meist via Redis/Lua oder Äquivalent), sonst wird er unter Peak selbst zum Bottleneck.
Nach stop(...) baut Runtime meist eine partielle Antwort für den Client mit explizitem Grund und retry_after_ms.
Beispiel für Rate-Limit-Konfiguration:
rate_limits:
per_user_10s: 6
per_tenant_min: 120
global_rps: 50
burst_tokens: 5
refill_per_second: 2
action = planner.next(state)
action_key = make_action_key(action.name, action.args)
if not action.is_external_call():
# execute_local — bedingter Helper für lokale Aktionen ohne externes API.
# Decision.allow — bedingter Helper, um ein einheitliches outcome/reason-Modell zu behalten.
local_result = execute_local(action)
local_decision = Decision.allow(reason=None)
audit.log(
run_id,
decision=local_decision.outcome,
reason=local_decision.reason,
scope="local",
action=action.name,
action_key=action_key,
result=local_result.status,
)
return local_result
decision = rate_limit.check(
user_id=state.user_id,
tenant_id=state.tenant_id,
action=action.name,
now_ms=clock.now_ms(),
)
if decision.outcome == "stop":
audit.log(
run_id,
decision=decision.outcome,
reason=decision.reason,
scope=decision.scope,
retry_after_ms=decision.retry_after_ms,
action=action.name,
action_key=action_key,
)
alerts.notify_if_needed(run_id, decision.reason, scope=decision.scope)
return stop(decision.reason, retry_after_ms=decision.retry_after_ms)
result = executor.execute(action)
audit.log(
run_id,
decision=decision.outcome,
reason=decision.reason,
scope=decision.scope,
action=action.name,
action_key=action_key,
result=result.status,
)
return result
Wie das während der Ausführung aussieht
Szenario 1: Stopp durch tenant-Limit
- Runtime bildet externen Aufruf
crm.search. - Policy erkennt überschrittene
per_tenant-Quote. - Entscheidung:
stop (reason=rate_limited_tenant). - Runtime gibt
retry_after_mszurück. - Aufruf wird nicht ausgeführt, Event wird ins Audit Log geschrieben.
Szenario 2: Burst-Spitze
- Mehrere Runs erzeugen gleichzeitig eine kurze Aufrufspitze.
- Policy verbraucht
burst_tokens. - Entscheidung:
stop (reason=burst_limited). - Ein Teil der Aufrufe wird mit
retry_after_msabgewiesen. - System bleibt stabil ohne cascade-failure.
Szenario 3: Normale Ausführung
- Runtime bildet externen Aufruf.
- Policy prüft Limits: alles im Rahmen.
- Entscheidung:
allow. - Aufruf wird ausgeführt.
- Entscheidung und Ergebnis werden ins Audit Log geschrieben.
Typische Fehler
- nur globales Limit setzen, ohne
per_user/per_tenant-Isolation - bei
stopkeinretry_afterzurückgeben - retry ohne backoff und jitter
- Rate Limit nur in einer Schicht prüfen (nur Runtime oder nur Gateway)
- stop-Entscheidungen nicht loggen (
reason,scope,retry_after_ms) - kein Alerting auf
rate_limited_*-Spitzen
Ergebnis: Das System wirkt kontrolliert, degradiert aber bei realen Spitzen schnell.
Selbstcheck
Schneller Rate-Limiting-Check vor dem Production-Start:
Fortschritt: 0/8
⚠ Grundlegende Governance-Kontrollen fehlen
Vor production brauchen Sie mindestens Zugriffskontrolle, Limits, audit logs und einen Not-Stopp.
FAQ
Q: Mit welchen Limits sollten wir starten?
A: Mindestens: per_user, per_tenant, global + eine kleine Burst-Kontrolle. Danach anhand realer Stop-Events nachjustieren.
Q: Wenn externe API schon 429 zurückgibt, brauchen wir trotzdem eigenes Rate Limiting?
A: Ja. Eigenes Rate Limiting schützt Runtime vor externem 429 und liefert kontrollierte stop reasons, retry_after und Audit.
Q: Was ist bei Limit-Treffer besser: stop oder Queue?
A: Für sync-runs ist stop + retry_after meist besser. Für async-Pipelines kann Queueing sinnvoll sein, aber weiterhin mit expliziten Limits und Timeout.
Q: Ersetzt Rate Limiting Budget Controls?
A: Nein. Rate Limiting steuert Aufruffrequenz, Budget Controls steuern die Gesamtkosten eines Runs.
Q: Wo sollten Counter gespeichert werden?
A: In gemeinsamem Low-Latency-Store mit atomaren Operationen (oft Redis). Ohne das werden Limits über Instanzen hinweg inkonsistent.
Wo Rate Limiting im Gesamtsystem liegt
Rate limiting ist eine der Ebenen von Agent Governance. Zusammen mit RBAC, Budgets, Step Limits, Approval und Audit bildet es ein einheitliches System zur Ausführungskontrolle.
Verwandte Seiten
Als Nächstes zum Thema:
- Agent Governance Überblick — Gesamtmodell für Agent-Kontrolle in Production.
- Budget Controls — wie die Gesamtkosten eines Runs begrenzt werden.
- Step limits — wie Loops auf Runtime-Ebene gestoppt werden.
- Kill switch — wie Aktionen ohne Release im Notfall gestoppt werden.
- Audit Logs für Agenten — wie stop reasons und Lastspitzen analysiert werden.