Python & AI Tutorials Logo
LangChain & LangGraph

4. Wiederverwendbare Prompts mit Templates entwerfen

In Kapitel 3 haben wir eine funktionierende Streaming-Chat-CLI erstellt, bei der Prompts direkt in unseren Python-Code eingebettet waren. Das funktioniert für schnelle Prototypen, aber wenn Ihre KI-Anwendungen wachsen, werden fest codierte Prompts zu einem Wartungsalbtraum. Stellen Sie sich vor, Sie müssten dieselbe Prompt-Logik über mehrere Dateien hinweg aktualisieren oder verschiedene Prompt-Varianten A/B-testen, ohne Code neu bereitzustellen.

Dieses Kapitel lehrt Sie, wie Sie wiederverwendbare, wartbare Prompts mit dem Template-System von LangChain entwerfen. Sie lernen, Prompt-Logik von Anwendungscode zu trennen, rollenbasiertes Messaging für bessere LLM-Kontrolle zu nutzen, Prompts in YAML-Dateien für Teamzusammenarbeit zu externalisieren und Templates vor der Ausführung zu validieren, um Fehler frühzeitig zu erkennen.

Was dieses Kapitel abdeckt (und was nicht):

In diesem Kapitel arbeiten wir mit Templates und Prompts manuell—Sie rendern Templates explizit zu Nachrichten und senden diese Nachrichten dann mit llm.invoke() an das LLM. Dieser praktische Ansatz hilft Ihnen, genau zu verstehen, was Templates tun und wie sie funktionieren.

In Kapitel 6 lernen Sie LCEL (LangChain Expression Language), mit dem Sie Templates und LLMs mit dem |-Operator zu Pipelines zusammensetzen können. Vorerst konzentrieren wir uns auf Template-Grundlagen ohne diese Orchestrierungsebene.

Am Ende dieses Kapitels verfügen Sie über ein robustes Prompt-Management-System, das von einfachen Chatbots bis zu komplexen Multi-Agent-Workflows skaliert.

4.1) Separation of Concerns: Code von Prompts entkoppeln

Warum Prompts vom Code trennen?

Wenn Sie Prompts direkt in Ihre Anwendungslogik fest codieren, erzeugen Sie eine enge Kopplung, die zu mehreren Problemen führt:

Wartungsaufwand: Das Ändern eines Prompts erfordert das Modifizieren von Python-Code, das Ausführen von Tests und das erneute Bereitstellen. Prompt-Änderungen erfolgen typischerweise weitaus häufiger als Code-Änderungen, was diesen Zyklus aus Code-Modifikation-Test-Neubereitstellung für das, was einfache Textbearbeitungen sein sollten, höchst ineffizient macht.

Versionskontroll-Herausforderungen: Wenn Code und Prompts vermischt sind, wird die Versionskontrolle schwierig. Merge-Konflikte sind wahrscheinlicher, und jeder Konflikt erfordert manuelle Auflösung und Refactoring.

Zusammenarbeits-Reibung: Nicht-technische Teammitglieder (Produktmanager, Domänenexperten) können Prompts, die in .py-Dateien leben, nicht direkt bearbeiten und müssen auf Entwicklerunterstützung angewiesen sein. Diese Abhängigkeit macht Prompt-Verbesserungszyklen erheblich langsamer.

Test-Komplexität: Das Testen verschiedener Prompt-Varianten bedeutet, Code zu kopieren, Strings zu modifizieren und mehrere Branches zu verwalten—was Experimente langsam und fehleranfällig macht.

Denken Sie an Prompts wie SQL-Abfragen in traditionellen Anwendungen. Sie würden SQL-Strings nicht im gesamten Python-Code fest codieren—Sie würden ein ORM verwenden oder zumindest Abfragen zentralisieren. Prompts verdienen dieselbe architektonische Disziplin.

Das Template-System von LangChain

LangChain bietet die Klassen PromptTemplate und ChatPromptTemplate, um die feste Struktur Ihres Prompts von den sich ändernden Daten zu trennen. Schreiben Sie Ihren Prompt einmal mit {Platzhaltern} und fügen Sie dann jedes Mal unterschiedliche Werte ein—kein erneutes Erstellen von Prompts mit f-Strings oder Verkettung mehr.

Template-Syntax und Verwendung

Platzhalter-Syntax

Templates verwenden {variablenname} als Platzhalter. Zur Laufzeit stellen Sie ein Dictionary mit passenden Schlüsseln bereit:

python
from langchain_core.prompts import PromptTemplate
 
# Template mit Platzhaltern definieren
template = PromptTemplate.from_template(
    "Translate {content} from {source_lang} to {target_lang}"
)
 
# Platzhalter mit Dictionary füllen
result = template.invoke({
    "content": "Hello world",
    "source_lang": "English", 
    "target_lang": "Korean"
})
 
print(result.text)

Ausgabe:

Translate Hello world from English to Korean

Wichtige Regeln:

  • Platzhalternamen müssen exakt mit Dictionary-Schlüsseln übereinstimmen
  • Alle Platzhalter müssen bereitgestellt werden (fehlende Schlüssel lösen KeyError aus)
  • Zusätzliche Dictionary-Schlüssel werden ignoriert
  • Verwenden Sie invoke(), um das Template mit Ihren Werten zu rendern

PromptTemplate vs ChatPromptTemplate

PromptTemplate: Gibt einen einfachen String zurück (verpackt in StringPromptValue)

  • Für einfache Textvervollständigung oder Legacy-Modelle
  • Ausgabe: Einzelner String wie "Summarize: {content}"

ChatPromptTemplate: Gibt strukturierte Nachrichten mit Rollen zurück (verpackt in ChatPromptValue)

  • Für moderne Chat-Modelle (GPT-4, Claude, Gemini)
  • Ausgabe: Rollengetrennte Nachrichten (system/user/assistant)
  • Bevorzugte Wahl: Besser für die Trennung von Systemanweisungen und Benutzereingaben

Wann welches verwenden?

  • Standardmäßig ChatPromptTemplate für Chat-Modelle—es ist klarer und wartbarer
  • Verwenden Sie PromptTemplate nur für einfache Vervollständigungen oder wenn Rollentrennung nicht benötigt wird
python
# PromptTemplate - einzelne String-Ausgabe
from langchain_core.prompts import PromptTemplate
 
template1 = PromptTemplate.from_template("Summarize: {content}")
result1 = template1.invoke({"content": "LangChain is a framework..."})  
print(result1)

Ausgabe:

text='Summarize: LangChain is a framework...'
python
# ChatPromptTemplate - rollenbasierte Nachrichten
from langchain_core.prompts import ChatPromptTemplate
 
template2 = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant"),
    ("user", "{question}")
])
result2 = template2.invoke({"question": "What is LangChain?"})
print(result2)

Ausgabe:

messages=[SystemMessage(content='You are a helpful assistant'), HumanMessage(content='What is LangChain?')]

Das Template wird einmal definiert. Sie können es mit unterschiedlichen Werten wiederverwenden, ohne die Template-Definition zu ändern. Sowohl PromptTemplate.invoke() als auch ChatPromptTemplate.invoke() geben Prompt-Werte zurück, die direkt an ein LLM gesendet werden können.

Von String-Formatierung zu Templates

Lassen Sie uns einen fest codierten Prompt zu Templates umgestalten. Hier ist die "Vorher"-Version aus Kapitel 3:

python
# Fest codierter Ansatz (Kapitel 3-Stil)
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
user_input = "Explain quantum computing"
# Prompt-Logik mit Code vermischt
prompt = f"You are a helpful assistant. Answer this question: {user_input}"
 
response = llm.invoke(prompt)
print(response.content)

Jetzt mit Templates—unter Verwendung des schrittweisen Ansatzes, den wir in diesem Kapitel üben werden:

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Template separat definiert
template = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant."),
    ("user", "{user_input}")
])
 
# Anwendungslogik - schrittweise Ausführung
llm = ChatOpenAI(model="gpt-4o-mini")
 
user_input = "Explain quantum computing"
 
# Schritt 1: Template zu Nachrichten rendern
messages = template.invoke({"user_input": user_input})
 
# Schritt 2: Nachrichten an LLM senden
response = llm.invoke(messages)
print(response.content)

Was hat sich geändert?

  1. Template-Definition: Die Prompt-Struktur wird einmal in template definiert, getrennt von der Ausführungslogik.
  2. Platzhalter-Syntax: {user_input} ist ein Platzhalter, der zur Laufzeit gefüllt wird.
  3. Schrittweise Ausführung: Wir rendern das Template explizit (template.invoke()), senden dann das Ergebnis an das LLM (llm.invoke()). Dieser zweistufige Prozess hilft Ihnen zu verstehen, was Templates tatsächlich tun.
  4. Wiederverwendbarkeit: Dasselbe template kann für jede Benutzerfrage ohne Änderung verwendet werden.
  5. Nachrichtenstruktur: template.invoke() gibt einen korrekt formatierten ChatPromptValue zurück, den das LLM erwartet.

Warum der schrittweise Ansatz?

In diesem Kapitel werden Sie dieses Muster wiederholt sehen:

python
messages = template.invoke(inputs)  # Schritt 1: Template rendern
response = llm.invoke(messages)     # Schritt 2: An LLM senden

Wir verwenden diesen zweistufigen Ansatz absichtlich zum Lernen—er zeigt genau, was Templates tun: Eingabedaten in strukturierte Nachrichten transformieren. In Kapitel 6 lernen Sie das reale Produktionsmuster: diese Schritte mit LCEL-Pipelines kombinieren (template | llm). Aber zuerst jeden Schritt separat zu verstehen, baut ein solides Fundament auf.

Template-Validierung

Templates erkennen Fehler frühzeitig. Wenn Sie auf einen nicht existierenden Platzhalter verweisen, löst LangChain einen Fehler aus, bevor ein API-Aufruf erfolgt:

python
template = PromptTemplate.from_template("Summarize: {text}")
 
# Dies wird fehlschlagen - fehlender 'text'-Schlüssel
try:
    template.invoke({"content": "Some text"})  # Falscher Schlüsselname
except KeyError as e:
    print(f"Template error: {e}")

Ausgabe:

Template error: "Input to PromptTemplate is missing variables {'text'}.  Expected: ['text'] Received: ['content']

Diese Validierung erfolgt zur Template-Rendering-Zeit, nicht während der LLM-Ausführung—was Ihnen sowohl Zeit als auch API-Kosten spart.

4.2) Rollenbasierte Prompt-Templates (System, User, Assistant)

Nachrichtenrollen verstehen

Moderne LLMs (GPT-4, GPT-5, Claude, Gemini) verstehen Konversationsstruktur durch Nachrichtenrollen. Jede Nachricht hat eine spezifische Rolle, die dem Modell sagt, wie es sie interpretieren soll.

Die drei Kernrollen:

System: Definiert, wie sich die KI verhalten soll

  • Zweck: Legt die Persönlichkeit, Expertise und Betriebsregeln der KI fest
  • Beispiel: "You are a Python expert who writes concise code examples"
  • Wann es gilt: Einmal am Anfang gesetzt, beeinflusst alle Antworten
  • Denken Sie daran als: Das Benutzerhandbuch der KI

User: Repräsentiert menschliche Eingabe

  • Zweck: Stellt Fragen oder macht Anfragen
  • Beispiel: "How do I read a file in Python?"
  • Wann es gilt: Jedes Mal, wenn ein Mensch eine Nachricht sendet
  • Denken Sie daran als: Die Fragen, die Sie stellen

Assistant: Repräsentiert die vorherigen Antworten der KI

  • Zweck: Bietet Konversationsverlauf
  • Beispiel: "You can use the open() function to read files"
  • Wann es gilt: Wenn Sie mehrstufige Konversationen benötigen
  • Denken Sie daran als: Das Gedächtnis der KI für vorherige Antworten

System-Nachrichten: Der Kontrollmechanismus

Die System-Nachricht sagt der KI, wer sie ist und wie sie operieren soll—vor jeder Benutzerinteraktion.

Was Sie kontrollieren können:

  1. Expertise: "You are a senior Python developer"
  2. Ausgabeformat: "Always respond in JSON format"
  3. Verhaltensregeln: "If unsure, say 'I don't know'"
  4. Antwortstil: "Be concise and technical"

Warum das wichtig ist:

Ohne System-Nachricht → generische, ausführliche Antworten

Mit System-Nachricht → konsistentes, maßgeschneidertes Verhalten

System-Nachrichten in Aktion

Lassen Sie uns die reale Auswirkung von System-Nachrichten sehen, indem wir dieselbe Frage mit und ohne eine vergleichen. Achten Sie darauf, wie dramatisch sich die Antwort ändert—nicht nur in der Länge, sondern in Ton, Komplexität und Lehransatz.

Ohne System-Nachricht:

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
template = ChatPromptTemplate.from_messages([
    ("user", "What is Python?")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({})
response = llm.invoke(messages)
print(response.content)

Ausgabe:

Python is a high-level, interpreted programming language known for its readability and simplicity. 
It was created by Guido van Rossum and first released in 1991. 
Python emphasizes code readability, allowing programmers to express concepts in fewer lines of code compared to languages such as C++ or Java.
 
Key features of Python include:
...

Mit System-Nachricht:

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Persona und Ausgabestil mit System-Nachricht kontrollieren
template = ChatPromptTemplate.from_messages([
    ("system", """You are a senior Python instructor with 15 years of teaching experience.
Your students are complete beginners who have never programmed before.
 
Teaching style:
- Use simple, everyday analogies
- Avoid technical jargon
- Show practical examples from daily life
- Be encouraging and patient"""),
    ("user", "What is Python?")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({})
response = llm.invoke(messages)
print(response.content)

Ausgabe:

Great question! Think of Python like a really helpful tool in your toolbox. 
Just like a hammer or a screwdriver helps you build or fix things around the house, Python helps you create software or automate tasks on a computer.
 
Imagine you wanted to bake a cake. 
You need a recipe to follow, right? In this analogy, Python is like that recipe. 
It tells the computer what steps to take to achieve a goal, whether it's doing math, organizing files, or even running a game.
...

Der Unterschied:

Ohne System-Nachricht:

  • Die KI verwendet ihr Standardverhalten: höflich, informativ, aber generisch
  • Antworten sind enzyklopädisch und formal—optimiert für breite Zielgruppen
  • Keine konsistente Persona: jede Antwort kann in Ton und Stil variieren
  • Keine Einschränkungen: die KI entscheidet selbst, wie detailliert oder technisch sie sein soll

Mit System-Nachricht:

  • Die KI folgt Ihren spezifischen Anweisungen: Persona, Stil und Regeln, die Sie definiert haben
  • Antworten sind konsistent und vorhersehbar—jede Antwort entspricht Ihren Anforderungen
  • Klare Persona beibehalten: agiert als die Rolle, die Sie zugewiesen haben (Lehrer, Experte, Assistent)
  • Explizite Einschränkungen angewendet: Ausgabeformat, Sprachniveau und Verhaltensgrenzen, die Sie festgelegt haben

Wichtige Erkenntnis: Ohne System-Nachricht erhalten Sie den Standardmodus der KI. Mit einer System-Nachricht erhalten Sie Ihre KI—maßgeschneidert auf die Bedürfnisse Ihrer Anwendung. Die System-Nachricht verwandelt die KI von einem Allzweckwerkzeug in einen spezialisierten Assistenten, der sich genau so verhält, wie Sie es wollen, jedes einzelne Mal.

User- und Assistant-Rollen: Konversationen aufbauen

Einzelne Frage (nur User):

python
template = ChatPromptTemplate.from_messages([
    ("system", "You are a Python expert."),
    ("user", "{question}")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({"question": "How do I read a CSV?"})
response = llm.invoke(messages)

Funktioniert gut für unabhängige Fragen.

Mehrstufig mit Kontext (User + Assistant):

Ohne Verlauf:

python
template = ChatPromptTemplate.from_messages([
    ("system", "You are a Python expert."),
    ("user", "How does it work?")  # "it" = ???
])

Die KI weiß nicht, worauf sich "it" bezieht.

Mit Verlauf:

python
template = ChatPromptTemplate.from_messages([
    ("system", "You are a Python expert."),
    ("user", "What's the pandas library?"),
    ("assistant", "Pandas is a data analysis library."),
    ("user", "How does it work?")  # Jetzt "it" = pandas
])

Der Konversationsverlauf (vorherige User-Frage + Assistant-Antwort) liefert Kontext. Die KI versteht jetzt, dass "it" pandas bedeutet.

Beispiel: Eine Konversation mit Verlauf aufbauen

Jetzt bauen wir ein Beispiel, das sich an vorherige Austausche erinnert. Diese Funktion pflegt den Konversationsverlauf und übergibt ihn mit jeder neuen Frage an die KI:

python
llm = ChatOpenAI(model="gpt-4o-mini")
 
def chat_with_history(user_input: str, history: list):
    messages = [("system", "You are a Python expert.")]
    
    # Verlauf hinzufügen
    for msg in history:
        messages.append((msg["role"], msg["content"]))
    
    # Aktuelle Eingabe hinzufügen
    messages.append(("user", user_input))
    
    # Mustache-Format verwenden, um Fehler zu vermeiden, wenn Inhalt {geschweifte Klammern} enthält
    template = ChatPromptTemplate.from_messages(messages, template_format="mustache")
    
    formatted = template.format()
    response = llm.invoke(formatted)
    return response.content
 
# Verwendung
history = []
 
# Runde 1
resp1 = chat_with_history("What's a Python dictionary?", history)
print(resp1)
 
history.append({"role": "user", "content": "What's a Python dictionary?"})
history.append({"role": "assistant", "content": resp1})
 
# Runde 2 - verwendet Kontext
resp2 = chat_with_history("Show an example.", history)
print(resp2)

Nachrichtenreihenfolge-Regeln

LLMs erwarten eine spezifische Konversationsstruktur: System → User → Assistant → User → Assistant → ...

Warum diese Reihenfolge?

Dieses Muster spiegelt natürliche Mensch-KI-Konversationen wider:

  1. System kommt zuerst (optional): Weil es Verhaltensregeln festlegt, die für die gesamte Konversation gelten, muss es definiert werden, bevor eine Interaktion beginnt. Genau wie Sie jemanden briefen, bevor er mit der Arbeit beginnt, nicht mitten in einer Aufgabe.

  2. User dann Assistant wechseln sich ab: In echten Konversationen sprechen Menschen (User), KI antwortet (Assistant), Menschen folgen nach (User), KI antwortet wieder (Assistant). Dieses Wechselmuster ist, wie die KI trainiert wurde, also erwartet sie diese Struktur.

  3. Muss mit User enden: Die KI generiert eine Antwort auf die letzte User-Nachricht. Wenn die Konversation mit Assistant endet, gibt es nichts, worauf die KI antworten könnte.

Gültige Beispiele:

python
# System + einzelner User
[("system", "..."), ("user", "...")]
 
# System + Konversation
[("system", "..."), ("user", "..."), ("assistant", "..."), ("user", "...")]

Problematische Muster:

python
# Assistant vor User - KI wird über Kontext verwirrt
[("system", "..."), ("assistant", "..."), ("user", "...")]
# Die KI sieht eine Antwort ohne Frage. Sie könnte halluzinieren, auf welche Frage 
# dies antwortete, was zu irrelevanten oder verwirrten Antworten führt.
python
# Zwei User-Nachrichten hintereinander - fehlende KI-Antwort
[("system", "..."), ("user", "..."), ("user", "...")]
# Die KI weiß nicht, auf welche User-Nachricht sie antworten soll, oder könnte sie 
# ungeschickt zusammenführen. Verliert den Konversationsfluss.
python
# Endet mit Assistant - nichts zum Antworten
[("system", "..."), ("user", "..."), ("assistant", "...")]
# Die Konversation ist abgeschlossen. Die KI hat nichts zu generieren, da es 
# keine ausstehende User-Frage gibt. Wird wahrscheinlich einen Fehler oder leere Antwort erzeugen.

Wichtiger Punkt: Diese Muster verursachen nicht immer harte Fehler, aber sie verwirren die KI, weil sie die Konversationslogik brechen, auf die sie trainiert wurde. Die KI könnte Antworten generieren, aber sie werden unzuverlässig oder unsinnig sein. Folgen Sie immer dem erwarteten Muster für vorhersehbares Verhalten.

Über einfachen Verlauf hinaus: Produktionsmuster (Vorschau)

Wichtiger Hinweis: Das Konversationsverlauf-Muster, das Sie gerade gelernt haben, ist eine großartige Grundlage, aber Produktionssysteme verwenden ausgefeiltere Ansätze.

Das Problem mit rohem Verlauf:

Das einfache Übergeben des gesamten Konversationsverlaufs an die KI hat Einschränkungen:

  1. Token-Verschwendung: Jede Nachricht (auch alte) zählt zu Ihrem Token-Limit und kostet
  2. Fokusverlust: Die KI könnte durch irrelevante frühere Konversationen abgelenkt werden
  3. Keine explizite Aufgabe: Die KI leitet ab, was zu tun ist, aus dem Verlauf, anstatt klare Anweisungen zu erhalten

Ein besserer Ansatz:

Produktionssysteme trennen Kontext von Anweisungen:

Einfacher Verlaufsansatz (was wir gerade gelernt haben):

python
messages = [
    ("system", "You are a Python expert."),
    ("user", "What's a dictionary?"),
    ("assistant", "A dictionary is a key-value data structure."),
    ("user", "Show an example.")
]

Produktionsansatz (kommt in späteren Kapiteln):

python
messages = [
    ("system", "You are a Python expert."),
    ("user", """Context: The user previously asked about Python dictionaries and learned they are key-value structures.
 
Task: Provide a code example demonstrating dictionary usage.""")
]

Der Unterschied:

  • Roher Verlauf: KI sieht die vollständige Konversation und findet heraus, was zu tun ist
  • Produktionsmuster: KI erhält zusammengefassten Kontext + explizite Anweisung

Vorteile der Trennung:

  • Weniger Tokens (niedrigere Kosten, schnellere Antworten)
  • Zuverlässigeres Verhalten (klare Anweisungen)
  • Bessere Kontrolle (Sie entscheiden, welcher Kontext wichtig ist)

Wo Sie dies lernen werden:

  • Kapitel 8: Verwaltung von Konversationszustand und Gedächtnis
  • Kapitel 11: Kontextuelle Retrieval (Kombination von RAG mit Konversationsgedächtnis)
  • Kapitel 16: Dynamisches Routing basierend auf Konversationskontext

Vorerst ist das Verstehen des rohen Verlaufs essentiell—es ist die Grundlage für diese fortgeschrittenen Muster. Aber bedenken Sie: Was Sie gerade gelernt haben, ist ein Lehrwerkzeug, nicht die endgültige Lösung.

4.3) Prompts externalisieren: Template-Dateien verwalten (.yaml)

Warum Prompts externalisieren?

Wenn Ihre KI-Anwendung wächst, wird die Verwaltung von Prompts im Python-Code unhandlich. Das Externalisieren von Prompts in YAML-Dateien bietet:

Nicht-technische Zusammenarbeit: Produktmanager, Domänenexperten und Prompt-Engineers können YAML-Dateien bearbeiten, ohne Python-Code zu berühren oder Programmierkonzepte zu verstehen.

Versionskontroll-Klarheit: Verfolgen Sie Prompt-Änderungen getrennt von Code-Änderungen. Keine gemischten Commits mehr, bei denen Prompt-Anpassungen und Logik-Updates zusammen erscheinen.

Umgebungsspezifische Prompts: Unterschiedliche Prompts für Entwicklung, Staging und Produktion ohne Code-Änderungen.

A/B-Testing: Testen Sie Prompt-Varianten, indem Sie verschiedene Dateien laden—keine Code-Änderungen erforderlich.

Denken Sie an YAML-Prompt-Dateien wie Konfigurationsdateien in traditionellen Anwendungen—sie definieren Verhalten ohne Code-Änderungen oder Neubereitstellung zu erfordern.

Was ist YAML?

YAML ist ein menschenlesbares Datenformat, das häufig für Konfigurationsdateien verwendet wird. Wenn Sie YAML noch nie gesehen haben, denken Sie daran als sauberere Alternative zu JSON—es verwendet Einrückung statt Klammern und ist einfacher zu lesen und zu bearbeiten.

YAML-Prompt-Struktur

LangChain definiert eine Standard-YAML-Dateistruktur für Prompts. Schauen wir uns Beispiele an:

Beispiel 1: Prompt ohne Variablen

Wenn ein Prompt keine Laufzeitwerte benötigt, setzen Sie input_variables auf eine leere Liste:

yaml
# prompts/system_prompt.yaml
_type: prompt
input_variables: []
template: |
  You are a helpful assistant.
  Please answer in a friendly and encouraging tone.

Das |-Symbol ermöglicht es Ihnen, mehrzeiligen Text zu schreiben, und Zeilenumbrüche werden beibehalten.

Beispiel 2: Prompt mit Variablen

Wenn ein Prompt Laufzeitwerte benötigt, listen Sie sie in input_variables auf:

yaml
# prompts/user_prompt.yaml
_type: prompt
input_variables:
  - user_input
template: |
  User question: {user_input}
  Please provide a clear answer.

Zur Laufzeit wird der {user_input}-Platzhalter durch den tatsächlichen Wert ersetzt.

Hauptkomponenten:

  • _type: prompt: Identifiziert dies als Prompt-Template
  • input_variables: Listet alle im Template verwendeten Platzhalter auf (leere Liste [], wenn keine)
  • template: Der tatsächliche Prompt-Text mit {Platzhaltern}

YAML-Prompts laden und verwenden

Grundlegendes Laden:

Jetzt laden wir die YAML-Dateien, die wir erstellt haben, und verwenden sie mit einem LLM:

python
from langchain_core.prompts import load_prompt, ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Prompts aus YAML-Dateien laden
system_prompt_template = load_prompt("prompts/system_prompt.yaml")
user_prompt_template = load_prompt("prompts/user_prompt.yaml")
 
# Geladene Prompts zu einem Chat-Template kombinieren
chat_template = ChatPromptTemplate.from_messages([
    ("system", system_prompt_template.template),
    ("user", user_prompt_template.template)
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
# Schritt 1: Template mit Laufzeitwerten rendern
messages = chat_template.invoke({"user_input": "What is LangChain?"})
 
# Schritt 2: An LLM senden
response = llm.invoke(messages)
print(response.content)

Geladene Templates verifizieren:

Bevor Sie ein Template verwenden, verifizieren Sie, dass es korrekt geladen wurde:

python
from langchain_core.prompts import load_prompt
 
# Template laden
user_prompt_template = load_prompt("prompts/user_prompt.yaml")
 
# Prüfen, welche Variablen es erwartet
print("Input variables:", user_prompt_template.input_variables)
 
# Template-Text anzeigen
print("Template:", user_prompt_template.template)

Ausgabe:

Input variables: ['user_input']
Template: User question: {user_input}
Please provide a clear answer.

Häufige YAML-Fehler

Fehler 1: Inkonsistente Einrückung

YAML erfordert konsistente Einrückung (typischerweise 2 Leerzeichen). Jede Ebene muss denselben Abstand verwenden:

Falsch:

yaml
_type: prompt
input_variables:
- user_input      # Falsch: Listenelemente sollten eingerückt sein
  - question      # Falsch: gemischte Einrückungsebenen

Richtig:

yaml
_type: prompt
input_variables:
  - user_input    # Richtig: beide Elemente auf derselben Einrückungsebene
  - question

Fehler 2: Platzhalter-Nichtübereinstimmung

Platzhalter in template müssen mit input_variables übereinstimmen:

Falsch:

yaml
input_variables:
  - user_input
template: "Question: {question}"  # 'question' nicht in input_variables!

Richtig:

yaml
input_variables:
  - user_input
template: "Question: {user_input}"

LangChain löst einen Fehler aus, wenn Platzhalter nicht mit deklarierten Variablen übereinstimmen.

4.4) Templates vor der Ausführung in der Vorschau anzeigen und validieren

Warum Templates in der Vorschau anzeigen?

Prompt Engineering ist iterativ. Sie optimieren Formulierungen, passen Strukturen an, fügen Beispiele hinzu—und jede Iteration kostet API-Tokens und Zeit. Das Anzeigen von Templates in der Vorschau vor der Ausführung ermöglicht Ihnen:

Zeit und Geld sparen: Fehler erkennen, bevor teure API-Aufrufe erfolgen.

Korrektheit verifizieren: Sicherstellen, dass Variablen korrekt gefüllt sind und die Formatierung wie erwartet ist.

Effizient debuggen: Den exakten Prompt sehen, der an das LLM gesendet wird, mit allen gefüllten Variablen und angewendeter Formatierung.

Denken Sie an Template-Vorschau wie Print-Debugging—Sie inspizieren den Zwischenzustand vor der Ausführung, um Korrektheit zu verifizieren.

Grundlegende Template-Vorschau

Template-Struktur inspizieren:

Bevor Sie ein Template mit einem LLM verwenden, inspizieren Sie seine Struktur und zeigen Sie in der Vorschau an, wie es mit Beispieldaten rendert:

python
from langchain_core.prompts import ChatPromptTemplate
 
template = ChatPromptTemplate.from_messages([
    ("system", "You are a {role}."),
    ("user", "{user_input}")
])
 
# Template-Struktur in Vorschau anzeigen
print("Input variables:", template.input_variables)
print("Message count:", len(template.messages))
 
# Vorschau mit Beispieldaten
prompt_value = template.invoke({
    "role": "Python programming expert",
    "user_input": "What is Python?"
})
 
print("\nPreview:")
for msg in prompt_value.to_messages():
    print(f"{msg.type}: {msg.content}")

Ausgabe:

Input variables: ['role', 'user_input']
Message count: 2
 
Preview:
system: You are a Python programming expert.
human: What is Python?

Dies zeigt genau, was an das LLM gesendet wird, und ermöglicht Ihnen, den Prompt vor der Ausführung zu verifizieren.

Templates validieren: Fehlende Variablen erkennen

Der häufigste Template-Fehler sind fehlende erforderliche Variablen. Hier ist eine wiederverwendbare Validierungsfunktion, die fehlende Variablen erkennt:

python
from langchain_core.prompts import ChatPromptTemplate
 
def preview_template(template: ChatPromptTemplate, inputs: dict):
    """Zeigt Template mit gegebenen Eingaben in Vorschau an, erkennt Fehler."""
    try:
        prompt_value = template.invoke(inputs)
        
        print("TEMPLATE PREVIEW")
        print("=" * 60)
        
        for i, msg in enumerate(prompt_value.to_messages(), 1):
            print(f"Message {i} ({msg.type.upper()}):")
            print(msg.content)
            print("-" * 60)
                
    except KeyError as e:
        print(f"ERROR: {e}")
        print(f"Required variables: {template.input_variables}")
 
# Verwendung
template = ChatPromptTemplate.from_messages([
    ("system", "You are a {role}."),
    ("user", "{user_input}")
])
 
# Gültige Eingaben
preview_template(template, {
    "role": "Python programming expert",
    "user_input": "What is Python?"
})
 
# Fehlende Variable
preview_template(template, {
    "user_input": "What is Python?"  # Fehlt 'role'
})

Ausgabe:

TEMPLATE PREVIEW
============================================================
Message 1 (SYSTEM):
You are a Python programming expert.
------------------------------------------------------------
Message 2 (HUMAN):
What is Python?
------------------------------------------------------------
 
ERROR: "Input to ChatPromptTemplate is missing variables {'role'}.
Expected: ['role', 'user_input'] Received: ['user_input']
...
Required variables: ['role', 'user_input']

Validierungs-Workflow:

Hier ist der typische Template-Validierungsprozess:

Fehler

Gültig

Nein

Ja

Template definieren

Beispieldaten laden

Eingaben validieren

Template/Daten korrigieren

Nachrichten in Vorschau anzeigen

Bereit?

Mit LLM ausführen

Dieser iterative Prozess hilft, Fehler vor teuren LLM-Aufrufen zu erkennen.

Checkliste vor der Ausführung

Bevor Sie Templates in die Produktion senden:

  • Alle input_variables sind in YAML/Template deklariert
  • Beispieldaten rendern ohne Fehler
  • Mehrzeilige Prompts werden korrekt angezeigt
  • Platzhalter stimmen exakt mit Variablennamen überein
  • Test mit Grenzfällen (leere Strings, langer Text)

Kapitelzusammenfassung:

Sie haben gelernt, wartbare, wiederverwendbare Prompts mit dem Template-System von LangChain zu entwerfen:

  1. Separation of Concerns: Prompts vom Code entkoppeln für einfachere Wartung und Iteration
  2. Rollenbasierte Templates: System-, User- und Assistant-Nachrichten für strukturierte LLM-Interaktionen mit korrekter Anweisungshierarchie verwenden
  3. Externalisierte Prompts: Prompts in YAML-Dateien für nicht-technische Zusammenarbeit und Versionskontrolle verwalten
  4. Vorschau und Validierung: Fehler frühzeitig erkennen und Templates vor der Ausführung verifizieren

Nächste Schritte:

In Kapitel 5 sehen Sie, wie Templates autonome Entscheidungsfindung in Vorschau-Agent-Beispielen ermöglichen. Dann lernen Sie in Kapitel 6 LCEL (LangChain Expression Language), um diese Templates mit dem |-Operator zu leistungsstarken Pipelines zusammenzusetzen.