7 Min. Lesezeit

Abgebrochene Agent-Aktionen aufzeichnen, ohne zu lügen

Definiere abgebrochene Agent-Aktionen mit ehrlichen Audit-Ergebnissen für Timeouts, Verbindungsabbrüche und verlorene Bestätigungen, damit unbekannte Wirkungen sicher untersucht werden können.

Abgebrochene Agent-Aktionen aufzeichnen, ohne zu lügen

Ein Audit-Log, das jede abgebrochene Agent-Aktion als «failed» bezeichnet, lügt. Der Agent hat vielleicht nur aufgehört zu warten, aber der Datenbankeintrag könnte bereits existieren, das Deployment bereits laufen oder der entfernte Befehl nach dem Verbindungsabbruch noch ausgeführt werden.

Die Lösung ist keine längere Liste von Fehlercodes. Es braucht ein Ergebnismodell, das zwischen dem unterscheidet, was das Gateway beobachtet hat, und dem, was am Ziel passiert ist. Bei einer Untersuchung muss erkennbar sein, ob eine Aktion den Rechner nie verlassen hat, vom Ziel abgelehnt wurde, mit einer Bestätigung abgeschlossen ist oder deren Wirkung unbekannt bleibt.

Das Ergebnis des Aufrufers ist nicht das Ergebnis der Aktion

Ein Aufrufer sieht nur einen kleinen Ausschnitt einer Aktion: Er sendet Arbeit ab und wartet auf eine Antwort. Die Aktion selbst durchläuft mehrere Systeme, die weder dieselbe Uhr noch dieselbe Prozesslebensdauer oder einen zuverlässigen Rückweg haben. Wenn der Aufrufer abbricht, die Verbindung verliert oder seine Frist erreicht, weiß er etwas über seine eigene Wartezeit. Über die endgültige entfernte Wirkung weiß er deshalb noch nichts.

Dieser Unterschied ist bei zustandsverändernden Vorgängen besonders wichtig. Ein Ticket anlegen, eine Rückerstattung auslösen, Infrastrukturänderungen anwenden, ein Objekt löschen, ein Zugriffstoken austauschen oder einen entfernten Befehl ausführen kann Folgen haben, obwohl die Antwort fehlt. Wenn dein Journal «failed» schreibt, weil der Agent-Prozess zuerst beendet wurde, kann ein späterer Retry ein zweites Ticket anlegen, eine zweite Rückerstattung auslösen oder den destruktiven Befehl zweimal ausführen.

Auch Lesevorgänge brauchen diese Ehrlichkeit, obwohl das Risiko anders aussieht. Ein abgebrochener Abruf kann eine unvollständige Sicht liefern, woraufhin ein Agent eine falsche Folgeentscheidung trifft. Von selbst verändert er die Welt normalerweise nicht. Ein POST-, PATCH- oder DELETE-Aufruf sowie ein entfernter Shell-Befehl können es sehr wohl.

Halte in jedem Eintrag drei Dinge getrennt:

  • Disposition des Aufrufers: abgeschlossen, abgebrochen, Verbindung getrennt oder Zeitüberschreitung.
  • Versandnachweise: nie gestartet, lokal gestartet, Bytes an den Transport übergeben oder vom entfernten System bestätigt.
  • Wirkungsergebnis: keine Wirkung, erfolgreich, abgelehnt, teilweise abgeschlossen oder unbekannte Wirkung.

Teams führen das erste und dritte Feld oft zusammen, weil eine einzige Statusspalte praktisch ist. Diese Bequemlichkeit hält bis zur Incident-Nachbesprechung. Dann muss jemand erklären, warum «Anfrage abgebrochen» neben einem Objekt steht, das in der Produktion eindeutig existiert.

Die praktische Regel ist klar: Schreibe keine Wirkung nur dann, wenn die Belege eine Ausführung ausschließen. Schreibe unbekannte Wirkung, wenn eine Ausführung plausibel bleibt und kein vertrauenswürdiges Ergebnis vorliegt. «Unbekannt» ist keine peinliche Lücke im Log. Es ist das korrekte Ergebnis einer verteilten Aktion mit unterbrochenem Beobachtungsweg.

Die Beleggrenze aufzeichnen, keine Geschichte erraten

Jede Aktion braucht eine ausdrückliche Grenze, ab der das Gateway nicht mehr ehrlich versprechen kann, dass nichts passiert ist. Nennen wir sie Versandgrenze. Bei HTTP kann sie erreicht sein, wenn die Anfrage für eine Verbindung festgeschrieben und an das Betriebssystem übergeben wurde oder wenn ein Upstream-Dienst ihre Annahme bestätigt. Bei SSH kann sie erreicht sein, wenn der Helfer die Befehlsanforderung über einen authentifizierten Kanal gesendet hat.

Tu nicht so, als würde ein einzelnes Boolean-Feld sent=true die Sache klären. Lokale Schreibvorgänge können gepuffert sein. Eine Transportbibliothek kann einen Schreibvorgang melden, bevor die Gegenstelle die Daten liest. Eine Gegenstelle kann eine Anfrage empfangen, die Änderung anwenden und die Antwort auf dem Rückweg verlieren. Das Log sollte den stärksten verfügbaren Nachweis beschreiben, statt ein Implementierungsdetail in einen Beweis umzuwandeln.

Ein sinnvoller Aktionseintrag enthält unveränderliche IDs und eine Folge von Beobachtungen. Diese kompakte Form funktioniert sowohl für einen API-Aufruf als auch für eine Befehlsausführung:

{
  "action_id": "act_01JQ7M4V6K",
  "session_id": "ses_01JQ7M2Y8A",
  "channel": "http",
  "intent": {
    "method": "POST",
    "target": "api.example.internal/v1/releases",
    "request_fingerprint": "sha256:...",
    "idempotency_token": "release_01JQ7M4V6K"
  },
  "observations": [
    {"at": "2026-07-22T16:40:01Z", "kind": "authorized"},
    {"at": "2026-07-22T16:40:02Z", "kind": "dispatch_started"},
    {"at": "2026-07-22T16:40:03Z", "kind": "transport_write_completed"},
    {"at": "2026-07-22T16:40:33Z", "kind": "caller_deadline_exceeded"}
  ],
  "caller_disposition": "timed_out",
  "effect_outcome": "unknown_effect",
  "outcome_basis": "response_not_observed_after_dispatch"
}

Der Request-Fingerprint zeigt, was versucht wurde, ohne ein Bearer-Token, den rohen Request-Body oder ein geheimes Befehlsargument im Journal abzulegen. Die Kennung muss über Retries und eine spätere Reconciliation hinweg stabil bleiben. Wenn ein Operator die ursprüngliche Anfrage, ihren Retry und das spätere entfernte Objekt nicht miteinander verbinden kann, beantwortet die Audit-Spur nicht die entscheidende Frage.

Zwischen dispatch_started und transport_write_completed besteht ein wichtiger Unterschied. Das erste bedeutet, dass das Gateway den Vorgang begonnen hat. Das zweite bedeutet, dass der lokale Transport die ausgehenden Daten angenommen hat. Beides sagt nicht, dass die entfernte Anwendung sie ausgeführt hat. Wenn deine Implementierung die beiden Zustände nicht unterscheiden kann, zeichne die schwächere Tatsache auf und nenne sie in der Grundlage des Ergebnisses.

Abbruch vor dem Versand kann «keine Wirkung» bedeuten

Ein Abbruch kann das Ergebnis «keine Wirkung» stützen, aber nur, bevor das Gateway die Aktion an einen externen Kanal übergibt. Das ist der eindeutige Fall: Der Agent widerruft seine Anfrage, während sie noch auf eine lokale Genehmigung wartet, bevor der Tresor entsperrt wird, bevor eine HTTP-Anfrage beginnt oder bevor ein SSH-Befehl an den Transporthelfer übergeben wird.

Der Audit-Eintrag sollte zeigen, warum diese Schlussfolgerung sicher ist. «Abgebrochen» allein sagt einem Untersuchenden nicht, an welcher Stelle der Abbruch erfolgte. Schreibe eine Stufe und einen lokalen Belegpunkt auf.

{
  "action_id": "act_01JQ7P1N2R",
  "caller_disposition": "canceled",
  "effect_outcome": "no_effect",
  "outcome_basis": "cancellation_observed_before_dispatch",
  "last_observed_stage": "awaiting_authorization"
}

Dieses Ergebnis passt auch, wenn ein lokales Gate die Aktion ablehnt, bevor eine externe Anfrage beginnen kann. Eine fehlende Genehmigung, ein gesperrter Tresor, eine widerrufene Sitzung oder eine ungültige lokale Anfrage können alle ohne Wirkung bleiben, sofern das Gateway keine Arbeit versendet hat. Das Journal sollte eine abgelehnte Anfrage von einer abgebrochenen unterscheiden, weil beide unterschiedliche Aussagen über menschliche Kontrolle und das Verhalten des Agenten machen. Beide können aber sicher festhalten, dass das Ziel nichts gesehen hat.

Verwende diese Kennzeichnung nicht, nachdem du eine Verbindung aufgebaut und mit dem Schreiben begonnen hast, nur weil der Transportaufruf einen Abbruchfehler zurückgibt. Viele Bibliotheken verwenden denselben Fehlerwert für mehrere Wege: Abbruch in der Warteschlange, Abbruch während des Schreibens, Abbruch beim Warten auf Antwort-Header oder Abbruch beim Lesen des Bodys. Diese Fälle sind nicht gleich.

Die Weitergabe einer Abbruchsignalisierung hat einen wichtigen Zweck, ist aber keine Zeitmaschine. Die gRPC-Richtlinien zum Abbruch besagen, dass ein Client-Abbruch signalisiert, dass der Client das RPC-Ergebnis nicht mehr benötigt, und empfehlen Servern, die Arbeit zu beenden und den Abbruch an nachgelagerte Vorgänge weiterzugeben. Das ist ein gutes Ressourcenverhalten. Es beweist nicht, dass eine frühere Nebenwirkung rückgängig gemacht wurde, und kann einen unabhängig festgeschriebenen Schreibvorgang nicht rückgängig machen.

Wenn der empfangende Dienst einen ausdrücklichen Abbruch-Endpunkt mit Bezug zu einer Operations-ID anbietet, zeichne ihn als zweite Aktion auf. Sein Ergebnis kann das ursprüngliche Wirkungsergebnis nur ändern, wenn der Dienst eine vertrauenswürdige Aussage über den ursprünglichen Vorgang liefert. Eine best-effort-Abbruchanfrage, deren Antwort nach einem Netzwerkabbruch ausbleibt, erzeugt eine weitere unbekannte Wirkung. Sie beseitigt die erste nicht auf magische Weise.

Ein Timeout nach dem Versand hat eine unbekannte Wirkung

Eine Frist ist eine lokale Grenze für das Warten. Sie ist kein Urteil über die entfernte Ausführung. Sobald eine Aktion die Versandgrenze überschritten hat, muss ein Timeout standardmäßig zu «unbekannte Wirkung» führen, sofern eine Protokollbestätigung oder eine spätere Prüfung nicht mehr beweist.

Bei HTTP wird das leicht falsch umgesetzt, weil Statusbezeichnungen endgültig klingen. RFC 9110 besagt, dass 408 bedeutet, dass der Server innerhalb der vorgesehenen Wartezeit keine vollständige Anfrage erhalten hat. 504 bedeutet, dass ein Gateway keine rechtzeitige Antwort von einem Upstream-Server erhalten hat. Beide Aussagen beschreiben einen bestimmten Beobachter und einen bestimmten Austausch. Keine davon beweist, dass ein anderes System auf bereits empfangene Daten nicht reagiert hat.

Stell dir einen Agenten vor, der POST /v1/releases mit einer Frist von 30 Sekunden sendet. Die API validiert die Anfrage, legt einen Release-Eintrag an, weist einen Deployment-Controller zum Start an und bleibt dann beim Erstellen der Antwort hängen. Nach 30 Sekunden sieht der Agent ein Timeout. Der Release existiert. Ein Retry ohne Idempotenz-Token kann einen weiteren Release anlegen, obwohl der erste Aufruf im Agent-Transkript als «failed» markiert ist.

Der ehrliche Eintrag sieht so aus:

{
  "caller_disposition": "timed_out",
  "effect_outcome": "unknown_effect",
  "outcome_basis": "deadline_after_transport_write_no_remote_receipt",
  "recovery_required": "lookup_by_idempotency_token"
}

Verwende failed nicht als Abkürzung für «unbekannt». Reserviere Fehlerergebnisse für Tatsachen, die du feststellen kannst: Ein entfernter Dienst hat einen Validierungsfehler zurückgegeben, ein Befehl hat einen Exit-Status ungleich null geliefert, eine Verbindung konnte nicht hergestellt werden, bevor eine Anfrage das Gateway verließ, oder eine lokale Genehmigungsentscheidung hat die Ausführung verweigert. Sobald ein ausgehender Versand möglich war, erfüllt ein Timeout diesen Maßstab nicht.

Teilweise abgeschlossene Vorgänge brauchen ein eigenes Ergebnis, wenn die entfernte Seite Belege liefert. Batch-APIs und Skripte erledigen häufig einen Teil der Arbeit, bevor sie fehlschlagen. Wenn ein Dienst eine Liste abgeschlossener Objekt-IDs zusammen mit einem Fehler zurückgibt, speichere partial_effect, bewahre die Objekt-IDs nach Möglichkeit auf und erfasse den vom Dienst genannten Grund. Das schlicht «failed» zu nennen, verbirgt die konkrete Bereinigung, die ein Operator durchführen muss.

Verlorene Bestätigungen brauchen einen eigenen Eintrag

Riskante Retries erneut genehmigen
Fordere für jede Nutzung eines sensiblen Schlüssels eine Genehmigung an, auch bei einem Retry nach einem unbestimmten Ergebnis.

Eine verlorene Bestätigung tritt ein, nachdem der Empfänger möglicherweise gehandelt hat, aber bevor das Gateway eine abschließende Bestätigung erhält. Das kommt häufig genug vor, um als eigene Beobachtung und nicht als allgemeiner Netzwerkfehler erfasst zu werden.

Der Ablauf wirkt bis zum letzten Moment gewöhnlich:

  1. Das Gateway genehmigt und versendet eine Aktion.
  2. Der entfernte Dienst nimmt sie an und führt die angeforderte Arbeit aus oder stellt sie in eine Warteschlange.
  3. Die Antwort verzögert sich, die Verbindung bricht ab oder der lokale Prozess endet.
  4. Dem Gateway fehlt ein dauerhafter Beleg für den Abschluss, obwohl das entfernte System möglicherweise einen solchen Beleg besitzt.

Der erste Fehler besteht darin, den ursprünglichen Eintrag zu überschreiben, sobald eine spätere Suche erfolgreich ist. Dann liest sich das Journal so, als hätte das Gateway das Ergebnis bereits zum Zeitpunkt des Aufrufs gekannt. Für eine Untersuchung braucht es beide Tatsachen: Der ursprüngliche Aufruf endete ohne Bestätigung, und eine spätere Reconciliation fand ein passendes entferntes Ergebnis.

Hänge stattdessen eine Beobachtung an:

{
  "action_id": "act_01JQ7M4V6K",
  "reconciliation": {
    "at": "2026-07-22T16:43:10Z",
    "method": "GET /v1/operations/release_01JQ7M4V6K",
    "remote_reference": "op_8f2c",
    "result": "succeeded"
  },
  "effect_outcome": "succeeded",
  "outcome_basis": "remote_operation_lookup"
}

Die Disposition des ursprünglichen Aufrufers bleibt timed_out. Ändere sie nicht in completed. Der Aufrufer hat tatsächlich ein Timeout erlebt. Das System hat später erfahren, dass die entfernte Aktion erfolgreich war. Beide Tatsachen widersprechen sich nicht.

Eine entfernte Bestätigung ist nur so vertrauenswürdig wie ihre Zuordnung. Ein Abgleich über einen Objektnamen, die aktuelle Uhrzeit oder natürliche Sprache des Agenten ist schwach. Besser sind ein vom Ziel akzeptiertes Idempotenz-Token, eine Operations-ID, die vor Beginn einer lang laufenden Arbeit zurückgegeben wird, oder eine Provider-Request-ID, deren Eindeutigkeit das Ziel garantiert. Wenn das Ziel nichts davon anbietet, nutze eine eng gefasste Rückabfrage und halte fest, warum sie ausreicht oder warum sie mehrdeutig bleibt.

Ein neu entdeckter Benutzer namens build-bot beweist zum Beispiel nicht, welche Create-Anfrage ihn angelegt hat. Ein Objekt mit einem gespeicherten Request-Token, das dem ursprünglichen Aktionstoken entspricht, ist deutlich aussagekräftiger. Dieser Unterschied entscheidet darüber, ob ein Retry sicher möglich ist.

Idempotenz macht die Wiederherstellung zu einer Prüfung statt zu einem Glücksspiel

Idempotenz ist keine nachträglich angehängte Erlaubnis zum Wiederholen. Sie ist ein Vertrag, der vor dem Versand festgelegt wird. Der Client liefert ein stabiles Token, und der Dienst garantiert, dass wiederholte Anfragen mit diesem Token dieselbe logische Operation bezeichnen, statt neue Wirkungen zu erzeugen.

Stelle dem Betreiber jeder zustandsverändernden HTTP-Integration vier direkte Fragen:

  • Akzeptiert der Dienst ein vom Aufrufer geliefertes Idempotenz-Token?
  • Welche Anfragefelder müssen bei einer Wiederholung mit diesem Token identisch bleiben?
  • Wie lange bewahrt der Dienst die Zuordnung von Token und Ergebnis auf?
  • Kann ein Aufrufer das ursprüngliche Ergebnis nach einer verlorenen Antwort abrufen?

Wenn die Antworten vage sind, darfst du automatische Retries nicht als sicher bewerben. «Wir deduplizieren normalerweise» ist kein Vertrag. Eine Cache-Bereinigung, ein regionaler Failover oder eine Änderung des Request-Parsers kann diese Annahme in doppelte Arbeit verwandeln.

Wenn das Ziel keine Idempotenz unterstützt, teile einen riskanten Vorgang nach Möglichkeit auf. Lege einen dauerhaften Entwurf mit einer eindeutigen externen Referenz an, prüfe diesen Entwurf und führe den unumkehrbaren Befehl anschließend mit seiner zurückgegebenen ID aus. Das macht nicht jede Aktion sicher, schafft aber vor dem teuren oder destruktiven Teil einen Punkt für die Reconciliation.

Ein Deployment-Ablauf zeigt den Unterschied. Ein einzelner Endpunkt, der einen Release anlegt und zugleich startet, lässt wenig Raum für die Wiederherstellung nach einer verlorenen Bestätigung. Ein Ablauf mit zwei Aufrufen kann einen Release mit der Aktions-ID als externer Referenz anlegen, diese Referenz nach einem Timeout abfragen und ihn erst starten, wenn eine bekannte Release-ID vorliegt. Der zusätzliche Aufruf ist meist günstiger, als eine unerwartete Änderung in der Produktion erklären zu müssen.

Erzeuge für einen automatischen Retry derselben Aktion niemals ein neues Idempotenz-Token. Ein neues Token erklärt den Retry zu einer neuen logischen Anfrage. Das kann richtig sein, wenn ein Mensch die Arbeit bewusst wiederholt, verhindert bei der Wiederherstellung aber die Deduplizierung. Bewahre das ursprüngliche Token im Aktionseintrag auf und verweise beim Retry auf die übergeordnete Aktions-ID.

Ein Idempotenzvertrag hilft auch bei der Incident-Bearbeitung. Untersuchende können eine konkrete Frage stellen: «Welche Entscheidung hat das Ziel für Token X getroffen?» Ohne diesen Vertrag müssen sie die Absicht aus Zeitstempeln, Logs und Namen ableiten. Das ist langsam, fehleranfällig und nach Ablauf von Aufbewahrungsfristen oft unmöglich.

Das Schließen von SSH und der Abschluss eines Befehls sind verschiedene Tatsachen

Keine API-Geheimnisse mehr weiterreichen
Sallyport führt authentifizierte HTTP-Anfragen aus, ohne Bearer-, Basic- oder Custom-Header-Geheimnisse an den Agenten weiterzugeben.

SSH führt zu einem ähnlichen Fehler, weil eine geschlossene Sitzung wie ein gestoppter Befehl wirkt. Das stimmt nicht. Ein Netzwerkabbruch kann den Client abschneiden, während der entfernte Prozess unter seiner übergeordneten Shell, einem Supervisor oder einem Service-Manager weiterläuft. Umgekehrt kann ein Prozess fertig werden, während der Client die Exit-Meldung verpasst.

RFC 4254 behandelt das Schließen eines Kanals, das Ende der Datei und den Exit-Status als getrennte Protokollereignisse. Die Spezifikation empfiehlt, beim Ende eines entfernten Befehls einen Exit-Status zurückzugeben, macht das Schließen des Kanals aber nicht zum Beweis, dass du ihn erhalten hast. Sie besagt außerdem, dass die Gegenstellen Close-Nachrichten austauschen, bevor jede Seite den Kanal als geschlossen betrachtet. Das sagt etwas über den Zustand des SSH-Kanals aus, nicht darüber, ob der entfernte Befehl vor dem Abbruch Änderungen am Dateisystem oder Dienst vorgenommen hat.

Zeichne SSH-Belege als getrennte Beobachtungen auf:

{
  "channel": "ssh",
  "observations": [
    {"kind": "command_request_sent"},
    {"kind": "stdout_received", "bytes": 1840},
    {"kind": "connection_lost"}
  ],
  "caller_disposition": "disconnected",
  "effect_outcome": "unknown_effect",
  "outcome_basis": "no_exit_status_or_remote_process_identity"
}

Wenn du nach der Ausgabe einen gültigen Exit-Status und ein vollständiges Schließen des Kanals erhältst, ist das ein starker Hinweis auf das Ergebnis des Befehlsprozesses, aber kein absoluter Beweis für jede ausgelöste externe Nebenwirkung. Ein Skript kann asynchrone Arbeit erfolgreich absenden und mit Exit 0 enden, bevor diese Arbeit abgeschlossen ist. Zeichne das Befehlsergebnis als erfolgreich auf und behandle den externen Job als eigene Operation, wenn das entfernte System eine ID liefert.

Für Befehle braucht es einen Wiederherstellungsplan, bevor ein Agent sie ausführt. Bevorzuge Befehle, die eine dauerhafte Operations-ID ausgeben oder speichern. Bei einem Neustart eines Dienstes solltest du den Service-Manager mit einem bekannten Unit-Namen sowie einem Zustand davor und danach abfragen. Bei einer Datenbankmigration prüfst du das Migrationsprotokoll, statt der Terminalausgabe zu vertrauen. Bei einer Dateioperation prüfst du einen Inhalts-Hash und eine Versions- oder Generationsmarkierung, nicht nur die Existenz eines Pfads.

Behandle ein Signal an einen lokalen Helfer nicht als Beweis, dass der entfernte Prozess gestoppt wurde. Das Signal kann vor dem entfernten Versand, nach Abschluss des entfernten Befehls oder nach dem Abbruch der Verbindung eintreffen. Logge das Signal als Ereignis des Aufrufers oder Gateways. Ändere das Wirkungsergebnis erst, wenn der entfernte Endpunkt einen Beleg liefert.

Eine Untersuchung beginnt mit der Chronologie, nicht mit einem Endlabel

Anmeldedaten nicht in Retries übernehmen
Halte API-Schlüssel aus dem Agent-Kontext heraus, während Sallyport den HTTP-Aufruf selbst ausführt.

Eine untersuchende Person sollte eine unbestimmte Aktion rekonstruieren können, ohne zu erraten, welche Logzeile zuerst kam. Dafür braucht es eine stabile Aktions-ID, eine Sitzungs-ID, eine monotone Ereignisreihenfolge innerhalb des Gateways und Zeitstempel für beobachtete Ereignisse. Die Wanduhr hilft bei der Korrelation, kann aber zwischen Systemen abweichen. Baue deine gesamte Schlussfolgerung nicht darauf, dass zwei Uhren auf die Millisekunde übereinstimmen.

Eine gute Untersuchung stellt diese Fragen in dieser Reihenfolge:

  1. Wer hat die Aktion genehmigt, und welcher Agent-Prozess hat die Anfrage gestellt?
  2. Hat das Gateway seine Versandgrenze überschritten?
  3. Welche Transportbeobachtungen gab es nach dem Versand?
  4. Ist eine vertrauenswürdige entfernte Bestätigung eingegangen?
  5. Wenn nicht, welche Reconciliation-Abfrage kann die ursprüngliche logische Operation identifizieren?

Diese Reihenfolge verhindert eine häufige schlechte Gewohnheit: Zuerst in entfernten Logs zu suchen, ein ähnliches Ereignis zu finden und es zur Antwort zu erklären. Beginne mit der beabsichtigten Aktion und ihrem Korrelationsmaterial. Prüfe dann, ob die entfernten Belege genau zu dieser Operation passen.

Der Audit-Eintrag muss stillen Änderungen widerstehen. Wenn ein Operator unknown_effect in succeeded ändern kann, ohne den vorherigen Zustand und die Grundlage der Änderung zu bewahren, wird der Eintrag zu einer Behauptung statt zu einem Beleg. Nur anhängbare Datensätze, Hashverkettung und Offline-Prüfung erschweren es, nachträgliche Bearbeitungen zu verbergen. Sie beweisen nicht, dass jedes entfernte System die Wahrheit gesagt hat, bewahren aber, was das Gateway wann beobachtet hat.

Sallyport führt Agent-Läufe und einzelne Aktionen in getrennten Journalen, die aus einem verschlüsselten, hashverketteten Audit-Log erstellt werden. Mit sp audit verify lässt sich die Kette offline über dem Chiffretext prüfen. Dieses Design ist hier hilfreich, weil eine spätere Reconciliation als neue Tatsache aufgezeichnet werden kann, ohne das ursprüngliche Timeout oder die ursprüngliche Trennung zu löschen.

Lege keine geheimhaltenden Request-Bodies nur deshalb in den Audit-Trail, um die Incident-Bearbeitung zu verbessern. Speichere bereinigte Ziele, Request-Fingerprints, genehmigte Operations-IDs, Antwortklassifikationen und die für die Reconciliation erforderlichen minimalen entfernten Referenzen. Ein Log, das eine Untersuchung durch das Offenlegen von Zugangsdaten löst, erzeugt den nächsten Incident selbst.

Ergebnisnamen sollten das sichere Verhalten des Agenten steuern

Eine Ergebnistaxonomie lohnt sich erst, wenn die Laufzeit des Agenten auf jedes Ergebnis anders reagiert. Wenn jeder Nicht-Erfolg zu einem sofortigen Retry führt, sind detaillierte Audit-Bezeichnungen nur Dekoration.

Nutze diese Regeln:

  • Wiederhole nach no_effect automatisch nur dann, wenn die ursprüngliche Absicht weiterhin genehmigt und aktuell ist.
  • Wiederhole nach rejected erst, wenn der Agent die ungültige Eingabe ändert oder ein Mensch den genannten Konflikt löst.
  • Führe vor einem Retry jeder zustandsverändernden Aktion eine Reconciliation für unknown_effect durch.
  • Behandle partial_effect als Bereinigungs- oder Fortsetzungsaufgabe, nicht als unbeschriebenes Blatt.
  • Eskaliere, wenn die Reconciliation keine einzelne passende entfernte Operation identifizieren kann.

Bei der letzten Regel werden Teams ungeduldig. Sie möchten, dass der Agent weitermacht. Bei einer Leseanfrage ist dieser Impuls verständlich, bei Aktionen, die Geld ausgeben, Zugriffe ändern, die Produktion verändern oder Daten löschen, ist er fahrlässig. Eine ungeklärte Aktion sollte sichtbar bleiben, bis ein Mensch oder eine zuverlässige entfernte Abfrage die Beleglücke schließt.

Auch der Genehmigungsablauf darf diesen Unterschied nicht verbergen. Ein Mensch, der eine Aktion genehmigt, genehmigt einen Versuch, nicht unbegrenzte Retries nach einem unbekannten Ergebnis. Wenn der nächste Versuch eine zweite Wirkung erzeugen kann, muss sichtbar sein, dass er ein Retry einer unbestimmten Aktion ist. Je nach Risiko braucht es dafür eine neue Entscheidung.

Baue dieses Modell, bevor du weitere Kanäle oder mehr autonomes Verhalten hinzufügst. Das erste Timeout, nachdem ein Agent etwas Wichtiges verändert hat, ist ein schlechter Zeitpunkt, um festzustellen, dass dein Audit-Log nur zwei Ergebnisse kennt: Erfolg und das, was der Client zufällig gesehen hat.

FAQ

Was bedeutet «unknown effect» in einem Audit-Log?

Verwende «unknown effect», wenn die Aktion das entfernte System erreicht haben könnte, das Gateway das endgültige Ergebnis aber nicht beweisen kann. Ein Client-Timeout, eine abgebrochene Antwort oder eine unterbrochene SSH-Verbindung fallen in diese Kategorie, sofern das Senden begonnen haben könnte. Ersetze die Unsicherheit nicht durch «failed», denn dadurch wirkt ein Retry sicherer, als er ist.

Wann kann eine Agent-Aktion als «no effect» markiert werden?

Schreibe nur dann «no effect», wenn die Aktion die Versandgrenze nie überschritten hat oder ein vertrauenswürdiges entferntes System beweist, dass es die Anfrage vor der Ausführung abgelehnt hat. Eine lokale Abmeldung vor dem Schreiben in den Socket ist ein gutes Beispiel. Ein Timeout, nachdem Bytes den Prozess verlassen haben, ist es nicht.

Bedeutet ein API-Timeout, dass die Aktion fehlgeschlagen ist?

Ein Timeout sagt dir, dass der Aufrufer nicht länger gewartet hat. Es sagt nicht, ob der Dienst die Anfrage empfangen, in eine Warteschlange gestellt oder ausgeführt hat, oder ob er sie unmittelbar danach ausgeführt hat. Behandle jedes Timeout bei einer zustandsverändernden Aktion als unbekannt, bis eine nachträgliche Prüfung etwas anderes beweist.

Ist eine abgebrochene Anfrage dasselbe wie eine fehlgeschlagene Anfrage?

Eine Abmeldung bedeutet, dass der Aufrufer das Ergebnis nicht mehr benötigt. Sie kann vor dem Versand, während der Übertragung, nach der Annahme durch das entfernte System oder nach der Ausführung, aber vor der Bestätigung eintreten. Das sind unterschiedliche Ereignisse und brauchen unterschiedliche Audit-Ergebnisse.

Was ist eine verlorene Bestätigung?

Eine verlorene Bestätigung tritt auf, wenn das entfernte System die Arbeit möglicherweise abgeschlossen hat, das Gateway aber nie die abschließende Antwort erhält. Das passiert häufig nach einer Netzwerkunterbrechung oder dem Ende eines Prozesses. Zeichne die fehlende Bestätigung getrennt vom Aktionsergebnis auf, damit erkennbar bleibt, warum der Zustand unbekannt ist.

Soll ein Agent nach einem Timeout wiederholen?

Wiederhole eine zustandsverändernde Anfrage nach einem unbestimmten Ergebnis nicht automatisch. Nutze zuerst ein Idempotenz-Token, eine Rückabfrage oder eine anbieterspezifische Suche nach der Operation. Wenn nichts davon möglich ist, muss ein Mensch entscheiden, weil die zweite Anfrage die erste duplizieren könnte.

Können HTTP 408 oder 504 beweisen, dass nichts passiert ist?

HTTP-Statuscodes beschreiben, was der antwortende Server oder das Gateway weiß, nicht automatisch, was beim Ziel passiert ist. RFC 9110 definiert 408 als unvollständige Anfrage beim Server und 504 als zu langes Warten eines Gateways auf eine Antwort vom Upstream. Keiner der beiden Statuscodes beweist, dass eine nachgelagerte Aktion keine Wirkung hatte.

Bedeutet eine SSH-Trennung, dass der entfernte Befehl gestoppt wurde?

Ein SSH-Exit-Status ist ein Hinweis darauf, dass der entfernte Befehl beendet wurde und einen Code über den Kanal zurückgegeben hat. Ein geschlossener Kanal ohne Exit-Status ist weniger aussagekräftig, weil die Verbindung abbrechen kann, während der Befehl weiterläuft. Zeichne den Abschluss der Ausgabe, den Exit-Status und das Schließen des Kanals als getrennte Beobachtungen auf.

Welche Felder sollte ein Audit-Ereignis für eine unbestimmte Aktion enthalten?

Ein nützliches Ereignis enthält die Absicht, die Genehmigungsentscheidung, den Versandversuch, Transportmeilensteine, eine entfernte Bestätigung, das beobachtete Ergebnis und ein mögliches Ergebnis der nachträglichen Prüfung. Es sollte außerdem stabile Korrelations-IDs und bereinigte Request-Fingerprints enthalten. Speichere keine Zugangsdaten oder rohen sensiblen Payloads, nur damit das Log vollständig wirkt.

Wie soll eine spätere Reconciliation einen Audit-Eintrag aktualisieren?

Lass das ursprüngliche Ereignis unveränderlich und hänge ein späteres Reconciliation-Ereignis an, das dieselbe Aktions-ID referenziert. So bleibt erhalten, was die Verantwortlichen zum damaligen Zeitpunkt wussten, statt die Vergangenheit nach einer Rückabfrage umzuschreiben. Außerdem lässt sich eine späte Bestätigung von einer Aktion unterscheiden, deren Erfolg sofort bekannt war.

Sallyport

Sallyport führt API-Aufrufe und SSH-Befehle für Ihren KI-Agenten aus. Die Schlüssel bleiben in einem lokalen Tresor auf Ihrem Mac; Sie geben jeden Lauf frei, und jede Aktion landet in einem versiegelten Journal.

© 2026 Sallyport · Open Source unter Apache-2.0 · Oleg Sotnikov