0Pricing
AI Engineering Academy · Lektion

Warum zustandslose LLMs externen Speicher benötigen

Sie verstehen, warum jeder API-Aufruf von vorn beginnt, wie naives Einfügen von Kontext zu einer explosionsartigen Zunahme der Tokenzahl führt und welche Möglichkeiten es bei Speicherstrategien von einfach bis komplex gibt.

Warum zustandslose LLMs externen Speicher benötigen ist eine kostenlose AI Engineering Academy-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des AI Engineering Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der AI Engineering Academy-Kurs umfasst insgesamt 4 Lektionen.

LLMs haben standardmäßig kein Gedächtnis

Jeder API-Aufruf an ein LLM ist vollständig unabhängig. Das Modell empfängt die Nachrichten, die Sie in dieser einen Anfrage senden, und verarbeitet sie – mehr nicht. Beim nächsten Aufruf erinnert sich das Modell nicht an die vorherige Unterhaltung. Es ist, als würden Sie mit jemandem sprechen, der kein Kurzzeitgedächtnis besitzt. Diese Zustandslosigkeit ist beabsichtigt: Sie erleichtert die Skalierung und Bereitstellung von Modellen, verlagert die Verantwortung für das Gedächtnis jedoch auf Ihre Anwendung.

Das Amnesieproblem in der Praxis

Ohne Gedächtnis scheitert ein Chatbot an grundlegenden Aufgaben über mehrere Dialogrunden hinweg. Wenn ein Benutzer in Runde 1 sagt: „Mein Name ist Alice“, und in Runde 3 fragt: „Wie ist mein Name?“, wird das Modell antworten, dass es das nicht weiß. Jede Runde wirkt wie ein neues Gespräch. Benutzer empfinden das als äußerst frustrierend. Die Lösung besteht darin, dass Ihre Anwendung den Gesprächsverlauf verwaltet und ihn in jeden API-Aufruf einbezieht.

# Naive stateless approach — model forgets everything
from openai import OpenAI

client = OpenAI()

def chat_stateless(user_message: str) -> str:
    response = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[  # Only the new message — no history!
            {'role': 'user', 'content': user_message}
        ]
    )
    return response.choices[0].message.content

chat_stateless('My name is Alice.')   # Model: 'Hello Alice!'
chat_stateless('What is my name?')    # Model: 'I do not know your name.'

Einfache Lösung: Den gesamten Verlauf einfügen

Der einfachste Ansatz für ein Gedächtnis besteht darin, jede Runde an eine wachsende Liste anzuhängen und die gesamte Liste mit jeder Anfrage zu senden. Das funktioniert, hat aber einen entscheidenden Nachteil: Das Kontextfenster ist begrenzt. Ein Fenster mit 128K Tokens klingt groß, doch ein langer Kundensupport-Chat mit Codeausschnitten kann es innerhalb weniger Minuten ausschöpfen. Wenn Sie den gesamten Verlauf senden, bezahlen Sie außerdem immer wieder für dieselben Tokens.

messages = []  # grows with every turn

def chat_with_full_history(user_message: str) -> str:
    messages.append({'role': 'user', 'content': user_message})
    response = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=messages  # all history every time
    )
    reply = response.choices[0].message.content
    messages.append({'role': 'assistant', 'content': reply})
    return reply

# Works at first, but messages list grows unboundedly
# After 100 turns: could be 50,000+ tokens per request

Der Lösungsraum für Gedächtnisstrategien

Es gibt ein Spektrum an Gedächtnisstrategien, die jeweils unterschiedliche Kompromisse zwischen Kontexttreue (wie viel Verlauf gespeichert wird) und Tokenkosten (wie viele Tokens pro Anfrage verwendet werden) eingehen. Die Strategien reichen von einfach bis anspruchsvoll: vollständiger Pufferspeicher, gleitendes Kontextfenster, zusammengefasster Speicher, Entitätenspeicher und vektorbasiertes episodisches Gedächtnis. Welche Wahl die richtige ist, hängt von der Länge Ihrer Gespräche und Ihrem Budget ab.

Tokenkosten des Gesprächsverlaufs

Jeder API-Aufruf kostet abhängig von der kombinierten Länge aller Nachrichten Tokens – sowohl der Eingabe (Ihres Nachrichten-Arrays) als auch der Ausgabe (der Antwort des Modells). Wenn Sie bei jeder Anfrage den vollständigen Verlauf mitsenden, steigen die Tokenkosten mit der Gesprächslänge quadratisch: Bei Runde N werden N vorherige Nachrichten gesendet. Ein Gespräch mit 50 Runden und 200 Tokens pro Runde sendet insgesamt 200+400+600+...+10.000 = über 250.000 Eingabe-Tokens.

import tiktoken

def estimate_conversation_cost(
    turns: int,
    tokens_per_turn: int,
    price_per_1k_input: float = 0.00015  # gpt-4o-mini
) -> float:
    total_input_tokens = sum(
        (i + 1) * tokens_per_turn  # each turn sends all previous turns
        for i in range(turns)
    )
    total_output_tokens = turns * tokens_per_turn
    cost = (total_input_tokens / 1000) * price_per_1k_input
    print(f'{turns} turns: {total_input_tokens:,} input tokens, ${cost:.4f}')
    return cost

estimate_conversation_cost(50, 200)

Wo der Gesprächsverlauf gespeichert wird

Der Gesprächsverlauf muss außerhalb des Python-Prozesses gespeichert werden, damit er Serverneustarts übersteht und über mehrere Instanzen hinweg verfügbar ist. Zu den gängigen Speicher-Backends gehören: Redis für schnellen Zugriff im Arbeitsspeicher mit Ablauf per TTL (ideal für aktive Sitzungen), PostgreSQL für dauerhafte Langzeitspeicherung und Analysen sowie DynamoDB für serverloses automatisches Skalieren. LangChain bietet standardmäßig Konnektoren für all diese Systeme.

# Three storage options for conversation history

# 1. In-process dict (development only — lost on restart)
from langchain_core.chat_history import InMemoryChatMessageHistory

# 2. Redis (production — fast, TTL-based expiry)
from langchain_community.chat_message_histories import RedisChatMessageHistory
history = RedisChatMessageHistory(session_id='user-123', url='redis://localhost:6379')

# 3. PostgreSQL (production — durable, queryable)
from langchain_community.chat_message_histories import PostgresChatMessageHistory
history = PostgresChatMessageHistory(
    session_id='user-123',
    connection_string='postgresql://user:pass@localhost/db'
)

Sitzungs-IDs: Benutzergespräche voneinander trennen

Wenn Ihre App mehrere Benutzer bedient, müssen Sie den Verlauf für jedes Gespräch getrennt verwalten. Eine session_id (typischerweise eine UUID oder eine Kombination aus Benutzer-ID und Gesprächs-ID) legt fest, welcher Verlauf für jede Anfrage geladen wird. LangChain's RunnableWithMessageHistory akzeptiert eine get_session_history-Funktion, die eine Sitzungs-ID entgegennimmt und das passende Verlaufsobjekt zurückgibt.

import uuid
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.chat_history import InMemoryChatMessageHistory

store = {}  # session_id -> history (use Redis in production)

def get_session_history(session_id: str) -> InMemoryChatMessageHistory:
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]

# Create a new session for each user conversation
def new_session() -> str:
    return str(uuid.uuid4())

alice_session = new_session()
bob_session = new_session()
# Alice and Bob's histories are completely independent

Der Wrapper RunnableWithMessageHistory

RunnableWithMessageHistory ist LangChains LCEL-native Methode, um jeder Chain einen Speicher hinzuzufügen. Er umschließt Ihre Chain, lädt den Verlauf automatisch vor jeder Ausführung, hängt die neue Benutzernachricht und die KI-Antwort an und speichert alles wieder im Speicher. Sie legen fest, welcher Eingabeschlüssel die Benutzernachricht enthält und welche Prompt-Variable den Verlauf erhalten soll.

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate.from_messages([
    ('system', 'You are a helpful assistant.'),
    MessagesPlaceholder(variable_name='history'),  # history injected here
    ('human', '{input}'),
])

chain = prompt | ChatOpenAI(model='gpt-4o-mini') | StrOutputParser()

chain_with_memory = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key='input',
    history_messages_key='history'
)

Eine speicherfähige Chain aufrufen

Beim Aufruf einer mit RunnableWithMessageHistory umschlossenen Chain übergeben Sie ein config-Dict mit configurable: {session_id: ...}. Dadurch weiß der Wrapper, welchen Verlaufsspeicher er laden soll. Die Chain erledigt alles Weitere: den Verlauf vor dem Aufruf laden, ihn in den Prompt einfügen und die neue Runde nach Eingang der Antwort speichern.

session_id = 'user-alice-session-1'

# First message
response1 = chain_with_memory.invoke(
    {'input': 'My name is Alice and I love Python.'},
    config={'configurable': {'session_id': session_id}}
)
print(response1)  # 'Hello Alice! Nice to meet you.'

# Second message — model now knows Alice's name and interest
response2 = chain_with_memory.invoke(
    {'input': 'What is my name and what do I love?'},
    config={'configurable': {'session_id': session_id}}
)
print(response2)  # 'Your name is Alice and you love Python!'

Zu vermeidende Fehler beim Speicher

Drei häufige Fehler bei der Implementierung des Speichers: Fehlende Sitzungsisolierung – wenn Sie für alle Benutzer dasselbe Verlaufsobjekt wiederverwenden, werden private Daten zwischen Gesprächen offengelegt. TTL ignorieren – wenn Verläufe unbegrenzt gespeichert werden, läuft Ihre Datenbank voll; legen Sie für inaktive Sitzungen einen Ablaufzeitpunkt fest. Dem Verlauf zu sehr vertrauen – Benutzer können falsche Erinnerungen einschleusen („Ich habe Ihnen gesagt, dass ich Administrator bin“). Prüfen Sie solche Angaben daher anhand einer vertrauenswürdigen Quelle und nicht allein anhand des Gesprächsverlaufs.

# Mistake 1: Shared history for all users
global_history = InMemoryChatMessageHistory()  # BAD!

# Fix: per-session history
store = {}  # keyed by session_id

# Mistake 2: No TTL on Redis history
# BAD: history = RedisChatMessageHistory(session_id=sid, url=url)
# Good: set TTL to 24 hours
history = RedisChatMessageHistory(
    session_id=session_id,
    url='redis://localhost',
    ttl=86400  # 24 hours in seconds
)

Die passende Speicherstrategie auswählen

Verwenden Sie vollständigen Pufferspeicher nur für kurze Gespräche, bei denen Sie wissen, dass der gesamte Kontext innerhalb der Grenzen bleibt. Verwenden Sie ein gleitendes Fenster für allgemeine Chatbots (behalten Sie die letzten N Runden). Verwenden Sie einen Zusammenfassungsspeicher, wenn Gespräche offen angelegt und möglicherweise sehr lang sind. Verwenden Sie einen Vektorspeicher, wenn Benutzer bestimmte Fakten aus einem viel früheren Abschnitt eines langen Gesprächs wiederfinden müssen. In den nächsten Lektionen sehen wir uns diese Strategien einzeln an.

Kurzer Test

Testen Sie Ihr Verständnis dafür, warum zustandslose LLMs externen Speicher benötigen.

Zusammenfassung der Lektion

In dieser Lektion haben Sie gelernt: LLMs sind von Grund auf zustandslos – jeder API-Aufruf sieht nur das, was Sie in dieser Anfrage mitsenden; das unkritische Mitsenden des vollständigen Verlaufs lässt die Tokenkosten quadratisch steigen und stößt irgendwann an die Kontextgrenzen; und RunnableWithMessageHistory ist LangChains elegante Methode, mit jedem Backend (Redis, PostgreSQL) externen Speicher hinzuzufügen, der über eine Sitzungs-ID adressiert wird. Als Nächstes sehen wir uns konkrete Speicherstrategien an: Puffer- und Fensterspeicher.

Häufig gestellte Fragen

Ist die Lektion „Warum zustandslose LLMs externen Speicher benötigen“ kostenlos?

Ja — der vollständige Text von „Warum zustandslose LLMs externen Speicher benötigen“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des AI Engineering Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der AI Engineering Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Warum zustandslose LLMs externen Speicher benötigen“?

Sie verstehen, warum jeder API-Aufruf von vorn beginnt, wie naives Einfügen von Kontext zu einer explosionsartigen Zunahme der Tokenzahl führt und welche Möglichkeiten es bei Speicherstrategien von e… Du übst AI Engineering Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um AI Engineering Academy zu starten?

Keine Vorkenntnisse erforderlich. AI Engineering Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.

Wie lange dauert die Lektion „Warum zustandslose LLMs externen Speicher benötigen“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser AI Engineering Academy-Lektion Code schreiben und ausführen?

Ja. Jede AI Engineering Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Warum zustandslose LLMs externen Speicher benötigen
  2. Puffer- und Fensterspeicher
  3. Zusammenfassungsspeicher und tokenbewusstes Kürzen
  4. Chatverläufe in Redis und PostgreSQL speichern
← Zurück zu AI Engineering Academy