Python & AI Tutorials Logo
LangChain & LangGraph

5. Agent-Vorschau — Der "Aha!"-Moment

Bis zu diesem Punkt haben Sie Systeme gebaut, bei denen Sie den Ablauf kontrollieren. Sie schreiben den Prompt, rufen das LLM auf und verarbeiten die Antwort. Das LLM ist leistungsstark, aber es folgt immer noch nur Ihren Anweisungen.

Dieses Kapitel führt eine grundlegende Veränderung ein: Was wäre, wenn das LLM entscheidet, was als Nächstes passiert?

Warum dieses Kapitel existiert

Das Problem: Wenn Sie weiterhin Chatbots bis Kapitel 11 bauen, ohne diese Vorschau, werden Sie wahrscheinlich denken: "Agenten sind nur bessere Chatbots."

Die Wahrheit: Agenten sind grundlegend anders. Sie treffen Entscheidungen, die Ihr Programm steuern, nicht nur Text generieren.

Die Lösung: Dieses Kapitel zeigt Ihnen, wohin wir gehen, damit Sie beim Aufbau von Pipelines und State Management in den nächsten Kapiteln verstehen, warum jedes Teil wichtig ist.

Was Sie lernen werden (und was nicht)

Dieses Kapitel (Kapitel 5):

  • Eine minimale 15-minütige Vorschau auf agentisches Denken
  • Wie LLM-Ausgaben verschiedene Code-Pfade auslösen können
  • Der konzeptionelle Unterschied zwischen Routing und Agent-Schleifen

Spätere Kapitel (ab Kapitel 12):

  • Echte Agent-Systeme mit Tools, Schleifen und Selbstkorrektur
  • Produktionsreife Implementierungen
  • Fortgeschrittene Muster wie Multi-Agent-Koordination

Warum die Lücke? Bevor Sie Agenten bauen, benötigen Sie Grundlagen: Prompt Engineering (Kap. 4), Ausführungs-Pipelines (Kap. 6), strukturierte Ausgabe (Kap. 7), State Management (Kap. 8) und Wissensintegration (Kap. 9-11).

Ein Hinweis zum Lernansatz: Dieses Kapitel konzentriert sich auf Konzepte, nicht auf Implementierungsdetails. Die tatsächlichen Design- und Entwicklungstechniken kommen in Kapitel 12 und darüber hinaus. Machen Sie sich keine Sorgen, wenn Sie nicht wissen, wie Sie alles implementieren, was Sie hier sehen—das ist beabsichtigt. Es reicht vorerst aus zu verstehen, was ein Agent konzeptionell ist.

Lassen Sie uns beginnen.

5.1) Vom Chatbot zum Entscheidungsträger

Das Konzept: Anstatt die Frage zu beantworten, entscheidet das LLM, welches Tool verwendet werden soll

In den Kapiteln 3 und 4 haben Sie Chat-Systeme gebaut, bei denen das LLM Textantworten generiert. Der Ablauf war unkompliziert:

  1. Benutzer stellt eine Frage
  2. LLM generiert eine Antwort
  3. Sie zeigen die Antwort an

Dies ist eine statische Chain: Der Ablauf ist vorbestimmt. Die einzige Aufgabe des LLM besteht darin, Text zu produzieren.

Lassen Sie uns nun die Art der Anfrage ändern. Ein Benutzer fragt: "Was ist 847 × 923?"

Ihr Chatbot aus Kapitel 3 würde versuchen, dies zu beantworten, aber LLMs berechnen nicht wirklich—sie sagen plausibel aussehende Token voraus. Bei "2 + 2" erscheint die Antwort "4" so oft in den Trainingsdaten, dass das LLM sie richtig bekommt. Aber bei "847 × 923"—einer Berechnung, die das LLM nie gesehen hat—wird es etwas generieren, das wie eine Zahl aussieht, aber wahrscheinlich falsch ist.

Was Sie wirklich wollen:

  1. LLM erkennt: "Das ist ein Matheproblem"
  2. LLM entscheidet: "Verwende das Taschenrechner-Tool, nicht meine Token-Vorhersage"
  3. Python führt aus: 847 × 923 = 781.781
  4. System gibt zurück: Die korrekte Antwort

Die Aufgabe des LLM ist nicht zu berechnen—es ist, zum Tool zu routen, das es kann.

Hier ist die Schlüsselerkenntnis: Das LLM muss die Mathematik nicht lösen—es muss entscheiden, einen Taschenrechner zu verwenden.

Dies ist die Verschiebung vom Chatbot zum Entscheidungsträger. Das LLM untersucht die Anfrage und leitet sie zum entsprechenden Tool weiter. Es trifft eine Entscheidung über den Programmablauf, nicht nur Text zu generieren.

Lassen Sie uns diesen Unterschied visualisieren:

Statische Chain (Chatbot)

Benutzer: Was ist 847 × 923?

LLM generiert Text

Anzeige: 'Ungefähr 780.000...'

Dynamisches Routing (Entscheidungsträger)

Benutzer: Was ist 847 × 923?

LLM entscheidet: CALC benötigt

Ausführen: Python-Taschenrechner

Anzeige: 781.781

In der statischen Chain versucht das LLM, alles direkt zu beantworten. Im dynamischen Routing-System routet das LLM die Anfrage zum richtigen Tool.

Die Visualisierung: Vergleich einer "Linearen Chain" (Statisch) vs. eines "Routers" (Dynamisch)

So behandelt eine statische Chain jede Benutzeranfrage:

Benutzereingabe

LLM

Textantwort

Anzeige für Benutzer

Das LLM versucht, alles direkt zu beantworten. Egal ob Sie fragen "Was ist die Hauptstadt von Frankreich?" oder "Was sind 15 % von 240?", das LLM generiert eine Textantwort. Es bekommt die Hauptstadt möglicherweise richtig (es hat "Paris" oft in den Trainingsdaten gesehen), wird aber wahrscheinlich den Prozentsatz falsch berechnen.

Das Problem: Das LLM verwendet denselben Ansatz für alle Fragen—Token-Vorhersage—selbst wenn bessere Tools existieren.


Lassen Sie uns nun ein System einführen, das verschiedene Arten von Anfragen unterschiedlich behandeln kann:

  1. Faktenfragen: "Was ist die Hauptstadt von Frankreich?" → Verwende ein Such-Tool
  2. Matheprobleme: "Was sind 15 % von 240?" → Verwende einen Taschenrechner
  3. Konversation: "Wie geht es Ihnen?" → LLM antwortet direkt

Hier ist ein dynamischer Router, der dies ermöglicht:

Faktisch

Mathematik

Konversation

Benutzereingabe

LLM-Entscheidungsebene

Such-Tool

Taschenrechner-Tool

Direkte Antwort

Ergebnis formatieren

Anzeige für Benutzer

Das LLM untersucht die Eingabe und wählt einen Pfad basierend darauf, welche Art von Anfrage es ist. Dies ist Routing-Logik, und das LLM fungiert als Router.

Der Hauptunterschied: Anstatt immer Text zu generieren, entscheidet das LLM jetzt, wie jede Anfrage behandelt werden soll—indem es sie zum entsprechenden Tool routet.

Wichtigste Erkenntnis: Von der Generierung von Antworten zur Auswahl von Aktionen

Dies ist die grundlegende Verschiebung in agentischen Systemen:

Traditionelle LLM-Nutzung: Sie stellen eine Frage, das LLM schreibt eine Antwort.

Agentische LLM-Nutzung: Sie stellen eine Frage, das LLM entscheidet, was zu tun ist, und Ihr Code führt diese Entscheidung aus.

Die Ausgabe des LLM ist nicht mehr nur Text für den Benutzer—es sind Anweisungen für Ihr Programm.

Stellen Sie es sich so vor: In einem traditionellen Chatbot ist das LLM ein Mitarbeiter, der Kundenfragen beantwortet. In einem agentischen System ist das LLM ein Manager, der entscheidet, welche Abteilung jede Anfrage bearbeiten soll.

Diese Entscheidungsfähigkeit ist die Essenz agentischer Systeme. Das LLM antwortet nicht nur—es steuert, was Ihr Programm als Nächstes tut. Basierend auf der Eingabe kann es einen Taschenrechner auslösen, eine Such-API aufrufen oder direkt antworten. Das Verhalten des Programms ändert sich basierend auf der Entscheidung des LLM.

Aber wie implementieren wir das tatsächlich? Lassen Sie uns den minimalen Code sehen, der dies ermöglicht.

5.2) Die minimale Ausführung

Der Auslöser: Ein System-Prompt, der strukturierte Ausgabe erzwingt

Um das LLM als Router fungieren zu lassen, müssen wir seine Ausgabe einschränken. Anstatt eine vollständige Antwort zu generieren, wollen wir, dass es eine strukturierte Entscheidung ausgibt—ein JSON-Objekt, das unserem Code sowohl mitteilt, was zu tun ist, als auch welche Daten zu verwenden sind.

Hier ist ein System-Prompt, der dies tut:

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
import json
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
system_prompt = """Sie sind ein Routing-Assistent. Analysieren Sie die Anfrage des Benutzers und antworten Sie mit JSON.
 
Ausgabeformat:
{
  "action": "CALC" | "SEARCH" | "CHAT",
  "input": "bereinigte Eingabe für das Tool"
}
 
Beispiele:
Benutzer: "Berechne 15 mal 7"
Ausgabe: {"action": "CALC", "input": "15 * 7"}
 
Benutzer: "Was ist die Hauptstadt von Japan?"
Ausgabe: {"action": "SEARCH", "input": "Hauptstadt von Japan"}
 
Benutzer: "Hallo!"
Ausgabe: {"action": "CHAT", "input": "Hallo!"}
 
Extrahieren Sie die wesentlichen Informationen und formatieren Sie sie für das entsprechende Tool.
Geben Sie nur gültiges JSON aus, sonst nichts."""
 
def get_routing_decision(user_input: str) -> dict:
    messages = [
        SystemMessage(content=system_prompt),
        HumanMessage(content=user_input)
    ]
    response = llm.invoke(messages)
    
    # JSON-Antwort parsen
    try:
        decision = json.loads(response.content.strip())
        return decision
    except json.JSONDecodeError:
        # Fallback, falls LLM kein gültiges JSON ausgibt
        return {"action": "CHAT", "input": user_input}
 
# Testen Sie es mit ANDEREN Eingaben als den Beispielen
print(get_routing_decision("Was ist 25 * 48?"))  
# Ausgabe: {'action': 'CALC', 'input': '25 * 48'}
 
print(get_routing_decision("Wer hat Hamlet geschrieben?"))  
# Ausgabe: {'action': 'SEARCH', 'input': 'Autor von Hamlet'}
 
print(get_routing_decision("Wie geht es Ihnen heute?"))  
# Ausgabe: {'action': 'CHAT', 'input': 'Wie geht es Ihnen heute?'}

Dieser Prompt ist absichtlich restriktiv. Wir bitten das LLM nicht, kreativ zu sein—wir bitten es, die Eingabe zu klassifizieren und die relevanten Informationen zu extrahieren in einem strukturierten Format.

Beachten Sie, was hier passiert:

  1. Das LLM liest die Frage des Benutzers
  2. Es bestimmt die Absicht (Berechnung, Faktensuche, Konversation)
  3. Es extrahiert und bereinigt die wesentlichen Informationen
  4. Es gibt ein JSON-Objekt mit sowohl der Aktion als auch der bereinigten Eingabe aus
  5. Unser Code empfängt diese strukturierten Daten und routet entsprechend

Das LLM beantwortet die Frage nicht—es entscheidet, was die Frage beantworten soll.

Die Brücke: Verbindung von LLM-Entscheidungen mit Tool-Ausführung

Jetzt müssen wir die Entscheidung des LLM mit der tatsächlichen Tool-Ausführung verbinden. Hier ist die minimale Brücke:

python
def safe_calculator(expression: str) -> float:
    try:
        # Verwenden Sie eval mit eingeschränktem Namespace aus Sicherheitsgründen
        # Hinweis: eval() hat Sicherheitsrisiken.
        # In der Produktion verwenden Sie eine Mathematik-Parsing-Bibliothek wie sympy oder ast-basierte Auswertung.
        result = eval(expression, {"__builtins__": {}}, {})
        return float(result)
    except:
        raise ValueError(f"Konnte nicht berechnen: {expression}")
 
def search(query: str) -> str:
    """Platzhalter-Suchfunktion"""
    # In der Realität würde dies eine Such-API aufrufen
    # Empfängt jetzt bereinigte Abfrage wie "Autor von Hamlet"
    return f"Suchergebnisse für: {query}"
 
def route_and_execute(user_input: str) -> str:
    """LLM-Entscheidung abrufen und das entsprechende Tool ausführen"""
    decision = get_routing_decision(user_input)
    
    action = decision["action"]
    tool_input = decision["input"]  # LLM-bereinigte Eingabe
    
    if action == "CALC":
        try:
            result = safe_calculator(tool_input)
            return f"Berechnungsergebnis: {result}"
        except ValueError as e:
            return f"Konnte Berechnung nicht durchführen: {e}"
    
    elif action == "SEARCH":
        result = search(tool_input)
        return result
    
    else:  # CHAT
        # Für Konversationsanfragen lassen Sie das LLM direkt antworten
        response = llm.invoke([HumanMessage(content=tool_input)])
        return response.content
 
# Testen Sie den vollständigen Ablauf
print(route_and_execute("Was ist 25 * 48?"))
# LLM extrahiert "25 * 48" → Taschenrechner erhält bereinigte Eingabe
# Ausgabe: Berechnungsergebnis: 1200.0
 
print(route_and_execute("Wer hat Hamlet geschrieben?"))
# LLM formatiert um zu "Autor von Hamlet" → bessere Suchanfrage
# Ausgabe: Suchergebnisse für: Autor von Hamlet
 
print(route_and_execute("Wie geht es Ihnen heute?"))
# Ausgabe: Mir geht es gut, danke der Nachfrage!

Dies ist das minimale Routing-Muster in seiner einfachsten Form:

  1. LLM-Entscheidung: Routing-Entscheidung vom LLM abrufen
  2. Code-Ausführung: Python-Code prüft die Entscheidung
  3. Tool-Aufruf: Entsprechende Funktion wird aufgerufen
  4. Ergebnis-Rückgabe: Ausgabe wird formatiert und zurückgegeben

Die kritische Erkenntnis: Die Ausgabe des LLM ist nicht mehr Text für den Benutzer—es sind strukturierte Daten, die das Verhalten Ihres Programms steuern. Das action-Feld teilt Ihrem Code mit, welchen Pfad er nehmen soll, und das input-Feld liefert die bereinigten Daten für dieses Tool. Diese JSON-Antwort wird zu ausführbarer Logik.

Dies ist grundlegend anders als ein Chatbot. In einem Chatbot geht die Ausgabe des LLM direkt zum Benutzer. Hier geht die Ausgabe des LLM zu Ihrem Code, der dann entscheidet, was ausgeführt werden soll.

Was wir in diesem Abschnitt gebaut haben, ist ein Router—ein Vorläufer vollständiger Agenten. Er demonstriert das grundlegende Prinzip (LLM steuert den Ablauf), aber es fehlt die Iteration und Selbstkorrektur, die echte Agent-Schleifen definieren.

Dieses einfache Routing-System hat eine große Einschränkung: Es kann seine eigenen Fehler nicht korrigieren. Lassen Sie uns untersuchen, warum das wichtig ist und was als Nächstes kommt.

5.3) Verständnis der Terminologie: Router vs. Agent

Was wir gerade gebaut haben, ist ein Router. Aber wie vergleicht er sich mit vollständigen Agenten? Lassen Sie uns klare Definitionen festlegen:

Router (was wir gerade gebaut haben)

  • Trifft eine Klassifizierungsentscheidung pro Anfrage
  • Wählt aus, welches Tool/welcher Pfad ausgeführt werden soll
  • Führt einmal aus und gibt zurück
  • Keine Iteration, kein State, keine Selbstkorrektur
  • Beispiel: E-Mail-Klassifizierer, Intent-Detektor, Tool-Selektor

Tool-calling Assistant (Kapitel 13)

  • LLM kann Tools direkt über Function-Calling-API aufrufen
  • Immer noch typischerweise Single-Turn (eine Anfrage → eine Antwort)
  • Ausgefeilter als Routing, aber nicht notwendigerweise iterativ
  • Beispiel: "Suche nach Wetter und fasse zusammen" in einem Aufruf

Agent (vollständige Schleife) (Kapitel 14-17)

  • Iteriert durch denken → handeln → beobachten-Zyklen
  • Trägt State zwischen Iterationen
  • Kann Entscheidungen basierend auf Ergebnissen revidieren
  • Implementiert Selbstkorrektur
  • Beispiel: Debugging-Assistent, der Fixes ausprobiert, bis Code funktioniert

Agentisches System (Oberbegriff)

  • Jedes System, bei dem LLM-Ausgabe den Kontrollfluss beeinflusst
  • Umfasst Router, Tool-calling Assistants und vollständige Agenten
  • "Agentisch" beschreibt die Eigenschaft; "Agent" beschreibt eine spezifische Architektur
  • Beispiel: Alle oben genannten zeigen agentisches Verhalten

Was wir in 5.2 gebaut haben, ist ein Router—er trifft eine Entscheidung und führt sie aus. Dies funktioniert gut für einfache Klassifizierungsaufgaben, kann aber keine Situationen bewältigen, die mehrere Schritte, Überprüfung oder Kurskorrektur erfordern. Wenn die anfängliche Entscheidung suboptimal ist oder sich die Aufgabe als komplexer herausstellt als erwartet, hat der Router keinen Mechanismus zur Anpassung.

Agenten lösen diese Einschränkung durch Einführung von Iteration. Sie können das Ergebnis einer Aktion beobachten, ihren Ansatz überdenken und es erneut versuchen. Dies macht sie geeignet für komplexe, mehrstufige Aufgaben, bei denen der Weg zur Lösung nicht von Anfang an klar ist.

In den Kapiteln 14-17 lernen Sie, wie Sie diese iterativen Agent-Schleifen bauen.

Hinweis: Kürzlich können Reasoning-Modelle wie o1 und o3 einige mehrstufige Aufgaben intern bewältigen, was den Bedarf an expliziten Schleifen in bestimmten Szenarien reduziert. Wir werden untersuchen, wann Schleifen vs. Reasoning-Modelle verwendet werden sollten, während Sie echte Agenten in Teil IV bauen.


Was Sie jetzt verstehen sollten:

  1. Agentische Systeme beginnen mit Kontrollfluss: Die Ausgabe des LLM bestimmt, was Ihr Code als Nächstes tut
  2. Routing ist die einfachste Form: Eine Entscheidung, ein Tool, ein Ergebnis—was wir in 5.2 gebaut haben
  3. Echte Agenten benötigen Schleifen: Um mehrstufige Aufgaben, Überprüfung und Fehlerwiederherstellung zu bewältigen (Kapitel 14-17)
  4. Selbstkorrektur ist der Hauptunterschied: Agenten können Ergebnisse beobachten und ihren Ansatz anpassen

Was wir nicht behandelt haben (und erst in späteren Kapiteln behandeln werden):

  • Wie man Ausführungs-Pipelines zusammensetzt (Kapitel 6)
  • Wie man strukturierte Ausgabe behandelt (Kapitel 7)
  • Wie man Konversationsspeicher verwaltet (Kapitel 8)
  • Wie man Tools richtig definiert (Kapitel 12)
  • Wie man Agent-Schleifen mit Selbstkorrektur implementiert (Kapitel 14-17)
  • Wie man Agenten produktionsreif macht (Kapitel 18-26)

Dieses Kapitel handelte von der konzeptionellen Verschiebung—dem Verständnis, was ein System agentisches Verhalten zeigen lässt. Die Implementierungsdetails kommen später.

Im nächsten Kapitel werden wir weiterhin praktische Grundlagen aufbauen: Zusammensetzen von Ausführungs-Pipelines mit LangChain Expression Language (LCEL). Dies sind die Bausteine, die Sie verwenden werden, wenn wir echte Agenten in Teil IV implementieren.

Der "Aha!"-Moment ist abgeschlossen. Sie verstehen jetzt, dass agentische Systeme solche sind, bei denen das LLM entscheidet und Ihr Code ausführt. Alles andere—Tools, Schleifen, State Management—dient dazu, diesen Entscheidungs-Ausführungs-Zyklus robuster und leistungsfähiger zu machen.

Lassen Sie uns weiterhin die Grundlage aufbauen.