Im vorherigen Artikel ging es darum, einen einzelnen Agenten zuverlässig zu machen: Ausführungsschleife, Tool-Routing, Kontextverdichtung, absturzsichere Sitzungen. Diese Ebene beantwortet die Frage "Wie bewältigt ein einzelner Agent eine Aufgabe, ohne auszufallen?" Dieser Artikel behandelt die Ebene darüber: Was passiert, wenn ein Agent nicht genügt und eine Aufgabe auf ein Team verteilt werden muss?
Das ist meist mit Multi-Agenten-Orchestrierung gemeint: Ein leitender Agent, der die Konversation verantwortet, zerlegt eine Anfrage, übergibt die Teile an spezialisierte Subagenten, wartet vor dem nächsten Schritt auf die nötigen Ergebnisse, reicht Resultate weiter und hält alles auf Kurs, wenn ein Schritt scheitert. Orkas führt dies vollständig auf dem Rechner des Nutzers aus. Im Folgenden zeigen wir den Aufbau dieser Orchestrierungsebene — der Code ist bereinigt und verallgemeinert, die Struktur aber real.
Leitender Agent und Subagenten
Das Denkmodell ist ein kleines Team mit klarer Zuständigkeitshierarchie. Ein leitender Agent — bei uns Commander — verantwortet die Konversation und den Gesamtkontext. Er erledigt nicht alles selbst; seine Aufgabe ist zu entscheiden, was in welcher Reihenfolge von wem erledigt werden muss. Die Subagenten sind Spezialisten, jeweils mit eigenem Systemprompt, eigenen erlaubten Tools und eigenen Skills. Ein Subagent beherrscht einen Teilbereich und wird aufgerufen, wenn dieser benötigt wird.
Zwei Dinge machen dies zu mehr als einem Schlagwort. Erstens sind Subagenten und Skills eigenständige Einheiten, keine Prompt-Tricks: Ein Subagent ist ein echter, separat konfigurierter Agent, und seine Beauftragung ist eine echte Übergabe mit eigenem Kontext. Zweitens wird die Koordination nicht den guten Absichten des Modells überlassen. Sie wird durch ein explizites Artefakt gesteuert, dessen Korrektheit das System sicherstellt, nicht das Modell. Dieses Artefakt ist der Plan.
Ein Plan ist ein Graph, kein Skript
Entscheidet der leitende Agent, dass eine Anfrage mehrere Schritte benötigt, schreibt er einen Plan. Der Plan ist weder freier Fließtext noch eine lineare Checkliste, sondern ein kleiner Abhängigkeitsgraph (DAG). Jeder Knoten ist ein Schritt, der alles enthält, was der Orchestrator zur Beauftragung benötigt:
interface PlanStep {
index: number; // 1-based, stable, never renumbered
title: string; // human-readable, shown in the UI
assignee: string; // who runs it: "user" | "commander" | a sub-agent
input?: string; // the dispatch payload — a template (see below)
wait_for?: number[]; // upstream step indexes; defaults to [index - 1]
on_failure?: "abort_plan" | "continue" | "ask_commander";
// --- runtime state, owned by the orchestrator, NOT written by the model ---
status: "pending" | "in_progress" | "done" | "failed" | "skipped" | "blocked";
output_summary?: string; // short summary of what the step produced
output_files?: string[]; // files the step produced
failure_reason?: string;
}Das Feld wait_for macht aus einer Liste einen Graphen. Standardmäßig wartet ein Schritt auf seinen Vorgänger — eine einfache Kette. Er kann aber auch Abhängigkeiten von mehreren früheren Schritten angeben: "Zusammenfassen" könnte sowohl auf "Markt recherchieren" als auch auf "Wettbewerber untersuchen" warten. Das ist eine Raute, keine Linie, und der Orchestrator behandelt sie entsprechend.
Wer die Wahrheit verwaltet: der Orchestrator, nicht das Modell
Betrachten Sie noch einmal die Aufteilung dieser Struktur. Das Modell trägt beim ersten Schreiben des Plans die Absicht ein — Titel, Zuständige, Eingaben und Abhängigkeiten. Alles zum Ausführungszustand — status, output_summary, failure_reason — gehört dagegen ausschließlich dem Orchestrator. Das Modell schlägt den Plan einmal vor; es darf seine eigenen Schritte nie als "erledigt" markieren.
Diese Trennung ist beabsichtigt und die wichtigste Entscheidung des gesamten Entwurfs. Ein Sprachmodell kann durchaus fröhlich "Schritt 3 abgeschlossen" verkünden, obwohl Schritt 3 fehlgeschlagen ist, oder in einer langen Konversation den Überblick über offene Schritte verlieren. Läge der Zustand im Kopf des Modells, würde der Plan von der Realität abdriften. Als strukturiertes Artefakt, das nur der Executor und nur bei tatsächlichem Abschluss eines Schritts schreibt, bleibt der Plan ein genaues Abbild des Geschehenen. Das Modell bestimmt die Form der Arbeit; die Laufzeit bestimmt die Wahrheit über ihren Fortschritt.
Bereite Schritte beauftragen
Sobald ein Plan vorliegt, treibt ihn eine kleine Engine — der Executor — voran. Der Kernvorgang lautet: "Bereite Schritte finden und beauftragen." Ein Schritt ist bereit, wenn er noch pending ist und alle Schritte, auf die er wartet, einen endgültigen, hinreichend erfolgreichen Zustand erreicht haben:
function findReadySteps(plan): PlanStep[] {
return plan.steps.filter((s) => {
if (s.status !== "pending") return false;
const deps = s.wait_for ?? (s.index > 1 ? [s.index - 1] : []);
return deps.every((d) => isTerminal(plan.step(d).status)); // done or skipped
});
}Dies läuft nicht zeitgesteuert, sondern ereignisgesteuert. Immer wenn ein Schritt endet — ein Subagent zurückkehrt oder der Commander einen Zusammenfassungsdurchgang abschließt —, gleicht der Executor ab: Er erfasst das Ergebnis des gerade abgeschlossenen Schritts, sucht erneut nach dadurch bereit gewordenen Schritten und beauftragt sie. Orchestrierung ist diese wiederkehrende Abgleichschleife, bis keine Schritte mehr offen sind.
Die Beauftragung läuft über denselben Chat, keinen Nebenkanal
Eine Entscheidung hält das System nachvollziehbar: Ein Schritt wird nicht über einen versteckten RPC-Kanal beauftragt. Stattdessen erscheint eine Nachricht in derselben Gruppenkonversation, die der Nutzer verfolgt, vom leitenden Agenten mit @-Erwähnung des Subagenten. Für den Subagenten ist die Beauftragung nicht von einer Ansprache im Chat zu unterscheiden — er führt einfach seinen normalen Durchgang aus. Es gibt keinen zweiten Ausführungspfad, der mit dem ersten synchron gehalten werden müsste.
Es gibt drei Arten von Zuständigen, deren Beauftragung sich jeweils etwas unterscheidet:
- Ein Subagent — der Normalfall. Der Executor löst den Agentennamen zu seiner ID auf und sendet vom Commander aus
@<agent> <rendered input>. Der Subagent greift dies auf und führt einen vollständigen Agentendurchgang aus. - Der Nutzer — benötigt ein Schritt tatsächlich menschliche Eingaben, wird er zu einem Formular und pausiert den Plan (mehr dazu unten). Nachgelagerte Arbeit läuft erst weiter, wenn der Nutzer antwortet.
- Der Commander selbst — für Zusammenfassungs- oder Entscheidungsschritte ("Alles oben lesen und die Zusammenfassung schreiben"). Dies ist eine interne Aktivierung ohne redundante sichtbare Nachricht; der leitende Agent führt einfach einen Durchgang mit dem gesammelten Kontext aus.
Kontext von einem Schritt zum nächsten weitergeben
Ein Team ist nur nützlich, wenn Arbeit zwischen seinen Mitgliedern fließt. Der Mechanismus ist die input-Vorlage. Schreibt der leitende Agent einen Schritt, ist dessen Eingabe kein unveränderlicher String: Sie kann frühere Ergebnisse referenzieren, und der Executor löst diese Verweise bei der Beauftragung auf:
// step 3.input, as written by the lead agent
"Using the findings below, draft the launch note.\n\n{{step_1.output_summary}}"
// what the sub-agent actually receives at dispatch time
"Using the findings below, draft the launch note.\n\n- Market is growing ~20% YoY; two incumbents…"Beachten Sie, was weitergegeben wird: output_summary, eine kurze Zusammenfassung jedes abgeschlossenen Schritts, nicht dessen gesamtes Protokoll. Das ist eine Budgetentscheidung und folgt demselben Gedanken wie die Kontextverdichtung des Harness. Würde jeder nachgelagerte Schritt den vollständigen Token-für-Token-Verlauf aller Vorgänger erben, würden Kontext und Kosten nach wenigen Übergaben explodieren. Zusammenfassungen halten Übergaben günstig und jeden Subagenten auf die tatsächlich benötigten Vorgängerinformationen fokussiert, statt ihn deren Entstehung durchgehen zu lassen. Auch die ursprüngliche Nutzernachricht und Anhänge werden mitgeführt, sodass ein Schritt nach drei Übergaben die ursprüngliche Anfrage noch kennt.
Standardmäßig seriell — und warum
Man könnte erwarten, dass der Orchestrator mehrere gleichzeitig bereit gewordene Schritte — etwa zwei Zweige einer Raute — parallel startet. Das könnte er; heute beauftragt er stattdessen jeweils einen bereiten Schritt, den mit dem frühesten Index, und lässt die übrigen bis zum nächsten Abgleich warten. Ein ganzes Team strikt nacheinander arbeiten zu lassen ist eine bewusste, vorsichtige Entscheidung. Die Gründe dafür sollten offen benannt werden.
Der Grund ist Korrektheit bei Nebenläufigkeit. Stellen Sie sich vor, zwei Subagenten enden fast gleichzeitig. Beide Abschlüsse lösen einen Abgleich aus; beide Abgleiche lesen den Plan; beide sehen denselben nachgelagerten Schritt noch als pending — und beide beauftragen ihn. Nun läuft derselbe Schritt zweimal. Um das auszuschließen, wird jeder Zyklus aus Lesen, Ändern und Beauftragen innerhalb einer Konversation durch eine konversationsbezogene Sperre serialisiert:
// all state-changing paths for one conversation run under one mutex
planLock(uid, cid).runExclusive(async () => {
const plan = await readPlan(uid, cid);
applyOutcomeOfFinishedStep(plan); // mark done / failed / skipped
await dispatchReady(plan); // dispatch the next ready step
});Die Sperre garantiert, dass "Abschluss erfassen" und "Nächsten Schritt bestimmen" als unteilbare Einheit ablaufen. So kann ein nachgelagerter Schritt nie zweimal beauftragt werden. Mit dieser Sperre ist das einzelne Beauftragen die einfachste offensichtlich korrekte Lösung. Echte parallele Verteilung ist eine machbare Erweiterung auf dieser Grundlage. Die Grundlage bleibt jedoch ein serialisierter Executor ohne Race Conditions. Genau darum geht es bei dieser Prioritätensetzung: erst korrekt, dann schnell.
Wenn ein Schritt schiefgeht
Bei der Ausführung auf einem echten Rechner mit externen Modell-APIs sind Fehler normal. Der Orchestrator unterscheidet deshalb einige Fälle, statt jeden Fehler gleich zu behandeln.
Zuerst prüft er, ob der Fehler nur vorübergehend war — eine unterbrochene Verbindung, ein Ratenlimit, eine kurze Störung. Falls ja und das kleine Wiederholungsbudget noch nicht ausgeschöpft ist, wird der Schritt still auf pending zurückgesetzt, damit der nächste Abgleich ihn erneut beauftragt. Dies liegt oberhalb der Wiederholungen innerhalb des Harness: Der Plan beauftragt erst erneut, wenn die Versuche des Agenten ausgeschöpft sind. Eine feste Obergrenze verhindert Endlosschleifen bei tatsächlich defekten Schritten.
Ist der Fehler dauerhaft, bestimmt die deklarierte on_failure-Richtlinie des Schritts das weitere Vorgehen des Teams:
abort_plan— der Schritt war so wichtig, dass nachgelagerte Arbeit ohne ihn keinen Sinn ergibt. Er wird als fehlgeschlagen markiert; anschließend werden alle noch ausstehenden Schritte alsskippedmarkiert. Der Plan stoppt sauber, statt auf fehlender Grundlage weiterzubauen.continue— der Schritt war optional. Er wird alsskippedmarkiert, und nachgelagerte Schritte laufen weiter, als hätte er einfach nichts geliefert.ask_commander(Standard) — weder blind abbrechen noch blind weitermachen. Der Schritt wird als fehlgeschlagen markiert und der leitende Agent aktiviert, um den Vorfall zu prüfen und zu entscheiden: anders wiederholen, umgehen oder stoppen und den Nutzer fragen.
Ein sechster Status verdient Erwähnung: blocked. Ein Subagent kann während eines Schritts erkennen, dass er etwas benötigt, das nur der Nutzer liefern kann, und ein Formular oder eine Frage anzeigen. Der Schritt schlägt nicht fehl, sondern wechselt zu blocked, und der gesamte Plan pausiert. Sobald der Nutzer antwortet, gleicht der Executor ab, und das Team setzt genau dort fort, wo es aufgehört hat. Ein blockierter Plan ist pausiert, nicht defekt.
Jeder Schritt ist ein vollständiger Agentenlauf
Hier schließt sich der Kreis zum vorherigen Artikel. Beauftragt der Orchestrator einen Subagenten mit einem Schritt, führt dieser keine abgespeckte Routine aus, sondern die vollständige Harness-Schleife: eine eigene Streaming-Ausführungsschleife, eigene Toolaufrufe, ein eigenes Kontextfenster mit Verdichtung und eine eigene absturzsichere Sitzung. Die Orchestrierung liegt sauber oberhalb der Einzelagentenlaufzeit und greift nie in sie hinein. Der leitende Agent bestimmt die Form der Arbeit und die Reihenfolge der Übergaben; jeder Subagent ist nach Übergabe seines Teilbereichs ein vollwertiger eigenständiger Agent.
Diese Schichtung verbindet die beiden Artikel. Der Harness macht einen Agenten für eine Aufgabe verlässlich. Der Orchestrator verbindet mehrere verlässliche Agenten zu einem Team für eine Aufgabe, die für einen allein zu groß oder zu vielfältig ist.
Einige entscheidende Entwurfsentscheidungen
Der Orchestrator verwaltet den Ausführungszustand, das Modell die Absicht. Das Modell schlägt den Plan vor; nur die Laufzeit markiert Schritte als erledigt, fehlgeschlagen oder übersprungen — und nur als Reaktion auf tatsächliche Ereignisse. Diese Grenze macht den Plan zu einem ehrlichen Abbild der Realität statt zu einer optimistischen Vermutung des Modells.
Beauftragung über dieselbe Konversation, nicht über einen Nebenkanal. Ein beauftragter Schritt ist nur eine Nachricht vom leitenden Agenten an einen Subagenten. Ein Ausführungspfad, nichts Verstecktes, das auseinanderlaufen könnte. Der Nutzer sieht das Team in demselben Thread arbeiten, den er ohnehin liest.
Zusammenfassungen statt vollständiger Protokolle zwischen Schritten. Jede Übergabe enthält eine kurze Zusammenfassung der vorgelagerten Ergebnisse. Das hält Kontextbudgets über lange Ketten beherrschbar und jeden Subagenten auf das Benötigte fokussiert, nicht auf den Weg seines Vorgängers dorthin.
Korrektheit vor Parallelität. Eine konversationsbezogene Sperre serialisiert jeden Zyklus aus Lesen, Ändern und Beauftragen; Schritte werden einzeln beauftragt. Die strikt serielle Variante ist offensichtlich frei von Wettläufen mit doppelter Beauftragung. Parallele Verteilung ist eine Optimierung auf einer bereits korrekten Grundlage.
Abschluss
Im Kern der Orkas-Orchestrierung steckt kein exotischer Algorithmus. Ihr Wert liegt in einigen konsequent eingehaltenen Grenzen: ein Plan als Abhängigkeitsgraph statt Skript; Ausführungszustand unter Kontrolle der Laufzeit statt des Modells; Beauftragung über denselben Chat, den der Nutzer verfolgt; Kontextweitergabe als Zusammenfassungen; und ein Executor, dem Korrektheit vor Nebenläufigkeit geht. Jede Entscheidung ist für sich einfach. Zusammen machen sie aus einem zuverlässigen Agenten ein Team, das Arbeit teilt, Ergebnisse weiterreicht und sich erholt, wenn ein Teil scheitert.
Für die darunterliegende Ebene lesen Sie, wie ein einzelner Agent für zuverlässige Ausführung entwickelt wird. Für die Ebene, die Agenten durch Nutzung verbessert, lesen Sie, wie Orkas-Agenten aus ihrer eigenen Arbeit lernen. Möchten Sie diese Ebene lieber führen als entwickeln, liefert Orkas sie als quelloffene KI-Agenten-Orchestrierung, die auf Ihrem eigenen Rechner läuft.