Design wechseln

Multi-Agent-Zusammenarbeit in der Praxis: Leitfaden zu 4 Architekturmustern

Laut Anthropic-Forschung liefern Multi-Agent-Systeme 90,2 % bessere Leistung als Single-Agent-Lösungen. Ein einzelner Agent hat drei kritische Schwachstellen: Kontextlimits (ab ca. 200k Token beginnt die Ausgabe zu driften), verstreute Fähigkeiten (zu viele Skills machen ihn zum „Alleskönner“) und schwieriges Debugging (wenn ein Agent ausfällt, ist die Fehlerquelle kaum zu finden). Multi-Agent-Systeme sind die Microservices-Architektur der KI-Welt: Jeder Agent erledigt genau eine Aufgabe und arbeitet über klare Nachrichtenübergabe zusammen.

Vier Kern-Architekturmustern decken unterschiedliche Szenarien ab: Subagents (zentrale Orchestrierung) für parallele, unabhängige Domänen, Skills (On-Demand-Laden) für einphasige Mehrstufen-Workflows, Handoffs (zustandsgetrieben) für mehrstufige Dialoge und Router (parallele Verteilung) für Abfragen über mehrere Datenquellen. Dieser Artikel führt Sie von der Auswahllogik über Production-Fallstricke bis zu LangGraph-Code – damit Sie entscheiden können, ob Subagents oder Skills passen und wie Sie den Zustand zwischen Agents verwalten.

Warum Multi-Agent-Systeme?

Ehrlich gesagt ist das größte Problem eines Single-Agent nicht „ob er funktioniert“, sondern „wie lange er zuverlässig bleibt“.

Kennen Sie das? Ein Agent schreibt anfangs sauberen Code – und plötzlich liefert er wirres Zeug. Sie fragen nach A, er redet über B, dann C und am Ende noch Z. Das ist keine „Dummheit“ des Agents, sondern ein überfülltes Kontextfenster.

Ein Single-Agent hat drei kritische Schwachstellen.

Kontextlimit. So leistungsfähig ein Agent auch ist – 200k Token Kontext reichen irgendwann. Lassen Sie ihn gleichzeitig Code reviewen, Sicherheit prüfen und Performance optimieren, behält er bestenfalls die Hälfte im Kopf. Ich habe einen Agent erlebt, der in Runde 20 noch über Python sprach und in Runde 21 JavaScript ausgab – er hatte vergessen, wofür er da war.

Verstreute Fähigkeiten. Je mehr Skills Sie einem Agent geben, desto mehr wird er zum „Alleskönner“ – ein bisschen von allem, nichts richtig gut. Soll er Code reviewen, schreibt er vielleicht Unit-Tests; soll er Dokumentation schreiben, refaktoriert er nebenbei den Code. Orientierung? Fehlanzeige.

Schwieriges Debugging. Fällt ein Agent aus, wissen Sie nicht, wo es hakt. Prompt zu lang? Tool-Aufruf fehlgeschlagen? Kontext kontaminiert? Die Fehlersuche ist wie die Nadel im Heuhaufen.

Multi-Agent-Systeme sind im Grunde die Microservices-Architektur der KI: Jeder Agent macht eine Sache und macht sie gut. Sie kooperieren über klare Nachrichten – statt alles in einen „Super-Agent“ zu stopfen.

Google, LangChain und Anthropic setzen auf diesen Ansatz. Laut O’Reilly-Bericht stieg die Zahl Agent-bezogener Papers 2025 von 820 Anfang des Jahres auf über 2.500 – mehr als verdreifacht. Warum? Weil klar wurde: Die Ära „ein Agent regiert alles“ ist vorbei.

Vier Kern-Architekturmustern

LangChain und Google haben mehrere etablierte Multi-Agent-Architekturen zusammengefasst. In eigenen Projekten habe ich gelernt: Jedes Muster hat seinen Einsatzbereich – falsch gewählt heißt Kanonen auf Spatzen oder Essen mit Stäbchen aus der Suppe.

Subagents (Unter-Agenten) – zentrale Orchestrierung

Das intuitivste Muster: Ein „Haupt-Agent“ als Kommandeur mit mehreren „Unter-Agenten“. Unter-Agenten sind Tools des Haupt-Agenten; er entscheidet, wann wen er aufruft.

Nutzeranfrage → Haupt-Agent (Koordinator) → Verteilung an Unter-Agent A/B/C → Ergebnisse zusammenführen → Antwort

Wann einsetzen? Wenn die Aufgabe mehrere unabhängige Domänen betrifft. Beispiel Kundenservice: ein Unter-Agent für Bestellabfragen, einer für Rückerstattungen, einer für Beschwerden – jede Domäne mit eigener Wissensbasis und Tools; der Haupt-Agent routet nur.

Code-Beispiel (LangGraph):

from langgraph.prebuilt import create_react_agent

# Unter-Agenten definieren
order_agent = create_react_agent(
    model="claude-3-5-sonnet-20241022",
    tools=[query_order, update_order],
    prompt="Du bist Bestellexperte und bearbeitest nur Bestellfragen."
)

refund_agent = create_react_agent(
    model="claude-3-5-sonnet-20241022",
    tools=[check_refund_policy, process_refund],
    prompt="Du bist Rückerstattungsexperte und bearbeitest nur Rückerstattungen."
)

# Haupt-Agent hält Unter-Agenten als Tools
main_agent = create_react_agent(
    model="claude-3-5-sonnet-20241022",
    tools=[order_agent, refund_agent],  # Unter-Agenten als Tools
    prompt="Du bist Service-Leitung und leitest Anfragen an passende Experten weiter."
)

Vorteile: Saubere Kontextisolation – jeder Unter-Agent sieht nur Sein. Hohe Effizienz bei paralleler Ausführung.

Nachteile: Jeder Unter-Agent ist ein eigener LLM-Aufruf – hoher Token-Verbrauch. Geteilter Zustand zwischen Unter-Agenten erfordert Umwege.

Skills – On-Demand-Laden

Ein Agent, mehrere „Persönlichkeiten“. Skills sind dynamisch geladene Prompt-Vorlagen. Der Agent wechselt je nach Aufgabe die Rolle – bleibt aber derselbe Agent.

Nutzeranfrage → ein Agent → Skill „Code-Review“ laden → ausführen → Skill „Dokumentation“ laden → ausführen

Wann einsetzen? Einphasige Verarbeitung mit unterschiedlichem Fachwissen pro Phase. Beispiel Coding-Assistent: „Entwickler“-Modus beim Schreiben, „Technical Writer“-Modus bei der Dokumentation.

Code-Beispiel:

# Skills-Verzeichnisstruktur
skills/
├── code_review.md      # Prompt für Code-Review
├── doc_writer.md       # Prompt für Dokumentation
└── security_audit.md   # Prompt für Security-Audit

# Skill dynamisch laden
def load_skill(skill_name: str) -> str:
    with open(f"skills/{skill_name}.md") as f:
        return f.read()

# Verwendung
agent = create_react_agent(
    model="claude-3-5-sonnet-20241022",
    tools=[...],
    prompt=load_skill("code_review")  # zur Laufzeit wechseln
)

Vorteile: Leichtgewichtig, kein zusätzlicher Koordinations-Overhead. Geringerer Token-Verbrauch als Subagents.

Nachteile: Kontext akkumuliert. Nach zehn Skill-Wechseln liegen neun frühere Skills noch im Kontext – wird unübersichtlich.

Handoffs (Übergabe) – zustandsgetrieben

Agents übergeben Aufgaben wie beim Staffellauf. Agent A beendet seinen Teil und „wirft“ den Zustand an Agent B weiter.

Nutzeranfrage → Agent A (Infos sammeln) → Übergabe → Agent B (analysieren) → Übergabe → Agent C (Lösung)

Wann einsetzen? Mehrstufige Dialoge. Beispiel Support: Problem erfassen → diagnostizieren → Lösung vorschlagen → Abschluss bestätigen – jede Phase braucht anderes Fachwissen.

Code-Beispiel:

from langchain_core.tools import tool

# Übergabe-Tools definieren
@tool
def handoff_to_diagnosis(issue_summary: str) -> str:
    """Problem an Diagnose-Experten übergeben."""
    return f"Problem übernommen: {issue_summary}, Diagnose startet..."

@tool
def handoff_to_solution(diagnosis_result: str) -> str:
    """Diagnoseergebnis an Lösungsexperten übergeben."""
    return f"Auf Basis der Diagnose: {diagnosis_result}, Lösung wird erarbeitet..."

# Agent-Kette
triage_agent = create_react_agent(
    tools=[handoff_to_diagnosis],
    prompt="Du sortierst Anfragen ein und übergibst an Diagnose-Experten."
)

diagnosis_agent = create_react_agent(
    tools=[handoff_to_solution],
    prompt="Du analysierst Ursachen und übergibst an Lösungsexperten."
)

Vorteile: Natürlicher Dialogfluss, intuitiv wie menschliche Zusammenarbeit. Jeder Agent fokussiert die aktuelle Phase.

Nachteile: Komplexes State-Management. Agent A muss an B exakt das richtige Format liefern – sonst bricht die Kette.

Router – parallele Verteilung

Ein Router-Agent klassifiziert die Anfrage, ruft parallel mehrere Spezialisten auf und führt die Ergebnisse zusammen.

Nutzeranfrage → Router (Klassifikation) → parallel Agent A/B/C → Synthese → Antwort

Wann einsetzen? Eine Anfrage braucht mehrere Datenquellen. Beispiel Unternehmens-Wissensbasis: Router erkennt Fragetyp, parallel Abfrage interner Docs, externer APIs und Datenbank – dann Gesamtantwort.

Code-Beispiel:

from langgraph.graph import StateGraph

# Parallele Knoten
async def query_internal_docs(state):
    # Interne Dokumente abfragen
    return {"internal_results": [...]}

async def query_external_api(state):
    # Externe API abfragen
    return {"external_results": [...]}

async def query_database(state):
    # Datenbank abfragen
    return {"db_results": [...]}

async def synthesize(state):
    # Alle Ergebnisse zusammenführen
    all_results = state["internal_results"] + state["external_results"] + state["db_results"]
    return {"final_answer": summarize(all_results)}

# Parallelen Graphen bauen
graph = StateGraph(State)
graph.add_node("internal", query_internal_docs)
graph.add_node("external", query_external_api)
graph.add_node("database", query_database)
graph.add_node("synthesize", synthesize)

# Parallel ausführen
graph.add_edge("router", ["internal", "external", "database"])
graph.add_edge(["internal", "external", "database"], "synthesize")

Vorteile: Parallel – am schnellsten. Zustandslos, jede Abfrage unabhängig.

Nachteile: Kein Mehr-Runden-Dialog. Jede Anfrage ist neu – der Agent erinnert sich nicht an die vorherige Runde.

Entscheidungsrahmen für die Architekturwahl

Theorie reicht nicht – welches Muster passt? Ein einfacher Entscheidungsfluss:

Was brauchen Sie?

         ├─→ Mehrere unabhängige Domänen parallel?
         │         │
         │         └─→ Subagents (zentrale Orchestrierung)

         ├─→ Ein Agent, mehrere Skill-Phasen?
         │         │
         │         └─→ Skills (On-Demand)

         ├─→ Sequenzieller Workflow, Staffelstab?
         │         │
         │         └─→ Handoffs (zustandsgetrieben)

         └─→ Mehrere Datenquellen, Antwort synthetisieren?

                   └─→ Router (parallel)

Zur Orientierung eine Vergleichstabelle:

MusterVerteilte EntwicklungParallelisierungMehr-Runden-DialogDirekte NutzerinteraktionToken-Verbrauch
Subagentshochhochhochniedrighoch
Skillshochmittelhochhochniedrig
Handoffsneinneinhochhochmittel
Routermittelhochneinmittelhoch

So lesen Sie die Tabelle:

  • Verteilte Entwicklung: Entwickelt Ihr Team Module getrennt? Dann passen Subagents und Skills – jede Person einen Unter-Agent oder Skill.
  • Parallelisierung: Brauchen Sie Geschwindigkeit? Router und Subagents parallelisieren am besten.
  • Mehr-Runden-Dialog: Braucht der Nutzer mehrere Runden? Handoffs und Skills unterstützen Dialogfluss natürlich.
  • Direkte Nutzerinteraktion: Spricht der Nutzer direkt mit Unter-Agenten? Skills und Handoffs ja, Router nein.
  • Token-Verbrauch: Kosten sensibel? Skills spart am meisten, Router und Subagents verbrauchen am meisten.

Meine Erfahrung: Einfach anfangen. MVP mit Skills oder Handoffs – bei Engpässen auf Subagents oder Router upgraden. Nicht sofort Distributed Architecture – Over-Engineering kenne ich aus eigener Erfahrung.

Production-Implementierung

Vom Demo zur Production liegt ein Ozean dazwischen. Diese Fallstricke habe ich alle selbst erlebt.

State-Management

Geteilter Zustand zwischen Agents – klassisches Problem: Race Conditions. Zwei Agents schreiben dieselbe Variable – wer gewinnt?

LangGraphs Antwort: output_key – jeder Agent schreibt nur in seinen eigenen Schlüssel.

from langgraph.graph import StateGraph, MessagesState

class GraphState(MessagesState):
    security_result: str = ""   # exklusiv für Security-Agent
    style_result: str = ""      # exklusiv für Style-Agent
    perf_result: str = ""       # exklusiv für Performance-Agent

# Security-Agent schreibt nur security_result
async def security_agent(state: GraphState):
    result = await analyze_security(state["messages"])
    return {"security_result": result}  # nur dieser Schlüssel

# Style-Agent schreibt nur style_result
async def style_agent(state: GraphState):
    result = await analyze_style(state["messages"])
    return {"style_result": result}

Parallel oder seriell – jeder Agent bleibt in seinem Bereich, ohne sich gegenseitig zu stören.

Kontext-Kontamination: Agent B liest Ausgabe von A, braucht sie aber nicht. Mein Ansatz: Feld relevant_keys im State – jeder Agent liest nur die nötigen Schlüssel.

Performance-Optimierung

Token-Verbrauch bei Multi-Agent-Systemen ist ein Fass ohne Boden. Ein paar Spar-Tricks:

1. Subagents sparen 67 % Token gegenüber Skills (Multi-Domain-Szenarien)

67%
Token-Einsparung von Subagents gegenüber Skills in Multi-Domain-Szenarien
Source: LangChain

LangChain-Testdaten: Bei drei unabhängigen Domänen verbraucht Subagents ein Drittel der Tokens von Skills. Warum? Kontextisolation – jeder Unter-Agent sieht nur seine Domäne. Bei Skills stapeln sich alle Skill-Kontexte.

2. Stateful-Modus spart 40–50 % wiederholte Aufrufe

Bei vielen wiederholten Abfragen (dieselbe Frage zehnmal) merkt sich ein stateful Handoffs-Agent frühere Antworten. LangChain: Stateful vs. stateless – fast halb so viele LLM-Aufrufe.

3. Reflexions-Modus: Iterationen begrenzen

Reflexion – Agent prüft Output, findet Fehler, generiert neu – ist gut, kann aber endlos laufen. Ich setze max_iterations=2 oder 3, danach harter Exit.

from langgraph.checkpoint.memory import MemorySaver

# Iterations-Obergrenze
graph = create_react_agent(
    model="claude-3-5-sonnet-20241022",
    tools=[...],
    checkpointer=MemorySaver(),
    config={"configurable": {"max_iterations": 3}}  # max. 3 Reflexions-Runden
)

Typische Fallstricke

Endlosschleife: Agent ruft sich selbst auf – immer wieder. Lösung: max_iterations und klare Exit-Bedingungen.

def should_continue(state):
    if state["iteration_count"] >= 3:
        return "end"
    if "done" in state["messages"][-1].content:
        return "end"
    return "continue"

Kontext-Inflation: Agent wird „dümmer“, Ausgaben kürzer – meist zu voller Kontext. Lösung: Blackboard-Muster (gemeinsame Tafel), nur nötigen Kontext behalten, regelmäßig aufräumen.

Koordinations-Overhead: Mehr Agents, exponentiell mehr Kommunikation. Mein Test: von 3 auf 10 Agents stieg die Antwortzeit von 2 auf 15 Sekunden. Lösung: ähnliche Rollen zusammenlegen, unter 5 Agents bleiben.

Vollständiges Implementierungsbeispiel

Genug Theorie – praxisnah. Ich habe ein Code-Review-Multi-Agent-System mit Router + ParallelAgent aufgebaut.

Architektur: Router erkennt Sprache und Typ → parallel Security-Audit, Style-Check, Performance-Analyse → Synthese als Report.

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langchain_anthropic import ChatAnthropic

# Zustand definieren
class CodeReviewState(TypedDict):
    code: str
    language: str
    security_issues: list
    style_issues: list
    perf_issues: list
    final_report: str

# LLM initialisieren
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")

# Router: Sprache erkennen
async def route_code(state: CodeReviewState) -> dict:
    code = state["code"]
    # Vereinfacht – in Production LLM-Klassifikation
    if "def " in code or "import " in code:
        language = "python"
    elif "function" in code or "const " in code:
        language = "javascript"
    else:
        language = "unknown"
    return {"language": language}

# Security-Audit-Agent
async def security_audit(state: CodeReviewState) -> dict:
    code = state["code"]
    prompt = f"""Du bist Security-Audit-Experte. Prüfe den Code auf:
- SQL-Injection-Risiken
- XSS-Schwachstellen
- Leak sensibler Daten
- Unsichere Abhängigkeiten

Code:
{code}

Ausgabe als JSON-Liste: line, severity, description pro Eintrag.
"""
    response = await llm.ainvoke(prompt)
    # Ergebnis parsen...
    return {"security_issues": []}

# Style-Check-Agent
async def style_check(state: CodeReviewState) -> dict:
    code = state["code"]
    language = state["language"]
    prompt = f"""Du bist Style-Experte. Prüfe {language}-Code auf:
- Namenskonventionen
- Formatierung
- Vollständigkeit der Kommentare

Code:
{code}

Ausgabe als JSON-Liste.
"""
    response = await llm.ainvoke(prompt)
    return {"style_issues": []}

# Performance-Analyse-Agent
async def perf_analysis(state: CodeReviewState) -> dict:
    code = state["code"]
    prompt = f"""Du bist Performance-Experte. Prüfe den Code auf:
- Hohe Zeitkomplexität
- Unnötige Schleifen
- Speicherleck-Risiken

Code:
{code}

Ausgabe als JSON-Liste.
"""
    response = await llm.ainvoke(prompt)
    return {"perf_issues": []}

# Gesamtbericht
async def generate_report(state: CodeReviewState) -> dict:
    security = state.get("security_issues", [])
    style = state.get("style_issues", [])
    perf = state.get("perf_issues", [])

    total_issues = len(security) + len(style) + len(perf)

    report = f"""# Code-Review-Bericht

## Überblick
- Sprache: {state['language']}
- Gesamtprobleme: {total_issues}

## Sicherheit ({len(security)})
{format_issues(security)}

## Stil ({len(style)})
{format_issues(style)}

## Performance ({len(perf)})
{format_issues(perf)}

## Empfehlung
Priorität auf Sicherheitsprobleme...
"""
    return {"final_report": report}

# Graph bauen
graph = StateGraph(CodeReviewState)
graph.add_node("router", route_code)
graph.add_node("security", security_audit)
graph.add_node("style", style_check)
graph.add_node("perf", perf_analysis)
graph.add_node("report", generate_report)

# Ablauf: Router → drei parallele Checks → Report
graph.set_entry_point("router")
graph.add_edge("router", "security")
graph.add_edge("router", "style")
graph.add_edge("router", "perf")
graph.add_edge("security", "report")
graph.add_edge("style", "report")
graph.add_edge("perf", "report")
graph.add_edge("report", END)

# Kompilieren
app = graph.compile()

# Nutzung
async def review_code(code: str):
    result = await app.ainvoke({"code": code})
    return result["final_report"]

Bei ~100 Zeilen Code liefern drei parallele Agents Ergebnisse in 3–5 Sekunden; seriell dauert es mindestens 10 Sekunden.

Das ist die Basisversion. In Production kommen noch Cache (gleicher Code nicht doppelt prüfen), inkrementelles Review (nur geänderte Teile) und Nutzer-Feedback (False Positives markieren) – alles Erweiterungen auf dieser Architektur.

Fazit

Kurz zusammengefasst:

Grundlogik: Die Musterwahl zählt mehr als das Framework. LangGraph, AutoGen, CrewAI – gute Tools. Router für ein Handoffs-Problem? Kein Framework rettet das.

Mittlere Strategie: Einfach starten, schrittweise skalieren. MVP mit Skills oder Handoffs – Engpass? Dann Subagents oder Router. Over-Engineering ist der größte Fallstrich – ich bin reingetappt, Sie müssen nicht.

Production: State-Management, Performance, Kosten. Token-Verbrauch, Endlosschleifen, Kontext-Kontamination – diese drei Themen gelöst, läuft Ihr Multi-Agent-System stabil.

Nächster Schritt: LangGraph-Dokumentation öffnen, ein Muster wählen, in 50 Zeilen das einfachste Multi-Agent-System bauen. Nicht zu lange nachdenken – erst laufen lassen.

Multi-Agent-Zusammenarbeitssystem aufbauen

Von null ein Code-Review-Multi-Agent-System implementieren

  1. 1

    Step 1: Architekturmuster wählen

    Passendes Muster nach Aufgabencharakteristik wählen
  2. 2

    Step 2: Zustandsstruktur definieren

    Gemeinsamen Multi-Agent-Zustand mit TypedDict definieren
  3. 3

    Step 3: Agent-Knoten erstellen

    Pro Agent eine eigene Knotenfunktion anlegen
  4. 4

    Step 4: Ausführungsgraph bauen

    Ablauf mit LangGraph StateGraph modellieren
  5. 5

    Step 5: State-Management ergänzen

    output_key nutzen, um Race Conditions zu vermeiden

FAQ

Was ist der Unterschied zwischen Subagents und Skills?
Subagents sind mehrere unabhängige Agents mit getrenntem Kontext – ideal für parallele Multi-Domain-Aufgaben. Skills lädt ein Agent dynamisch verschiedene Prompts – ideal für einphasige Mehrstufen-Workflows. Subagents verbrauchen mehr Tokens, isolieren Kontext besser; Skills ist leichtgewichtig, Kontext akkumuliert.
Wann sollte ich Handoffs nutzen?
Bei klarem mehrstufigem Ablauf und unterschiedlichem Fachwissen pro Phase – z. B. Support: Problem erfassen → diagnostizieren → Lösung → Bestätigung. Jeder Agent fokussiert die aktuelle Phase – intuitiv wie menschliche Zusammenarbeit.
Wie vermeide ich Race Conditions in Multi-Agent-Systemen?
Mit output_key: Jeder Agent schreibt nur in seinen Schlüssel – Security-Agent in security_result, Style-Agent in style_result. Alternativ Blackboard mit relevant_keys, damit Agents nur benötigte Felder lesen.
Wie kontrolliere ich den Token-Verbrauch?
Drei Strategien: 1) Multi-Domain: Subagents spart ca. 67 % gegenüber Skills; 2) Wiederholte Abfragen: stateful Modus spart 40–50 % Aufrufe; 3) Reflexion auf max_iterations=2 oder 3 begrenzen. Unter 5 Agents vermeidet Koordinations-Overhead.
Für welche Szenarien eignet sich Router?
Parallele Abfrage mehrerer Datenquellen – z. B. Unternehmens-Wissensbasis: Router klassifiziert, parallel Docs, API und DB, dann Synthese. Schnellste Variante, aber ungeeignet für Mehr-Runden-Dialoge.
Wie verhindere ich Endlosschleifen?
max_iterations setzen (typisch 2–3) und klare Exit-Bedingungen – z. B. state['iteration_count'] prüfen oder Fertig-Marker in Nachrichten. LangGraph MemorySaver hilft beim Tracking des Iterationszustands.

11 Min. Lesezeit · Veröffentlicht am: 25. März 2026 · Aktualisiert am: 9. Juli 2026

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog