Alejandro Rioja.
AI Agents Operations

Multi-Agenten-Orchestrierungsmuster: Queues, State und Übergaben

Alejandro Rioja
Alejandro Rioja
6 Min. Lesezeit
TL;DR

Zuverlässige Multi-Agenten-Systeme entstehen nicht durch clevere Prompts — sie entstehen durch die langweilige Disziplin verteilter Systeme: dauerhafte Queues zwischen Agenten, State außerhalb des Modells und idempotente Übergaben, die Wiederholungen überstehen. Das Modell ist der Worker; die Queue ist das Rückgrat.

Kostenloser Newsletter

Jeden Mittwoch. 28.400+ Experten. Kein Füllstoff.

Inhaltsverzeichnis

Aktualisiert Juni 2026.

TL;DR: Zuverlässige Multi-Agenten-Systeme gewinnt man nicht mit cleveren Prompts — man gewinnt sie mit der langweiligen Disziplin verteilter Systeme. Setze eine dauerhafte Queue zwischen die Agenten, halte den State außerhalb des Modells und mache jede Übergabe idempotent, damit eine Wiederholung nicht doppelt handeln kann. Das Modell ist der Worker; die Queue ist das Rückgrat. Bekommst du diese drei richtig hin, hört Orchestrierung auf, beängstigend zu sein.

Operator-Sicht: Die meisten meiner über 100 Agenten sind einstufig. Die, die es nicht sind — die Pipelines, die klassifizieren, dann anreichern, dann handeln — wurden erst zuverlässig, als ich aufhörte, in „Prompt-Kette” zu denken, und anfing, in „Job-Queue mit LLM-Workern” zu denken. Das ist Architektur, nicht Prompt-Engineering.

„Multi-Agent” klingt so, als würden die Agenten miteinander reden. In der Praxis ist die zuverlässige Version das Gegenteil: Agenten kommunizieren überhaupt nicht direkt. Sie legen Nachrichten auf eine Queue und nehmen Arbeit aus einer Queue, und die Orchestrierung lebt in der Verrohrung zwischen ihnen. Hier sind die Muster, die in der Produktion standhalten.

Muster 1: Setze eine dauerhafte Queue zwischen jeden Agenten

Der erste Instinkt ist, Agent B direkt aus Agent A heraus aufzurufen. Tu das nicht. Direkte Aufrufe koppeln die beiden: Ist B langsam, blockiert A; schlägt B fehl, ist A’s Arbeit verloren; musst du B skalieren, kannst du das nicht, ohne A anzufassen.

Stattdessen beendet A seine Arbeit und reiht eine Nachricht für B ein. B ist ein separater Worker, der die Queue in seinem eigenen Tempo leert.

typescript
// Agent A ist fertig und übergibt via Queue — kein direkter Aufruf von B
await env.ENRICH_QUEUE.send({
  traceId,
  type: "enrich",
  payload: classifierResult,
});
// A's Job ist erledigt. B wird das unabhängig aufgreifen.

Auf Cloudflare nutze ich Workers Queues genau dafür — dieselben Primitiven hinter dem Agenten-Stack, den ich verwende. Die Queue gibt dir vier Dinge gratis: Buffering (B kann ausfallen, ohne Arbeit zu verlieren), Wiederholungen (fehlgeschlagene Nachrichten werden erneut zugestellt), Gegendruck (eine Spitze wird eingereiht, statt abzustürzen) und Entkopplung (skaliere oder redeploye B, ohne A anzufassen). Jedes davon ist etwas, das du sonst von Hand bauen und falsch machen müsstest.

Muster 2: Halte den State immer außerhalb des Modells

Der häufigste Multi-Agenten-Bug ist die Annahme, dass das Modell sich zwischen Schritten an irgendetwas erinnert. Das tut es nicht. Jeder Modellaufruf ist zustandslos; das einzige Gedächtnis ist das, was du in den Prompt schreibst. Also muss die Quelle der Wahrheit für „wo steht dieser Job in der Pipeline” in einer Datenbank leben, nicht in einer Konversation.

Ich halte einen einzigen Job-Datensatz, den jeder Agent liest und aktualisiert:

typescript
interface JobState {
  traceId: string;
  stage: "classified" | "enriched" | "acted" | "done" | "failed";
  data: Record<string, unknown>;
  attempts: number;
  updatedAt: number;
}

Jeder Agent durchläuft dieselbe Schleife: den Job-State lesen, seine Arbeit tun, den neuen State schreiben, die nächste Stufe einreihen. Das Modell hält niemals den State — es erhält den relevanten Ausschnitt als Eingabe und gibt ein Ergebnis zurück. Genau das macht das System neustartbar: Stirbt ein Worker mitten im Job, sagt der State-Datensatz immer noch genau, wo die Dinge standen, und die erneut zugestellte Queue-Nachricht setzt von dort fort. Es macht auch das Debugging handhabbar, denn die State-Tabelle ist ein abfragbarer Datensatz der Reise jedes Jobs — dieselbe Instrumentierungs-Denkweise wie in wie ich messe, ob ein Agent funktioniert.

Muster 3: Mache jede Übergabe idempotent

Queues garantieren Mindestens-einmal-Zustellung, nicht Genau-einmal. Das heißt, eine Nachricht kann zweimal zugestellt werden — Netzwerkausfälle, Wiederholungen, Redeployments. Ist die Aktion deines Agenten nicht idempotent, handelt eine Doppelzustellung doppelt: zwei Bestätigungsmails, zwei Buchungen, zwei Abbuchungen. Das ist die übelste Klasse von Orchestrierungs-Bug, und es ist die, die Teams in der Produktion entdecken.

Die Lösung ist, Aktionen mit einem Schlüssel idempotent zu machen:

typescript
async function handleEnrich(msg: QueueMessage, env: Env) {
  const job = await getJob(env, msg.traceId);
  if (job.stage !== "classified") {
    // Bereits über diese Stufe hinaus verarbeitet — eine Doppelzustellung. Überspringen.
    return;
  }
  const result = await enrich(job.data);
  await advanceJob(env, msg.traceId, "enriched", result);
  await env.ACT_QUEUE.send({ traceId: msg.traceId, type: "act" });
}

Die Stufenprüfung macht die Operation sicher zweimal ausführbar: Die zweite Zustellung sieht, dass der Job bereits vorangeschritten ist, und tut nichts. Für externe Nebeneffekte (eine Mail senden, eine Karte belasten) übergib einen Idempotenz-Schlüssel an die nachgelagerte API, damit sie ebenfalls dedupliziert. Geh davon aus, dass jede Nachricht zweimal zugestellt wird, und entwirf so, dass das harmlos ist — denn irgendwann wird es passieren.

Muster 4: Orchestrator vs. Choreografie — bewusst wählen

Es gibt zwei Wege, den Fluss zu verdrahten, und die richtige Wahl hängt von der Komplexität ab.

Choreografie (mein Standard): Jeder Agent kennt nur den nächsten Schritt und reiht ihn ein. Der Fluss ergibt sich aus der Kette. Einfach, dezentral, leicht erweiterbar — füge eine Stufe hinzu, indem du eine Queue einfügst. Der Nachteil ist, dass kein einzelner Ort den gesamten Fluss beschreibt, sodass eine komplexe Pipeline schwer nachvollziehbar werden kann.

Orchestrierung (ein zentraler Koordinator): Ein Orchestrator besitzt den Fluss, ruft jeden Agenten der Reihe nach auf und entscheidet anhand der Ergebnisse, was als Nächstes kommt. Der gesamte Fluss lebt an einem lesbaren Ort, und die Verzweigungslogik ist explizit. Der Preis ist eine zentrale Komponente, die selbst dauerhaft sein muss — ist der eigene State des Orchestrators nicht ausgelagert (Muster 2), wird er zum Single Point of Failure.

Meine Regel: Choreografie, bis die Verzweigung komplex wird, dann ein dauerhafter Orchestrator. Eine lineare dreistufige Pipeline ist Choreografie. Ein Fluss mit bedingtem Routing, parallelem Fan-out und Joins will einen Orchestrator, dessen State in der Datenbank lebt, damit er nach einem Absturz fortsetzen kann.

Muster 5: Fan-out, Fan-in ohne Teile zu verlieren

Wenn ein Job N parallele Teilaufgaben erzeugt (50 Datensätze anreichern, 20 Dokumente zusammenfassen) und du auf alle warten musst, bevor es weitergeht, brauchst du einen Join. Der Trick ist ein Zähler im Job-State:

  1. Der Parent reiht N Child-Nachrichten ein und schreibt expected: N, completed: 0 in den Job-Datensatz.
  2. Jedes Child tut seine Arbeit und inkrementiert atomar completed.
  3. Das Child, das completed auf expected hochbringt, reiht die nächste Stufe ein.

Das atomare Inkrement ist tragend — ohne es können zwei gleichzeitig fertig werdende Children beide glauben, sie seien nicht das letzte, und der Join feuert nie. Verwende einen Zähler, den der Datastore atomar inkrementieren kann, oder eine Transaktion. Dieses Muster lässt dich die teure Mitte einer Pipeline parallelisieren (oft Haiku-günstige Arbeit — siehe die Haiku-vs-Sonnet-Kostenrechnung) und am Ende einen sauberen Join behalten.

Was ich auslassen würde

Du brauchst kein schwergewichtiges Agenten-Framework, um irgendetwas davon zu tun. Queues, eine State-Tabelle und Idempotenz-Schlüssel sind Primitiven, die jede Plattform bereits hat. Ich habe Teams gesehen, die zu aufwendigen Multi-Agenten-Frameworks griffen, um Features zu bekommen, die eine Queue ihnen gratis gibt, und sich eine Blackbox einhandelten, die schwerer zu debuggen war als die Verrohrung, die sie ersetzte. Beginne mit den langweiligen Primitiven. Greife erst zu einem Framework, wenn du einen konkreten Schmerz gespürt hast, den es löst.

Die Zusammenfassung: Agenten sind zustandslose Worker, Queues sind das dauerhafte Rückgrat, der State lebt in einer Datenbank und jede Übergabe ist sicher zweimal ausführbar. Das ist das ganze Spiel.

FAQ

Sollten sich Agenten direkt aufrufen oder über eine Queue gehen?

Über eine Queue. Direkte Aufrufe koppeln Agenten — der Ausfall oder die Langsamkeit des einen pflanzt sich auf den anderen fort, und du kannst nicht unabhängig skalieren oder redeployen. Eine dauerhafte Queue gibt dir Buffering, Wiederholungen, Gegendruck und Entkopplung gratis.

Wo sollte Multi-Agenten-State leben?

Außerhalb des Modells, in einer Datenbank, als Job-Datensatz, den jeder Agent liest und aktualisiert. Modellaufrufe sind zustandslos, also muss die Quelle der Wahrheit für den Pipeline-Fortschritt extern sein — genau das macht das System nach einem Absturz neustartbar.

Wie verhindere ich, dass ein Agent zweimal auf denselben Job handelt?

Mache Übergaben idempotent. Prüfe die Stufe des Jobs vor dem Handeln und tue nichts, wenn er bereits vorangeschritten ist, und übergib Idempotenz-Schlüssel an externe APIs. Queues stellen mindestens einmal zu, also geh davon aus, dass jede Nachricht zweimal ankommen kann, und entwirf so, dass Duplikate harmlos sind.

Brauche ich ein Multi-Agenten-Framework?

Meistens nein. Dauerhafte Queues, eine State-Tabelle und Idempotenz-Schlüssel decken die meisten Produktionsbedürfnisse mit Primitiven ab, die deine Plattform bereits bietet. Übernimm ein Framework nur, wenn du auf ein konkretes Problem triffst, das es einzigartig löst, nicht standardmäßig.

Weiterlesen

Holen Sie sich das KI-Playbook in Ihr Postfach

Jeden Mittwoch. 28.400+ Experten. Kein Füllstoff.

↵ alle Ergebnisse anzeigen esc esc zum Schließen