3. Bauen Sie Ihren ersten Streaming-CLI-Chat
In Kapitel 1 haben Sie Ihren ersten LLM-Call gemacht und gesehen, wie eine vollständige Antwort auf einmal erscheint. In Kapitel 2 haben Sie die konzeptionellen Grundlagen der agentischen KI kennengelernt und warum es LangChain gibt. Jetzt ist es Zeit, etwas Praktisches zu bauen: eine Streaming-Chat-Anwendung, die sich reaktionsschnell und professionell anfühlt.
Warum Streaming wichtig ist: Wenn Sie einem LLM eine komplexe Frage stellen, wirkt es wie ein Bug, 10–30 Sekunden auf eine vollständige Antwort zu warten. Streaming lässt Tokens erscheinen, während sie generiert werden, und schafft so einen natürlichen Gesprächsfluss. Dieses Kapitel baut eine CLI-Chat-Anwendung mit Streaming-Ausgabe, sauberem Konfigurationsmanagement, Debugging-Fähigkeiten und robuster Fehlerbehandlung.
Was Sie bauen werden: Am Ende dieses Kapitels haben Sie ein funktionierendes chat.py-Skript, das:
- LLM-Antworten Token für Token in das Terminal streamt
- API-Keys sicher aus Umgebungsvariablen lädt
- Verschiedene Modelltypen (Chat- vs. Reasoning-Modelle) mit passenden Parametern behandelt
- Debugging-Tools bereitstellt, um zu prüfen, was tatsächlich an das LLM gesendet wird
- Häufige Fehler sauber behandelt (fehlende API-Keys, Netzwerkfehler, ungültige Eingaben)
3.1) Erstellen Sie einen Arbeitsordner und installieren Sie Pakete
Bevor Sie Code schreiben, benötigen Sie eine saubere Projektstruktur und die richtigen Abhängigkeiten. Dieser Abschnitt legt das Fundament für ein wartbares Python-Projekt.
Projektstruktur
Erstellen Sie ein neues Verzeichnis für Ihre Chat-Anwendung:
mkdir langchain-chat
cd langchain-chatPython-Umgebung einrichten
Erstellen Sie eine virtuelle Umgebung, um Abhängigkeiten zu isolieren:
# Virtuelle Umgebung erstellen
python -m venv venv
# Aktivieren (macOS/Linux)
source venv/bin/activate
# Aktivieren (Windows)
venv\Scripts\activateWarum virtuelle Umgebungen? LangChain hat viele Abhängigkeiten (z. B. OpenAI SDK, Pydantic, Async-Libraries). Eine virtuelle Umgebung stellt sicher:
- Ihr System-Python bleibt sauber
- Verschiedene Projekte können unterschiedliche LangChain-Versionen verwenden
- Abhängigkeiten sind reproduzierbar (über
requirements.txt)
Sie sehen (venv) in Ihrem Terminal-Prompt, wenn es aktiviert ist.
LangChain installieren
Installieren Sie die Kernpakete von LangChain:
pip install langchain-core==1.2.7 langchain-openai==1.1.7 python-dotenvPaket-Aufschlüsselung:
langchain-core: Kernabstraktionen (Messages, Prompts, Chains, Runnables)langchain-openai: OpenAI-spezifische Implementierungen (ChatOpenAI, Embeddings)python-dotenv: Lädt Umgebungsvariablen aus.env-Dateien
Versionshinweis: Dieses Buch verwendet LangChain 1.2.x (Stand Januar 2026). Wenn Sie das später lesen, prüfen Sie die LangChain-Dokumentation auf die neueste Version.
Installation verifizieren
Erstellen Sie einen einfachen Test, um zu bestätigen, dass alles funktioniert:
# test_install.py
try:
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
print("✓ langchain-core: OK")
print("✓ langchain-openai: OK")
print("\nInstallation erfolgreich!")
except ImportError as e:
print(f"✗ Import fehlgeschlagen: {e}")
print("Stellen Sie sicher, dass Ihre virtuelle Umgebung aktiviert ist.")Führen Sie ihn aus:
python test_install.pyErwartete Ausgabe:
✓ langchain-core: OK
✓ langchain-openai: OK
Installation erfolgreich!Wenn Sie „Installation erfolgreich!“ sehen, können Sie fortfahren. Wenn Sie einen Import-Fehler erhalten, prüfen Sie noch einmal, dass:
- Ihre virtuelle Umgebung aktiviert ist (achten Sie auf
(venv)in Ihrem Prompt) - Die Pakete erfolgreich installiert wurden (versuchen Sie
pip listauszuführen)
requirements.txt erstellen
Sie haben gerade Pakete mit pip install-Befehlen installiert. Das funktioniert zum Lernen, aber es gibt eine bessere Methode: requirements.txt-Dateien. Das ist Standardpraxis in Python-Projekten aus mehreren Gründen:
Warum requirements.txt verwenden?
- Reproduzierbarkeit: Andere (oder Sie in 6 Monaten) können exakt dieselben Paketversionen installieren
- Klare Abhängigkeitsverwaltung: Sie sehen auf einen Blick, welche Pakete Ihr Projekt braucht
- Team-Zusammenarbeit: Teammitglieder nutzen identische Versionen und vermeiden „works on my machine“-Probleme
- Automatisierung: Server oder CI/CD-Pipelines können die Umgebung mit einer Zeile aufsetzen:
pip install -r requirements.txt
Erstellen Sie eine requirements.txt-Datei im Projekt-Root:
# requirements.txt
langchain-core==1.2.7
langchain-openai==1.1.7
python-dotenvBeachten Sie die Syntax:
==1.2.7pinnt auf eine exakte Version (empfohlen für Reproduzierbarkeit)- Kein Versions-Spezifizierer (wie
python-dotenv) installiert die neueste stabile Version - Zeilen, die mit
#beginnen, sind Kommentare
Jetzt kann jeder alle Abhängigkeiten mit einem einzigen Befehl installieren:
pip install -r requirements.txtDas ist viel besser, als jedes Paket einzeln zu tippen. Wenn ein Teamkollege Ihr Projekt klont, muss er nur:
- Eine virtuelle Umgebung erstellen
pip install -r requirements.txtausführen
Keine Paketnamen oder Versionen merken—alles steht in der Datei.
Ihre Projektstruktur
Nach Abschluss dieses Abschnitts sollte Ihr Ordner so aussehen:
langchain-chat/
├── venv/ # Virtuelle Umgebung (nicht in git committen)
├── requirements.txt # Abhängigkeitsliste
└── test_install.py # Skript zur InstallationsprüfungAls Nächstes: Abschnitt 3.2 zeigt, wie Sie API-Keys sicher über .env-Dateien laden.
3.2) Umgebungsvariablen mit .env
API-Keys sind Geheimnisse. Sie direkt in den Code zu schreiben ist ein Sicherheitsrisiko (insbesondere, wenn Sie in git committen). Dieser Abschnitt zeigt den Standardansatz: Umgebungsvariablen, die aus einer .env-Datei geladen werden.
Warum Umgebungsvariablen?
Das Problem mit hartcodierten Keys:
# ❌ MACHEN SIE DAS NIEMALS
llm = ChatOpenAI(api_key="sk-proj-abc123...")Wenn Sie diesen Code auf GitHub committen, ist Ihr API-Key öffentlich. Jeder kann ihn nutzen, Kosten auf Ihrem Account verursachen oder dafür sorgen, dass Ihr Key widerrufen wird.
Die Lösung: Speichern Sie Secrets in Umgebungsvariablen und laden Sie sie zur Laufzeit.
Die .env-Datei erstellen
Erstellen Sie eine .env-Datei im Projekt-Root:
# .env
OPENAI_API_KEY=sk-proj-ihren-echten-key-hierHolen Sie sich Ihren API-Key:
- Gehen Sie zu platform.openai.com/api-keys
- Erstellen Sie einen neuen Secret Key
- Kopieren Sie ihn sofort (Sie können ihn danach nicht mehr ansehen)
- Fügen Sie ihn in Ihre
.env-Datei ein und ersetzen Siesk-proj-ihren-echten-key-hier
Kritischer Sicherheitsschritt: Bevor Sie irgendetwas anderes tun, schützen Sie Ihren API-Key davor, in git committed zu werden.
Erstellen Sie eine .gitignore-Datei im Projekt-Root und fügen Sie diese Zeilen hinzu:
# .gitignore
venv/
__pycache__/
*.pyc
.envDie Zeile .env sagt git, dass es Ihre API-Key-Datei ignorieren soll. Das verhindert, dass Secrets versehentlich in die Versionskontrolle gelangen.
Ihre Projektstruktur jetzt:
langchain-chat/
├── venv/
├── .env # Ihr API-Key (von git ignoriert)
├── .gitignore # Enthält: .env, venv/, etc.
├── requirements.txt
└── test_install.pyUmgebungsvariablen laden
Das Paket python-dotenv lädt .env-Dateien in os.environ:
# chat.py
import os
from dotenv import load_dotenv
# .env-Datei laden
load_dotenv()
# Zugriff auf Umgebungsvariablen
api_key = os.environ.get("OPENAI_API_KEY")
if not api_key:
raise ValueError("OPENAI_API_KEY wurde in der Umgebung nicht gefunden")
print(f"API-Key geladen: {api_key[:8]}...") # Nur die ersten 8 Zeichen anzeigenWie load_dotenv() funktioniert:
- Sucht nach einer
.env-Datei, beginnend dort, wo Sie das Skript ausführen - Liest jede Zeile im Format
KEY=value - Fügt jede Variable zu
os.environhinzu - Wenn eine Variable bereits gesetzt ist (z. B. durch Ihre Hosting-Plattform), wird sie nicht überschrieben—der vorhandene Wert bleibt bestehen
Den API-Key mit LangChain verwenden
LangChains OpenAI-Implementierungen (ChatOpenAI, etc.) suchen automatisch nach OPENAI_API_KEY in os.environ:
from langchain_openai import ChatOpenAI
load_dotenv()
# Das verwendet automatisch os.environ["OPENAI_API_KEY"]
llm = ChatOpenAI(model="gpt-4o-mini")LangChains Konvention: Wenn Sie ChatOpenAI() ohne den Parameter api_key erstellen, sucht es automatisch nach OPENAI_API_KEY in der Umgebung. Das ist ein Standardmuster über LangChain-Integrationen hinweg.
Expliziter API-Key (für Tests oder mehrere Keys):
llm = ChatOpenAI(
model="gpt-4o-mini",
api_key=os.environ.get("OPENAI_API_KEY")
)Das ist nützlich, wenn Sie mehrere API-Keys haben (Development vs Production) oder explizit festlegen möchten, welcher Key genutzt wird.
Umgebungsvariablen in Produktion
In Produktionsumgebungen (Cloud-Plattformen, Docker-Container) verwenden Sie keine .env-Dateien. Stattdessen konfigurieren Sie Umgebungsvariablen über die Einstellungen der Plattform:
- Docker: Verwenden Sie das Flag
-ebeim Starten von Containern - Cloud-Plattformen: Setzen Sie Umgebungsvariablen in Konfigurations-Dashboards
- CI/CD: Verwenden Sie Secret-Management-Tools
Der wichtige Punkt: Ihr Code ändert sich nicht. os.environ.get("OPENAI_API_KEY") funktioniert genauso, egal ob die Variable aus einer .env-Datei oder von einer Cloud-Plattform kommt. Deployment behandeln wir in späteren Kapiteln im Detail.
Setup verifizieren
Um zu bestätigen, dass alles funktioniert, können Sie den Code zum Laden von Umgebungsvariablen von oben testen. Wenn Ihre .env-Datei korrekt konfiguriert ist, gibt os.environ.get("OPENAI_API_KEY") Ihren API-Key zurück.
Wenn os.environ.get("OPENAI_API_KEY") None zurückgibt, prüfen Sie:
- Sie haben
load_dotenv()aufgerufen, bevor Sie auf die Umgebungsvariable zugreifen .envexistiert im Projekt-RootOPENAI_API_KEY=sk-proj-...ist korrekt in.envgeschrieben- Sie führen aus dem Projekt-Root-Verzeichnis aus
Als Nächstes: Abschnitt 3.3 implementiert die eigentliche Chat-Schleife mit Streaming-Ausgabe.
3.3) Implementieren der Chat-Schleife mit Streaming-Ausgabe
Jetzt bauen Sie die Kern-Chat-Schleife. Dieser Abschnitt führt Streaming ein – den entscheidenden Unterschied zwischen einem trägen Chatbot und einem responsiven Chatbot.
Streaming verstehen
Ohne Streaming (Ansatz aus Kapitel 1):
response = llm.invoke("Schreibe einen Aufsatz mit 500 Wörtern über KI")
print(response.content) # 20 Sekunden warten, dann erscheint der ganze EssayMit Streaming:
for chunk in llm.stream("Schreibe einen Aufsatz mit 500 Wörtern über KI"):
print(chunk.content, end="", flush=True) # Tokens erscheinen, während sie generiert werdenWarum Streaming wichtig ist:
- Sofortiges Feedback: Statt 20 Sekunden auf einen leeren Bildschirm zu starren, sehen Sie sofort Wörter erscheinen
- Natürliches Gesprächsgefühl: Genau wie beim Sprechen mit einer Person – Antworten kommen nach und nach, nicht auf einmal
- Zeit und Geld sparen: Wenn das LLM anfängt, die falsche Antwort zu geben, können Sie früh stoppen, statt auf eine vollständige (nutzlose) Antwort zu warten
- Besseres Debugging: Beim Bauen von Anwendungen erkennen Sie Probleme (wie Formatierungsfehler) sofort, nicht erst nach langem Warten
Was Streaming eigentlich ist: Streaming ist die inkrementelle Auslieferung desselben Antworttexts. Es legt keine versteckten Reasoning-Schritte oder internen Modellprozesse offen – es zeigt Ihnen nur Teilausgaben, sobald sie über die API verfügbar sind. Stellen Sie es sich wie das Herunterladen einer Datei vor: Sie sehen Fortschritt, wenn Chunks ankommen, aber der Dateiinhalt ist derselbe, egal ob Sie alles auf einmal oder in Teilen herunterladen.
Hinweis zu Chunk-Grenzen: Chunks sind nicht garantiert an Wörter oder Sätze ausgerichtet. Die API sendet Tokens aus Effizienzgründen in kleinen Batches, daher kann ein Chunk „Hel“, „lo! How“, „ can I“, „ help you“, „?“ sein. Das ist normal und zu erwarten – versuchen Sie nicht, Bedeutung aus einzelnen Chunks zu lesen.
Die grundlegende Chat-Schleife
Hier ist eine minimale Streaming-Chat-Schleife:
# chat.py
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
def main():
load_dotenv()
llm = ChatOpenAI(model="gpt-4o-mini")
print("Chat gestartet. Tippen Sie 'quit' oder 'exit', um zu beenden.\n")
while True:
user_input = input("You: ")
if user_input.lower() in ["quit", "exit"]:
print("Auf Wiedersehen!")
break
print("Assistant: ", end="", flush=True)
for chunk in llm.stream([HumanMessage(content=user_input)]):
print(chunk.content, end="", flush=True)
print("\n")
if __name__ == "__main__":
main()So funktioniert das:
while True:: Endlosschleife für fortlaufende Konversationinput("You: "): Benutzerinput aus dem Terminal holenllm.stream([HumanMessage(...)]): LLM-Antwort streamen- Streaming-Ausgabe mit speziellen Parametern:
end="": Kein Zeilenumbruch nach jedem Chunk (hält die Ausgabe in derselben Zeile)flush=True: Erzwingt sofortige Ausgabe ins Terminal ohne Buffering
Warum [HumanMessage(content=user_input)]?
LangChains Chat-Modelle erwarten eine Liste von Messages, keinen rohen String. Jede Message hat eine Rolle:
- HumanMessage: Benutzereingabe
- AIMessage: LLM-Antwort
- SystemMessage: Instruktionen für das LLM (behandeln wir in Kapitel 4)
Selbst für eine einzelne Benutzernachricht übergeben Sie eine Liste: [HumanMessage(content="Hallo")].
Wichtige Einschränkung – Single-Turn-Konversationen: Diese Chat-Schleife ist absichtlich stateless. Jeder Request sendet nur die aktuelle Nachricht, nicht die bisherige Konversationshistorie. Das bedeutet:
- Das LLM wird sich nicht daran erinnern, was Sie zuvor gefragt haben
- Anschlussfragen wie „Wie groß ist seine Bevölkerung?“ funktionieren nicht, nachdem Sie „Was ist die Hauptstadt von Frankreich?“ gefragt haben
- Das ist eine grundlegende Eigenschaft von LLMs – sie haben kein Gedächtnis, solange Sie nicht explizit Kontext mitgeben
Beispiel der Einschränkung:
You: Was ist die Hauptstadt von Frankreich?
Assistant: Paris.
You: Wie groß ist seine Bevölkerung?
Assistant: Ich habe nicht genug Kontext. Welche Stadt meinen Sie?Die while True-Schleife bietet UX-Kontinuität (Sie können weiterchatten), aber jeder Dialogschritt ist unabhängig. In Kapitel 8: Wir implementieren Konversationsspeicher, indem wir die Message-Historie speichern und bei jedem Request erneut mitsenden.
Die Chat-Schleife ausführen
python chat.pyBeispielinteraktion:
Chat gestartet. Tippen Sie 'quit' oder 'exit', um zu beenden.
You: Was ist LangChain?
Assistant: LangChain ist ein Framework zur Entwicklung von Anwendungen, die von Sprachmodellen angetrieben werden. Es bietet Tools für Prompt-Management, Chains, Agents und Memory.
You: Gib mir ein einfaches Beispiel
Assistant: Hier ist ein einfaches Beispiel: ...
You: quit
Auf Wiedersehen!Die Streaming-API verstehen
Was ist ein „Chunk“?
Jeder Chunk ist ein AIMessageChunk-Objekt mit:
content: Die generierten Text-Tokensresponse_metadata: Modellinfos, Token-Nutzung, usw.
for chunk in llm.stream([HumanMessage(content="Hello")]):
print(f"Chunk: {chunk}")
print(f"Content: {chunk.content}")
print(f"Type: {type(chunk)}")Ausgabe:
Chunk: content='Hello' response_metadata={'model_provider': 'openai', ...}
Content: Hello
Type: <class 'langchain_core.messages.ai.AIMessageChunk'>
Chunk: content='!' response_metadata={...}
Content: !
Type: <class 'langchain_core.messages.ai.AIMessageChunk'>
Chunk: content=' How' response_metadata={...}
Content: How
Type: <class 'langchain_core.messages.ai.AIMessageChunk'>Die vollständige Antwort akkumulieren
Manchmal benötigen Sie die komplette Antwort (für Logging, Tests oder weitere Verarbeitung):
def chat_with_accumulation():
load_dotenv()
llm = ChatOpenAI(model="gpt-4o-mini")
user_input = input("You: ")
full_response = ""
print("Assistant: ", end="", flush=True)
for chunk in llm.stream([HumanMessage(content=user_input)]):
print(chunk.content, end="", flush=True)
full_response += chunk.content
print("\n")
# Jetzt haben Sie die vollständige Antwort
print(f"[DEBUG] Vollständige Antwortlänge: {len(full_response)} Zeichen")
return full_responseDieses Muster ist üblich, wenn Sie:
- Die Konversation in einer Datenbank speichern wollen
- Die Antwort für strukturierte Daten parsen wollen
- Token-Nutzung oder Kosten berechnen wollen
Ihre Projektstruktur nach diesem Abschnitt:
langchain-chat/
├── venv/
├── .env
├── .gitignore
├── requirements.txt
├── test_install.py
└── chat.py # Streaming-Chat-Schleife (neu!)Als Nächstes: Abschnitt 3.4 zeigt, wie Sie unterschiedliche Modelltypen mit smarter Parameter-Konfiguration handhaben.
3.4) Smart Config: Parameter für Reasoning- vs. Chat-Modelle handhaben
OpenAI bietet zwei Arten von Modellen mit unterschiedlichen Fähigkeiten und Steuermechanismen:
Chat-Modelle (gpt-4o, gpt-4o-mini):
- Schnell und konversationell
- Unterstützen
temperature, um Zufälligkeit und Kreativität zu steuern - Am besten für allgemeine Aufgaben, kreatives Schreiben, Routine-Coding
Reasoning-Modelle (o1, o3, GPT-5):
- Langsamer, aber logischer und konsistenter
- Unterstützen
temperatureNICHT (nutzen stattdessen internes Reasoning) - Am besten für komplexe Mathematik, mehrstufige Planung, formale Analyse
Der entscheidende Unterschied: Chat-Modelle nutzen probabilistisches Sampling (Sie steuern die Zufälligkeit), während Reasoning-Modelle deterministische interne Logik nutzen (das Modell steuert seinen eigenen Reasoning-Prozess).
Temperature verstehen (nur Chat-Modelle)
Was ist temperature?
Temperature ist eine Zahl zwischen 0.0 und 2.0, die steuert, wie kreativ die Antworten des Modells sind. Bei niedrigen Werten (nahe 0) erhalten Sie konsistente, vorhersehbare Antworten. Bei hohen Werten (nahe 2.0) erhalten Sie kreative, variierte Antworten. Stellen Sie es sich wie einen „Kreativitätsregler“ vor.
Wie es funktioniert: Beim Generieren jedes Wortes sieht das Modell viele mögliche nächste Wörter mit unterschiedlichen Wahrscheinlichkeiten. Temperature beeinflusst, wie das Modell auswählt:
- Niedrige temperature (0.0): Wählt fast immer das Wort mit der höchsten Wahrscheinlichkeit → konsistente, fokussierte Antworten
- Hohe temperature (2.0): Wählt eher Wörter mit geringerer Wahrscheinlichkeit → vielfältige, kreative Antworten
Wichtig: Temperature funktioniert nur mit Chat-Modellen (gpt-4o, gpt-4o-mini). Sie gilt nicht für Reasoning-Modelle (GPT-5, o1, o3), die stattdessen interne Logik statt probabilistischem Sampling verwenden.
Leitfaden für temperature-Werte:
-
0.0: Hochgradig deterministisch, fokussiert und konsistent
- Verwenden für: faktische Q&A, Routine-Codegenerierung, strukturierte Ausgabe
- Gleicher Input → nahezu identischer Output jedes Mal
- Beispiel: „Was ist 2+2?“ → Immer „4“
-
0.7–1.0: Standard-Sampling-Verhalten (Standard ist 1.0)
- Verwenden für: allgemeine Konversation, Erklärungen, ausgewogene Antworten
- Moderate Variation in Formulierungen und Beispielen
- Beispiel: „Erkläre Photosynthese“ → Jedes Mal andere Worte, gleiche Kerninfos
-
1.2–2.0: Kreativer und vielfältiger, weniger vorhersehbar
- Verwenden für: kreatives Schreiben, Brainstorming, Ideation
- Hohe Variation in Ton, Struktur und Wortwahl
- Beispiel: „Schreibe ein Gedicht über den Mond“ → Sehr unterschiedliche Stile jedes Mal
Hinweis: Werte über 1.0 erhöhen die Kreativität, können aber faktische Genauigkeit und Kohärenz verringern. Der Maximalwert ist 2.0.
Beispiel: Einfluss von temperature (nur Chat-Modelle)
# Temperature 0.0 - deterministisch, gleiche Antwort jedes Mal
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0)
response = llm.invoke([HumanMessage(content="Was ist 2+2?")])
print(response.content) # Output: 4
# Temperature 1.0 - Standardverhalten, leichte Variation möglich
llm = ChatOpenAI(model="gpt-4o-mini", temperature=1.0)
response = llm.invoke([HumanMessage(content="Was ist 2+2?")])
print(response.content) # Output: 4 (kann eine kurze Erklärung enthalten)Für geschlossene, faktische Fragen hat temperature wenig Einfluss auf die Korrektheit.
Für offene oder kreative Aufgaben beeinflusst temperature deutlich Vielfalt, Ton und Stil.
Was passiert, wenn Sie Chat-Modell-Parameter bei Reasoning-Modellen verwenden?
Es hängt vom Modell ab – manche lehnen es ab, andere ignorieren es stillschweigend:
# ❌ Das wird bei o3-Modellen fehlschlagen
llm = ChatOpenAI(model="o3-mini", temperature=0.7)Fehler:
BadRequestError: Temperature is not supported with this modelUnterschiedliche Modelle, unterschiedliche Policies:
- o1 / o3-Modelle: Lehnen nicht unterstützte Parameter explizit ab. Wenn temperature enthalten ist, liefert die API sofort einen 400 BadRequest-Fehler.
- GPT-5-Modelle: Permissiver – der Parameter wird akzeptiert, aber stillschweigend ignoriert. Ihr Request ist erfolgreich, aber temperature hat keine Wirkung.
Warum das wichtig ist: Prüfen Sie immer, welches Modell Sie verwenden, und konfigurieren Sie Parameter entsprechend. Falsche Parameter können entweder Fehler verursachen oder stillschweigend scheitern, was Debugging-Zeit verschwendet.
Wie Sie Reasoning-Modell-Verhalten steuern
Jetzt wissen Sie, dass Chat-Modelle temperature verwenden und Reasoning-Modelle nicht. Wie steuert man also Reasoning-Modelle?
Reasoning-Modelle werden über Prompt-Design getunt, nicht über Parameter:
- Reasoning-Modelle stellen keine
temperatureoder ähnliche Controls bereit - Stattdessen leiten Sie das Verhalten darüber, wie Sie den Prompt schreiben:
- Explizite Instruktionen: „Denken Sie Schritt für Schritt“, „Zeigen Sie Ihre Arbeit“
- Constraints als Regeln: „Sie dürfen nicht annehmen...“, „Überprüfen Sie immer...“
- Strukturierte Anforderungen: „Geben Sie im JSON-Format aus“, „Fügen Sie Reasoning vor der Antwort ein“
- Entscheidungslogik: „Wenn Bedingung A, dann tun Sie X, sonst tun Sie Y“
Beispiel: Chat-Parameter vs. Reasoning-Prompts
# ❌ Chat-Ansatz - funktioniert nicht mit Reasoning-Modellen
llm = ChatOpenAI(model="o3-mini", temperature=0.5)
# Error: BadRequestError: Temperature is not supported
# ✅ Reasoning-Ansatz - über Prompt-Struktur leiten
prompt = """
Lösen Sie dieses Problem Schritt für Schritt:
1. Nennen Sie, was Sie wissen
2. Zeigen Sie Ihre Berechnungen
3. Verifizieren Sie Ihre Antwort
Problem: Wenn x + 5 = 12, was ist x?
"""
llm = ChatOpenAI(model="o3-mini")
response = llm.invoke([HumanMessage(content=prompt)])
print(response.content)Ausgabe:
1. Was ich weiß: x + 5 = 12
2. Berechnungen: x = 12 - 5 = 7
3. Verifikation: 7 + 5 = 12 ✓
Antwort: x = 7Kernaussage: Chat-Modelle werden über Parameter gesteuert, Reasoning-Modelle werden über Prompts gesteuert.
Entscheidungstabelle zur Modellauswahl
Jetzt, da Sie verstehen, wie man beide Modelltypen steuert, sehen Sie hier, wann Sie welches verwenden:
| Aufgabentyp | Empfohlenes Modell | Warum |
|---|---|---|
| Allgemeine Konversation | gpt-4o-mini | Schnell, kostengünstig, konversationell |
| Einfache Q&A | gpt-4o-mini | Ausreichend für faktisches Nachschlagen |
| Kreatives Schreiben | gpt-4o-mini (temp 0.8–1.0) | Temperature ermöglicht Kreativität |
| Codegenerierung | GPT-5 | Bessere logische Planung |
| Komplexes Reasoning | GPT-5 | Optimiert für mehrstufige Logik |
| Matheaufgaben | o3 / o1 | Dedizierte Reasoning-Modelle |
| Mehrstufige Planung | GPT-5 | Stark bei Planung über langen Horizont |
| Formale Analyse (rechtlich/policy) | o3 | Strikt deterministisch |
Kosten- und Latenz-Abwägungen
Wenn Sie die praktischen Abwägungen verstehen, können Sie das richtige Modell für Ihren Anwendungsfall auswählen:
| Modelltyp | Geschwindigkeit (typische Latenz) | Kosten (relativ) | Am besten für |
|---|---|---|---|
| gpt-4o-mini | Sehr schnell (<2s) | Sehr niedrig | Allgemeine Konversation, einfache Aufgaben |
| gpt-4o | Schnell (1–4s) | Mittel | Höherwertiger Chat, multimodale Aufgaben |
| GPT-5 | Moderat (3–8s) | Hoch | Komplexes Reasoning, Planung |
| o1 / o3 | Am langsamsten (5–15s+) | Am höchsten | Deterministisches Reasoning, formale Logik |
Hinweise:
- Geschwindigkeit reflektiert typische Antwortlatenz (variiert je nach Prompt-Länge und Komplexität)
- Kosten sind ein relativer Vergleich – prüfen Sie die aktuellen Preise auf der OpenAI-Website
- Reasoning-Modelle tauschen Geschwindigkeit und Kosten gegen Konsistenz und Korrektheit
- Chat-Modelle priorisieren Responsiveness und Effizienz
Wann Sie Reasoning-Modelle verwenden sollten (GPT-5, o1, o3):
- Mehrstufige Mathe- und STEM(Science, Technology, Engineering, Mathematics)-Probleme, die korrekte Zwischenschritte erfordern
- Komplexe logische Analyse mit Abhängigkeiten und Constraints
- Code-Debugging mit mehreren interagierenden Ursachen
- Planungsaufgaben mit vielen Regeln, Edge Cases oder Trade-offs
- Agent-Workflows, die Konsistenz und Denken über langen Horizont erfordern
Wann Sie Chat-Modelle verwenden sollten (gpt-4o, gpt-4o-mini):
- Allgemeine Konversation und interaktiver Chat
- Einfache Q&A mit begrenzter Reasoning-Tiefe
- Content-Generierung (Blogs, Zusammenfassungen, kreatives Schreiben)
- Routine-Codegenerierung und Boilerplate-Aufgaben
- Anwendungen, bei denen Geschwindigkeit und Kosten wichtiger sind als tiefes Reasoning
Als Nächstes: Abschnitt 3.5 zeigt Debugging-Techniken, um zu prüfen, was tatsächlich an das LLM gesendet wird.
3.5) Debugging: Antworten und Token-Nutzung inspizieren
Wenn Ihr LLM sich unerwartet verhält, müssen Sie exakt sehen, was gesendet und empfangen wurde. Dieser Abschnitt zeigt, wie Sie LLM-Calls inspizieren und Probleme debuggen.
Warum Debugging wichtig ist
Häufige Debugging-Szenarien:
- „Warum hat das LLM diese Antwort gegeben?“ → Prüfen Sie den exakten Prompt
- „Wie viel hat dieser Request gekostet?“ → Prüfen Sie die Token-Nutzung
- „Warum ist das so langsam?“ → Messen Sie die Latenz
- „Ist meine Message-Formatierung korrekt?“ → Inspizieren Sie die Message-Struktur
Die Herausforderung: Wenn Sie llm.invoke() aufrufen, erhalten Sie ein Response-Objekt. Aber was steckt tatsächlich drin? Welche Informationen sind fürs Debugging verfügbar?
Das Response-Objekt verstehen
Bevor Sie debuggen, müssen Sie verstehen, was llm.invoke() zurückgibt.
Grundstruktur:
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke([HumanMessage(content="Hello")])
# Was ist in der Response?
print(type(response)) # AIMessage
print(response.content) # Der eigentliche Text
print(response.response_metadata) # Token-Nutzung, Modellinfos, etc.Ausgabe:
<class 'langchain_core.messages.ai.AIMessage'>
Hello! How can I assist you today?
{
'token_usage': {
'completion_tokens': 9,
'prompt_tokens': 8,
'total_tokens': 17
},
'model_name': 'gpt-4o-mini-2024-07-18',
'finish_reason': 'stop',
...
}Wichtige Teile der Response:
response.content: Der Text, den das LLM generiert hatresponse.response_metadata: Dictionary mit:token_usage: Wie viele Tokens verwendet wurden (für Kostenkalkulation)model_name: Exakte Modellversion, die geantwortet hatfinish_reason: Warum die Generierung gestoppt hat (siehe Debug-Mode-Abschnitt für Details)
Zugriff auf Token-Nutzung:
token_usage = response.response_metadata['token_usage']
print(f"Prompt-Tokens: {token_usage['prompt_tokens']}")
print(f"Antwort-Tokens: {token_usage['completion_tokens']}")
print(f"Gesamt: {token_usage['total_tokens']}")Ausgabe:
Prompt-Tokens: 8
Antwort-Tokens: 9
Gesamt: 17Warum das wichtig ist: Sie benötigen diese Werte fürs Debugging, Kosten-Tracking und die Optimierung Ihrer Prompts.
Kosten aus Token-Nutzung berechnen
Token-Nutzung bestimmt die Kosten. Jedes Modell hat unterschiedliche Preise:
GPT-4o-mini (Stand Januar 2026):
- Input: $0.15 pro 1M Tokens
- Output: $0.60 pro 1M Tokens
GPT-4o:
- Input: $2.50 pro 1M Tokens
- Output: $10.00 pro 1M Tokens
Funktion zur Kostenberechnung:
def calculate_cost(token_usage, model_name):
"""Berechnet die Kosten basierend auf der Token-Nutzung."""
prompt_tokens = token_usage.get('prompt_tokens', 0)
completion_tokens = token_usage.get('completion_tokens', 0)
# Preise pro 1M Tokens (Stand Januar 2026)
pricing = {
'gpt-4o-mini': {'input': 0.15, 'output': 0.60},
'gpt-4o': {'input': 2.50, 'output': 10.00},
'gpt-5': {'input': 1.25, 'output': 10.00},
}
if model_name not in pricing:
return None
input_cost = (prompt_tokens / 1_000_000) * pricing[model_name]['input']
output_cost = (completion_tokens / 1_000_000) * pricing[model_name]['output']
return input_cost + output_cost
# Beispiel
response = llm.invoke([HumanMessage(content="Erklären Sie Quantencomputing")])
token_usage = response.response_metadata['token_usage']
cost = calculate_cost(token_usage, "gpt-4o-mini")
print(f"Kosten: ${cost:.6f}")Ausgabe:
Kosten: $0.000123Warum das wichtig ist: Produktions-Apps können 50.000+ Requests/Tag verarbeiten. Bei $0.002 pro Request sind das $3.000/Monat. Verwenden Sie das falsche Modell oder aufgeblähte Prompts, springen die Kosten auf $30.000/Monat. Ein Bug in einer Retry-Schleife kann über Nacht Tausende verbrennen. Tracken Sie Token-Nutzung von Tag eins an.
Debug-Modus aktivieren (wenn Sie rohe API-Details brauchen)
Das Response-Objekt und ein eigener Wrapper decken die meisten Debugging-Bedürfnisse ab. Manchmal müssen Sie aber sehen, was LangChain exakt an OpenAI sendet – die rohe JSON-Request und -Response.
Wann Sie das brauchen könnten:
- Debugging von LangChains Message-Formatierung
- Verifizieren, dass API-Parameter korrekt gesetzt sind
- Untersuchung unerwarteter API-Fehler
- Verstehen des exakten API-Payloads
LangChain hat eingebautes Debug-Logging über langchain_core.globals:
from langchain_core.globals import set_debug
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
set_debug(True)
# Jetzt werden alle LLM-Calls Debug-Infos ausgeben
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke([HumanMessage(content="Hello")])Ausgabe:
[llm/start] [llm:ChatOpenAI] Entering LLM run with input:
{
"prompts": [
"Human: Hello"
]
}
[llm/end] [llm:ChatOpenAI] [1.45s] Exiting LLM run with output:
{
"generations": [
[
{
"text": "Hello! How can I assist you today?",
"generation_info": {
"finish_reason": "stop",
"logprobs": null
},
"type": "ChatGeneration",
...
}
]
],
"llm_output": {
"token_usage": {
"completion_tokens": 9,
"prompt_tokens": 8,
"total_tokens": 17,
...
},
"model_provider": "openai",
"model_name": "gpt-4o-mini-2024-07-18",
...
},
}Hinweis: Das Ausgabeformat variiert je nach LLM-Provider. Dieses Beispiel zeigt die Struktur von OpenAI.
Was die Debug-Ausgabe offenlegt:
Der Debug-Modus zeigt den vollständigen Kommunikationsfluss LangChain → OpenAI:
1. Transformation des Message-Formats:
# Ihr Code
[HumanMessage(content="Hello")]
# Was Sie in der Debug-Ausgabe sehen
{
"prompts": ["Human: Hello"]
}Der Debug-Modus zeigt, wie LangChain Ihre Message intern repräsentiert, bevor sie an das LLM gesendet wird.
2. Abschlussstatus der Generierung:
"finish_reason": "stop"Warum die Generierung endete:
"stop": Das Modell hat die Antwort natürlich abgeschlossen"length": Die Antwort wurde abgeschnitten, weil das max_tokens-Limit erreicht wurde"tool_calls": Das Modell beendete die Generierung, indem es Tool-Call-Instruktionen statt einer finalen Textantwort erzeugte (Kapitel 12)"content_filter": Die Antwort wurde aufgrund von Safety- oder Content-Moderationsregeln blockiert oder unterdrückt
Wenn Sie "length" sehen, erhöhen Sie max_tokens, um die vollständige Antwort zu erhalten.
3. Aufschlüsselung der Token-Nutzung:
"token_usage": {
"completion_tokens": 9,
"prompt_tokens": 8,
"total_tokens": 17,
"completion_tokens_details": {
"reasoning_tokens": 0 # Für Reasoning-Modelle (o1/o3, etc.)
},
"prompt_tokens_details": {
"cached_tokens": 0 # Prompt-Caching (spart Kosten)
}
}Über die Basiszahlen hinaus sehen Sie:
- reasoning_tokens: Interne Reasoning-Schritte (nur für Reasoning-Modelle)
- cached_tokens: Wie viele Prompt-Tokens aus dem Cache bedient wurden (reduziert Kosten)
4. Modellversion und Fingerprint:
"model_name": "gpt-4o-mini-2024-07-18",
"system_fingerprint": "fp_8bbc38b4db"- model_name: Exakte Snapshot-Version (erklärt, warum Antworten sich über die Zeit ändern)
- system_fingerprint: OpenAIs Backend-Konfigurations-ID (ändert sich, wenn sie Systeme aktualisieren)
5. Request-Timing:
[llm/end] [llm:ChatOpenAI] [1.56s]Die [1.45s] zeigen die gesamte Request-Dauer – nützlich, um langsame Queries zu identifizieren.
Als Nächstes: Abschnitt 3.6 zeigt, wie Sie häufige Fehler sauber behandeln.
3.6) Ausfälle behandeln (Häufige Fehler simulieren und beheben)
Produktionsreife LLM-Anwendungen müssen mit vorhersehbaren Ausfallmodi umgehen: fehlende Credentials, Netzwerkzeitüberschreitungen, Rate-Limits und ungültige Inputs. Dieser Abschnitt zeigt Ihnen, wie Sie diese Fehler sauber behandeln und von Tag eins an robuste Anwendungen bauen.
Die sechs häufigen Fehler
1. Fehlender API-Key
Wann das passiert: Sie versuchen, eine ChatOpenAI-Instanz zu erstellen, aber OPENAI_API_KEY ist nicht in Ihrer Umgebung gesetzt.
Beispiel:
# .env-Datei existiert nicht, oder OPENAI_API_KEY ist nicht definiert
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke([HumanMessage(content="Hello")])Fehler, den Sie sehen:
OpenAIError: The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variableSo beheben Sie das:
- Prüfen Sie, ob Ihre
.env-Datei im Projekt-Root existiert - Verifizieren Sie, dass der Key-Name exakt
OPENAI_API_KEYist (häufiger Tippfehler:OPENAPI_KEY) - Stellen Sie sicher, dass
load_dotenv()vor dem Erstellen des LLM aufgerufen wird
2. Falscher API-Key
Wann das passiert: Ihre .env-Datei enthält einen ungültigen, abgelaufenen oder falsch kopierten API-Key.
Beispiel:
# .env hat: OPENAI_API_KEY=sk-invalid-key-12345
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke([HumanMessage(content="Hello")])Fehler, den Sie sehen:
AuthenticationError: Incorrect API key providedSo beheben Sie das:
- Gehen Sie zu https://platform.openai.com/api-keys
- Prüfen Sie, ob Ihr Key noch aktiv ist (nicht widerrufen oder abgelaufen)
- Erstellen Sie bei Bedarf einen neuen Key
- Kopieren Sie den gesamten Key sorgfältig (häufiger Fehler: erste/letzte Zeichen fehlen)
- Fügen Sie ihn ohne zusätzliche Leerzeichen in
.envein:
OPENAI_API_KEY=sk-proj-exactkeyhere3. Netzwerkfehler
Wann das passiert: Ihre Internetverbindung bricht ab oder OpenAIs Server sind während eines Requests temporär nicht erreichbar.
Beispiel:
# WiFi trennt sich während des Requests, oder OpenAI API ist down
response = llm.invoke([HumanMessage(content="Hello")])Fehler, den Sie sehen:
APIConnectionError: Connection errorSo beheben Sie das:
- Prüfen Sie Ihre Internetverbindung
- Prüfen Sie den OpenAI-Status unter https://status.openai.com
4. Rate-Limits
Wann das passiert: Sie senden in kurzer Zeit zu viele Requests und überschreiten Ihr API-Kontingent.
Beispiel:
# 1000 Requests sofort senden
for i in range(1000):
llm.invoke([HumanMessage(content=f"Request {i}")])Fehler, den Sie sehen:
RateLimitError: Rate limit reached for requestsSo beheben Sie das:
- Prüfen Sie Ihre Rate-Limits unter https://platform.openai.com/account/limits
- Upgraden Sie Ihren Plan, wenn Sie höhere Limits benötigen
- Verwenden Sie Batch-Processing für große Workloads (behandeln wir in Kapitel 6)
5. Ungültiger Modellname
Wann das passiert: Sie geben einen Modellnamen an, der nicht existiert oder in Ihrem Plan nicht verfügbar ist.
Beispiel:
llm = ChatOpenAI(model="gpt-99-ultra") # Existiert nicht
response = llm.invoke([HumanMessage(content="Hello")])Fehler, den Sie sehen:
NotFoundError: The model `gpt-99-ultra` does not exist or you do not have access to itSo beheben Sie das:
- Prüfen Sie verfügbare Modelle in Ihrem Plan unter https://platform.openai.com/docs/models
6. Token-Limit überschritten
Wann das passiert: Ihr Prompt ist zu lang und überschreitet das maximale Context-Window des Modells.
Beispiel:
# Einen 1-Millionen-Zeichen-Prompt erstellen
huge_prompt = "x" * 1_000_000
response = llm.invoke([HumanMessage(content=huge_prompt)])Fehler, den Sie sehen:
BadRequestError: This model's maximum context length is 128000 tokens. However, your messages resulted in 250000 tokens.So beheben Sie das:
- Prüfen Sie die Input-Länge vor dem Senden
- Kennen Sie die Limits Ihres Modells:
- gpt-4o-mini: 128K Tokens
- gpt-4o: 128K Tokens
- gpt-5: 400K Tokens
- Für lange Dokumente: Verwenden Sie Chunking oder Summarization (behandeln wir in Kapitel 9)
Nächste Schritte: Kapitel 4 zeigt, wie Sie wiederverwendbare Prompt-Templates entwerfen, die Prompt Engineering vom Anwendungscode trennen.