Frameworks wie LangChain und LangGraph haben sich in der Data-Science- und AI-Welt zum absoluten Standard entwickelt, wenn es darum geht, leistungsfähige Systeme auf Basis von Large Language Models (LLMs) zu entwerfen. Sie nehmen uns Entwicklern eine Menge Boilerplate-Code ab und bieten klare, modulare Strukturen.
Aber wann nutzt man was?
- LangChain ist das Basis-Framework. Es hilft dir dabei, LLMs in deine Anwendungen, Tools, und Datenquellen einzubinden. Es ist ideal für klassische RAG-Anwendungen (Retrieval-Augmented Generation), einfache Chatbots oder wenn du strukturierte Daten aus unstrukturiertem Text extrahieren willst.
- LangGraph baut direkt auf LangChain auf. Während klassische Chains linear ablaufen, ermöglicht LangGraph die Orchestrierung von zyklischen, graphenbasierten Workflows. Es nutzt Zustandsmaschinen (State Machines) und ist damit die perfekte Wahl für komplexe Agenten-Systeme, Multi-Agent-Szenarien und autonome Prozesse, bei denen der Agent selbst entscheidet, wann er welches Tool nutzt und wann er fertig ist.
Einrichten der Umgebung
API Key im Google AI Studio generieren
- Gehe zum Google AI Studio.
- Melde dich mit deinem Google-Konto an.
- Klicke links in der Navigation auf Get API key.
- Wähle Create API key in new project (oder wähle ein bestehendes Google Cloud Projekt aus, falls du schon eines hast).
- Kopiere den generierten Schlüssel.
GCP Projekt einrichten
Das gewählte Google Cloud Projekt muss einen aktiviertes Billing Account (Rechnungskonto) haben. Das kann man unter console.google.com unter Abrechnung / Kontoverwaltung tun.
Außerdem muss die Agent Platform API aktiviert sein.
Langchain einrichten
Neben LangChain und LangGraph benötigen wir speziell das Paket für die Google-Integration:
uv pip install -U langchain-google-genai langchain langgraph pydantic python-dotenvDer API Key muss im Projektordner hinterlegt sein. Das geht am besten über eine Environment-Datei namens .env liegen, die den API Key enthält:
GOOGLE_API_KEY=''In Python muss nun das Environment geladen werden mit:
from dotenv import load_dotenv
load_dotenv()Das LLM initialisieren
Starten wir mit den Basics. Wir initialisieren das Modell und schicken einen einfachen Prompt ab. Für die meisten Agenten- und RAG-Tasks ist gemini-3.1-flash aktuell eine hervorragende Wahl, da es extrem schnell ist und ein großes Kontextfenster bietet.
from langchain_google_genai import ChatGoogleGenerativeAI
# Das LLM initialisieren. Der API-Key wird automatisch aus der Umgebungsvariable geladen.
model = ChatGoogleGenerativeAI(
model="gemini-3.1-flash",
temperature=0.7,
max_retries=2,
)
# Einfachen Prompt an das Modell senden
response = model.invoke("Nenne mir die wichtigste Geheimzutat für eine richtig gute Tomatensoße.")
print(response.content)Konversationen und Nachrichten-Typen
LangChain standardisiert, wie wir mit Modellen kommunizieren, über spezifische Message-Klassen. Die wichtigsten sind:
SystemMessage: Setzt das Verhalten und die Persona des Modells.HumanMessage: Die Eingabe des Nutzers.AIMessage: Die Antwort des Modells.
Statt nur eines einzelnen Strings können wir dem Modell eine komplette Konversationshistorie als Liste von Nachrichten übergeben.
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from pprint import pprint
messages = [
SystemMessage(content="Du bist ein erfahrener italienischer Koch."),
HumanMessage(content="Was ist das Geheimnis einer echten Carbonara?"),
AIMessage(content="Guanciale, Pecorino Romano, Eier und viel schwarzer Pfeffer – niemals Sahne!"),
HumanMessage(content="Klingt lecker! Welches Nudelgericht empfiehlst du mir stattdessen, wenn ich lieber etwas mit Tomaten möchte?")
]
response = model.invoke(messages)
print(response.content)System Prompt
Ein System Prompt kann dem Agenten über einen weiteren Parameter einfach als String übergeben werden. Dort könne auch techniken wie Few Shot Prompting oder strukturierte System Prompts verwendet werden.
system_prompt = "Du bist ein freundlicher Sous-Chef. Erstelle ein kreatives 3-Gänge-Menü auf Basis der Nutzer-Vorlieben."
agent = create_agent(
model=model
system_prompt=system_prompt
)
Streaming für bessere UX
Bei längeren Antworten oder komplexen Erklärungen möchte niemand 10 Sekunden auf einen Textblock warten. Mit Streaming können wir die Antwort Stück für Stück (Token für Token) ausgeben, sobald sie vom Modell generiert wird. Das reduziert die gefühlte Latenz enorm.
# Stream_mode liefert uns Chunks zurück, während sie generiert werden
for chunk in model.stream(messages):
if chunk.content:
# flush=True sorgt dafür, dass die Konsole sofort aktualisiert wird
print(chunk.content, end="", flush=True)Strukturierter Output (JSON)
Im Data Science Alltag wollen wir oft keine prosaischen Texte vom LLM, sondern saubere, strukturierte Daten – beispielsweise um sie in eine Datenbank zu schreiben oder in einer Pipeline weiterzuverarbeiten. Pydantic ist hier das Mittel der Wahl, um das Schema zu definieren.
Mit .with_structured_output() zwingen wir Gemini dazu, sich strikt an unsere Pydantic-Klasse zu halten.
from pydantic import BaseModel, Field
# Wir definieren unser gewünschtes Daten-Schema
class Rezept(BaseModel):
name: str = Field(description="Name des Gerichts")
zubereitungszeit_min: int = Field(description="Geschätzte Zubereitungszeit in Minuten")
schwierigkeitsgrad: str = Field(description="Schwierigkeitsgrad (z.B. einfach, mittel, schwer)")
hauptzutat: str = Field(description="Die wichtigste Zutat des Gerichts")
# Das Modell anweisen, exakt dieses Schema zurückzugeben
structured_model = model.with_structured_output(Rezept)
question = "Ich habe nach der Arbeit wenig Zeit, bin kein Profi-Koch und habe noch frischen Lachs im Kühlschrank. Was kann ich machen?"
response = structured_model.invoke(question)
# Das Ergebnis ist ein sauberes Pydantic-Objekt, kein unstrukturierter Text!
print(f"Gericht: {response.name}")
print(f"Dauer: {response.zubereitungszeit_min} Min.")
print(f"Schwierigkeit: {response.schwierigkeitsgrad}")
print(f"Fokus: {response.hauptzutat}")Tools und LangGraph Agenten
Ein LLM allein kann nur Text generieren. Wenn wir dem Modell aber Tools an die Hand geben, kann es Code ausführen, APIs abfragen oder Berechnungen anstellen. Dazu müssen wir zunächst einen Agenten erzeugen.
from langchain.agents import create_agent
agent = create_agent(model=model)Wir definieren ein einfaches Python-Tool über den @tool Dekorator. Wichtig: Der Docstring ist entscheidend, denn das LLM liest ihn, um zu verstehen, wann und wie es das Tool einsetzen soll.
Eine Nachricht an den Agenten geben:
from langchain_core.tools import tool
@tool
def berechne_kalorien(kohlenhydrate_g: float, protein_g: float, fett_g: float) -> float:
"""
Berechnet die Gesamtkalorien eines Gerichts anhand der Makronährstoffe.
Benutze dieses Tool immer dann, wenn der Nutzer nach den Kalorien
oder dem Brennwert eines Essens fragt.
"""
return (kohlenhydrate_g * 4) + (protein_g * 4) + (fett_g * 9)
tools = [berechne_kalorien]Um dem Modell nun Autonomie zu geben (sodass es selbst entscheidet, ob es das Tool nutzen muss), verwenden wir LangGraph. Die Funktion create_react_agent baut im Hintergrund einen Workflow, der die ReAct-Logik (Reasoning and Acting) implementiert.
from langgraph.prebuilt import create_react_agent
# Der Agent kombiniert unser Gemini-Modell mit unserer Tool-Liste
agent = create_react_agent(model, tools)
question = HumanMessage(content="Mein Teller Linsen-Dal hat 50g Kohlenhydrate, 25g Protein und 10g Fett. Wie viele Kalorien sind das insgesamt?")
# Agent ausführen
response = agent.invoke({"messages": [question]})
# Die letzte Nachricht enthält das finale Ergebnis nach der Tool-Nutzung
print(response['messages'][-1].content)Wenn wir uns die Historie dieses Aufrufs genau ansehen (print(response["messages"])), sehen wir, dass das LLM tatsächlich das Tool berechne_uebersetzung(32, 52) aufgerufen, das Ergebnis (0.615) erhalten und erst danach die finale Antwort formuliert hat.
Gedächtnis als Memory hinzufügen
Ein klassischer Agent vergisst nach jedem Aufruf alles, was davor passiert ist. In LangGraph können wir über einen Checkpointer den State des Graphen speichern. Wir nutzen hierfür den MemorySaver (früher InMemorySaver).
from langgraph.checkpoint.memory import MemorySaver
# Checkpointer initialisieren
memory = MemorySaver()
# Agent mit Gedächtnis erstellen
stateful_agent = create_react_agent(
model,
tools,
checkpointer=memory
)
# Wir müssen eine Thread-ID übergeben, damit das System weiß, zu welcher Konversation es gehört
config = {"configurable": {"thread_id": "session_1"}}
# Nachricht 1
msg1 = HumanMessage(content="Hi, ich bin Julia und mein absolutes Lieblingsessen ist scharfes Thai-Curry.")
stateful_agent.invoke({"messages": [msg1]}, config)
# Nachricht 2 (Der Agent kennt den Kontext aus Nachricht 1 noch)
msg2 = HumanMessage(content="Welche Länderküche mag ich vermutlich am liebsten und wie heiße ich?")
response = stateful_agent.invoke({"messages": [msg2]}, config)
print(response['messages'][-1].content)Der Agent wird nun korrekt antworten, dass du Julia heißt und vermutlich die thailändische Küche liebst – der State bleibt über die thread_id erhalten. Das ist die absolute Grundlage für jeden Chatbot oder komplexen Assistenten, den man heute in Produktion bringt.