Plattform · 01 Runtime

Ein Lauf, der einen Node­ausfall überlebt

Durable Execution heißt: Der Fortschritt eines Agenten liegt nicht im Prozessspeicher, sondern in einem append-only Event Log. Fällt der ausführende Node aus, setzt der Lauf auf dem letzten Checkpoint auf — ohne bereits ausgeführte Seiteneffekte zu wiederholen.

Durable Execution persistiert den Fortschritt eines Agenten-Laufs außerhalb des Prozesses. Nach einem Absturz wird der Lauf auf dem letzten Checkpoint fortgesetzt statt neu gestartet. Idempotenz-Keys verhindern, dass Tool-Calls mit Seiteneffekten — Buchungen, Erstattungen, Mails — ein zweites Mal ausgeführt werden.

< 5 s Wiederaufnahme nach Verlust des ausführenden Node Zielwert, Methodik in der Architektur
exactly‑once Semantik für Tool-Calls mit Seiteneffekt über Idempotenz-Keys, nicht über Hoffnung
72 h maximale Laufzeit eines einzelnen Runs längere Läufe als Kette geplanter Runs

Warum ein Agent kein normaler Request ist

Ein HTTP-Request dauert Millisekunden. Schlägt er fehl, wiederholt man ihn. Ein Agenten-Lauf dauert Sekunden bis Stunden, ruft dabei fremde Systeme auf und hinterlässt Spuren: eine Gutschrift ist gebucht, eine Mail ist raus, ein Ticket ist geschlossen. Ein naiver Neustart wiederholt diese Seiteneffekte.

Deshalb behandelt die Runtime einen Lauf wie einen Workflow, nicht wie einen Request. Jeder Schritt wird als Ereignis geschrieben, bevor er wirkt. Der Zustand ist keine Variable im Speicher, sondern eine Ableitung aus dem Ereignisstrom.

Durable Execution
Ausführungsmodell, bei dem der Fortschritt eines langlaufenden Prozesses persistent außerhalb des ausführenden Prozesses gehalten wird, sodass die Ausführung nach einem Ausfall an der letzten bekannten Stelle fortgesetzt werden kann.
Checkpoint
Konsistenter, persistierter Zustand eines Laufs an einer definierten Grenze — in der Regel unmittelbar vor und nach jedem Tool-Call. Ein Checkpoint enthält Schrittzähler, Kontextreferenzen und die Ergebnisse abgeschlossener Aufrufe.
Idempotenz-Key
Eindeutiger, aus Lauf-ID und Schrittnummer abgeleiteter Schlüssel, der einem Tool-Call mitgegeben wird. Erhält das Zielsystem denselben Schlüssel zweimal, führt es die Aktion nur einmal aus und liefert beim zweiten Mal dasselbe Ergebnis zurück.

Was bei einem Ausfall tatsächlich passiert

Der folgende Ablauf zeigt einen Lauf, dessen ausführender Node während des dritten Tool-Calls verloren geht.

EVENT LOG · APPEND-ONLY run.started plan.created tool.call#1 tool.ok#1 tool.call#2 tool.ok#2 checkpoint tool.call#3 letzter Checkpoint Node A · Prozess verloren t + 38 s, mitten in tool.call#3 Node B · Wiederaufnahme replay 7 events → Zustand rekonstruiert Ergebnis: tool.call#3 wird genau einmal ausgeführt — der Idempotenz-Key verhindert die Dopplung.
Wiederaufnahme aus dem Event Log. Der Zustand wird nicht kopiert, sondern aus den Ereignissen abgeleitet.

Schritte, die Sie selbst schreiben

Ein Schritt ist eine gewöhnliche Funktion. Die Runtime umgibt sie mit Persistenz, Retry-Politik und Idempotenz — sichtbar bleibt nur die Deklaration.

refund-agent.ts
import { defineAgent, step } from "@hostspezial/agent-sdk";

export default defineAgent({
  name: "refund-agent",
  // Harte Obergrenze pro Lauf. Wird sie erreicht, endet der Lauf
  // mit status=budget_exceeded — nicht mit einer Endlosschleife.
  budget: { tokens: 200_000, eur: 0.50, wallClockMs: 120_000 },

  async run(ctx, input) {
    // Lesende Schritte sind frei wiederholbar.
    const order = await step(ctx, "load-order", () =>
      ctx.tools.postgres.query("SELECT * FROM orders WHERE id = $1", [input.orderId])
    );

    // Schreibende Schritte bekommen einen Idempotenz-Key aus
    // Lauf-ID und Schrittnamen. Nach einem Absturz liefert das
    // Zielsystem dasselbe Ergebnis statt einer zweiten Buchung.
    const refund = await step(ctx, "issue-refund", {
      idempotencyKey: `${ctx.runId}:issue-refund`,
      retry: { attempts: 3, backoff: "exponential", maxDelayMs: 8_000 },
    }, () => ctx.tools.stripe.refund({ orderId: order.id, amount: order.total }));

    return { refundId: refund.id, status: "settled" };
  },
});

Fehlermodi und das Verhalten der Runtime

Die interessante Frage ist nie, ob etwas ausfällt, sondern was danach passiert.

Verhalten der Runtime je Fehlermodus
FehlermodusVerhaltenSichtbar als
Tool antwortet mit 5xxRetry mit exponentiellem Backoff bis zur konfigurierten Obergrenze, danach Eskalation an den definierten Fehlerpfad.tool.failed → tool.retried
Tool antwortet gar nichtTimeout je Tool, Abbruch des Aufrufs, Checkpoint bleibt auf dem Stand davor.tool.timeout
Node fällt zwischen zwei Schritten ausEin anderer Node übernimmt, spielt das Event Log ein und setzt am nächsten Schritt an.run.resumed
Node fällt während eines Schreib-Calls ausWiederaufnahme wiederholt den Call mit demselben Idempotenz-Key. Das Zielsystem führt ihn nicht erneut aus.run.resumed (dedup)
Modell liefert unbrauchbare AntwortSchema-Validierung schlägt an, ein Reparaturversuch mit engerem Prompt, danach Abbruch.plan.invalid
Budget aufgebrauchtHarter Stopp. Der Lauf endet mit budget_exceeded, der bisherige Zustand bleibt einsehbar.run.budget_exceeded
Mensch antwortet nicht auf FreigabeLauf bleibt im Wartezustand, ohne Rechenkosten zu erzeugen, bis Freigabe oder Ablauffrist.run.awaiting_approval

Was das nicht löst

Ehrliche Grenzen

Exactly-once gilt nur, soweit das Zielsystem mitspielt. Ein Tool ohne Idempotenz-Unterstützung kann die Runtime nicht nachrüsten — sie kann den Aufruf nur als „bereits versucht“ markieren und die Entscheidung an Ihre Fehlerbehandlung geben. Und: Wiederaufnahme reproduziert den Zustand, nicht die Modellantwort. Wer bitgleiche Wiederholbarkeit braucht, arbeitet mit dem deterministischen Replay gegen aufgezeichnete Antworten.

Häufige Fragen

Durable Execution im Detail

Was unterscheidet Durable Execution von einer Retry-Schleife?

Eine Retry-Schleife wiederholt den gesamten Vorgang. Durable Execution wiederholt nur den fehlgeschlagenen Schritt und kennt den Zustand aller vorherigen. Der Unterschied wird bei Seiteneffekten entscheidend: Die Schleife bucht zweimal, die Runtime einmal.

Event-Sourcing oder Snapshotting?

Beides. Jeder Schritt wird als Ereignis geschrieben; zusätzlich wird periodisch ein Snapshot abgelegt, damit die Wiederaufnahme nicht bei langen Läufen tausende Ereignisse einspielen muss. Die Begründung für diese Kombination steht im Architektur-Kapitel.

Wie lange darf ein Lauf dauern?

Bis zu 72 Stunden am Stück — das deckt auch Wartezustände auf menschliche Freigaben ab. Längere Vorhaben wie eine Codebase-Migration werden als Kette geplanter Läufe modelliert, damit jeder Abschnitt einzeln bewertbar bleibt.

Kostet ein wartender Lauf Geld?

Rechenzeit nicht. Ein Lauf im Wartezustand hält keinen Prozess offen, sondern nur einen Datensatz. Abgerechnet werden Schritte, Tool-Calls und Tokens — die Aufteilung steht auf der Preisseite.

Kann ich einen abgebrochenen Lauf manuell fortsetzen?

Ja. Jeder Lauf lässt sich ab einem beliebigen Checkpoint fortsetzen — auch mit geändertem Modell, geänderter Policy oder korrigierten Eingaben. Der neue Zweig wird als eigener Lauf geführt, der ursprüngliche bleibt unverändert im Log.

Was passiert bei einem Rechenzentrums-Ausfall?

Der Zustandsspeicher wird an den DR-Standort repliziert. Laufende Runs werden dort fortgesetzt, sobald die Kontrollebene übernimmt. Verfügbarkeits- und Wiederanlaufziele werden vertraglich je Umgebung vereinbart — Details auf der Trust-Seite.

Weiter in der Plattform

Testen Sie den Fehlerfall, nicht den Glücksfall

In der Sandbox schalten wir einen Node während Ihres Laufs ab. Sie sehen im Trace, wo er wieder aufsetzt — und dass die Buchung nur einmal existiert.

Fragen vorab: 09571 873149