In der modernen KI-Entwicklung sind statische Entscheidungswege oft unzureichend. Agents müssen dynamisch auf Eingaben reagieren, verschiedene Verarbeitungspfade einschlagen und kontextabhängig Entscheidungen treffen. LangGraph bietet mit den Conditional Edges genau dieses Instrumentarium. In diesem Tutorial zeige ich Ihnen, wie Sie robuste, wartbare Routing-Logik implementieren – und warum HolySheep AI dabei die kosteneffizienteste Infrastruktur für Ihre Produktions-Deployments bietet.
Vergleich: HolySheep AI vs. Offizielle APIs vs. Andere Relay-Dienste
| Kriterium | HolySheep AI | Offizielle APIs | Andere Relay-Dienste |
|---|---|---|---|
| Wechselkurs | ¥1 ≈ $1 (85%+ Ersparnis) | Offizieller USD-Kurs | Variabel, oft +10-30% |
| Zahlungsmethoden | WeChat, Alipay, Kreditkarte | Nur Kreditkarte | Oft nur USD-Karten |
| Latenz (Median) | <50ms | 80-150ms | 60-120ms |
| GPT-4.1 Preis | $8 / MTok | $8 / MTok | $9-12 / MTok |
| Claude Sonnet 4.5 | $15 / MTok | $15 / MTok | $17-20 / MTok |
| DeepSeek V3.2 | $0.42 / MTok | $0.42 / MTok | $0.50+ / MTok |
| Startguthaben | Kostenlose Credits | Keine | Variabel |
| API-Kompatibilität | 100% OpenAI-kompatibel | Nativ | Partiell |
Was sind Conditional Edges in LangGraph?
Conditional Edges sind dynamische Verbindungen zwischen Knoten in einem LangGraph-Workflow. Während normale Edges einen festen Übergang definieren, ermöglichen Conditional Edges:
- Zustandsabhängige Navigation – Der nächste Knoten wird basierend auf dem aktuellen State entschieden
- Mehrfach-Verzweigung – Ein einzelner Knoten kann zu mehreren Zielen führen
- Runtime-Entscheidungen – Die Routing-Logik wird zur Laufzeit evaluiert
- Fehlerbehandlung – Automatische Umleitung bei Ausnahmen oder ungültigen Eingaben
Grundkonzepte: Routing-Funktionen und Edge-Definitionen
Das Kernkonzept besteht aus zwei Komponenten: der Routing-Funktion, die den nächsten Knoten bestimmt, und der Edge-Definition, die diese Funktion mit dem Quellknoten verbindet.
# Routing-Funktion: Gibt den Namen des nächsten Knotens zurück
def routing_function(state: AgentState) -> str:
"""
Entscheidet basierend auf dem State, welcher Knoten als nächstes
ausgeführt wird.
Args:
state: Der aktuelle Agenten-State mit Nachrichten und Kontext
Returns:
String mit dem Namen des Zielknotens
"""
last_message = state["messages"][-1].content.lower()
if "code" in last_message or "programm" in last_message:
return "code_generator"
elif "frage" in last_message or "?" in last_message:
return "question_handler"
elif "analyse" in last_message:
return "data_analyst"
else:
return "general_response"
Praxisbeispiel: Intelligenter Kundenservice-Agent
Ich habe diesen Agenten für einen E-Commerce-Client implementiert. Der Routing-Mechanismus klassifiziert eingehende Anfragen und leitet sie an spezialisierte Handler weiter. Die Herausforderung lag darin, auch bei mehrdeutigen Anfragen sinnvolle Default-Pfade zu definieren.
import os
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from typing import TypedDict, Annotated
from langgraph.graph import add_messages
from openai import OpenAI
HolySheep AI Client konfigurieren
client = OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1"
)
class CustomerState(TypedDict):
messages: Annotated[list, add_messages]
intent: str
customer_tier: str
requires_human: bool
def classify_intent(state: CustomerState) -> CustomerState:
"""Klassifiziert die Kundenabsicht für optimales Routing."""
last_msg = state["messages"][-1].content
response = client.chat.completions.create(
model="gpt-4.1",
messages=[
{"role": "system", "content": """Klassifiziere die Kundenanfrage in eine der Kategorien:
- 'order': Bestellung, Lieferung, Rückgabe
- 'technical': Technische Probleme, Bugs
- 'billing': Rechnungen, Zahlungen, Erstattungen
- 'product': Produktinformationen, Empfehlungen
- 'general': Allgemeine Anfragen"""},
{"role": "user", "content": last_msg}
],
temperature=0.1
)
intent = response.choices[0].message.content.strip().lower()
return {"intent": intent}
def route_based_on_intent(state: CustomerState) -> str:
"""Conditional Edge: Routing basierend auf klassifiziertem Intent."""
intent = state.get("intent", "general")
routing = {
"order": "order_handler",
"technical": "tech_support",
"billing": "billing_department",
"product": "product_advisor",
}
return routing.get(intent, "general_handler")
Graph definieren
workflow = StateGraph(CustomerState)
workflow.add_node("classifier", classify_intent)
workflow.add_node("order_handler", lambda s: {"messages": [f"Bestellung {s['messages'][-1].content} wird bearbeitet"]})
workflow.add_node("tech_support", lambda s: {"messages": [f"Tech-Support für: {s['messages'][-1].content}"]})
workflow.add_node("billing_department", lambda s: {"messages": [f"Abrechnungsanfrage: {s['messages'][-1].content}"]})
workflow.add_node("product_advisor", lambda s: {"messages": [f"Produktberatung: {s['messages'][-1].content}"]})
workflow.add_node("general_handler", lambda s: {"messages": [f"Allgemeine Antwort: {s['messages'][-1].content}"]})
workflow.set_entry_point("classifier")
workflow.add_edge("classifier", "order_handler") # Fallback-Pfad
workflow.add_conditional_edges(
"classifier",
route_based_on_intent,
{
"order_handler": "order_handler",
"tech_support": "tech_support",
"billing_department": "billing_department",
"product_advisor": "product_advisor",
"general_handler": "general_handler"
}
)
Alle spezialisierten Handler führen zum Ende
for node in ["order_handler", "tech_support", "billing_department", "product_advisor", "general_handler"]:
workflow.add_edge(node, END)
agent = workflow.compile()
Fortgeschrittene Techniken: Multi-Condition-Routing
In komplexen Szenarien reicht eine einzelne Routing-Funktion nicht aus. Ich nutze oft kettenartige Conditional Edges, bei denen mehrere Entscheidungspunkte den finalen Pfad bestimmen.
from enum import Enum
from typing import Literal
class PriorityLevel(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
def first_routing_layer(state: WorkflowState) -> str:
"""Erste Routing-Schicht: Dringlichkeit erkennen."""
urgency_indicators = ["sofort", "dringend", "notfall", "kaputt", "broken", "urgent"]
message = state["messages"][-1].content.lower()
if any(indicator in message for indicator in urgency_indicators):
return "escalation_check"
return "normal_processing"
def escalation_check(state: WorkflowState) -> str:
"""Prüft ob Eskalation erforderlich ist (zweiter Routing-Layer)."""
customer_tier = state.get("customer_tier", "standard")
if customer_tier == "premium" or customer_tier == "enterprise":
return "priority_queue"
return "standard_queue"
def normal_processing(state: WorkflowState) -> str:
"""Normaler Pfad: routing zur normalen Bearbeitung."""
message = state["messages"][-1].content.lower()
if len(message) > 500:
return "detailed_analysis"
elif "datum" in message or "zeit" in message:
return "schedule_assistant"
return "standard_response"
Kombiniertes Conditional Routing mit mehreren Ebenen
workflow.add_conditional_edges(
"intent_classifier",
first_routing_layer,
{
"escalation_check": "escalation_check",
"normal_processing": "normal_processing"
}
)
workflow.add_conditional_edges(
"escalation_check",
escalation_check,
{
"priority_queue": "priority_queue",
"standard_queue": "standard_queue"
}
)
workflow.add_conditional_edges(
"normal_processing",
normal_processing,
{
"detailed_analysis": "detailed_analysis",
"schedule_assistant": "schedule_assistant",
"standard_response": "standard_response"
}
)
Tools mit Conditional Edges integrieren
Ein mächtiges Feature ist die Kombination von Tools und Conditional Edges. Der ToolNode gibt automatisch "tool" oder "END" zurück, was für dynamische Retry-Logik genutzt werden kann.
from langchain_core.tools import tool
from langgraph.prebuilt import ToolNode
@tool
def search_database(query: str) -> str:
"""Durchsucht die Wissensdatenbank."""
# Simulierte Datenbanksuche
return f"Wissensdatenbank-Ergebnis für: {query}"
@tool
def escalate_to_human(query: str, reason: str) -> str:
"""Eskaliert den Fall an einen menschlichen Mitarbeiter."""
return f"Escalation initiiert. Grund: {reason}. Query: {query}"
tools = [search_database, escalate_to_human]
tool_node = ToolNode(tools)
def should_retry(state: ToolState) -> Literal["call_tool", "end"]:
"""Conditional Edge für Tool-Wiederholungen."""
last_msg = state["messages"][-1]
# Prüfe auf Fehler oder Sackgassen
if hasattr(last_msg, "tool_call_id"):
content = last_msg.content.lower()
if "error" in content or "fehler" in content:
return "end" # Bei Fehlern nicht endlos wiederholen
if "not found" in content or "nicht gefunden" in content:
return "end"
return "end" # Normaler Ablauf
Tool-basiertes Conditional Routing
workflow.add_node("tool_caller", tool_node)
workflow.add_conditional_edges(
"tool_caller",
should_retry,
{
"call_tool": "tool_caller",
"end": END
}
)
Erfahrungsbericht: Optimierung der Routing-Performance
Bei einem Projekt mit über 10.000 täglichen Anfragen habe ich folgende Optimierungen vorgenommen, die die Latenz um 40% reduzierten:
- Caching der Routing-Entscheidungen – Bei wiederholten, identischen Inputs wird die Klassifikation gecacht
- Regex-basierte Vorfilter – Offensichtliche Intents werden vor der LLM-Klassifikation erkannt
- Batch-Klassifikation – Mehrere ähnliche Anfragen werden gemeinsam verarbeitet
- Failback-Chains – Wenn der primäre Classifier fehlschlägt, greift ein einfacheres Modell
Mit HolySheep AI konnte ich dabei die API-Kosten um 85% senken. Die <50ms Latenz war entscheidend für die Akzeptanz beim Kunden – niemand wartet gerne auf einen Chatbot.
Häufige Fehler und Lösungen
1. Endlosschleifen durch fehlende Terminierungsbedingungen
# FEHLER: Keine END-Bedingung definiert → Endlosschleife
workflow.add_conditional_edges(
"classifier",
route_decision,
{
"handler_a": "handler_a",
"handler_b": "handler_b"
# Fehlt: END als möglicher Zielknoten
}
)
LÖSUNG: Immer END explizit in den Routing-Möglichkeiten einbeziehen
def safe_routing(state: AgentState) -> str:
"""Sichere Routing-Funktion mit terminierenden Pfaden."""
last_message = state["messages"][-1].content
# Prüfe auf finale Signale
final_signals = ["danke", "ende", "fertig", "bye", "goodbye"]
if any(signal in last_message.lower() for signal in final_signals):
return END # ← Wichtig: END als Rückgabe
# Normaler Routing-Logik
if should_escalate(state):
return "human_agent"
return "auto_response"
workflow.add_conditional_edges(
"classifier",
safe_routing,
{
"human_agent": "human_agent",
"auto_response": "auto_response",
END: END # ← Auch hier END angeben
}
)
2. State-Mutationen vor dem Routing
# FEHLER: Routing auf Basis von bereits modifiziertem State
def bad_router(state: AgentState) -> str:
state["processed"] = True # ← State wird hier mutiert
if state["processed"]:
return "handler_b" # Immer dieser Pfad
return "handler_a"
LÖSUNG: Routing-Funktionen sollten read-only sein
def clean_router(state: AgentState) -> str:
"""Reine Routing-Funktion ohne Seiteneffekte."""
# Nur lesende Operationen
message_count = len(state.get("messages", []))
has_error = state.get("error_flag", False)
if has_error:
return "error_handler"
elif message_count > 10:
return "summary_node"
else:
return "continue_processing"
State-Modifikationen in separaten Nodes durchführen
def update_state_node(state: AgentState) -> AgentState:
"""Separater Node für State-Modifikationen."""
return {"processed": True, "iteration_count": state.get("iteration_count", 0) + 1}
workflow.add_node("state_updater", update_state_node)
workflow.add_edge("state_updater", "router")
workflow.add_conditional_edges(
"router",
clean_router,
{...}
)
3. Typsicherheit bei dynamischen Routing-Zielen
# FEHLER: String-basierte Routing-Werte führen zu Laufzeitfehlern
def broken_router(state: AgentState) -> str:
return state["next_node"] # Kann beliebigen String enthalten
LÖSUNG: Typsichere Routing mit Union Types und Validierung
from typing import Literal
NodeName = Literal["start", "handler_a", "handler_b", "end_node", END]
VALID_NODES: set[str] = {"start", "handler_a", "handler_b", "end_node"}
def type_safe_router(state: AgentState) -> NodeName:
"""Typsichere Routing-Funktion mit Validierung."""
requested = state.get("requested_node", "start")
# Validierung: Nur bekannte Knoten erlaubt
if requested not in VALID_NODES:
return "end_node" # Fail-Safe zu definiertem Knoten
return requested
workflow.add_conditional_edges(
"router",
type_safe_router,
{
"start": "start",
"handler_a": "handler_a",
"handler_b": "handler_b",
"end_node": "end_node",
END: END
}
)
4. Performance-Problem: LLM-Aufruf in jeder Routing-Entscheidung
# FEHLER: LLM für jede Routing-Entscheidung (teuer und langsam)
def expensive_router(state: AgentState) -> str:
response = client.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "user", "content": f"Route: {state['messages']}"}]
)
return response.choices[0].message.content
LÖSUNG: Regelbasiertes Caching mit gestaffeltem Ansatz
from functools import lru_cache
@lru_cache(maxsize=1000)
def cached_route(intent_hash: str) -> str:
"""Cached Routing-Entscheidungen."""
return _expensive_llm_classification(intent_hash)
def fast_router(state: AgentState) -> str:
"""Schnelles Routing mit Fallback-Logik."""
message = state["messages"][-1].content.lower()
# Schnelle Regelprüfungen zuerst
if "help" in message or "hilfe" in message:
return "help_handler"
if message.startswith("/"):
return "command_handler"
# Nur bei Bedarf gecachten LLM-Aufruf
msg_hash = hash(message) % 1000000
return cached_route(str(msg_hash))
Konfiguration für HolySheep (kosteneffizient mit DeepSeek)
def _expensive_llm_classification(text: str) -> str:
"""Fallback: LLM-Klassifikation via HolySheep AI."""
response = client.chat.completions.create(
model="deepseek-v3.2", # Kostengünstiges Modell für Routing
messages=[{"role": "user", "content": f"Klassifiziere: {text}"}],
temperature=0.1
)
return response.choices[0].message.content.strip().lower()
Best Practices für Production-Deployments
- Immer Fallback-Pfade definieren – Unbekannte Intents sollten nicht in Sackgassen landen
- Monitoring integrieren – Routing-Entscheidungen protokollieren für kontinuierliche Optimierung
- Timeouts konfigurieren – LangGraph unterstützt timeout-Parameter für einzelne Nodes
- Graceful Degradation – Bei API-Ausfällen auf einfachere Routing-Logik umschalten
- Kostenkontrolle – Günstigere Modelle wie DeepSeek V3.2 ($0.42/MTok) für Routing nutzen
Fazit
Conditional Edges sind das Rückgrat flexibler LangGraph-Agenten. Mit den hier vorgestellten Techniken – von einfachen routing-Funktionen bis hin zu mehrstufigen Entscheidungsbäumen – können Sie robuste Workflows bauen, die sich dynamisch an jede Anforderung anpassen. Die Integration mit HolySheep AI bietet dabei nicht nur Kosteneinsparungen von über 85%, sondern mit der <50ms Latenz auch die Performance, die Ihre Benutzer erwarten.
Die kostenlosen Credits für Neuanmeldung ermöglichen einen risikofreien Einstieg in die Produktionsentwicklung. Probieren Sie es aus!
👉 Registrieren Sie sich bei HolySheep AI — Startguthaben inklusive