Skip to content
IRC-CodingIRC-Coding
langgraphmultiagentensystemepythonagent-frameworktutoriallangchain

LangGraph Tutorial: Multiagentensysteme Schritt für Schritt

Praktische Einführung in LangGraph für Multiagentensysteme. Lerne State Graphs, Nodes, Edges und Agent-Koordination mit Python-Beispielen.

I

IRC-Coding Team

15 min read
LangGraph Tutorial: Multiagentensysteme Schritt für Schritt

LangGraph Tutorial: Multiagentensysteme Schritt für Schritt

LangGraph ist das derzeit mächtigste Framework für die Entwicklung von zustandsbehafteten, Multi-Actor-Anwendungen mit LLMs. Es wurde von LangChain entwickelt und ermöglicht es Dir, komplexe Agenten-Workflows als gerichtete Graphen zu modellieren — mit Zuständen, bedingten Übergängen, Schleifen und paralleler Ausführung.

Wenn Du schon mal versucht hast, mit einfachen LangChain-Chains einen Multi-Agent-Workflow zu bauen, weißt Du, wie schnell es chaotisch wird. LangGraph löst genau dieses Problem: Statt spaghetticode-artige Prompt-Ketten zu bauen, definierst Du einen klaren Graphen mit Nodes (Agenten), Edges (Übergänge) und einem gemeinsamen State (Zustand). Das ist näher an echter Softwarearchitektur als an Prompt-Engineering.

In diesem Tutorial baue ich Dich von den Grundkonzepten bis zu einem vollständigen Multi-Agent-System mit Human-in-the-Loop, Memory und Fehlerbehandlung auf. Alle Code-Beispiele sind lauffähig.

TL;DR — LangGraph in 90 Sekunden

LangGraph ist ein Framework für zustandsbehaftete Multi-Agent-Anwendungen, das auf Graph-Theorie basiert: Agenten sind Nodes, Übergänge sind Edges, und ein gemeinsamer State wird zwischen allen Nodes geteilt.

---

Die 4 Kernkonzepte: State (geteilter Zustand), Nodes (Agenten-Funktionen), Edges (Übergänge), Conditional Edges (bedingte Routen).

Der größte Vorteil: Du kannst Schleifen (Cycles) bauen — ein Reviewer-Agent kann den Writer-Agenten zurücksenden, wenn die Qualität nicht reicht. Das ist mit einfachen Chains nicht möglich.

Die Lernkurve: Steiler als CrewAI, aber Du hast feingranulare Kontrolle über jeden Schritt. Wenn Du komplexe Workflows baust, ist LangGraph die beste Wahl.

Ende der kompakten Erklärung!

Was ist LangGraph — und warum brauchst Du es?

Das Problem mit einfachen Chains

Stell Dir vor, Du willst einen Content-Creation-Pipeline bauen: Ein Agent recherchiert, ein Agent schreibt, ein Agent reviewed. Mit einfachen LangChain-Chains sieht das so aus:

# Naiver Ansatz — funktioniert, aber nicht gut
research = llm.invoke("Recherchiere Thema X")
draft = llm.invoke(f"Schreibe Artikel: {research}")
final = llm.invoke(f"Verbessere: {draft}")

Das funktioniert für einfache Fälle. Aber was, wenn:

  • Der Reviewer den Artikel ablehnt und der Writer überarbeiten soll? → Schleife nötig
  • Du den State zwischen Agenten teilen willst (z.B. alle bisherigen Nachrichten)? → State Management nötig
  • Du basierend auf dem Inhalt unterschiedlich routen willst? → Bedingte Übergänge nötig
  • Du den Workflow anhalten und auf menschliche Bestätigung warten willst? → Human-in-the-Loop nötig
  • Du mehrere Agenten parallel laufen lassen willst? → Parallele Ausführung nötig

Genau das löst LangGraph.

Die LangGraph-Architektur

LangGraph modelliert Agenten-Workflows als State Graphs — gerichtete Graphen, in denen:

  • Nodes (Knoten) = Agenten-Funktionen, die den State empfangen, verarbeiten und zurückgeben
  • Edges (Kanten) = Übergänge zwischen Nodes, die sequenziell oder bedingt sein können
  • State = Ein geteiltes Dictionary (oder Pydantic-Model), das zwischen allen Nodes fließt
  • Conditional Edges = Funktionen, die basierend auf dem State entscheiden, welche Node als nächstes ausgeführt wird
  • Cycles = Schleifen, die eine Node mehrfach durchlaufen können (z.B. Writer → Reviewer → Writer)

Das Konzept ist inspiriert von Workflow-Engines wie Apache Airflow oder Temporal, aber speziell für LLM-basierte Agenten optimiert.

Warum Graphen und nicht einfach Ketten?

EigenschaftEinfache ChainLangGraph
Lineare Abfolge
Bedingte Verzweigungen
Schleifen (Cycles)
Geteilter StateSchwierig✅ Nativ
Parallele Ausführung
Human-in-the-Loop
Checkpointing / Memory
Graph-Visualisierung

Installation und Setup

pip install langgraph langchain-openai langchain-core

Zusätzlich empfohlen für dieses Tutorial:

pip install langchain-anthropic  # Für Claude-Modelle
pip install langgraph-checkpoint-sqlite  # Für SQLite-Persistence
pip install grandalf  # Für Graph-Visualisierung im Terminal

API-Keys setzen:

export OPENAI_API_KEY="sk-..."
# Optional für Claude:
export ANTHROPIC_API_KEY="sk-ant-..."

Wichtig: Verwende niemals hardcoded API-Keys im Code. Nutze immer Umgebungsvariablen oder .env-Dateien mit python-dotenv.

Die 4 Kernkonzepte — Im Detail

1. State — Das Herzstück

Der State ist ein geteiltes Datenobjekt, das zwischen allen Nodes im Graphen weitergegeben wird. Jede Node kann den State lesen und aktualisieren. Das ist der fundamentale Unterschied zu einfachen Chains, wo jeder Schritt nur die Ausgabe des vorherigen sieht.

State als TypedDict (empfohlen für einfache Fälle):

from typing import TypedDict, List, Optional

class ContentState(TypedDict):
    topic: str                    # Das Thema, das bearbeitet wird
    research: str                 # Rechercheergebnisse
    draft: str                    # Aktueller Entwurf
    final_article: str            # Finaler Artikel
    messages: List[str]           # Vollständiger Nachrichtenverlauf
    revision_count: int           # Anzahl der Überarbeitungen
    approved: bool                # Wurde der Artikel genehmigt?
    feedback: Optional[str]       # Feedback des Reviewers

Warum TypedDict? Es gibt Dir Type-Safety und IDE-Autovervollständigung, ohne die Flexibilität eines Dictionaries zu verlieren. Für komplexere Anwendungen kannst Du auch Pydantic-Models verwenden:

from pydantic import BaseModel, Field

class ContentState(BaseModel):
    topic: str = Field(description="Das zu bearbeitende Thema")
    research: str = Field(default="", description="Rechercheergebnisse")
    draft: str = Field(default="", description="Aktueller Entwurf")
    revision_count: int = Field(default=0)
    approved: bool = Field(default=False)

State-Reduction — Wie Updates funktionieren:

Standardmäßig überschreibt ein Node-Return den State. Aber Du kannst auch Reducer-Funktionen definieren, um State-Felder zu akkumulieren:

from typing import Annotated
from operator import add

class ContentState(TypedDict):
    # messages wird appended, nicht überschrieben
    messages: Annotated[List[str], add]
    # research wird überschrieben (Standard)
    research: str

Wenn jetzt zwei Nodes messages zurückgeben, werden die Listen kombiniert statt überschrieben. Das ist extrem nützlich für Nachrichtenverläufe.

2. Nodes — Die Agenten

Nodes sind Python-Funktionen, die den State empfangen, verarbeiten und einen aktualisierten State (oder ein Partial-Update) zurückgeben. Jede Node ist ein Agent oder eine Verarbeitungseinheit.

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o", temperature=0)

def researcher(state: ContentState) -> dict:
    """Recherche-Agent: Sammelt Informationen zum Thema."""
    topic = state["topic"]
    
    prompt = f"""Du bist ein Recherche-Experte. Sammle die wichtigsten 
    Informationen zum Thema: {topic}.
    
    Strukturiere Deine Antwort in:
    1. Definition und Grundlagen
    2. Aktuelle Entwicklungen
    3. Praxisbeispiele
    4. Häufige Missverständnisse
    
    Gib nur die Recherche zurück, keine Einleitung."""
    
    response = llm.invoke(prompt)
    
    # Partial-Update: Nur veränderte Felder zurückgeben
    return {
        "research": response.content,
        "messages": [f"Researcher: {response.content[:200]}..."]
    }

Wichtige Punkte zu Nodes:

  1. Partial Updates: Du musst nicht den gesamten State zurückgeben, nur die Felder, die Du änderst. LangGraph merged das mit dem bestehenden State.
  2. Keine Side Effects: Nodes sollten idealerweise reine Funktionen sein (Input → Output). Side Effects (Datei-Schreiben, API-Calls) sollten explizit gehandhabt werden.
  3. Error Handling: Wenn eine Node eine Exception wirft, wird der gesamte Graph gestoppt. Nutze try/except für fehleranfällige Operationen.
def researcher(state: ContentState) -> dict:
    try:
        response = llm.invoke(prompt)
        return {"research": response.content}
    except Exception as e:
        # Fehler in State schreiben statt Graph abzubrechen
        return {
            "research": f"Fehler bei Recherche: {str(e)}",
            "messages": [f"Researcher ERROR: {str(e)}"]
        }

3. Edges — Die Übergänge

Edges definieren, welche Node nach der aktuellen ausgeführt wird. Es gibt drei Arten:

Simple Edges (feste Übergänge):

# Nach researcher kommt immer writer
workflow.add_edge("researcher", "writer")

Conditional Edges (bedingte Übergänge): Das ist die mächtigste Funktion von LangGraph. Eine Routing-Funktion entscheidet basierend auf dem State, welche Node als nächstes kommt:

def route_after_review(state: ContentState) -> str:
    """Entscheidet nach dem Review, was passiert."""
    if state.get("approved"):
        return "publish"           # Genehmigt → veröffentlichen
    elif state.get("revision_count", 0) >= 3:
        return "human_review"      # Zu viele Revisionen → Mensch prüfen
    else:
        return "writer"            # Nicht genehmigt → Writer überarbeitet

# Conditional Edge hinzufügen
workflow.add_conditional_edges(
    "reviewer",           # Source-Node
    route_after_review,   # Routing-Funktion
    {
        "publish": "publisher",
        "human_review": "human_node",
        "writer": "writer"
    }
)

Entry Point (Start-Node):

workflow.set_entry_point("researcher")

4. Cycles — Schleifen für iterative Verbesserung

Cycles sind der wichtigste Unterschied zwischen LangGraph und einfachen Chains. Ein Cycle ermöglicht es einem Agenten-Team, iterativ zu arbeiten:

researcher → writer → reviewer → (nicht gut?) → writer → reviewer → (gut!) → publish

In LangGraph erreichst Du das mit Conditional Edges, die zurück zu einer früheren Node führen:

# Reviewer kann Writer zurücksenden (Cycle!)
workflow.add_conditional_edges(
    "reviewer",
    route_after_review,  # Kann "writer" zurückgeben
)

# Writer geht immer zum Reviewer
workflow.add_edge("writer", "reviewer")

Achtung — Endlosschleifen vermeiden: Implementiere immer einen Zähler oder eine maximale Anzahl von Iterationen:

def route_after_review(state: ContentState) -> str:
    revision_count = state.get("revision_count", 0)
    
    if state.get("approved"):
        return "publish"
    
    if revision_count >= 5:
        return "human_review"  # Notbremse
    
    return "writer"  # Nochmal versuchen

Ein vollständiges Beispiel — Schritt für Schritt

Wir bauen jetzt ein vollständiges Multi-Agent-System: Researcher + Writer + Reviewer + Publisher mit Human-in-the-Loop, Revision-Limit und Memory.

Schritt 1: State definieren

from typing import TypedDict, List, Optional, Annotated
from operator import add

class ContentState(TypedDict):
    topic: str
    research: str
    draft: str
    final_article: str
    messages: Annotated[List[str], add]  # Wird appended, nicht überschrieben
    revision_count: int
    approved: bool
    feedback: Optional[str]

Schritt 2: Nodes (Agenten) definieren

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o", temperature=0.7)  # Etwas Kreativität für Writer

def researcher(state: ContentState) -> dict:
    """Recherche-Agent: Sammelt strukturierte Informationen."""
    topic = state["topic"]
    
    prompt = f"""Du bist ein Recherche-Experte. Sammle die wichtigsten 
    Informationen zum Thema: {topic}.
    
    Berücksichtige:
    - Definition und Grundlagen
    - Aktuelle Entwicklungen (2025-2026)
    - Praxisbeispiele und Use Cases
    - Häufige Missverständnisse und Fallstricke
    
    Gib nur die strukturierte Recherche zurück."""
    
    response = llm.invoke(prompt)
    return {
        "research": response.content,
        "messages": [f"Researcher: Recherche abgeschlossen ({len(response.content)} Zeichen)"]
    }

def writer(state: ContentState) -> dict:
    """Writer-Agent: Schreibt Artikel basierend auf Recherche und Feedback."""
    research = state["research"]
    feedback = state.get("feedback", "")
    revision_count = state.get("revision_count", 0)
    
    if feedback and revision_count > 0:
        prompt = f"""Du bist ein professioneller Autor. Überarbeite den 
        folgenden Artikel basierend auf dem Feedback des Reviewers.
        
        Recherche: {research}
        
        Bisheriger Entwurf: {state.get('draft', '')}
        
        Feedback des Reviewers: {feedback}
        
        Schreibe den verbesserten Artikel. Berücksichtige alle Punkte 
        aus dem Feedback."""
    else:
        prompt = f"""Du bist ein professioneller Autor. Schreibe einen 
        umfassenden, gut strukturierten Artikel zum Thema: {state['topic']}
        
        Basierend auf dieser Recherche: {research}
        
        Der Artikel soll:
        - Eine klare Einleitung haben
        - Zwischenüberschriften verwenden
        - Praxisbeispiele enthalten
        - Eine Zusammenfassung am Ende haben"""
    
    response = llm.invoke(prompt)
    return {
        "draft": response.content,
        "revision_count": revision_count + 1,
        "messages": [f"Writer: Entwurf v{revision_count + 1} erstellt"]
    }

def reviewer(state: ContentState) -> dict:
    """Reviewer-Agent: Prüft Qualität und gibt Feedback."""
    draft = state["draft"]
    revision_count = state.get("revision_count", 0)
    
    prompt = f"""Du bist ein strenger Reviewer. Bewerte den folgenden Artikel 
    nach diesen Kriterien:
    
    1. Struktur und Lesbarkeit (1-10)
    2. Fachliche Korrektheit (1-10)
    3. Praxisbezug (1-10)
    4. Vollständigkeit (1-10)
    
    Artikel: {draft}
    
    Wenn alle Kriterien mindestens 7/10 erreichen, antworte mit "APPROVED".
    Sonst antworte mit "REJECTED" und gib konkretes Feedback zur Verbesserung."""
    
    response = llm.invoke(prompt)
    content = response.content
    
    approved = "APPROVED" in content.upper()
    
    return {
        "approved": approved,
        "feedback": content if not approved else None,
        "final_article": draft if approved else "",
        "messages": [f"Reviewer: {'Genehmigt' if approved else 'Abgelehnt (Revision ' + str(revision_count) + ')'}"]
    }

def publisher(state: ContentState) -> dict:
    """Publisher-Agent: Formatiert den finalen Artikel."""
    article = state["final_article"]
    
    prompt = f"""Formatiere diesen Artikel als Markdown mit:
    - Titel als H1
    - Metadaten (Autor, Datum, Lesezeit)
    - Saubere Abschnittsüberschriften
    
    Artikel: {article}"""
    
    response = llm.invoke(prompt)
    return {
        "final_article": response.content,
        "messages": [f"Publisher: Artikel veröffentlicht"]
    }

def human_review(state: ContentState) -> dict:
    """Human-in-the-Loop: Mensch entscheidet bei zu vielen Revisionen."""
    print(f"\n=== HUMAN REVIEW ===")
    print(f"Thema: {state['topic']}")
    print(f"Revisionen: {state.get('revision_count', 0)}")
    print(f"Feedback des Reviewers: {state.get('feedback', 'Keines')}")
    print(f"\nLetzter Entwurf:\n{state.get('draft', '')[:500]}...")
    
    approval = input("\nArtikel genehmigen? (y/n): ")
    return {
        "approved": approval.lower() == "y",
        "messages": [f"Human: {'Genehmigt' if approval.lower() == 'y' else 'Abgelehnt'}"]
    }

Schritt 3: Routing-Funktionen definieren

from langgraph.graph import END

def route_after_review(state: ContentState) -> str:
    """Routing nach dem Review mit Notbremse."""
    if state.get("approved"):
        return "publisher"
    
    if state.get("revision_count", 0) >= 3:
        return "human_review"
    
    return "writer"  # Zurück zum Writer

def route_after_human(state: ContentState) -> str:
    """Routing nach menschlicher Prüfung."""
    if state.get("approved"):
        return "publisher"
    return END  # Abbrechen, wenn Mensch ablehnt

Schritt 4: Graph zusammenbauen

from langgraph.graph import StateGraph

# Graph erstellen
workflow = StateGraph(ContentState)

# Alle Nodes hinzufügen
workflow.add_node("researcher", researcher)
workflow.add_node("writer", writer)
workflow.add_node("reviewer", reviewer)
workflow.add_node("publisher", publisher)
workflow.add_node("human_review", human_review)

# Edges definieren
workflow.set_entry_point("researcher")

# researcher → writer (immer)
workflow.add_edge("researcher", "writer")

# writer → reviewer (immer)
workflow.add_edge("writer", "reviewer")

# reviewer → conditional (writer, publisher, oder human_review)
workflow.add_conditional_edges(
    "reviewer",
    route_after_review,
    {
        "writer": "writer",
        "publisher": "publisher",
        "human_review": "human_review"
    }
)

# human_review → conditional (publisher oder END)
workflow.add_conditional_edges(
    "human_review",
    route_after_human,
    {
        "publisher": "publisher",
        END: END
    }
)

# publisher → END (immer)
workflow.add_edge("publisher", END)

# Graph kompilieren
app = workflow.compile()

Schritt 5: Ausführen

# Initialer State
initial_state = {
    "topic": "KI-Programmierung: Best Practices 2026",
    "research": "",
    "draft": "",
    "final_article": "",
    "messages": [],
    "revision_count": 0,
    "approved": False,
    "feedback": None
}

# Graph ausführen
result = app.invoke(initial_state)

# Ergebnis anzeigen
print("\n" + "=" * 60)
print("FINAL ARTICLE:")
print("=" * 60)
print(result["final_article"])
print("\n" + "=" * 60)
print(f"Revisionen: {result['revision_count']}")
print(f"Nachrichtenverlauf:")
for msg in result["messages"]:
    print(f"  - {msg}")

Was passiert hier?

  1. Researcher sammelt Informationen zum Thema
  2. Writer schreibt ersten Entwurf basierend auf der Recherche
  3. Reviewer bewertet den Entwurf nach 4 Kriterien
  4. Wenn nicht genehmigt: Zurück zum Writer mit Feedback (Cycle!)
  5. Nach 3 gescheiterten Revisionen: Human Review (Notbremse)
  6. Wenn genehmigt: Publisher formatiert den finalen Artikel
  7. END — Artikel ist fertig

Der gesamte Workflow ist ein Graph mit einem Cycle (writer ↔ reviewer) und zwei Conditional Edges (reviewer und human_review).

Fortgeschrittene Patterns

Memory und Persistence mit Checkpointing

LangGraph kann den State nach jeder Node-Ausführung speichern (Checkpointing). Das ermöglicht:

  • Wiederherstellung nach Abstürzen
  • Fortsetzung von pausierten Workflows
  • Konversationsverläufe über mehrere Sessions
from langgraph.checkpoint.memory import MemorySaver
# Für Persistenz über Neustarts hinweg:
# from langgraph.checkpoint.sqlite import SqliteSaver

# In-Memory Checkpointer
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)

# Mit Thread-ID ausführen (wichtig für Memory!)
result = app.invoke(
    initial_state,
    config={"configurable": {"thread_id": "article-ki-programmierung"}}
)

# Später fortsetzen (z.B. nach Human-Approval)
result2 = app.invoke(
    {"approved": True},  # Nur das Update schicken
    config={"configurable": {"thread_id": "article-ki-programmierung"}}
)

Wie es funktioniert: Der Checkpointer speichert den State nach jeder Node-Ausführung. Mit der thread_id kannst Du später denselben Thread fortsetzen. Das ist besonders nützlich für Human-in-the-Loop, wo der Workflow pausiert wird, bis ein Mensch entscheidet.

Parallele Ausführung

Du kannst mehrere Nodes parallel ausführen und dann zusammenführen:

def researcher_general(state: ContentState) -> dict:
    """Recherchiert allgemeine Informationen."""
    response = llm.invoke(f"Allgemeine Infos zu: {state['topic']}")
    return {"research_general": response.content}

def researcher_technical(state: ContentState) -> dict:
    """Recherchiert technische Details."""
    response = llm.invoke(f"Technische Details zu: {state['topic']}")
    return {"research_technical": response.content}

def merge_research(state: ContentState) -> dict:
    """Führt beide Recherchen zusammen."""
    combined = f"Allgemein:\n{state['research_general']}\n\nTechnisch:\n{state['research_technical']}"
    return {"research": combined}

# Im Graph:
workflow.add_node("researcher_general", researcher_general)
workflow.add_node("researcher_technical", researcher_technical)
workflow.add_node("merge", merge_research)

# Beide Researcher parallel vom Start
workflow.set_entry_point("researcher_general")
workflow.add_edge("researcher_general", "researcher_technical")
# Eigentlich parallel: Beide vom Entry, beide → merge
# LangGraph führt parallele Nodes automatisch zusammen

Tool-Integration — Agenten mit Werkzeugen

Agenten werden erst mächtig, wenn sie Tools nutzen können. LangGraph integriert nahtlos LangChain-Tools:

from langchain.tools import Tool
from langchain_community.tools import DuckDuckGoSearchRun

# Web-Search-Tool
search = DuckDuckGoSearchRun()

def researcher_with_tools(state: ContentState) -> dict:
    """Recherche-Agent mit Web-Search."""
    topic = state["topic"]
    
    # Erst suchen
    search_results = search.run(f"{topic} 2026 latest developments")
    
    # Dann LLM mit Suchergebnissen füttern
    prompt = f"""Basierend auf diesen Suchergebnissen, erstelle eine 
    strukturierte Recherche zum Thema {topic}:
    
    Suchergebnisse: {search_results}"""
    
    response = llm.invoke(prompt)
    return {"research": response.content}

Sub-Graphs — Graphen in Graphen

Du kannst einen LangGraph als Node in einem anderen LangGraph verwenden. Das ist nützlich für modulare Architekturen:

# Sub-Graph für Recherche
research_graph = StateGraph(ContentState)
research_graph.add_node("web_search", web_searcher)
research_graph.add_node("summarize", summarizer)
research_graph.set_entry_point("web_search")
research_graph.add_edge("web_search", "summarize")
research_graph.add_edge("summarize", END)
research_app = research_graph.compile()

# Haupt-Graph nutzt Sub-Graph als Node
main_workflow = StateGraph(ContentState)
main_workflow.add_node("research", research_app)  # Sub-Graph als Node!
main_workflow.add_node("write", writer)
main_workflow.set_entry_point("research")
main_workflow.add_edge("research", "write")
main_workflow.add_edge("write", END)

Streaming — Ergebnisse in Echtzeit

LangGraph unterstützt Streaming, sodass Du Zwischenergebnisse siehst, während der Graph läuft:

# Stream alle Node-Updates
for event in app.stream(initial_state):
    for node_name, node_output in event.items():
        print(f"[{node_name}] → {list(node_output.keys())} updated")

# Stream nur die Messages
for event in app.stream(
    initial_state,
    stream_mode="values"
):
    messages = event.get("messages", [])
    if messages:
        print(f"Latest: {messages[-1]}")

Graph visualisieren

LangGraph kann den Graphen visualisieren — extrem nützlich für Debugging:

# Im Jupyter Notebook:
from IPython.display import Image, display

# Graph als Bild rendern
display(Image(app.get_graph().draw_mermaid_png()))

# Im Terminal:
app.get_graph().print_ascii()

Das gibt Dir eine visuelle Darstellung aller Nodes, Edges und Conditional Routes — unverzichtbar bei komplexen Graphen.

LangGraph vs. CrewAI — Welches Framework für welchen Anwendungsfall?

AspektLangGraphCrewAI
ParadigmaState Graph (Graph-Theorie)Role-Based Agents (Team-Metapher)
FlexibilitätSehr hoch — jeder Schritt kontrollierbarMittel — Abstraktion verbirgt Details
LernkurveSteil — Graph-Konzepte müssen verstanden werdenFlach — “Definiere Agenten, starte Task”
KontrolleFeingranular — Conditional Edges, State-ManagementAbstrahiert — Framework entscheidet viel selbst
DebuggingGraph-Visualisierung, Streaming, Tracing mit LangSmithLogs, einfache Print-Statements
Cycles/SchleifenNativ unterstütztEingeschränkt (max_iterations)
Memory/StateNativ (Checkpointing, State-Reduction)Eingeschränkt (Memory-Parameter)
Human-in-the-LoopNativ (interrupt_before, interrupt_after)Manuell implementiert
EignungKomplexe Workflows, Produktions-SystemeSchnelle Prototypen, einfache Agenten-Teams
CommunityLangChain-Ökosystem (sehr groß)Wachsend, aber kleiner

Meine Empfehlung:

  • CrewAI für Prototypen und einfache Agenten-Teams (2-3 Agenten, lineare Abläufe)
  • LangGraph für Produktions-Systeme, komplexe Workflows mit Schleifen, Bedingungen und Human-in-the-Loop
  • Beide für unterschiedliche Projekte — sie schließen sich nicht gegenseitig aus

Best Practices aus der Praxis

1. State so klein wie möglich halten

Nicht jede Information muss in den State. Lokale Variablen in Nodes sind oft ausreichend. Der State sollte nur Daten enthalten, die wirklich zwischen Nodes geteilt werden müssen.

2. Revision-Limits immer setzen

Ohne Limit kann ein Cycle endlos laufen. Setze immer einen Zähler und eine Notbremse:

MAX_REVISIONS = 5

def route_after_review(state: ContentState) -> str:
    if state.get("revision_count", 0) >= MAX_REVISIONS:
        return "human_review"  # oder END
    # ...

3. Tracing mit LangSmith aktivieren

LangSmith (von LangChain) gibt Dir detaillierte Einblicke in jeden Schritt:

import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls-..."

Du siehst dann jeden LLM-Call, jeden State-Übergang und jede Token-Berechnung im LangSmith-Dashboard.

4. Kosten im Auge behalten

Jeder Node macht mindestens einen LLM-Call. Bei 3 Agenten und 3 Revisionen sind das 9+ LLM-Calls. Mit GPT-4o können das schnell 5-10$ pro Durchlauf sein. Nutze günstigere Modelle für einfache Tasks (z.B. GPT-4o-mini für den Reviewer).

5. Lokale Modelle für Entwicklung

Für die Entwicklung und Tests nutze lokale Modelle (Ollama), um Kosten zu sparen:

from langchain_community.chat_models import ChatOllama

llm = ChatOllama(model="llama3.1:8b", temperature=0)

Häufige Probleme und Lösungen

Problem: “Graph doesn’t have an entry point”

Lösung: Du musst workflow.set_entry_point("node_name") vor dem Kompilieren aufrufen.

Problem: “Node ‘xyz’ not found”

Lösung: Jede Node, die in Edges referenziert wird, muss mit add_node hinzugefügt werden, bevor Edges definiert werden.

Problem: Endlosschleife

Lösung: Conditional Edge gibt immer dieselbe Node zurück. Prüfe die Routing-Funktion — sie muss basierend auf dem State unterschiedliche Werte zurückgeben.

Problem: State wird nicht aktualisiert

Lösung: Node gibt Partial-Update zurück, aber Feld nutzt Annotated-Reducer. Prüfe, ob der Reducer korrekt definiert ist. Bei Annotated[List[str], add] muss die Node eine Liste zurückgeben, die addiert wird.

Prüfungsrelevante Punkte

  • LangGraph: Framework für zustandsbehaftete Multi-Agent-Anwendungen, basierend auf Graph-Theorie
  • 4 Kernkonzepte: State (geteilter Zustand), Nodes (Agenten-Funktionen), Edges (Übergänge), Conditional Edges (bedingte Routen)
  • Cycles: Schleifen ermöglichen iterative Verbesserung (Writer ↔ Reviewer)
  • State-Reduction: Annotated-Reducer (z.B. add) für akkumulierende State-Felder
  • Human-in-the-Loop: Workflow pausieren für menschliche Entscheidungen
  • Checkpointing: State nach jeder Node speichern, Wiederherstellung nach Absturz
  • Parallele Ausführung: Mehrere Nodes gleichzeitig, automatisches Zusammenführen
  • Sub-Graphs: Graph als Node in anderem Graph — modulare Architektur
  • Streaming: Echtzeit-Updates während der Graph-Ausführung
  • LangSmith: Tracing und Debugging für LangGraph-Workflows
  • Vergleich: LangGraph (flexibel, komplex, Produktionsreif) vs. CrewAI (einfach, schnell, Prototypen)

FAQ

Braucht man LangChain-Kenntnisse für LangGraph? Ja, grundlegende LangChain-Kenntnisse sind hilfreich, da LangGraph auf LangChain aufbaut (gleiche LLM-Integration, gleiche Tools, gleiche Runnables). Wenn Du LangChain noch nicht kennst, solltest Du zuerst die Grundlagen von LangChain lernen.

Ist LangGraph kostenlos? LangGraph selbst ist Open Source (MIT-Lizenz). Die LLM-Kosten (OpenAI, Anthropic) fallen zusätzlich an. LangSmith (Tracing) hat eine kostenlose Stufe, ist aber für intensiven Gebrauch kostenpflichtig.

Kann LangGraph mit lokalen Modellen arbeiten? Ja, über Ollama, llama.cpp oder LM Studio. Das ist besonders für Entwicklung und Tests empfehlenswert, um API-Kosten zu sparen. Für Produktion sind Cloud-Modelle meist qualitativ besser.

Wie debugge ich einen LangGraph-Workflow? Drei Tools: 1) app.get_graph().draw_mermaid_png() für visuelle Graph-Darstellung. 2) Streaming mit app.stream() für Echtzeit-Updates. 3) LangSmith für detailliertes Tracing jedes LLM-Calls.

Was ist der Unterschied zwischen LangGraph und LangChain? LangChain ist ein allgemeines Framework für LLM-Anwendungen (Chains, Agents, Tools). LangGraph ist spezialisiert auf zustandsbehaftete, Multi-Actor-Workflows mit Graph-Struktur. LangGraph baut auf LangChain auf, ersetzt es aber nicht.

Kann ich LangGraph in Produktion einsetzen? Ja, LangGraph ist für Produktion geeignet. Mit Checkpointing (Persistence), Error Handling und LangSmith-Tracing hast Du alles, was Du für zuverlässige Produktions-Systeme brauchst. LangGraph Cloud bietet zudem Managed-Hosting für LangGraph-Anwendungen.

Empfohlene Literatur

KI-Agenten

Bücher über KI-Agenten, Multiagentensysteme und Agent-Orchestrierung

The AI Agent Handbook

The AI Agent Handbook

Bei Amazon ansehen

Affiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.

Multi-Agent Systems: A Modern Approach

Multi-Agent Systems: A Modern Approach

Bei Amazon ansehen

Affiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.

Coding mit KI: Das Praxisbuch für die Softwareentwicklung

Coding mit KI: Das Praxisbuch für die Softwareentwicklung

Bei Amazon ansehen

Affiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.

Zurück zum Blog
Share:

Ähnliche Beiträge