Aus dem Englischen übersetzt
ExperimentsJuly 18, 202615 Min.

Drei Muster für Agent-Orchestrierung, die die Produktion überstanden haben

Ein kurzer Katalog von Agent-Topologien - Router, Planner-Executor, Critic - mit Anmerkungen dazu, welche unter realer Tool-Call-Latenz und Fehlermodellen funktioniert haben.

AgentsLLMsOrchestration

By Hussain Nazary

Drei Muster für Agent-Orchestrierung, die die Produktion überstanden haben

Die Landschaft der Agent-Orchestrierung

Im vergangenen Jahr haben wir LLM-Agent-Systeme in drei Domänen eingesetzt:

1. Kundensupport-Automatisierung — 12.000 Tickets/Monat, 8 integrierte Tools

2. Business-Intelligence-Analyse — 200 Analysten, 15 Datenquellen

3. Rechtsdokumentenverarbeitung — 50k Dokumente/Monat, 6 Extraktionspipelines

Jedes Deployment lehrte uns, welche Orchestrierungsmuster in der Theorie funktionieren und welche unter Produktionsstress standhalten — Tool-Timeouts, API-Ratenlimits, mehrdeutige Abfragen und Benutzererwartungen an Antworten unter 3 Sekunden.

Dieser Artikel katalogisiert drei Muster, die überstanden haben: Router, Planner-Executor und Critic.

Muster 1: Router (Einfaches Dispatch)

Architektur

                Benutzerabfrage
                    ↓
            ┌───────────────┐
            │  Router LLM   │  "Welches Tool behandelt das?"
            └───────┬───────┘
                    │
        ┌───────────┼───────────┬───────────┐
        ↓           ↓           ↓           ↓
    [Tool A]    [Tool B]    [Tool C]    [Tool D]
    Suche       Taschenrechner Wetter    Kalender
        ↓           ↓           ↓           ↓
                Antwort (von einem Tool)

Wann verwenden

  • Mehrere spezialisierte Tools mit klaren, nicht überlappenden Domänen.
  • Einzelne Schritte (ein Tool-Aufruf → Ergebnis).
  • Latenzsensitive Anwendungen (<1s Antwortzeit).

Implementierung

router_agent.py

from typing import Dict, Callable

class RouterAgent: """Einfacher Routing-Agent — dispatch an ein Tool."""

def __init__(self, tools: Dict[str, Callable]): self.tools = tools self.tool_descriptions = self._generate_tool_docs()

def _generate_tool_docs(self) -> str: """Tool-Dokumentation für Router-Prompt generieren.""" docs = [] for name, tool in self.tools.items(): docs.append(f"- {name}: {tool.__doc__}") return "\n".join(docs)

async def route(self, query: str) -> str: """Abfrage an passendes Tool leiten.""" router_prompt = f""" Sie sind ein Tool-Router. Wählen Sie das einzelne beste Tool zur Beantwortung einer Benutzerabfrage.

Verfügbare Tools: {self.tool_descriptions}

Benutzerabfrage: {query}

Antworten Sie mit JSON: {{"tool": "tool_name", "reasoning": "warum dieses Tool"}} """

routing_decision = await llm.generate(router_prompt) tool_name = json.loads(routing_decision)['tool']

# Ausgewähltes Tool ausführen if tool_name not in self.tools: return f"Fehler: Unbekanntes Tool {tool_name}"

return await self.toolstool_name

Verwendung

tools = { "search": search_knowledge_base, "calculator": calculate_expression, "weather": get_weather_forecast, "calendar": check_calendar_availability }

agent = RouterAgent(tools) response = await agent.route("Wie wird das Wetter morgen in Paris?")

Produktionsdaten (Kundensupport)

MetrikWert
Behandelte Abfragen12.000/Monat
Korrektes Routing94%
Durchschnittliche Latenz820ms
p95-Latenz1.2s
Mehrdeutiges Routing6% (Eskalation an Mensch)

Stärken

Niedrige Latenz — Einzelner LLM-Aufruf + eine Tool-Ausführung ✅ Vorhersagbar — Lineare Ausführung, einfach zu durchdenken ✅ Debugging-fähig — Einfach zu loggen: "Abfrage → Routing-Entscheidung → Tool → Ergebnis" ✅ Kosteneffektiv — Minimale LLM-Aufrufe

Schwächen

Kein Tool-Chaining — Kann Tools nicht kombinieren ("Suche nach X, berechne dann Y") ❌ Routing-Fehler sind fatal — Falsche Tool-Auswahl = falsche Antwort ❌ Mehrdeutige Abfragen scheitern — "Termin buchen, wenn es nicht regnet" erfordert zwei Tools

Produktions-Lektionen

Lektion 1: Einen Fallback-Klassifikator aufbauen

Wenn die Routing-Konfidenz niedrig ist (<70%), an Mensch eskalieren:

routing_confidence = routing_decision['confidence']
if routing_confidence < 0.70:
    return escalate_to_human(query, reason="mehrdeutiges Routing")

Lektion 2: Routing-Entscheidungen zwischenspeichern

Häufige Abfragen ("Bestellstatus prüfen") routen jedes Mal gleich:

@cache(ttl=3600)
def route_query(query: str):
    # Routing 1 Stunde zwischenspeichern
    return router.route(query)

Lektion 3: Routing-Genauigkeit überwachen

Verfolgen Sie, welche Tools ausgewählt wurden vs. was Benutzer tatsächlich brauchten:

Routing-Entscheidungen loggen

log_routing_decision( query=query, selected_tool=tool_name, user_satisfaction=feedback # Interaktion nachverfolgen )

Wöchentliche Analyse

routing_errors = query_logs.filter(user_satisfaction < 3) print(f"Top falsch geroutete Abfragen: {routing_errors.most_common(10)}")

Ergebnis: Wir verbesserten die Routing-Genauigkeit von 87% → 94%, indem wir falsch geroutete Abfragen nachtrainierten.

Muster 2: Planner-Executor (Mehrstufiges Reasoning)

Architektur

            Benutzerabfrage: "Vergleiche Umsatz Q1 vs Q2"
                    ↓
            ┌───────────────┐
            │  Planner LLM  │  Ausführungsplan generieren
            └───────┬───────┘
                    ↓
            Plan: [Schritt 1, Schritt 2, Schritt 3]
            1. Q1-Umsatz aus DB abrufen
            2. Q2-Umsatz aus DB abrufen
            3. Differenz berechnen
                    ↓
            ┌───────────────┐
            │   Executor    │  Plan nacheinander ausführen
            └───────┬───────┘
                    ↓
        ┌───────────┼───────────┐
        ↓           ↓           ↓
    [DB abfragen] [DB abfragen] [Berechnen]
        ↓           ↓           ↓
    $120K       $145K        +$25K (+21%)
                    ↓
            Finale Antwort

Wann verwenden

  • Mehrstufige Workflows, die Tool-Zusammensetzung erfordern.
  • Dynamische Tool-Auswahl (Tool-Sequenz kann nicht im Voraus vorhergesagt werden).
  • Strukturierte Aufgaben (Datenanalyse, Berichtgenerierung).

Implementierung

planner_executor_agent.py

from typing import List, Dict import json

class PlannerExecutorAgent: """Agent, der vor der Ausführung plant."""

def __init__(self, tools: Dict[str, Callable]): self.tools = tools

async def plan(self, query: str) -> List[Dict]: """Ausführungsplan generieren.""" planner_prompt = f""" Sie sind ein Aufgabenplaner. Zerlegen Sie diese Abfrage in ausführbare Schritte mit verfügbaren Tools.

Verfügbare Tools: {self._tool_docs()}

Benutzerabfrage: {query}

Generieren Sie einen Plan als JSON-Array: [ {{"step": 1, "tool": "tool_name", "input": "...", "output_var": "var1"}}, {{"step": 2, "tool": "tool_name", "input": "verwende {{var1}}", "output_var": "var2"}}, ... ] """ plan_json = await llm.generate(planner_prompt) return json.loads(plan_json)

async def execute(self, plan: List[Dict]) -> Dict: """Plan Schritt für Schritt ausführen.""" context = {} # Zwischenergebnisse speichern

for step in plan: tool_name = step['tool'] tool_input = step['input']

# Variablen aus Kontext ersetzen for var, value in context.items(): tool_input = tool_input.replace(f"{{{var}}}", str(value))

# Tool ausführen result = await self.toolstool_name

# Ergebnis im Kontext speichern output_var = step.get('output_var', f"step_{step['step']}") context[output_var] = result

print(f"Schritt {step['step']}: {tool_name}({tool_input}) → {result}")

return context

async def run(self, query: str) -> str: """Planen und ausführen.""" plan = await self.plan(query) context = await self.execute(plan)

# Finale Antwort mit Kontext generieren final_prompt = f""" Benutzerabfrage: {query}

Ausführungsergebnisse: {json.dumps(context, indent=2)}

Geben Sie eine natürlichsprachliche Antwort an den Benutzer. """ return await llm.generate(final_prompt)

Verwendung

tools = { "sql_query": execute_sql, "calculator": calculate, "search_docs": search_documentation, "send_email": send_email }

agent = PlannerExecutorAgent(tools) response = await agent.run("Vergleiche Umsatz Q1 vs Q2 und sende Zusammenfassung per E-Mail an CFO")

Produktionsdaten (Business Intelligence)

MetrikWert
Behandelte Abfragen1.200/Monat
Erfolgreiche Abschlüsse89%
Durchschnittliche Latenz3.2s
p95-Latenz8.4s
Planfehler11% (ungültiges Tool, falsche Reihenfolge)

Stärken

Behandelt komplexe Workflows — Multi-Tool-Zusammensetzung ✅ Flexibel — Passt sich dynamisch an die Abfragenkomplexität an ✅ Transparent — Plan ist menschenlesbar, debugging-fähig ✅ Wiederherstellbar — Einzelne Schritte können bei Fehlern wiederholt werden

Schwächen

Höhere Latenz — N+1 LLM-Aufrufe (1 für Planung, N für Ausführung) ❌ Pläne können falsch sein — Ungültige Tool-Auswahl, falsche Reihenfolge, fehlende Schritte ❌ Fehlerfortpflanzung — Früher Schrittfehler bricht den gesamten Plan ❌ Kosten skalieren mit Schritten — 5-Schritt-Plan = 6 LLM-Aufrufe

Produktions-Lektionen

Lektion 1: Pläne vor der Ausführung validieren

Vertrauen Sie LLM-generierten Plänen nicht blind:

def validate_plan(plan: List[Dict]) -> bool:
    """Auf häufige Fehler prüfen."""
    for step in plan:
        # Prüfen ob Tool existiert
        if step['tool'] not in self.tools:
            raise PlanError(f"Unbekanntes Tool: {step['tool']}")

# Variablenabhängigkeiten prüfen required_vars = extract_variables(step['input']) available_vars = [s['output_var'] for s in plan[:step['step']-1]]

for var in required_vars: if var not in available_vars: raise PlanError(f"Variable {var} nicht verfügbar bei Schritt {step['step']}")

return True

Lektion 2: Schritt-Level-Wiederholungen hinzufügen

Netzwerkfehler und Ratenlimits passieren. Wiederholen Sie einzelne Schritte:

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10)) async def execute_step(tool_name: str, tool_input: str): return await self.toolstool_name

Lektion 3: Plan-Caching für ähnliche Abfragen implementieren

Abfragen wie "Vergleiche Umsatz Q1 vs Q2" haben ähnliche Pläne:

Plan-Vorlagen zwischenspeichern

plan_template = cached_plans.get(query_category) if plan_template: plan = instantiate_template(plan_template, query_params) else: plan = await self.plan(query)

Ergebnis: Reduzierte Planungslatenz um 40% für wiederkehrende Abfragemuster.

Muster 3: Critic (Iterative Verfeinerung)

Architektur

            Benutzerabfrage: "Entwerfe eine professionelle Entschuldigungs-E-Mail"
                    ↓
            ┌───────────────┐
            │ Generator LLM │  Initiale Antwort generieren
            └───────┬───────┘
                    ↓
            Entwurf v1: "Entschuldigen Sie sich für das Problem..."
                    ↓
            ┌───────────────┐
            │  Critic LLM   │  Qualität bewerten
            └───────┬───────┘
                    ↓
        [Bestanden: Score ≥ 8/10] ────→ Antwort zurückgeben
                    │
        [Nicht bestanden: Score < 8/10]
                    ↓
            Feedback: "Zu locker. Fügen Sie spezifische Details hinzu."
                    ↓
            ┌───────────────┐
            │  Generator    │  Mit Feedback neu generieren
            └───────┬───────┘
                    ↓
            Entwurf v2: "Wir entschuldigen uns aufrichtig für [spezifisches Problem]..."
                    ↓
            [Bis zu max_iterations=3 wiederholen]

Wann verwenden

  • Qualitätskritische Ausgaben (Rechtsdokumente, Kundenkommunikation).
  • Iterative Verfeinerung erforderlich.
  • Latenztoleranz (Benutzer erwarten 3-10s für komplexe Aufgaben).

Implementierung

critic_agent.py

from typing import Tuple

class CriticAgent: """Agent mit Selbstkritik-Schleife."""

def __init__(self, max_iterations: int = 3): self.max_iterations = max_iterations

async def generate(self, query: str, feedback: str = None) -> str: """Antwort generieren (mit optionalem Feedback).""" if feedback: prompt = f""" Benutzeranfrage: {query}

Der vorherige Versuch erhielt dieses Feedback: {feedback}

Generieren Sie eine verbesserte Antwort, die das Feedback berücksichtigt. """ else: prompt = f"Benutzeranfrage: {query}\n\nGenerieren Sie eine Antwort."

return await llm.generate(prompt)

async def critique(self, query: str, response: str) -> Tuple[float, str]: """Antwortqualität bewerten (Score 0-10, Feedback).""" critic_prompt = f""" Bewerten Sie diese Antwort auf Qualität, Genauigkeit und Professionalität.

Benutzeranfrage: {query} Antwort: {response}

Geben Sie an:

1. Score (0-10)

2. Spezifisches Feedback zur Verbesserung

Format: {{"score": X, "feedback": "..."}} """ critique = await llm.generate(critic_prompt) result = json.loads(critique) return result['score'], result['feedback']

async def run(self, query: str, min_score: float = 8.0) -> Dict: """Mit iterativer Verfeinerung generieren.""" history = []

for iteration in range(self.max_iterations): # Antwort generieren (mit Feedback der vorherigen Iteration) feedback = history[-1]['feedback'] if history else None response = await self.generate(query, feedback)

# Antwort kritisieren score, feedback = await self.critique(query, response)

history.append({ "iteration": iteration + 1, "response": response, "score": score, "feedback": feedback })

# Prüfen ob Qualitätsschwelle erreicht if score >= min_score: return { "response": response, "iterations": iteration + 1, "final_score": score, "history": history }

# Maximale Iterationen erreicht, besten Versuch zurückgeben best = max(history, key=lambda x: x['score']) return { "response": best['response'], "iterations": self.max_iterations, "final_score": best['score'], "history": history, "warning": "Maximale Iterationen erreicht ohne Qualitätsschwelle zu treffen" }

Verwendung

agent = CriticAgent(max_iterations=3) result = await agent.run("Entwerfe eine professionelle Entschuldigung für verspätete Lieferung") print(f"Finale Antwort (Score: {result['final_score']}):\n{result['response']}")

Produktionsdaten (Rechtsdokumentgenerierung)

MetrikWert
Generierte Dokumente800/Monat
Erfolg im ersten Versuch62% (Score ≥ 8/10)
Erfolg im zweiten Versuch89%
Erfolg im dritten Versuch96%
Durchschnittliche Latenz4.2s
p95-Latenz11.8s

Stärken

Höhere Ausgabequalität — Selbstkorrektur fängt Fehler ab ✅ Anpassungsfähig — Lernt innerhalb einer Sitzung aus eigenen Fehlern ✅ Transparent — Kritik-Feedback erklärt Qualitätsprobleme ✅ Graceful Degradation — Gibt besten Versuch zurück, wenn Schwelle nicht erreicht

Schwächen

Hohe Latenz — 2-6 LLM-Aufrufe (2x pro Iteration) ❌ Teuer — Kosten skalieren mit Iterationen ❌ Kann endlos schleifen — max_iterations muss gesetzt werden ❌ Critic kann falsch liegen — Falsch-negative (gute Antwort niedrig bewertet)

Produktions-Lektionen

Lektion 1: Aggressive max_iterations-Grenze setzen

Unsere Anfangsgrenze war 5. 12% der Abfragen erreichten diese Grenze (verschwendeten 10 LLM-Aufrufe). Reduziert auf 3:

Kostenanalyse

avg_cost_per_llm_call = $0.02 max_iterations = 5 → avg_cost = $0.20 (10 Aufrufe) max_iterations = 3 → avg_cost = $0.12 (6 Aufrufe)

40% Kostenreduktion mit minimalem Qualitätseinfluss

Lektion 2: Schnelle Modelle für Kritik verwenden

Critic braucht keine Frontier-Modell-Intelligenz. Wir verwenden GPT-4 für Generierung, GPT-3.5-turbo für Kritik:

async def critique(self, query: str, response: str):
    # Billigeres, schnelleres Modell für Kritik verwenden
    critique = await llm.generate(critic_prompt, model="gpt-3.5-turbo")
    # ...

Ergebnis: Reduzierte Kritikslatenz um 60% (600ms → 240ms) bei gleicher Genauigkeit.

Lektion 3: Frühen Stopp bei "perfekten" Scores hinzufügen

Wenn der erste Versuch 9.5/10 punktet, überspringen Sie weitere Iterationen:

if score >= 9.5:  # "Perfekt"-Schwelle
    return early_with_success(response, score)

Latenzvergleich: Reale Produktionsdaten

MusterDurchschn. Latenzp95-Latenzp99-LatenzLLM-Aufrufe
Router820ms1.2s1.8s1
Planner-Executor (3 Schritte)3.2s8.4s14.1s4
Critic (durchschn. 1.8 Iterationen)4.2s11.8s18.5s3.6

Kostenvergleich

Annahmen:

  • GPT-4 Input: $0.01/1K Tokens.
  • GPT-4 Output: $0.03/1K Tokens.
  • Durchschnittliche Abfrage: 200 Input-Tokens.
  • Durchschnittliche Antwort: 500 Output-Tokens.

MusterLLM-AufrufeDurchschn. Kosten
Router1$0.017
Planner-Executor4$0.068
Critic3.6$0.061

Entscheidungsmatrix: Welches Muster verwenden?

Wählen Sie Router, wenn:

✅ Einzeltool-Dispatch reicht ✅ Latenz <1s erforderlich ✅ Abfragenrouting ist eindeutig ✅ Kosten pro Abfrage sind wichtig

Wählen Sie Planner-Executor, wenn:

✅ Mehrstufige Workflows benötigt ✅ Tool-Zusammensetzung erforderlich ✅ Latenz <5s akzeptabel ✅ Transparenz (sichtbarer Plan) ist wertvoll

Wählen Sie Critic, wenn:

✅ Ausgabequalität ist mission-kritisch ✅ Latenz <10s akzeptabel ✅ Selbstkorrektur schafft Mehrwert ✅ Erstentwurfsqualität ist unzureichend

Hybride Muster, die wir getestet haben

Muster 4: Router + Planner-Executor

Einfache Abfragen an einzelne Tools leiten, komplexe an Planner:

if query_complexity(query) < 0.5:
    return router.route(query)  # Schneller Pfad
else:
    return planner_executor.run(query)  # Langsamer Pfad

Ergebnis: 70% der Abfragen nehmen den schnellen Pfad (durchschn. 850ms), 30% den langsamen Pfad (durchschn. 3.5s). Gesamtdurchschnitt: 1.6s.

Muster 5: Planner-Executor + Critic

Planen, ausführen, dann finale Antwort kritisieren:

context = await planner_executor.execute(query)
final_response = await generate_response(context)
score, feedback = await critic.critique(query, final_response)

if score < 8.0: final_response = await regenerate_with_feedback(context, feedback)

Ergebnis: Für hochrangige Berichte verwendet. Latenz: 8-12s. Qualität: 98% Benutzerzufriedenheit.

Fazit

Nach 18+ Monaten in der Produktion:

1. Router behandelt 80% der Abfragen mit hervorragender Latenz

2. Planner-Executor glänzt bei mehrstufigen Workflows, erfordert aber Planvalidierung

3. Critic verbessert die Qualität um 15-20%, verdoppelt aber Kosten und Latenz

Unsere Standard-Empfehlung:

  • Beginnen Sie mit Router für MVP.
  • Fügen Sie Planner-Executor hinzu, wenn Benutzer mehrstufige Aufgaben anfordern.
  • Reservieren Sie Critic für qualitätskritische Ausgaben (Recht, Finanzen, Medizin).

Das beste Muster hängt von Ihrem Latenzbudget, Ihren Qualitätsanforderungen und Ihren Kostenbeschränkungen ab. Überengineering Sie nicht — deployen Sie einfach, steigern Sie die Komplexität nach Bedarf.


Bauen Sie Agent-Systeme? Kontaktieren Sie uns um Ihre Architektur zu besprechen. Wir bieten Agent-Design-Beratung, Implementierungsunterstützung und Produktionsoptimierungsdienste an.

Möchten Sie dies in Ihrer Organisation umsetzen?

Wir helfen Teams bei der Bereitstellung produktionsreifer KI-Systeme. Teilen Sie uns Ihre Anforderungen mit und wir besprechen den besten Ansatz für Ihren Anwendungsfall.

Ihr Projekt besprechen
Next

Continue exploring