Wer mit LLMs arbeitet, kennt das Grundprinzip: Eingabe rein, Antwort raus. Ein String wird an ein Modell geschickt, das Modell liefert einen String zurück. So einfach könnte es sein.
Ist es aber nicht.
Denn zwischen der scheinbar simplen Zeichenkette
"Schreibe ein Haiku über den Frühling" und dem
tatsächlichen API-Aufruf an GPT-4, Claude oder Gemini liegt eine
entscheidende Abstraktionsschicht: Messages. Sie sind
die fundamentale Einheit jeder Interaktion mit Chat-Modellen in
LangChain – und wer ihre Struktur versteht, versteht, wie moderne
LLM-Anwendungen unter der Haube funktionieren.
Messages sind keine bloßen Container für Text. Sie sind strukturierte Informationsträger, die neben dem eigentlichen Inhalt auch Rollen, Metadaten und multimodale Daten transportieren. Sie definieren, wer etwas sagt (System, Mensch, Modell, Tool), was gesagt wird (Text, Bild, Audio, Datei) und wie es kontextualisiert ist (IDs, Token-Zähler, Response-Metadaten).
LangChain normalisiert diese Nachrichten providerunabhängig.
Das bedeutet: Egal ob OpenAI, Anthropic, Google oder ein lokales Modell – die Message-Objekte bleiben identisch. Sie bilden eine stabile Schnittstelle, die es erlaubt, Konversationen zwischen Modellen zu portieren, Historie zu persistieren und komplexe Interaktionsmuster zu implementieren, ohne sich in providerspezifischen Formaten zu verlieren.
Die Frage ist legitim. Warum nicht einfach Strings übergeben?
Weil Konversationen nicht linear sind. Ein typischer Chat mit einem LLM besteht aus mehreren Rollen, die in Wechselwirkung treten:
Diese Interaktion lässt sich nicht als einzelner String darstellen. Sie braucht Struktur. Sie braucht Rollen. Sie braucht Kontext.
Messages liefern genau das.
Jede Message in LangChain besteht aus drei Kernkomponenten:
1. Rolle – Wer spricht?
Die Rolle identifiziert den Absender der Nachricht. LangChain definiert
vier primäre Rollentypen, die sich direkt aus der
Chat-Completion-API-Spezifikation ableiten:
system – Systemanweisungen, die das Verhalten des
Modells steuernuser – Benutzereingaben (in LangChain als
HumanMessage bezeichnet)assistant – Modellantworten (in LangChain als
AIMessage bezeichnet)tool – Tool-Ausführungsergebnisse (als
ToolMessage dargestellt)Die Rolle ist kein Detail. Sie ist Semantik. Ein Modell interpretiert eine Systemnachricht anders als eine Benutzernachricht – selbst wenn beide denselben Text enthalten. Die Rolle bestimmt, wie eine Nachricht im Kontext der gesamten Konversation gewichtet wird.
2. Content – Was wird gesagt?
Der Inhalt ist die eigentliche Nutzlast der Nachricht. Er kann in
verschiedenen Formaten vorliegen:
LangChain v1.0 führte das Konzept der Content Blocks
ein – standardisierte Datenstrukturen, die unterschiedliche Inhaltstypen
einheitlich repräsentieren. Ein ImageBlock hat immer die
gleiche Struktur, egal ob das zugrunde liegende Modell von OpenAI,
Anthropic oder Google stammt.
3. Metadata – Wie wird kontextualisiert?
Metadaten sind das unterschätzte Rückgrat produktionsreifer
LLM-Anwendungen. Sie beinhalten:
id – Eine eindeutige Kennung für Tracing und
Debuggingname – Optionale Identifikation verschiedener Nutzer
oder Rollenresponse_metadata – Providerantworten, die zusätzliche
Informationen enthaltenusage_metadata – Token-Zähler für Kosten- und
Kontextfenster-ManagementWer Messages richtig nutzt, denkt in Metadaten. Wer Metadaten ignoriert, verliert Transparenz.
Eine SystemMessage ist das, was das Modell niemals
vergessen soll. Sie definiert die Persona, die Regeln, den Rahmen. Sie
ist das erste Element einer Konversation – und oft das wichtigste.
Systemanweisungen sind keine Empfehlungen. Sie sind Direktiven. Ein gut formulierter System-Prompt kann den Unterschied zwischen einer generischen Antwort und einer präzisen, kontextbewussten Lösung ausmachen.
Wichtig: Nicht alle Modelle behandeln Systemnachrichten gleich. Manche Provider (z.B. Anthropic) räumen ihnen hohe Priorität ein. Andere (wie manche Open-Source-Modelle) behandeln sie eher als Hinweis. Ein Grund mehr, providerunabhängige Abstraktionen zu nutzen.
HumanMessage repräsentiert Eingaben von menschlichen
Akteuren. Das ist offensichtlich – und doch subtiler, als es
scheint.
Denn eine HumanMessage kann mehr als nur Text enthalten.
Sie kann Bilder referenzieren, Audio-Dateien einbinden, strukturierte
Dokumente mitführen. Das gesamte multimodale Potenzial moderner LLMs
wird über diesen Message-Typ zugänglich gemacht.
Ein Detail, das oft übersehen wird: Das name-Feld. Es
ermöglicht die Unterscheidung verschiedener Nutzer in einer
Multi-User-Konversation oder die Simulation von Dialogen zwischen
mehreren Personas. Nicht jeder Provider unterstützt dieses Feld – aber
wo es funktioniert, eröffnet es mächtige Interaktionsmuster.
AIMessage ist die Rückgabe eines Modellinvocations. Auf
den ersten Blick ist das simpel: Das Modell wird aufgerufen, es gibt ein
AIMessage-Objekt zurück, fertig.
Aber ein AIMessage ist mehr als nur die Antwort. Es ist
ein vollständiges Response-Objekt mit allen zugehörigen Metadaten:
text – Der extrahierte Textinhaltcontent – Der rohe Content (String oder
Content-Block-Liste)tool_calls – Werkzeugaufrufe, die das Modell initiiert
hatusage_metadata – Token-Verbrauch für Input und
Outputresponse_metadata – Anbieterantworten mit zusätzlichen
InformationenEin Entwickler, der nur response.text liest, verschenkt
Potenzial. Die Metadaten einer AIMessage enthalten
Debugging-Informationen, Kostendaten und Hinweise darauf, wie das Modell
zu seiner Antwort gekommen ist.
Ein weiterer Aspekt: AIMessage-Objekte
können manuell erstellt werden. Das ist keine Randnotiz. Es ist ein
zentrales Pattern beim Aufbau von Konversationshistorien. Wenn ein
System eine frühere Interaktion wiederherstellen will, konstruiert es
die Nachrichtenfolge durch manuell erstellte
AIMessage-Objekte – als wären sie vom Modell selbst
gekommen.
ToolMessage ist der vierte Typ – und der
spezialisierteste. Er repräsentiert das Ergebnis eines Tool-Aufrufs und
ermöglicht es dem Modell, die Ausgabe eines Werkzeugs als Teil der
Konversation zu verarbeiten.
Die Mechanik ist simpel: Das Modell generiert einen Tool-Call (als
Teil einer AIMessage), die Anwendung führt das Tool aus,
und das Ergebnis wird als ToolMessage zurück ins Modell
gespeist. Das Modell nutzt diese Information, um seine finale Antwort zu
formulieren.
Ohne ToolMessage gäbe es keine strukturierte
Agentic-Interaktion. Sie bilden das Bindeglied zwischen LLM-Reasoning
und externer Funktionalität.
Hier wird es technisch präzise.
Wenn ein Entwickler einen einfachen String an ein Chat-Modell
übergibt – etwa model.invoke("Erkläre mir Rekursion") –
geschieht intern eine Transformation. LangChain konvertiert diesen
String automatisch in eine HumanMessage:
# Was der Entwickler schreibt
response = model.invoke("Erkläre mir Rekursion")
# Was intern passiert
response = model.invoke([HumanMessage(content="Erkläre mir Rekursion")])Diese implizite Konversion ist Komfort – aber auch Abstraktion. Wer sie versteht, erkennt, warum LangChain so konsequent auf Messages setzt: Jede Interaktion ist eine Message-Sequenz, auch wenn sie nur aus einem Element besteht.
Das Dictionary-Format ist ein weiterer Konvergenzpunkt. LangChain erlaubt die Spezifikation von Nachrichten im OpenAI-Chat-Completion-Format:
messages = [
{"role": "system", "content": "Du bist ein Experte für funktionale Programmierung."},
{"role": "user", "content": "Erkläre mir Higher-Order Functions."},
{"role": "assistant", "content": "Higher-Order Functions sind..."}
]
response = model.invoke(messages)Auch hier findet eine Transformation statt: LangChain wandelt diese
Dictionaries in die entsprechenden SystemMessage,
HumanMessage und AIMessage-Objekte um, bevor
sie an den Provider geschickt werden.
Warum diese Flexibilität? Weil Entwickler aus verschiedenen Ökosystemen kommen. Manche bevorzugen explizite Objekte, andere minimale Syntax. LangChain unterstützt beides – und normalisiert alles intern zu Message-Objekten.
Mit LangChain v1.0 wurde das Konzept der Content Blocks eingeführt – eine standardisierte Repräsentation für unterschiedliche Inhaltstypen innerhalb einer Nachricht.
Ein Content Block kann sein:
TextContentBlock – Reiner TextImageBlock – Ein Bild (als URL, Base64 oder
File-ID)AudioBlock – Audio-DatenVideoBlock – Video-InhalteFileBlock – Referenzen zu externen Dateien (z.B.
PDFs)PlainTextContentBlock – Dokument-Text (Markdown, Plain
Text)Die Abstraktion ist bewusst generisch. Sie ermöglicht es, multimodale
Inhalte strukturiert zu transportieren, ohne sich in
providerspezifischen Formaten zu verlieren. Ein ImageBlock
hat immer ein data-Feld und optional ein
mime_type-Feld – egal, ob das zugrunde liegende Modell von
OpenAI, Google oder Anthropic kommt.
Entscheidend: Content Blocks sind kein Ersatz für
das content-Feld einer Message. Sie sind eine zusätzliche
Property (content_blocks), die eine standardisierte Sicht
auf den Inhalt bietet. Das content-Feld bleibt für
Backward-Kompatibilität erhalten.
Die Theorie ist gelegt. Jetzt wird implementiert.
Der einfachste Fall: Ein Entwickler will ein Modell ansprechen. Kein Kontext, kein System-Prompt, nur eine Frage.
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
# Modell initialisieren
model = ChatOpenAI(model="gpt-4o")
# Einfache Nachricht
message = HumanMessage(content="Was ist der Unterschied zwischen asyncio und Threading?")
# Modell aufrufen
response = model.invoke([message])
print(response.content)Das funktioniert. Aber es ist rudimentär. Eine produktionsreife Anwendung würde einen System-Prompt ergänzen, um das Verhalten des Modells zu steuern.
Ein typisches Pattern in produktionsnahen Anwendungen: Das Modell wird durch einen System-Prompt konditioniert, bevor die eigentliche Nutzeranfrage verarbeitet wird.
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
model = ChatOpenAI(model="gpt-4o")
# Definiere das Verhalten des Modells
system_prompt = SystemMessage(content="""
Du bist ein erfahrener Backend-Entwickler mit Fokus auf Python und asynchrone Programmierung.
Antworte präzise, technisch fundiert und mit Codebeispielen, wo sinnvoll.
Erkläre Konzepte Schritt für Schritt und gehe auf potenzielle Fallstricke ein.
""")
# Nutzeranfrage
user_query = HumanMessage(content="Wann sollte ich asyncio statt Threading nutzen?")
# Konversation zusammenstellen
messages = [system_prompt, user_query]
# Modell aufrufen
response = model.invoke(messages)
print(response.content)Das Modell antwortet jetzt im Kontext der definierten Persona. Es denkt wie ein Backend-Entwickler, nicht wie ein generischer Chatbot.
Konversationen sind selten einschüssig. Ein Nutzer stellt eine Frage, das Modell antwortet, der Nutzer fragt nach – und so weiter. Um diesen Dialog zu führen, muss die gesamte Historie als Message-Sequenz verwaltet werden.
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
model = ChatOpenAI(model="gpt-4o")
# Konversationshistorie aufbauen
conversation_history = [
SystemMessage(content="Du bist ein Experte für Python-Webframeworks."),
HumanMessage(content="Wie baue ich eine REST-API mit FastAPI?"),
AIMessage(content="FastAPI ist ein modernes Framework für APIs. Du definierst Routen mit Decorators..."),
HumanMessage(content="Und wie validiere ich die Eingabedaten?")
]
# Modell mit gesamter Historie aufrufen
response = model.invoke(conversation_history)
print(response.content)Die Antwort berücksichtigt jetzt den Kontext der vorherigen Interaktion. Das Modell weiß, dass es bereits über FastAPI gesprochen hat, und kann die Folgefrage präzise beantworten.
Wichtig: Jede Nachricht in dieser Liste ist ein eigenständiges Objekt. Die Historie ist eine Sequenz von Messages – keine verschachtelte Datenstruktur, kein komprimiertes Format. Nur eine Liste.
Wer Messages ernst nimmt, arbeitet mit Metadaten. Sie liefern Transparenz über Token-Verbrauch, Kosten und Debugging-Informationen.
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
model = ChatOpenAI(model="gpt-4o")
message = HumanMessage(content="Erkläre mir Closures in Python.")
response = model.invoke([message])
# Ausgabe des vollständigen Response-Objekts
print("Antworttext:", response.content)
print("\nToken-Nutzung:", response.usage_metadata)
print("\nResponse-Metadaten:", response.response_metadata)
print("\nMessage-ID:", response.id)Die usage_metadata enthält typischerweise:
{
'input_tokens': 15,
'output_tokens': 248,
'total_tokens': 263
}Diese Informationen sind entscheidend für Kostenmanagement und Kontextfenster-Optimierung. Wer blind mit LLMs arbeitet, verliert den Überblick.
Ein fortgeschrittenes Pattern: AIMessages manuell erstellen, um frühere Interaktionen zu simulieren oder persistierte Konversationen wiederherzustellen.
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
model = ChatOpenAI(model="gpt-4o")
# Simuliere eine frühere Konversation
conversation = [
SystemMessage(content="Du bist ein Python-Tutor."),
HumanMessage(content="Was ist ein Decorator?"),
AIMessage(content="Ein Decorator ist eine Funktion, die eine andere Funktion modifiziert..."),
HumanMessage(content="Kannst du mir ein Beispiel zeigen?")
]
response = model.invoke(conversation)
print(response.content)Das Modell behandelt die manuell erstellte AIMessage wie
eine echte Antwort. Es „erinnert sich” an die Interaktion – obwohl sie
nie stattgefunden hat.
Dieses Pattern ist essentiell für die Wiederherstellung von Konversationen aus Datenbanken oder für das Few-Shot-Learning, bei dem Beispieldialoge ins Modell eingespeist werden.
Für Entwickler, die mit der OpenAI-API vertraut sind, bietet LangChain ein kompatibles Dictionary-Format.
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o")
# OpenAI-kompatibles Format
messages = [
{"role": "system", "content": "Du bist ein hilfsbereiter Assistent."},
{"role": "user", "content": "Was ist eine Lambda-Funktion?"},
{"role": "assistant", "content": "Eine Lambda-Funktion ist eine anonyme Funktion..."},
{"role": "user", "content": "Gib mir ein Beispiel."}
]
response = model.invoke(messages)
print(response.content)LangChain konvertiert diese Dictionaries intern in die entsprechenden Message-Objekte. Das Ergebnis ist identisch zur objektbasierten Variante – nur die Syntax unterscheidet sich.
Messages sind nicht auf Text beschränkt. LangChain v1.0 unterstützt multimodale Content Blocks, die es erlauben, Bilder, Audio oder Dateien in Nachrichten einzubetten.
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
model = ChatOpenAI(model="gpt-4o")
# Multimodale Nachricht mit Text und Bild
message = HumanMessage(
content=[
{"type": "text", "text": "Was ist auf diesem Bild zu sehen?"},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/image.jpg"
}
}
]
)
response = model.invoke([message])
print(response.content)Die Content-Liste kombiniert Text und Bild in einer einzigen Nachricht. Das Modell verarbeitet beide Modalitäten parallel und liefert eine kontextbezogene Antwort.
Alternativer Ansatz mit Base64-kodierten Bildern:
import base64
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
def encode_image(image_path):
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
model = ChatOpenAI(model="gpt-4o")
# Bild einlesen und kodieren
image_data = encode_image("diagramm.png")
message = HumanMessage(
content=[
{"type": "text", "text": "Analysiere dieses Architekturdiagramm."},
{
"type": "image_url",
"image_url": {
"url": f"data:image/png;base64,{image_data}"
}
}
]
)
response = model.invoke([message])
print(response.content)Base64-Kodierung eignet sich für lokal gespeicherte Bilder, die nicht über eine URL erreichbar sind.
Das name-Feld einer HumanMessage erlaubt
die Identifikation verschiedener Nutzer in einer Konversation. Nicht
alle Provider unterstützen dieses Feld – aber wo es funktioniert,
ermöglicht es mächtige Interaktionsmuster.
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
model = ChatOpenAI(model="gpt-4o")
conversation = [
SystemMessage(content="Du moderierst ein Diskussionsforum über Python."),
HumanMessage(content="Ich finde List Comprehensions verwirrend.", name="alice"),
AIMessage(content="List Comprehensions können anfangs komplex wirken. Lass mich das erklären..."),
HumanMessage(content="Ich finde sie super elegant!", name="bob"),
HumanMessage(content="Kannst du mir ein Beispiel für nested Comprehensions geben?", name="alice")
]
response = model.invoke(conversation)
print(response.content)Das Modell unterscheidet zwischen Alice und Bob. Es erkennt, dass Alice derjenige ist, der ursprünglich Schwierigkeiten hatte, und kann seine Antwort entsprechend anpassen.
Messages sind mächtig. Aber sie sind nicht allmächtig.
Provider-spezifische Features bleiben
providerespezifisch. Ein NonStandardContentBlock
bietet zwar eine Escape-Hatch für experimentelle Funktionen, aber er
durchbricht die Abstraktion. Wer ihn nutzt, bindet sich an einen
Provider.
Content Blocks sind keine perfekte Normalisierung. Manche Modelle interpretieren Bilder anders als andere. Manche unterstützen Audio, andere nicht. Die Abstraktion verbirgt Unterschiede – aber sie eliminiert sie nicht.
Metadaten variieren zwischen Providern. OpenAI
liefert andere response_metadata als Anthropic. LangChain
garantiert eine einheitliche Struktur für die Kern-Metadaten
(usage_metadata, id), aber
providerübergreifende Konsistenz endet dort, wo Provider-APIs
divergieren.
Messages sind kein Wundermittel. Sie sind eine pragmatische Abstraktion für ein heterogenes Ökosystem.
Messages sind mehr als eine technische Notwendigkeit. Sie sind ein Denkmodell.
Wer in Messages denkt, denkt in Rollen. In Kontext. In strukturierten Konversationen. Wer in Messages denkt, baut Anwendungen, die über triviale Chatbots hinausgehen – Anwendungen, die Konversationshistorie persistent halten, die multimodale Inhalte verarbeiten, die Tool-Calls orchestrieren und die providerübergreifend funktionieren.
LangChain’s Message-Modell ist keine theoretische Spielerei. Es ist das Rückgrat produktionsreifer LLM-Anwendungen. Es ist die Schnittstelle, über die Prompts zu strukturierten Dialogen werden, über die Modelle zu Agenten werden und über die LLMs zu verlässlichen Systemen werden.
Messages sind die Sprache, in der LangChain mit LLMs spricht.
Verstehe sie – und du verstehst, wie moderne KI-Anwendungen gebaut werden.