13 Messages: Das Konversationsrückgrat von LangChain

13.1 Die Abstraktion zwischen Prompt und Modell

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.

13.1.1 Warum überhaupt Messages?

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.

13.1.2 Die drei Säulen eines Message-Objekts

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:

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:

Wer Messages richtig nutzt, denkt in Metadaten. Wer Metadaten ignoriert, verliert Transparenz.

13.2 Message-Typen im Detail

13.2.1 SystemMessage: Die unsichtbare Hand

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.

13.2.2 HumanMessage: Die Stimme des Nutzers

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.

13.2.3 AIMessage: Die Antwort mit Kontext

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:

Ein 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.

13.2.4 ToolMessage: Der Rückkanal

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.


13.3 Von Prompts zu Messages: Die interne Transformation

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.

13.3.1 Content Blocks: Die multimodale Ebene

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:

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.


13.4 Exemplarischer Teil: Messages in der Praxis

Die Theorie ist gelegt. Jetzt wird implementiert.

13.4.1 Basis-Nutzung: Eine einfache Konversation

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.

13.4.2 System-Prompt und Kontext aufbauen

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.

13.4.3 Multi-Turn-Konversation: Historie aufbauen

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.

13.4.4 Metadaten nutzen: Token-Zählung und Tracing

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.

13.4.5 Manuell konstruierte AIMessages: Historie simulieren

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.

13.4.6 Dictionary-Format: Kompatibilität mit OpenAI-API

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.

13.4.7 Multimodale Messages: Bilder und Text kombinieren

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.

13.4.8 Name-Feld: Multi-User-Konversationen unterscheiden

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.


13.5 Die Grenzen der Abstraktion

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.


13.6 Messages als Denkmodell

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.