KI-Agent-Idempotenz: Doppelte Abrechnungen durch Wiederholungen stoppen

Wiederholungsversuche von Agenten erzeugen doppelte Abbuchungen und doppelte Bestellungen. Erfahren Sie, wie Idempotenzschlüssel funktionieren, wie man sie pro Aufgabenschritt generiert und wie man testet, dass der zweite Aufruf nichts ändert.

Ashley Innocent

Ashley Innocent

26 August 2026

KI-Agent-Idempotenz: Doppelte Abrechnungen durch Wiederholungen stoppen

Apidog für Unternehmen

On-Premises Bereitstellung

SSO & RBAC

SOC 2 konform

Apidog Enterprise entdecken

Ihr Agent hat den Zahlungs-Endpunkt aufgerufen. Die Anfrage wurde verarbeitet, die Abbuchung erfolgte, und dann kam es auf dem Rückweg zu einem Timeout der Antwort. Der Agent hat nie einen 200-Status gesehen, also hat er bei einem Fehler das getan, was Sie ihm aufgetragen haben: Er hat es erneut versucht. Nun wurde dem Kunden zweimal etwas berechnet, und in Ihren Protokollen sieht nichts wie ein Fehler aus.

Dies ist der Fehlermodus, der Agenten von gewöhnlichen API-Clients unterscheidet. Ein Mensch, der einmal auf „Bezahlen“ klickt, sieht einen Ladekreis und wartet. Ein Agent in einer Wiederholungsschleife sieht Stille und versucht es erneut, manchmal drei- oder viermal hintereinander, schneller als jeder Mensch. Jede Wiederholungsrichtlinie, die Sie hinzufügen, um den Agenten zuverlässiger zu machen, erhöht auch die Wahrscheinlichkeit doppelter Schreibvorgänge. Die Lösung ist Idempotenz: Eine wiederholte Anfrage soll dasselbe Ergebnis liefern wie eine einzelne Anfrage.

Dieser Leitfaden behandelt, was Idempotenz auf HTTP-Ebene bedeutet, wie Schlüssel generiert werden, die ein Agent tatsächlich wiederverwenden kann, was der Server speichern muss, um sie zu berücksichtigen, und wie das Ganze getestet wird, bevor einem echten Kunden zweimal etwas berechnet wird. Wenn Sie unseren Grundsatzartikel „Warum KI-Agenten in der Produktion versagen“ noch nicht gelesen haben, sind doppelte Schreibvorgänge der Fehlermodus, der sich unter den meisten Meldungen „der Agent hat es zweimal getan“ verbirgt.

Apidog kommt in der Test-Hälfte dieser Thematik ins Spiel. Idempotenz ist etwas, das Sie in Ihre API und die Tool-Schicht Ihres Agenten integrieren. Was Sie danach benötigen, ist eine Möglichkeit, dieselbe Anfrage zweimal auszulösen und zu beweisen, dass die zweite nichts geändert hat – ein Test, den Sie speichern und in Ihrer CI ausführen können.

Warum Agenten die Idempotenz häufiger brechen als Menschen

Drei Aspekte des Agenten-Traffics machen Duplikate häufig.

Der erste ist das Volumen der Wiederholungsversuche. Agenten-Frameworks versuchen standardmäßig aggressiv erneut, da vorübergehende Netzwerkfehler die häufigste Ursache für einen fehlgeschlagenen Durchlauf sind. Unser Leitfaden zur Fehlerbehebung bei Agenten erklärt Backoff und Circuit Breaker, und jede darin beschriebene Technik erhöht die Anzahl der Male, die eine bestimmte Anfrage Ihren Server erreicht.

Der zweite ist die Mehrdeutigkeit eines Timeouts. Wenn eine Anfrage ein Timeout hat, erfährt der Client nichts darüber, ob der Server sie verarbeitet hat. Ein 504 von einem Proxy könnte bedeuten, dass der Schreibvorgang nie stattgefunden hat oder dass er stattgefunden hat und die Antwort verloren ging. Menschen überprüfen dies normalerweise, bevor sie es erneut versuchen. Agenten tun dies in der Regel nicht, da „zuerst prüfen“ ein zusätzlicher Tool-Aufruf ist, den das Modell entscheiden muss.

Der dritte ist die Schleife. Ein Agent, der eine Aufgabe nicht erfüllt, kann die gesamte Aufgabe neu starten, nicht nur den fehlgeschlagenen Schritt. Wenn Schritt eins eine Bestellung erstellt und Schritt vier fehlschlägt, erstellt ein naiver Neustart eine zweite Bestellung. Hier unterscheiden sich mehrstufige Agenten stark von einem Skript: Die Wiederholungsgrenze ist unscharf, und das Modell, nicht Ihr Code, entscheidet, wo sie beginnt.

Fasst man dies zusammen, ergibt sich das Problem. Es ist nicht so, dass Agenten fehlerhafte Anfragen senden. Sie senden korrekte Anfragen mehrfach.

Was Idempotenz tatsächlich garantiert

Eine Operation ist idempotent, wenn ihre mehrfache Ausführung denselben Effekt hat wie ihre einmalige Ausführung. GET, PUT und DELETE sind in RFC 9110, der HTTP-Semantikspezifikation, als idempotent definiert. POST ist es nicht, und genau deshalb sind gefährliche Operationen oft POST-Aufrufe: eine Bestellung erstellen, eine Nachricht senden, eine Überweisung starten.

Zwei Klarstellungen ersparen viel Verwirrung.

Idempotent ist nicht dasselbe wie sicher. Eine sichere Methode ändert nichts. DELETE ist idempotent, aber destruktiv: Fünfmal aufrufen löscht die Ressource, genau wie einmal aufrufen, aber die Ressource ist trotzdem weg. Agenten benötigen beide Eigenschaften separat betrachtet, was der Ansatz unseres Beitrags über API-Schlüssel mit geringsten Berechtigungen für Agenten von der Berechtigungsseite her ist.

Idempotent ist auch nicht dasselbe wie eine identische Antwort. Der zweite Aufruf kann das gespeicherte Ergebnis des ersten zurückgeben, und er kann einen anderen Statuscode zurückgeben. Was sich nicht ändern darf, ist der Zustand auf dem Server. Eine Abbuchung. Eine Bestellung. Eine E-Mail.

Idempotenzschlüssel: Das Muster, das POST sicher macht

Die Standardlösung ist ein vom Client generierter Schlüssel, der mit der Anfrage gesendet wird. Der Server speichert den Schlüssel zusammen mit dem Ergebnis, und jede spätere Anfrage, die denselben Schlüssel enthält, gibt das gespeicherte Ergebnis zurück, anstatt die Arbeit erneut auszuführen.

Stripe hat den Header populär gemacht, und die Stripe-Idempotenz-Dokumentation ist immer noch die klarste Beschreibung der Semantik. Es gibt auch eine IETF-Anstrengung, ihn als Idempotency-Key Header-Feld zu standardisieren, was es wert ist, gelesen zu werden, bevor Sie Ihren eigenen Headernamen erfinden.

Die Anfrage sieht wie folgt aus:

POST /v1/payments HTTP/1.1
Host: api.yourservice.com
Authorization: Bearer sk_live_...
Idempotency-Key: 9f2b7c14-6d3a-4b18-9d55-1e2a7c0b4f31
Content-Type: application/json

{
  "amount": 4900,
  "currency": "usd",
  "customer_id": "cus_8812",
  "description": "Pro plan, August"
}

Der Schlüssel ist eine UUID. Er hat für den Server keine weitere Bedeutung als „Dies ist dieselbe logische Operation.“ Der Server speichert ihn zusammen mit einem Fingerabdruck des Anfrage-Bodys und der von ihm erzeugten Antwort.

Einen Schlüssel generieren, den der Agent wiederverwenden kann

Hier laufen die meisten Agenten-Implementierungen schief. Wenn der Tool-Wrapper bei jedem Aufruf eine neue UUID generiert, ändert sich der Schlüssel bei jedem Wiederholungsversuch, und die Idempotenz bewirkt nichts. Der Schlüssel muss an die logische Operation gebunden sein, nicht an den HTTP-Versuch.

Die Regel: Generieren Sie den Schlüssel, wenn der Agent entscheidet, eine Aktion auszuführen, und halten Sie ihn für jeden Wiederholungsversuch dieser Entscheidung bereit.

import uuid

class PaymentTool:
    def __init__(self, client):
        self.client = client
        self._keys = {}

    def charge(self, task_id, step_id, amount, customer_id):
        # Ein Schlüssel pro (Aufgabe, Schritt). Wiederholungsversuche desselben Schritts verwenden ihn wieder.
        op = f"{task_id}:{step_id}"
        if op not in self._keys:
            self._keys[op] = str(uuid.uuid4())

        return self.client.post(
            "/v1/payments",
            headers={"Idempotency-Key": self._keys[op]},
            json={"amount": amount, "customer_id": customer_id},
        )

Ein deterministischer Schlüssel funktioniert ebenfalls und überlebt Prozessneustarts, was ein In-Memory-Wörterbuch nicht tut:

import hashlib

def idempotency_key(task_id: str, step_id: str, payload: dict) -> str:
    raw = f"{task_id}|{step_id}|{sorted(payload.items())}"
    return hashlib.sha256(raw.encode()).hexdigest()[:32]

Leiten Sie den Schlüssel vom Aufgabenlauf und dem Schritt ab, niemals von einem Zeitstempel oder einem zufälligen Wert, der pro Versuch neu generiert wird. Wenn der Agent die gesamte Aufgabe neu startet und tatsächlich eine neue Abbuchung beabsichtigt, ändert sich die Aufgaben-ID und damit auch der Schlüssel. Das ist das gewünschte Verhalten.

Was der Server tun muss

Die korrekte Handhabung des Headers erfordert mehr als nur ein Nachschlagen. Eine funktionierende Implementierung tut vier Dinge:

  1. Bei Ankunft versuchen, den Schlüssel zu beanspruchen. Fügen Sie ihn in eine Tabelle mit einer Unique-Constraint ein, bevor Sie weitere Arbeiten ausführen. Schlägt das Einfügen fehl, gehört er einem anderen Versuch.
  2. Wenn der Schlüssel existiert und der gespeicherte Anfrage-Fingerabdruck abweicht, mit 422 ablehnen. Derselbe Schlüssel mit einem anderen Body bedeutet einen Client-Fehler, und das stillschweigende Zurückgeben des alten Ergebnisses würde dies verbergen.
  3. Wenn der Schlüssel existiert und der erste Versuch noch läuft, 409 zurückgeben, damit der Aufrufer zurückweicht, anstatt zu konkurrieren.
  4. Wenn die Arbeit abgeschlossen ist, den Statuscode und den Body unter dem Schlüssel speichern und ihn dann bei jedem späteren Treffer zurückgeben.
CREATE TABLE idempotency_records (
  key             TEXT PRIMARY KEY,
  request_hash    TEXT NOT NULL,
  state           TEXT NOT NULL,      -- in_Bearbeitung | abgeschlossen
  response_status INT,
  response_body   JSONB,
  created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
  expires_at      TIMESTAMPTZ NOT NULL
);

Legen Sie ein Ablaufdatum fest. Vierundzwanzig Stunden decken jedes realistische Wiederholungsfenster ab, und das ewige Speichern von Schlüsseln macht die Tabelle zu einer Belastung. Stripe lässt Schlüssel nach 24 Stunden ablaufen, was ein vernünftiger Standardwert zum Übernehmen ist.

Testen, dass der zweite Aufruf nichts ändert

Idempotenz aufzubauen ist die halbe Miete. Zu beweisen, dass sie funktioniert, ist die andere Hälfte, und diese Hälfte wird oft übersprungen, weil der Normalfall identisch aussieht, egal ob die Funktion funktioniert oder nicht.

Der Test ist einfach zu beschreiben: Senden Sie die Anfrage, erfassen Sie das Ergebnis, senden Sie genau dieselbe Anfrage erneut und stellen Sie sicher, dass der Server die Arbeit nicht zweimal ausgeführt hat. Der schwierige Teil ist die letzte Zusicherung, da die Antwort allein Ihnen das nicht verraten wird. Zwei erfolgreiche Abbuchungen geben beide 200 zurück.

Prüfen Sie also den Zustand, nicht die Antwort:

In Apidog können Sie dies als Testszenario konfigurieren: Schritt eins sendet den POST mit einem festen Idempotency-Key, Schritt zwei wiederholt ihn, und Schritt drei listet die Ressource auf und überprüft die Anzahl. Speichern Sie die Antwort-ID aus Schritt eins in einer Variablen und prüfen Sie, ob Schritt zwei denselben Wert zurückgibt. Da das gesamte Szenario gespeichert ist, läuft es in der CI bei jeder Änderung des Zahlungspfads, wo Regressionen tatsächlich auftreten. Dieselbe Technik lässt sich auf die breiteren Muster in unserem API-Vertragstest-Leitfaden übertragen.

Zwei weitere Fälle, die es wert sind, behandelt zu werden, da sie echte Fehler aufdecken:

Mocking hilft auch hier. Wenn Sie den Agenten noch entwickeln und die Zahlungs-API noch nicht existiert, mocken Sie sie mit einer idempotenzbewussten Antwort, damit die Wiederholungslogik des Agenten frühzeitig trainiert werden kann. Unser Beitrag „Warum Agenten Mock-APIs statt der Produktion verwenden sollten“ erläutert den breiteren Fall für diese Gewohnheit.

Wenn Sie keinen Schlüssel hinzufügen können

Manchmal gehört die API nicht Ihnen und sie bietet keine Idempotenz-Unterstützung. Sie haben immer noch Optionen, in etwa der Reihenfolge der Präferenz.

Machen Sie die Operation von Natur aus idempotent. Ein PUT auf einen vom Client gewählten Ressourcenpfad ist per Konstruktion idempotent: PUT /orders/{client_order_id}. Wenn Sie das API-Design kontrollieren, bevorzugen Sie dies gegenüber POST plus einem Header. Es benötigt keine zusätzliche Tabelle.

Prüfen Sie vor dem Schreiben. Lassen Sie den Agenten nach einem bestehenden Datensatz mit demselben natürlichen Schlüssel suchen, bevor er einen erstellt. Dies ist schwächer, da ein Race-Condition zwischen der Prüfung und dem Schreibvorgang immer noch zwei Datensätze erzeugen kann, aber es eliminiert den häufigen Timeout-Fall.

Downstream deduplizieren. Wenn der Schreibvorgang eine Nachricht oder ein Ereignis ist, legen Sie die Deduplizierung in den Consumer. Fügen Sie eine stabile Nachrichten-ID hinzu und lassen Sie den Consumer Wiederholungen verwerfen. Dies ist gängige Praxis in ereignisgesteuerten Systemen und passt zu den Anweisungen in unserem Leitfaden für zuverlässige Webhooks.

Aktion absichern. Für Operationen, die wirklich irreversibel sind und nicht idempotent gemacht werden können, schalten Sie einen Menschen davor. Dies ist das „Approval-Gate“-Muster aus unserem Beitrag zu KI-Agenten-Schutzmaßnahmen, und es ist die richtige Antwort, wenn die Kosten eines Duplikats hoch genug sind.

Wissen, welcher Lauf was getan hat

Idempotenz stoppt das Duplikat. Sie verrät Ihnen nicht, welcher Versuch den Datensatz erstellt hat, und das ist die Frage, die Ihnen nach einem Vorfall gestellt wird.

Behalten Sie die Lauf-Identität an die Arbeit geknüpft. Wenn der Agent Ihr eigener Dienst ist, bedeutet dies die Task-ID und Schritt-ID aus der obigen Schlüsselableitung, die bei jedem Versuch protokolliert werden. Wenn der Agent eine Code-Laufzeitumgebung ist, die zugewiesene Arbeit ausführt, speichert die Plattform dies normalerweise für Sie: In Sharkly ist jeder Lauf mit der Task verknüpft, von der er stammt, wobei sein Ausführungsstatus und Ergebnis zusammen mit dem Kommentar-Thread gespeichert werden, sodass ein wiederholter Schreibvorgang auf einen bestimmten Lauf zurückverfolgt werden kann und nicht auf einen anonymen Wiederholungsversuch.

Eine Checkliste vor dem Versand

Arbeiten Sie diese Liste durch, und die Geschichte der doppelten Abbuchung ist nicht mehr möglich, was bedeutet, dass Ihre Wiederholungsrichtlinie aggressiver statt weniger aggressiv sein kann. Das ist der wahre Gewinn: Idempotenz ermöglicht es Ihnen, einen Agenten widerstandsfähig zu machen, ohne ihn gefährlich zu machen.

Häufig gestellte Fragen

Benötige ich Idempotenzschlüssel für schreibgeschützte Tools? Nein. GET-Anfragen sind bereits idempotent und sicher, sodass das Wiederholen einer solchen Anfrage Sie nur ein wenig Latenz und nichts weiter kostet. Reservieren Sie Schlüssel für Aufrufe, die Zustände erstellen, belasten, senden oder anderweitig ändern.

Wo sollte der Schlüssel generiert werden, im Agenten oder im Tool-Wrapper? Im Tool-Wrapper, basierend auf den Aufgaben- und Schritt-Identifikatoren des Agenten. Das Generieren des Schlüssels durch das Modell ist ein Fehler: Modelle generieren Werte bei Wiederholungen neu und können Kollisionen über Aufgaben hinweg erzeugen.

Welchen Statuscode sollte eine wiederholte Anfrage zurückgeben? Geben Sie den gespeicherten Status des ursprünglichen Aufrufs zurück, sodass ein zweiter POST, der ursprünglich 201 zurückgegeben hat, erneut 201 mit demselben Body zurückgibt. Einige APIs fügen einen Header wie Idempotent-Replay: true hinzu, um die Wiederholung zu kennzeichnen, was für das Debugging nützlich und für Clients, die ihn ignorieren, harmlos ist.

Wie lange sollten Schlüssel aufbewahrt werden? Vierundzwanzig Stunden decken nahezu jedes Wiederholungsfenster ab. Eine längere Aufbewahrung hilft selten und lässt die Tabelle unbegrenzt wachsen. Wenn ein Client nach diesem Fenster erneut versucht, behandeln Sie es als eine neue Operation.

Ersetzt dies Transaktionen? Nein. Idempotenzschlüssel verhindern, dass doppelte Anfragen doppelte Effekte erzeugen. Transaktionen halten eine einzelne Anfrage atomar. Sie benötigen beides, und die Schlüsselbehauptung sollte immer dann in derselben Transaktion wie die Arbeit geschrieben werden, wenn Ihre Datenbank dies zulässt.

Wie teste ich das ohne einen echten Zahlungsanbieter? Richten Sie den Agenten auf einen Mock, der die Schlüssel-Semantik implementiert, einschließlich des 422 bei Payload-Diskrepanz. Unser Leitfaden zum Testen von KI-Agenten mit Mock-APIs behandelt die Einrichtung, und laden Sie Apidog herunter, wenn Sie den Mock und den Wiederholungstest im selben Projekt haben möchten.

Praktizieren Sie API Design-First in Apidog

Entdecken Sie eine einfachere Möglichkeit, APIs zu erstellen und zu nutzen