Robuste Fehlerbehandlung für KI-Agenten: Retries, Timeouts, Circuit Breaker und Idempotenz
Ihr Agent ruft eine API auf, erhält eine 429 Too Many Requests und versucht es sofort erneut. Es folgt die nächste 429, dann der nächste Retry. Ohne Begrenzung, Backoff und serverseitige Vorgaben entsteht eine Schleife, die einen bereits gedrosselten Dienst weiter belastet, bis der Lauf fehlschlägt oder unnötige Kosten verursacht. Diese Art naiver Fehlerbehandlung ist ein häufiges Thema im Anthropic-SDK-Diskussionsforum.
Apidog noch heute ausprobieren
Robuste Agenten unterscheiden sich nicht primär durch das Modell, sondern durch ihr Verhalten bei langsamen, gedrosselten oder fehlerhaften Tool-Aufrufen. Dieser Artikel zeigt vier grundlegende Muster:
- Retries mit exponentiellem Backoff und Jitter
- Timeouts pro Aufruf und pro Agentenlauf
- Circuit Breaker für ausgefallene Abhängigkeiten
- Idempotenzschlüssel für sichere schreibende Retries
Anschließend testen Sie diese Pfade mit einem Mock, statt Fehler erst in Produktion zu entdecken. Für den breiteren Kontext lesen Sie auch warum KI-Agenten in der Produktion ausfallen.
Sie können Wiederherstellung nicht gegen eine fehlerfreie API testen
In der Entwicklung funktioniert eine Abhängigkeit oft zuverlässig: Aufrufe sind erfolgreich, die Demo läuft, und der Recovery-Code bleibt unberührt. Das erste Mal, dass Backoff, Timeout oder Circuit Breaker ausgeführt werden, ist dann im echten Ausfall mit echten Nutzern.
Testen Sie Fehlerpfade deshalb absichtlich:
- Erstellen Sie einen Mock für die externe API.
- Konfigurieren Sie Antworten wie
429,500, Timeouts oder fehlerhafte Bodies. - Richten Sie das Tool Ihres Agenten auf die Mock-URL.
- Prüfen Sie Wartezeiten, Retry-Anzahl, Header und Abbruchverhalten.
Mit Apidog können Sie einen Mock bereitstellen und Antwortsequenzen skripten. So wird ein Produktionsfehler zu einem reproduzierbaren Testfall.
Retries mit exponentiellem Backoff und Jitter
Ein sofortiger Retry kann bei einem kurzen Netzwerkfehler helfen. Bei einem überlasteten Dienst verschärft er jedoch das Problem: Viele Clients schlagen gleichzeitig fehl und versuchen gleichzeitig erneut.
Verwenden Sie stattdessen:
- Exponentielles Backoff: Wartezeiten wachsen beispielsweise von 1 auf 2, 4 und 8 Sekunden.
- Jitter: Ein zufälliger Anteil verhindert, dass alle Clients synchron erneut senden.
- Maximale Verzögerung: Verhindert minutenlange Wartezeiten.
- Maximale Retry-Anzahl: Beendet dauerhafte Fehler kontrolliert.
Ein einfaches Python-Beispiel:
import random
import time
import requests
MAX_RETRIES = 4
MAX_DELAY_SECONDS = 16
RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}
def request_with_retry(method: str, url: str, **kwargs):
for attempt in range(MAX_RETRIES + 1):
try:
response = requests.request(method, url, timeout=(3, 15), **kwargs)
if response.status_code not in RETRYABLE_STATUS_CODES:
response.raise_for_status()
return response
if attempt == MAX_RETRIES:
response.raise_for_status()
except requests.Timeout:
if attempt == MAX_RETRIES:
raise
except requests.ConnectionError:
if attempt == MAX_RETRIES:
raise
base_delay = min(2 ** attempt, MAX_DELAY_SECONDS)
jitter = random.uniform(0, base_delay * 0.25)
time.sleep(base_delay + jitter)
raise RuntimeError("Unerreichbarer Code")
Wiederholen Sie nicht jeden Fehler blind. Typische Kandidaten für Retries sind:
- Verbindungsfehler
- Timeouts
429 Too Many Requests- temporäre Serverfehler wie
500,502,503und504
Fehler wie 400 Bad Request, 401 Unauthorized oder Validierungsfehler sollten normalerweise nicht erneut versucht werden.
Das Anthropic SDK übernimmt einen Teil dieser Logik für eigene API-Aufrufe. Externe APIs, die Ihre Agent-Tools verwenden, müssen Sie jedoch selbst absichern. Besonders bei Zahlungen oder anderen kritischen Aktionen ist das entscheidend; mehr dazu finden Sie in der Übersicht zur Wiederholungslogik für hochriskante APIs.
Legen Sie für jeden Aufruf ein Timeout fest
Retries helfen nur, wenn ein Aufruf auch tatsächlich fehlschlägt. Eine Anfrage kann jedoch eine Verbindung öffnen und dann dauerhaft hängen. Ohne Timeout blockiert der Tool-Aufruf den Agentenlauf, verbraucht Zeit und möglicherweise Token-Budget.
Definieren Sie mindestens drei Budgets:
| Budget | Zweck |
|---|---|
| Verbindungs-Timeout | Maximale Zeit für den Verbindungsaufbau |
| Lese-Timeout | Maximale Zeit bis zur Antwort |
| Gesamtbudget des Agenten | Obergrenze für den gesamten Lauf |
Beispiel mit requests:
response = requests.get(
"https://api.example.com/search",
timeout=(3, 15), # 3 s Connect, 15 s Read
)
Für einen Agentenlauf können Sie zusätzlich ein Gesamtbudget verfolgen:
import time
RUN_BUDGET_SECONDS = 45
started_at = time.monotonic()
def ensure_budget_remaining():
elapsed = time.monotonic() - started_at
if elapsed >= RUN_BUDGET_SECONDS:
raise TimeoutError("Gesamtzeitbudget des Agentenlaufs überschritten")
Wählen Sie Werte anhand realer Latenzdaten. Ein praktikabler Ausgangspunkt ist ein Timeout oberhalb des p99-Werts der Abhängigkeit plus Puffer. Zu kurze Werte brechen legitime Anfragen ab; zu lange Werte lassen Agenten bei defekten Diensten hängen.
Streaming-Antworten benötigen ein separates Budget. Eine lange, aber fortlaufende Ausgabe ist nicht dasselbe wie ein blockierter Socket.
Circuit Breaker: Bei Ausfällen schnell fehlschlagen
Backoff hilft bei kurzzeitiger Überlastung. Wenn eine Abhängigkeit vollständig ausgefallen ist, sollten Sie nicht bei jeder neuen Agentenanfrage mehrere langsame Timeouts abwarten.
Ein Circuit Breaker hat drei Zustände:
- Closed: Anfragen werden normal gesendet, Fehler werden gezählt.
- Open: Nach zu vielen Fehlern werden neue Anfragen sofort abgelehnt.
- Half-open: Nach einer Abkühlphase darf eine Probe-Anfrage durch. Bei Erfolg schließt der Breaker, bei Fehler öffnet er wieder.
Vereinfachtes Beispiel:
import time
class CircuitBreaker:
def __init__(self, failure_threshold=5, reset_timeout=30):
self.failure_threshold = failure_threshold
self.reset_timeout = reset_timeout
self.failures = 0
self.opened_at = None
def allow_request(self):
if self.opened_at is None:
return True
if time.monotonic() - self.opened_at >= self.reset_timeout:
return True # Half-open: eine Probe zulassen
return False
def record_success(self):
self.failures = 0
self.opened_at = None
def record_failure(self):
self.failures += 1
if self.failures >= self.failure_threshold:
self.opened_at = time.monotonic()
Wichtig: Verwenden Sie einen Circuit Breaker pro Abhängigkeit. Eine ausgefallene Such-API darf nicht verhindern, dass der Agent eine weiterhin funktionierende Abrechnungs- oder Datenbank-API nutzt.
Retries mit Idempotenzschlüsseln sicher machen
Ein Retry ist nicht automatisch sicher.
Beispiel: Ihr Agent sendet POST /charge. Der Server verarbeitet die Zahlung, aber die Antwort geht auf dem Rückweg verloren. Der Agent sieht nur einen Fehler und wiederholt den Request. Ohne Schutz kann die Zahlung doppelt ausgeführt werden.
Ein Idempotenzschlüssel löst dieses Problem:
- Der Client erzeugt für eine logische Aktion eine eindeutige ID.
- Er sendet diese ID im Header
Idempotency-Key. - Der Server speichert das Ergebnis beim ersten Request.
- Kommt derselbe Schlüssel erneut, liefert der Server das gespeicherte Ergebnis zurück, statt die Aktion erneut auszuführen.
import uuid
import requests
idempotency_key = str(uuid.uuid4())
response = requests.post(
"https://api.example.com/charge",
json={"amount": 4999, "currency": "EUR"},
headers={
"Idempotency-Key": idempotency_key,
},
timeout=(3, 15),
)
Der Schlüssel muss für alle Retries derselben logischen Aktion stabil bleiben:
# Richtig: einmal vor der Retry-Schleife erzeugen
idempotency_key = str(uuid.uuid4())
for attempt in range(3):
send_charge(idempotency_key=idempotency_key)
# Falsch: jeder Versuch erhält einen neuen Schlüssel
for attempt in range(3):
send_charge(idempotency_key=str(uuid.uuid4()))
Verwenden Sie Idempotenzschlüssel für alle zustandsändernden Aktionen:
- Zahlungen und Abbuchungen
- Bestellungen
- E-Mails und Nachrichten
- Erstellen oder Ändern von Datensätzen
- Provisionierung und Deployments
Der Leitfaden zu Idempotenzschlüsseln behandelt Generierung und serverseitige Verarbeitung ausführlicher.
Ratenbegrenzungen und die RateLimitError-Schleife überleben
Eine 429 ist nicht nur ein Fehler, sondern oft eine konkrete Anweisung. Viele APIs senden den Header Retry-After, der angibt, wie lange Sie warten sollen.
Beispiel:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Warten Sie in diesem Fall mindestens 30 Sekunden. Ein Retry nach zwei Sekunden produziert wahrscheinlich nur die nächste 429.
from email.utils import parsedate_to_datetime
from datetime import datetime, timezone
def parse_retry_after(value: str) -> float:
try:
return max(0, float(value))
except ValueError:
retry_at = parsedate_to_datetime(value)
now = datetime.now(timezone.utc)
return max(0, (retry_at - now).total_seconds())
Integrieren Sie die Servervorgabe in Ihre Retry-Logik:
if response.status_code == 429:
retry_after = response.headers.get("Retry-After")
if retry_after:
delay = parse_retry_after(retry_after)
else:
delay = min(2 ** attempt, MAX_DELAY_SECONDS) + random.uniform(0, 1)
time.sleep(delay)
continue
Eine Antwort bei überschrittener Ratenbegrenzung sollte daher diese Reihenfolge auslösen:
-
Retry-Afterlesen. - Mindestens so lange warten.
- Falls der Header fehlt: exponentielles Backoff mit Jitter verwenden.
- Nach dem Retry-Limit sauber fehlschlagen.
Ein separater SDK-Thread beschreibt dieselbe Fehlerklasse: Zu frühe Wiederholungen machen eine bestehende Drosselung schlimmer.
Zusätzlich zur Wiederherstellung brauchen Sie proaktive Taktung. Wenn ein Anbieter ein festes Anfragelimit pro Minute vorgibt, kann ein Token-Bucket helfen, die Grenze gar nicht erst zu erreichen.
So testen Sie den Wiederherstellungspfad
Testen Sie nicht nur den Happy Path. Erzwingen Sie die Fehler, die eine gesunde API in der lokalen Entwicklung selten liefert.
Szenario 1: 429, dann 500, dann Erfolg
Skripten Sie für einen Mock-Endpunkt diese Reihenfolge:
-
429 Too Many RequestsmitRetry-After: 2 500 Internal Server Error-
200 OKmit gültigem Response-Body
Prüfen Sie danach:
- Der Agent wartete nach der
429mindestens zwei Sekunden. - Der Retry nach dem
500nutzte Backoff. - Der dritte Aufruf war erfolgreich.
- Das konfigurierte Retry-Limit wurde nicht überschritten.
Szenario 2: Permanenter Fehler
Lassen Sie den Mock bei jedem Aufruf fehlschlagen.
Prüfen Sie:
- Der Agent beendet die Retry-Schleife am Limit.
- Der Fehler ist für Aufrufer verständlich.
- Der Lauf wartet nicht unendlich.
- Die Anzahl der Requests entspricht Ihrer Konfiguration.
Szenario 3: Circuit Breaker
Lassen Sie mehrere aufeinanderfolgende Requests mit 500 oder Timeout fehlschlagen.
Prüfen Sie:
- Nach Erreichen des Fehlerlimits öffnet der Circuit Breaker.
- Weitere Requests schlagen schnell fehl.
- Nach dem Abkühlfenster wird genau eine Probe-Anfrage zugelassen.
- Ein Erfolg schließt den Breaker wieder.
Szenario 4: Idempotenz bei verlorener Antwort
Skripten Sie einen mutierenden Request so:
- Der Mock akzeptiert
POST /charge. - Der Mock verarbeitet die Aktion, verwirft aber die Antwort.
- Der Agent führt einen Retry aus.
- Der Mock erhält den Request erneut.
Prüfen Sie:
- Beide Requests haben denselben
Idempotency-Key. - Der Mock zählt nur eine logische Aktion.
- Der zweite Request erhält das Ergebnis der ersten Aktion.
Ein neuer Schlüssel beim Retry oder zwei ausgeführte Aktionen zeigen einen Fehler, bevor ein Kunde doppelt belastet wird. Das End-to-End-Vorgehen finden Sie auch im Leitfaden zum Testen von Agenten, die Ihre APIs aufrufen.
Die Checkliste zur Fehlerbehebung
Vor dem Produktionsstart sollte Ihr Agent diese Punkte erfüllen:
- Jeder ausgehende Aufruf hat ein Verbindungs-Timeout, ein Lese-Timeout und ein Gesamt-Laufbudget.
- Retries verwenden exponentielles Backoff mit Jitter.
- Verzögerung und Retry-Anzahl sind begrenzt.
-
429-Antworten respektierenRetry-After. - Bei fehlendem
Retry-Aftergreift Backoff als Fallback. - Ein Circuit Breaker schützt jede Abhängigkeit separat.
- Zustandsändernde Requests verwenden stabile Idempotenzschlüssel.
- Der Abbruchpfad liefert einen klaren Fehler statt einer Endlosschleife.
- Jeder Fehlerpfad wird gegen einen Mock getestet.
Wenn diese Punkte erfüllt sind, erholt sich Ihr Agent bewusst und reproduzierbar – nicht nur zufällig in einer grünen Demo.
Wo Apidog passt – und wo nicht
Apidog kann in diesem Workflow drei Aufgaben übernehmen:
- Abhängigkeiten mocken: Ersetzen Sie Live-Dienste durch steuerbare Mock-Endpunkte.
-
Fehlersequenzen skripten: Simulieren Sie
429mitRetry-After,500, Timeouts und fehlerhafte Response-Bodies. -
Requests validieren: Prüfen Sie Header, Request-Form und Aufrufanzahl – etwa, ob ein
Idempotency-Keyvorhanden und über Retries hinweg stabil ist.
Das Tool ersetzt nicht Ihre Retry-, Timeout- oder Circuit-Breaker-Implementierung. Es hilft Ihnen aber dabei, die Fehler zu erzeugen, gegen die Ihr Agent bestehen muss, und das resultierende Verhalten zu überprüfen.
Häufig gestellte Fragen
Übernimmt das Anthropic SDK Retries für mich?
Für eigene API-Aufrufe übernimmt das SDK Teile der Retry-Logik, einschließlich exponentiellem Backoff und der Berücksichtigung von Retry-After. Die Retry-Obergrenze wird über eine max_retries-Option konfiguriert.
Das gilt nicht automatisch für andere APIs, die Ihre Agent-Tools aufrufen. Diese Abhängigkeiten benötigen dieselben Muster in Ihrem eigenen Code.
Wann benötige ich einen Idempotenzschlüssel?
Bei jedem Request, der Zustand erstellt oder verändert: Zahlungen, Bestellungen, versendete Nachrichten oder neue Datensätze.
Reine Lesezugriffe können normalerweise sicher erneut versucht werden. Für schreibende Aktionen gilt: Erzeugen Sie den Schlüssel einmal pro logischer Aktion und behalten Sie ihn über alle Retries hinweg bei.
Proben Sie diese Woche einen Fehler
Sie müssen nicht alle Muster gleichzeitig implementieren. Beginnen Sie mit dem Fehler, der für Ihren Agenten den größten Schaden verursachen würde – oft eine 429-Schleife oder ein nicht-idempotenter Zahlungs-Retry.
Skripten Sie dafür einen Mock:
- Geben Sie eine
429mitRetry-Afterzurück. - Verwerfen Sie eine Antwort nach einer mutierenden Aktion.
- Prüfen Sie, wie lange der Agent wartet.
- Prüfen Sie, ob er denselben Idempotenzschlüssel erneut sendet.
- Prüfen Sie, ob er nach dem Retry-Limit sauber abbricht.
Wenn Sie einen kontrollierten Backoff und genau eine logische Aktion sehen, haben Sie einen belastbaren Grund, Ihrem Agenten auch außerhalb der Demo zu vertrauen.

Top comments (0)