Automatische Übersetzung
Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.
Architektur für AI-Agent-Memory im Jahr 2026: Checkpoints, Vector Stores und dateibasierter Speicher
Teil 2 der Reihe Engineering the Agentic Stack
Zustand ist das, was einen Chatbot von einem AI-Agenten unterscheidet. Ohne Agent-Memory beginnt jede Interaktion bei null. Der Agent kann nicht pausieren und fortsetzen, nicht aus vergangenen Sitzungen lernen und nicht personalisieren. Teil 1 behandelte die Reasoning-Loops von AI-Agenten: wie ein Agent denkt. In diesem Beitrag geht es um die Speicherarchitektur, die bestimmt, was er sich merkt.
Ich gehe die Speicherarchitektur des Market Analyst Agent durch und zeige, wie Hot-Checkpoints, Cold-Vector-Stores und dateibasiertes Dokumenten-Memory für langlebige Agenten zusammenspielen. Danach gehe ich darauf ein, wann PostgreSQL, Redis, Qdrant, Key-Value-Stores und einfache Markdown-Dateien jeweils sinnvoll sind.
TL;DR: AI-Agent-Memory teilt sich in drei Ebenen auf. Hot Memory ist Thread-Level-Checkpointing in Redis oder PostgreSQL für Pause/Resume. Cold Memory ist sitzungsübergreifendes Wissen in einem Vector Store oder Key-Value-Backend für Personalisierung. Document Memory sind menschenlesbare Dateien, die der Agent liest und schreibt, um persistentes Projektwissen zu halten. Das Checkpoint-System von LangGraph übernimmt die Hot-Ebene nativ. Für Cold Memory liefert Vector Search mit Qdrant semantischen Recall; für strukturierte Fakten reicht ein Key-Value-Store. Für Document Memory bietet ein dateibasierter Store Debuggability und null Embedding-Infrastruktur.
Warum die Architektur von AI-Agent-Memory wichtig ist
Ein zustandsloser Agent ist fancy Autocomplete. Er nimmt eine Anfrage entgegen, gibt eine Antwort zurück und vergisst alles. Das funktioniert für Single-Turn-Q&A. Es bricht in dem Moment, in dem du Folgendes brauchst:
- Pause und Resume: Ein Nutzer startet eine Research-Aufgabe, klappt den Laptop zu und kommt morgen zurück. Ohne checkpointed State startet der Agent wieder von vorn.
- Multi-Turn-Kohärenz: Über eine lange Unterhaltung hinweg muss sich der Agent merken, welche Tools er aufgerufen hat, welche Daten er gesammelt hat und welche Planschritte abgeschlossen sind.
- Personalisierung: Ein wiederkehrender Nutzer erwartet, dass der Agent seine Risikotoleranz, seine bevorzugte Analysetiefe und vergangene Interaktionen kennt.
- Human-in-the-loop (HITL): Der Agent erstellt einen Berichtsentwurf und wartet auf Freigabe. Dieser „wartende“ Zustand muss Prozessneustarts überleben.
Nimm den Market Analyst Agent aus Teil 1. Ein Nutzer fragt „Analyze NVDA“. Der Agent erstellt einen Plan, ruft fünf Tools auf, sammelt Daten und formuliert einen Bericht. Der Nutzer antwortet „looks good, but add competitor analysis.“ Ohne checkpointed State hat der Agent keine Ahnung, worauf sich „looks good“ bezieht, und muss von vorn anfangen. Mit einem Checkpoint-Store lädt er den Zustand des letzten Knotens, einschließlich der gesammelten Recherche, und ergänzt einfach einen Wettbewerber-Schritt.
Stell dir nun vor, derselbe Nutzer kommt eine Woche später zurück: „Update my NVDA analysis.“ Ohne Long-Term-Memory weiß der Agent nicht, dass dieser Nutzer konservative Risikobewertungen bevorzugt oder dass er an Halbleiteraktien interessiert ist. Mit einem vectorbasierten Memory-Store ruft er diese Fakten ab und personalisiert die Antwort, ohne erneut nachfragen zu müssen.
LangGraph trennt das sauber. Jede Graph-Ausführung läuft in einem Thread – einer einzelnen Unterhaltung oder Aufgabe. Zustand innerhalb eines Threads ist Working Memory. Zustand, der threadübergreifend bestehen bleibt, ist Long-Term Memory. Die Engineering-Frage ist, welches Storage-Backend du für beides verwendest.
Eine Taxonomie von AI-Agent-Memory
Bevor wir in die Implementierung einsteigen, hilft es, zu klassifizieren, was sich Agenten merken müssen. Das CoALA-Framework (Sumers, Yao et al., 2023) ist die Standardtaxonomie und lehnt sich an die Kognitionswissenschaft an. Ich habe Memory-Scoping bereits in meinem Beitrag zu Context Engineering eingeführt; hier erweitere ich es auf sechs Kategorien:
| Memory-Typ | Scope | Lebensdauer | Beispiel | Storage-Muster |
|---|---|---|---|---|
| Working | Aktueller Schritt | Millisekunden | Tool-Call-Argumente, aktuelle LLM-Antwort | In-Process (Python dict) |
| Short-term | Aktueller Thread | Minuten–Stunden | Konversationsverlauf, Planfortschritt, gesammelte Daten | Checkpoint-Store |
| Episodic | Thread-übergreifend | Tage–Monate | „Letzte Woche fragte der Nutzer nach NVDA earnings“ | Vector Store / KV-Store |
| Semantic | Thread-übergreifend | Monate–dauerhaft | „Nutzer bevorzugt konservative Investments“ | Vector Store / KV-Store |
| Document | Thread-übergreifend | Tage–dauerhaft | Projektnotizen, Research-Zusammenfassungen, gelernte Muster | File Store (Markdown/JSON) |
| Procedural | Systemweit | Dauerhaft | „Bei Aktienanalysen immer SEC-Filings prüfen“ | Config / System Prompt |
Working Memory ist das, womit das LLM gerade aktiv arbeitet: Python-Variablen in der aktuellen Funktion, der Inhalt des Context Windows, Tool-Call-Argumente während der Ausführung. Es ist die schnellste und flüchtigste Ebene. Nichts bleibt über den aktuellen Schritt hinaus bestehen. Working Memory ist durch das Context Window des Modells begrenzt – und genau das ist der eigentliche Bottleneck. Alles, was der Agent zum Entscheidungszeitpunkt „weiß“, muss hier hineinpassen, egal ob es aus dem Checkpoint-Store, einer Vector-Query oder einem File-Read stammt. Die anderen Ebenen existieren, um die richtigen Informationen zur richtigen Zeit in das Working Memory zu bringen.
Short-Term Memory ist der Checkpoint-Store, in den LangGraph nach jeder Knotenausführung schreibt. Episodic und Semantic Memory sind Long-Term-Stores, die threadübergreifend bestehen bleiben. Document Memory ist strukturiertes Wissen, das der Agent über die Zeit ansammelt – Projektnotizen, Research-Zusammenfassungen, gelernte Konventionen – gespeichert in menschenlesbaren Dateien, die sowohl der Agent als auch der Entwickler inspizieren und bearbeiten können. Procedural Memory ist im System Prompt und in Tool-Definitionen kodiert; es ändert sich nicht pro Nutzer.
In der Praxis reduziert sich das auf drei Ebenen: Hot Memory (Working + Short-Term) für die aktuelle Sitzung, Cold Memory (Episodic + Semantic) für sitzungsübergreifenden Recall und Document Memory für angesammeltes Projektwissen, das davon profitiert, menschenlesbar und direkt editierbar zu sein.
Die meisten Frameworks und Übersichtsarbeiten fokussieren sich auf den Hot/Cold-Split und lassen Document Memory aus, obwohl es breit eingesetzt wird. Das CoALA-Framework klassifiziert Memory als working, episodic, semantic und procedural; Dateien werden nicht erwähnt. Die Survey Memory in the Age of AI Agents behandelt Vector Stores und Knowledge Graphs, aber keine Dokumentdateien. Die LangGraph-Dokumentation behandelt Checkpoints und das Store-Interface, hat aber kein natives Konzept für dateibasiertes Memory.
In der Praxis ist Document Memory inzwischen Standard in AI-Coding-Assistenten. Claude Code, Cursor, Windsurf und Devin behandeln dateibasiertes Memory alle als Kernfeature. Und das Muster breitet sich über Coding hinaus aus: Open-World-Game-Agents speichern wiederverwendbare Skills als Code-Bibliotheken (Voyager), in Enterprise-Wettbewerben erfolgreiche Agenten iterieren über Läufe hinweg auf prozeduralen Prompt-Dokumenten (ECR3 winning approaches), und Web-Automation-Agents synthetisieren wiederverwendbare Workflow-APIs aus erfolgreichen Episoden (Agent Workflow Memory). Die Vorteile – Debuggability, Versionskontrolle, null Infrastruktur – sind nicht coding-spezifisch.
Eine nützliche Beobachtung aus derselben Survey: Agent-Memory ist nicht dasselbe wie RAG oder Context Engineering. Das unterscheidende Merkmal ist, dass der Agent selbst die Read/Write-Operationen ausführt. Er entscheidet, was gespeichert und was vergessen wird, statt sich auf eine feste Retrieval-Pipeline zu verlassen.
Das Generative Agents Paper (Park et al., 2023) zeigte, wie weit das gehen kann: simulierte Agenten, die ihre eigenen Erinnerungen speicherten, reflektierten und abfragten, erzeugten auffallend menschenähnliches Verhalten. Das Architektur-Muster – ein Memory Stream mit Retrieval, bewertet nach Recency, Importance und Relevance – ist die Vorlage, auf der die meisten modernen Agent-Memory-Systeme noch immer aufbauen.
Kurzfristiges Agent-Memory: der Checkpoint-Store
Jedes Mal, wenn ein LangGraph-Knoten ausgeführt wird, serialisiert das Framework den vollständigen Graph-State und schreibt ihn in einen Checkpoint-Store. Das ist die Grundlage für Pause/Resume, Time-Travel-Debugging und HITL-Workflows.
Ein Checkpoint enthält den Graph-State, der zum Fortsetzen nötig ist: das AgentState aus Teil 1 (Messages, Identity, User Profile, Plan Steps, Research Data, Execution Mode) sowie LangGraph-Metadaten wie den Knoten, der ihn erzeugt hat, und eine monoton steigende Sequenznummer. Wenn der Graph fortgesetzt wird – nach einer HITL-Unterbrechung oder einem Prozessneustart – lädt er den neuesten Checkpoint und macht genau dort weiter, wo er aufgehört hat. Ein Checkpoint ist nicht dasselbe wie ein Append-Only-Event-Log oder Trace; Teil 5 trennt diese Runtime-Observability-Flächen explizit.
So funktioniert Checkpointing in LangGraph
LangGraphs BaseCheckpointSaver ist ein einfaches Interface: put() schreibt einen Checkpoint, get_tuple() liest den neuesten für einen Thread, list() liefert die Historie. Jeder Checkpoint ist über (thread_id, checkpoint_ns, checkpoint_id) gekeyed, wobei thread_id die Unterhaltung identifiziert, checkpoint_ns das Subgraph-Namespaceing behandelt und checkpoint_id eine eindeutige Version ist.
Die entscheidende Frage ist, welches Backend dahinter liegt. LangGraph bringt zwei produktionsreife Optionen mit: PostgreSQL und Redis.
PostgreSQL vs Redis
| Dimension | PostgreSQL (langgraph-checkpoint-postgres) |
Redis (langgraph-checkpoint-redis) |
|---|---|---|
| Read-Latenz | ~0.65ms | ~0.095ms |
| Write-Latenz | ~2ms (unlogged) bis 10ms (mit WAL) | ~0.095ms |
| Durchsatz | ~15K txn/s | ~893K req/s |
| Durability | Volles ACID, WAL + Replikation | Konfigurierbar (AOF/RDB), Risiko von Datenverlust |
| Checkpoint-Historie | Vollständige Historie (Resume, Time-Travel-Debugging) | Konfigurierbare Retention über maxcount |
| Betriebskosten | Mittel (standardmäßige RDBMS-Operations) | Höher (RAM-gebunden, Memory-Management) |
| Skalierungsmuster | Vertikal + Read Replicas | Horizontal (Redis Cluster) |
| Am besten geeignet für | Durability-first Resume und Debugging | Niedrige Latenz, hoher Durchsatz, Echtzeit |
Latenz-Benchmarks aus den Vergleichen von CyberTec und RisingWave.
PostgreSQL: der robuste Default
PostgreSQL ist für die meisten Teams der sicherere Default. Checkpoints überleben Crashes, du bekommst vollständige Transaktionssemantik, und die Checkpoint-Historie macht Time-Travel-Debugging einfach.
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
async def create_postgres_checkpointer(connection_string: str) -> AsyncPostgresSaver:
"""Create a PostgreSQL-backed checkpoint store.
PostgreSQL gives us ACID guarantees — if a checkpoint write succeeds,
the state is durable even if the process crashes immediately after.
"""
checkpointer = AsyncPostgresSaver.from_conn_string(connection_string)
# Create the checkpoint tables if they don't exist.
# This is idempotent — safe to call on every startup.
await checkpointer.setup()
return checkpointer
# Usage: wire into the graph compilation
checkpointer = await create_postgres_checkpointer(
"postgresql://user:pass@localhost:5432/agent_memory"
)
graph = create_graph(checkpointer=checkpointer)
# Every invoke/stream call now persists state automatically
config = {"configurable": {"thread_id": "user-123-session-1"}}
result = await graph.ainvoke({"messages": [HumanMessage(content="Analyze NVDA")]}, config)
# Resume later — loads the latest checkpoint for this thread
result = await graph.ainvoke({"messages": [HumanMessage(content="approved")]}, config)
Der AsyncPostgresSaver verwendet das Paket langgraph-checkpoint-postgres, das drei Tabellen anlegt: checkpoints (der serialisierte State), checkpoint_blobs (große Binärdaten) und checkpoint_writes (pending writes für Crash Recovery). Das Schema unterstützt gleichzeitigen Zugriff und verwendet Advisory Locks, um Schreibkonflikte zu verhindern.
Redis: wenn Latenz der Bottleneck ist
Wenn Submillisekunden-Latenz für Checkpoints wichtig ist (Echtzeit-Konversationsagenten, hochfrequente Tool-Loops), ist Redis die bessere Wahl.
from langgraph.checkpoint.redis.aio import AsyncRedisSaver
async def create_redis_checkpointer(redis_url: str) -> AsyncRedisSaver:
"""Create a Redis-backed checkpoint store.
Redis stores checkpoints in memory for sub-millisecond access.
Trade-off: less durable than PostgreSQL unless AOF is enabled.
"""
checkpointer = AsyncRedisSaver.from_conn_string(redis_url)
# Initialize Redis data structures
await checkpointer.setup()
return checkpointer
# Usage: same graph API, different backend
checkpointer = await create_redis_checkpointer("redis://localhost:6379")
graph = create_graph(checkpointer=checkpointer)
Der AsyncRedisSaver aus langgraph-checkpoint-redis speichert Checkpoints als JSON-Dokumente, gekeyed über die Thread-ID. Das v0.1.0-Redesign ersetzte mehrere Search-Operationen durch einen einzelnen JSON.GET-Call und reduzierte so die Latenz deutlich. Redis 8.0+ enthält RedisJSON und RediSearch standardmäßig – keine zusätzlichen Module zur Installation.
Für Deployments mit knappen Ressourcen speichert ShallowRedisSaver nur den neuesten Checkpoint pro Thread – keine Historie, aber minimaler RAM-Verbrauch. Das ist sinnvoll, wenn du Pause/Resume brauchst, aber kein Time-Travel-Debugging.
Wann welches System sinnvoll ist
Nutze PostgreSQL, wenn:
- du die vollständige Checkpoint-Historie für Time-Travel-Debugging oder reproduzierbares Resume brauchst
- Durability nicht verhandelbar ist (Finanzdienstleistungen, Gesundheitswesen)
- du PostgreSQL bereits in deinem Stack betreibst
- dein Agent lange Aufgaben ausführt, bei denen Zustandsverlust Stunden an Rechenzeit kostet
- du einen vereinheitlichten Datastore willst – PostgreSQL mit pgvector kann als einzelnes Backend für Checkpoints, Long-Term-Memory und Vector Search dienen und vereinfacht so deine Infrastruktur
Nutze Redis, wenn:
- Checkpoint-Latenz dein Bottleneck ist (Echtzeit-Chat, Streaming-UX)
- du Voice Bots baust – STT-to-LLM-to-TTS-Pipelines brauchen State Access im Submillisekundenbereich
- du horizontale Skalierung über viele parallele Threads brauchst
- du High-Concurrency-Fan-Out-Muster hast, in denen mehrere Agenten State teilen
- du kurzlebige Sessions hast, bei denen ein verlorener Checkpoint tolerierbar ist
- du semantisches Caching willst, um redundante LLM-Calls zu reduzieren (Redis LangCache cached semantisch ähnliche Queries, um wiederholte LLM-Calls zu vermeiden)
Andere Optionen: langgraph-checkpoint-sqlite funktioniert für lokale Entwicklung und Single-Process-Deployments. Für AWS-native Stacks bietet langgraph-checkpoint-aws einen DynamoDBSaver mit intelligenter Payload-Behandlung – kleine Checkpoints (<350 KB) bleiben in DynamoDB, große werden automatisch nach S3 ausgelagert. Serverless-Pricing und keine zu verwaltende Infrastruktur machen das attraktiv für Deployments mit variabler Last.
Langfristiges Memory: sitzungsübergreifend erinnern
Hot Memory kümmert sich um die aktuelle Unterhaltung. Aber was ist mit dem Nutzer, der nächste Woche wiederkommt? Long-Term-Memory speichert Fakten, Präferenzen und Interaktionshistorie, die threadübergreifend erhalten bleiben.
LangGraph bietet ein Store-Interface für threadübergreifendes Memory über seine Klasse BaseStore. Jedes Memory-Item ist ein Paar aus (namespace, key) mit einem JSON-Wert und optionalem Vector Embedding. Der Namespace kodiert typischerweise Nutzer oder Organisation: ("user", "user-123", "preferences").
Vector Storage: semantischer Recall mit Qdrant
Wenn der Agent unstrukturierte Fakten abrufen muss („What did the user say about their investment timeline?“), liefert Vector Search semantischen Recall. Statt exakter Key-Lookups fragt der Agent nach Bedeutung ab.
Qdrant ist eine zweckgebaute Vektordatenbank in Rust, die Embedding-Speicherung, Indexierung (HNSW) und gefilterte Suche übernimmt. HNSW und seine Trade-offs habe ich in meinem Beitrag zu Search Ranking im Detail behandelt. Qdrant bietet außerdem einen MCP-Server, der als semantische Memory-Schicht fungiert – nützlich, wenn dein Agent-Framework das Model Context Protocol unterstützt.
Aus memory_store.py:
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct, Distance, VectorParams
from langchain_anthropic import ChatAnthropic
import hashlib
import json
class UserMemoryStore:
"""Long-term memory backed by Qdrant vector search.
Stores user facts as embedded vectors for semantic retrieval.
Each fact is a short natural-language statement about the user.
"""
def __init__(self, qdrant_url: str, collection_name: str = "user_memory"):
self.client = QdrantClient(url=qdrant_url)
self.collection_name = collection_name
self._ensure_collection()
def _ensure_collection(self):
"""Create the collection if it doesn't exist."""
collections = [c.name for c in self.client.get_collections().collections]
if self.collection_name not in collections:
self.client.create_collection(
collection_name=self.collection_name,
vectors_config=VectorParams(
size=1536, # text-embedding-3-small dimensions
distance=Distance.COSINE,
),
)
def store_fact(self, user_id: str, fact: str, embedding: list[float]):
"""Store a user fact with its embedding."""
point_id = hashlib.md5(f"{user_id}:{fact}".encode()).hexdigest()
self.client.upsert(
collection_name=self.collection_name,
points=[PointStruct(
id=point_id,
vector=embedding,
payload={"user_id": user_id, "fact": fact},
)],
)
def recall(self, user_id: str, query_embedding: list[float], top_k: int = 5):
"""Retrieve the most relevant facts for a user given a query."""
results = self.client.query_points(
collection_name=self.collection_name,
query=query_embedding,
query_filter={"must": [{"key": "user_id", "match": {"value": user_id}}]},
limit=top_k,
)
return [hit.payload["fact"] for hit in results.points]
Der Ablauf ist: (1) Nach jeder Unterhaltung extrahiert ein LLM Schlüsselfakten aus der Interaktion („user has high risk tolerance“, „user is interested in semiconductor stocks“), (2) diese Fakten werden embedded und in Qdrant gespeichert, (3) zu Beginn der nächsten Unterhaltung fragt der Agent Qdrant mit der neuen Nachricht des Nutzers ab, um relevanten Kontext zurückzuholen.
Retrieval-Scoring: mehr als Cosine Similarity
Rohe Cosine Similarity ist ein Startpunkt, aber produktive Memory-Systeme brauchen reichhaltigeres Retrieval. Das Generative Agents Paper (Park et al., 2023) führte eine Scoring-Funktion ein, die drei Signale kombiniert:
- Recency: regelbasierter Decay, damit aktuelle Erinnerungen höher scoren. Eine exponentielle Decay-Funktion sorgt dafür, dass ein Fakt von gestern vor einem gleichwertigen Fakt von vor sechs Monaten liegt.
- Importance: vom LLM bewertete Signifikanz auf einer Skala von 1 bis 10. „User's portfolio is down 40%“ scored höher als „user said hello.“
- Relevance: Embedding-Cosine-Similarity zwischen der Query und dem gespeicherten Fakt.
Der finale Retrieval-Score ist eine gewichtete Summe: score = alpha * recency + beta * importance + gamma * relevance. So werden frische, wichtige Fakten nicht von veralteten, aber semantisch ähnlichen Fakten verdrängt. Für den Market Analyst Agent gewichte ich Relevance am höchsten (0.5), gefolgt von Recency (0.3) und Importance (0.2), da die aktuelle Query-Intention des Nutzers am wichtigsten ist. Das sind Startgewichte, adaptiert aus dem Generative-Agents-Paper (das gleiche Gewichtung verwendete); für Finanzanalyse-Queries funktionierte eine stärkere Gewichtung von Relevance in meiner Erfahrung besser, aber die Werte sind intuitiv gewählt, nicht empirisch optimiert.
Alternativen zur Vector Search
Vector Search ist mächtig, aber nicht immer das richtige Werkzeug. Hier ist, wann Alternativen sinnvoll sind:
| Ansatz | Am besten geeignet für | Latenz | Komplexität |
|---|---|---|---|
| Vector Search (Qdrant) | Semantischer Recall unstrukturierter Fakten | 5–20ms | Mittel |
| Key-Value-Store (Redis) | Strukturierte User-Profile, Präferenzen | <1ms | Niedrig |
| Document Store (Dateien) | Projektwissen, agentenverwaltete Notizen | 1–5ms | Niedrig |
| Full-Text-Search (PostgreSQL GIN index) | Keyword-basierter Recall von Konversationshistorie | 2–10ms | Niedrig |
| Knowledge Graph (Neo4j) | Entity-Relationships, Multi-Hop-Reasoning | 10–50ms | Hoch |
| Hybrid (Vector + Keyword) | Bester Recall bei variierender Query-Intention | 10–30ms | Mittel |
Key-Value-Stores funktionieren gut für strukturierte Daten. Wenn dein Long-Term-Memory ein User-Profil ist – Risikotoleranz, Anlagehorizont, bevorzugte Sektoren – ist ein Redis-Hash oder eine PostgreSQL-JSONB-Spalte einfacher und schneller als Vektoren zu embeden und abzufragen. Nutze Vector Search, wenn das Memory unstrukturiert ist und die Formulierung der Retrieval-Query variiert.
LangGraphs eingebauter Store bietet ein namespacebasiertes Key-Value-Interface mit optionaler Vector Search. Die API BaseStore ist einfach: put(), get(), search() und delete() mit hierarchischem Namespace-Scoping. Es sind drei Implementierungen verfügbar:
InMemoryStore– für Entwicklung und Tests (Daten gehen beim Prozessende verloren)PostgresStore– persistenter Produktions-Store mit vollständigem SQL-QueryingAsyncRedisStore– threadübergreifendes Memory mit Vector Search, TTL-Support und Metadata-Filtering
Die Konfiguration index aktiviert Vector Search über gespeicherte Items unter Verwendung eines konfigurierbaren Embedding-Modells. Für viele Use Cases reicht dieser eingebaute Store aus, ohne auf eine dedizierte Vektordatenbank zurückzugreifen.
from langgraph.store.memory import InMemoryStore
# Create a store with vector search enabled
store = InMemoryStore(
index={
"dims": 1536,
"embed": my_embedding_function, # e.g., OpenAI text-embedding-3-small
}
)
# Store a user preference (namespace scopes to user)
await store.aput(
namespace=("user", "user-123", "preferences"),
key="risk-profile",
value={"risk_tolerance": "high", "horizon": "long-term"},
)
# Semantic search across user's memories
results = await store.asearch(
namespace=("user", "user-123"),
query="What is their investment style?",
limit=5,
)
Die richtige Long-Term-Memory-Strategie wählen
Starte mit Key-Value, wenn dein Memory strukturiert und klar definiert ist (User-Profile, Settings, benannte Entitäten). Ergänze Vector Search, wenn du semantisches Retrieval über unstrukturierte Fakten brauchst oder wenn die Formulierung der Query unvorhersehbar variiert.
Knowledge Graphs lohnen sich, wenn Beziehungen zwischen Entitäten wichtig sind, z. B. „Welche Unternehmen hat der Nutzer angefragt, die Konkurrenten von NVDA sind?“ Das interessanteste aktuelle Projekt hier ist Graphiti (von Zep), das einen zeitlich bewussten Knowledge Graph aufbaut, der verfolgt, wann Fakten gültig waren, nicht nur was gültig war. Jede Kante trägt Gültigkeitsintervalle, sodass eine Änderung der Risikotoleranz des Nutzers den alten Wert ungültig macht, statt ihn stillschweigend zu überschreiben. Graphiti berichtet 94.8% Genauigkeit auf dem DMR-Benchmark, und sein bi-temporales Modell behandelt das Problem veralteter Memorys auf der Datenebene.
Der Haken ist der Betrieb. Eine Graph-Datenbank zu betreiben ist nicht trivial, und für die meisten Agent-Anwendungen deckt Vector Search mit Metadata-Filtering denselben Bereich mit weniger Infrastruktur ab.
Managed-Memory-Frameworks wie Mem0 und Letta (früher MemGPT) übernehmen die Extraction-Consolidation-Retrieval-Pipeline für dich. Der Ansatz von Mem0 ist bemerkenswert: Ein LLM extrahiert Kandidaten-Memorys, eine Decision Engine vergleicht jeden neuen Fakt mit bestehenden Einträgen im Vector Store, und ein Resolver entscheidet über Add, Update oder Delete, wodurch der Memory-Store kohärent und nicht redundant bleibt. Letta verfolgt einen Betriebssystem-Ansatz: Agenten verwalten ihr eigenes Context Window mit Memory-Management-Tools und verschieben Daten autonom zwischen „core memory“ (im Kontext) und „archival memory“ (außerhalb des Kontexts). Beide sind eine Evaluation wert, wenn du schneller produktiv werden willst und nicht die volle Kontrolle über die Memory-Pipeline brauchst.
Document Memory: der Aktenschrank des Agenten
Das Muster ist in der Praxis weit verbreitet, aber in der akademischen Literatur untererforscht. Framework-Dokumentationen behandeln Vector Stores, Knowledge Graphs und Context Management im Detail, aber dateibasiertes Memory bekommt kaum mehr als eine Fußnote. Diese Lücke lohnt es zu schließen, denn genau so persistieren die effektivsten AI-Coding-Assistenten tatsächlich Wissen. Lettas Benchmark zeigte, dass ein einfacher Filesystem-Ansatz im LoCoMo-Benchmark für Conversation Memory 74.0% erreichte und mehrere spezialisierte Memory-Libraries übertraf. Frontier-Modelle sind bereits auf agentische Coding-Tasks trainiert und beherrschen Dateizugriffe nativ – das Muster passt also zu ihren Stärken.
Der Wandel kam durch längere Context Windows. 2023–2024, mit 8k–32k Tokens, hattest du keine Wahl, als Dokumente in Chunks zu zerlegen und zu embeden. Mit 1M+ Token-Fenstern (Gemini 1.5, Claude 4) ist es in der Regel effektiver, den Agenten die ganze Datei lesen zu lassen, als zu raten, welche Chunks relevant sind. Auch operativ ist die Geschichte einfacher. Du kannst eine Markdown-Datei lesen, bearbeiten und git diff. Eine Vektordatenbank kannst du nicht git diff.
Vector Stores und Key-Value-Backends sind gut für semantischen Recall und strukturierte Lookups. Aber es gibt eine dritte Kategorie von Agent-Wissen, die keines von beiden sauber abdeckt: angesammelter Projektkontext, also Konventionen, Research-Notizen und Entscheidungen, die der Agent sitzungsübergreifend braucht und die davon profitieren, menschenlesbar und versioniert zu sein.
Das ist Document Memory: Der Agent liest und schreibt strukturierte Dateien (Markdown, JSON, YAML) in ein bekanntes Verzeichnis. Keine Embeddings, keine Datenbank, keine Infrastruktur. Nur Dateien auf dem Datenträger, die sowohl der Agent als auch der Entwickler cat, grep, git diff und von Hand bearbeiten können.
Warum Dateien?
Für langlebige Agent-Workflows ist das effektivste Muster, das ich gesehen habe, keine Vektordatenbank. Es ist ein Verzeichnis mit gut organisierten Notizen. Betrachte, was passiert, wenn ein Coding-Agent über Wochen an einem Projekt arbeitet:
- Er lernt, dass das Projekt Pydantic v2 und nicht v1 verwendet
- Er entdeckt, dass Tests mit
pytest -x --tb=shortlaufen müssen - Er sammelt Wissen über die Architektur der Codebase
- Er lernt die Präferenzen des Entwicklers („immer
pathlibverwenden, niemalsos.path“)
Diese Fakten sind zu strukturiert für Vector Search (du brauchst exakten Recall, keine fuzzy Similarity) und zu zahlreich für einen Key-Value-Store (sie bilden zusammenhängende Dokumente, keine isolierten Fakten). Außerdem sind es Fakten, die der Entwickler direkt sehen und bearbeiten will. Wenn der Agent etwas Falsches lernt, öffnest du die Datei und korrigierst es.
So funktionieren Claude Codes CLAUDE.md und das Verzeichnis .claude/. Der Agent liest projektweite Dateien CLAUDE.md für Konventionen und Instruktionen und schreibt in ~/.claude/MEMORY.md für sitzungsübergreifende Learnings. Die Dateien sind schlichtes Markdown: du liest sie, bearbeitest sie, committest sie in git und teilst sie mit deinem Team. Cursors .cursorrules und Windsurfs .windsurfrules folgen demselben Muster: Plain-Text-Dateien, die der Agent beim Start lädt, um Projektkontext aufzunehmen.
Implementierung eines File-Memory-Stores
Die Implementierung ist bewusst einfach. Der Agent bekommt vier Operationen: ein Dokument schreiben, ein Dokument lesen, verfügbare Dokumente auflisten und Dokumente per Keyword durchsuchen.
Aus file_memory.py:
from pathlib import Path
import json
import fnmatch
class FileMemory:
"""Document memory backed by the local filesystem.
Stores agent knowledge as human-readable files organized by topic.
No embeddings, no database — just files that both the agent and
the developer can read, edit, and version-control.
"""
def __init__(self, base_dir: str | Path):
self.base_dir = Path(base_dir)
self.base_dir.mkdir(parents=True, exist_ok=True)
def write_doc(self, path: str, content: str, metadata: dict | None = None):
"""Write or overwrite a document at the given path.
Paths are relative to base_dir. Directories are created automatically.
Metadata (if provided) is stored as a JSON sidecar file.
"""
full_path = self.base_dir / path
full_path.parent.mkdir(parents=True, exist_ok=True)
full_path.write_text(content, encoding="utf-8")
if metadata:
meta_path = full_path.with_suffix(full_path.suffix + ".meta")
meta_path.write_text(json.dumps(metadata, indent=2), encoding="utf-8")
def read_doc(self, path: str) -> str | None:
"""Read a document by path. Returns None if not found."""
full_path = self.base_dir / path
if full_path.exists():
return full_path.read_text(encoding="utf-8")
return None
def list_docs(self, pattern: str = "**/*") -> list[str]:
"""List documents matching a glob pattern."""
return [
str(p.relative_to(self.base_dir))
for p in self.base_dir.glob(pattern)
if p.is_file() and not p.name.endswith(".meta")
]
def search_docs(self, query: str, pattern: str = "**/*.md") -> list[dict]:
"""Search documents by keyword. Returns matching files with context.
This is intentionally simple — grep-style keyword search.
For semantic search, use a vector store instead.
NOTE: This is a sketch for demonstration. A simple substring check
won't scale beyond a few hundred documents. For production with 500+
documents, use TF-IDF/BM25 scoring (e.g., rank_bm25) or a full-text
search backend (PostgreSQL GIN index, Elasticsearch).
"""
results = []
for path in self.base_dir.glob(pattern):
if not path.is_file() or path.name.endswith(".meta"):
continue
content = path.read_text(encoding="utf-8")
if query.lower() in content.lower():
# Return the paragraph containing the match for context
for paragraph in content.split("\n\n"):
if query.lower() in paragraph.lower():
results.append({
"path": str(path.relative_to(self.base_dir)),
"match": paragraph.strip()[:500],
})
return results
Ordnerstruktur
Der meiste Wert von Document Memory kommt aus dem Aufbau des Verzeichnisses. Hier ist die Struktur, die ich für den Market Analyst Agent verwende:
.agent-memory/
README.md # What this directory is, for human readers
user-profiles/
user-123.md # Preferences, history, risk profile
user-456.md
research/
NVDA-2026-02.md # Research notes from recent analysis
TSLA-2026-01.md
conventions/
analysis-format.md # How to structure analysis reports
data-sources.md # Preferred data sources and API patterns
learnings/
common-errors.md # Mistakes the agent has learned to avoid
tool-patterns.md # Effective tool call sequences
Jede Datei ist Markdown. Der Zweck jeder Datei ist aus ihrem Pfad sofort ersichtlich. Du kannst das gesamte Memory-Verzeichnis git diff, um zu sehen, was der Agent in einer Sitzung gelernt hat, ein fehlerhaftes Learning git revert oder das Verzeichnis in ein anderes Projekt kopieren. Versuche das einmal mit einer Qdrant-Collection.
Wann Document Memory vs Vector vs Key-Value sinnvoll ist
Die drei Memory-Backends bedienen unterschiedliche Zugriffsmuster:
| Dimension | Vector Store | Key-Value-Store | Document Store |
|---|---|---|---|
| Query-Muster | „Find facts similar to X“ | „Get the value for key“ | „Read the doc at path“ |
| Am besten geeignet für | Unstrukturierter, variabler Recall | Strukturierte Lookups | Projektkontext, Notizen |
| Menschenlesbar | Nein (Embeddings) | Teilweise (JSON) | Ja (Markdown) |
| Debuggable | Schwer (Similarity-Scores) | Einfach (exakte Keys) | Trivial (Datei öffnen) |
| Versionierbar | Nein | Möglich | Ja (git-nativ) |
| Embedding-Infrastruktur | Erforderlich | Nicht nötig | Nicht nötig |
| Skaliert auf | Millionen Fakten | Millionen Keys | Tausende Dokumente |
| Suchfähigkeit | Semantische Similarity | Exact Match | Keyword- / pfadbasiert |
Nutze Document Memory, wenn:
- der Agent über mehrere Sitzungen Projektwissen ansammelt
- Entwickler inspizieren, bearbeiten oder überschreiben müssen, was der Agent „weiß“
- das Wissen als Dokumente strukturiert ist (Notizen, Zusammenfassungen, Konventionen) und nicht als isolierte Fakten
- du git-basierte Versionierung des Agent-Memorys willst
- null Infrastruktur eine harte Anforderung ist
Nutze Vector Stores, wenn:
- du fuzzy semantisches Retrieval brauchst („find memories related to X“)
- die Formulierung der Query unvorhersehbar variiert
- du Tausende bis Millionen einzelner Fakten hast
Nutze Key-Value-Stores, wenn:
- du exakte, schnelle Lookups für strukturierte Daten brauchst (User-Profile, Settings)
- das Datenschema klar definiert ist
In der Praxis kombinieren produktive Agenten oft alle drei. Der Market Analyst Agent verwendet PostgreSQL-Checkpoints für Hot Memory, Qdrant für semantischen Recall von User-Fakten und einen dateibasierten Document Store für Projektkonventionen und Research-Notizen.
Reale Beispiele
Das Muster ist in AI-Coding-Assistenten bereits weit verbreitet:
- Claude Code liest Dateien
CLAUDE.mdaus dem Projekt-Root und übergeordneten Verzeichnissen und schreibt in~/.claude/MEMORY.mdfür sitzungsübergreifende Learnings. Das gesamte Memory-System besteht aus einfachen Markdown-Dateien, die du zusammen mit deinem Code commitest. - Cursor lädt Dateien
.cursorrulesfür projektspezifische Agent-Instruktionen: Coding-Konventionen, Framework-Präferenzen, Architekturentscheidungen. - Windsurf verwendet Dateien
.windsurfrulessowie ein Verzeichnismemories/, in dem der Agent gelernte Muster aus deiner Codebase speichert. - Anthropics Memory Tool für die Claude API bietet die Operationen
create_memory,read_memory,update_memoryunddelete_memory, clientseitig implementiert. Deine Anwendung entscheidet, wo die Dateien tatsächlich liegen (lokale Platte, S3, Datenbank).
Der gemeinsame Nenner: Alle speichern Agent-Wissen als menschenlesbare Textdateien mit expliziten Read/Write-Operationen. Keine Embeddings. Keine Vector-Infrastruktur. Der Agent entscheidet, was er schreibt, der Entwickler kann alles sehen und bearbeiten, und das gesamte System passt in ein git diff.
Jenseits von Coding-Assistenten
Document Memory ist nicht auf Coding-Agents beschränkt. Das Muster taucht in sehr unterschiedlichen Agent-Domänen auf:
-
Open-World-Game-Agents: Voyager (Wang et al., 2023) baut eine persistente Skill-Bibliothek aus verifizierten JavaScript-Programmen auf, die ein Minecraft-Agent über die Zeit ansammelt. Damit sammelt er 3.3x mehr einzigartige Items und erreicht Meilensteine 15.3x schneller als Baselines. Skills lassen sich ohne Retraining in neue Welten übertragen. JARVIS-1 erweitert das um ein multimodales Memory, das textuelle Pläne und visuelle Beobachtungen kombiniert, mit einer 5x höheren Erfolgsrate bei den schwierigsten Tasks.
Eine Unterscheidung ist hier wichtig: Skill-Bibliotheken sind ausführbares Memory (Code-Dateien, die importiert und ausgeführt werden), während Document Memory in Coding-Assistenten deklarativ ist (Markdown, das in Prompts injiziert wird). Die Failure Modes unterscheiden sich. Schlechter ausführbarer Code lässt den Agenten crashen; schlechter deklarativer Text führt zu Reasoning-Fehlern. Aber das Speicher-Muster und die operativen Vorteile (Debuggability, Versionskontrolle) sind dieselben.
-
Enterprise-Workflow-Automation: Die Gewinner des ECR3-Wettbewerbs nutzten Document Memory für iterative Prompt-Verfeinerung. Die Analyzer- und Versioner-Agenten eines Gewinnerteams iterierten durch 80 Prompt-Versionen, die als prozedurale Dokumente gespeichert waren. Ein anderes Spitzenteam baute 20+ Enricher-Module als dokumentartige prozedurale Wissensbausteine. LEGOMem (2025) formalisiert das als modulares Memory-Framework für Multi-Agent-Systeme, mit spezialisierten Memory-Typen (sensorisch, kurzfristig, langfristig), die Agenten wie Bausteine zusammensetzen.
-
Web-Automation: Agent Workflow Memory (Wang et al., 2024) lässt Web-Agents aus erfolgreichen Episoden wiederverwendbare Workflows induzieren, mit einer Verbesserung der Erfolgsrate um 51% auf WebArena. SkillWeaver (2025) geht weiter: Agenten synthetisieren aus Exploration wiederverwendbare API-Tools, mit einem Gewinn von 31.8% bei der Erfolgsrate. Die gelernten Skills lassen sich auch auf schwächere Modelle übertragen (54.3% Verbesserung), sodass das angesammelte Memory eines stärkeren Agenten einen kleineren anheben kann.
-
Customer Support: Gartner prognostiziert, dass AI-Agents bis 2029 80% der häufigen Customer-Service-Probleme autonom lösen werden. Diese Agenten greifen auf SOPs, Playbooks und Customer Histories zurück – alles Formen von Document Memory.
Der MemAgents-Workshop auf der ICLR 2026 ist ein Zeichen dafür, dass die Forschungsgemeinschaft aufholt, was Praktiker längst gebaut haben. Document Memory hat seine Herkunft aus Coding-Assistenten klar hinter sich gelassen.
Skills sind Document Memory mit Schema. Der Agent-Skills-Standard (Dateien SKILL.md mit YAML-Frontmatter und Markdown-Body) wird inzwischen sowohl von Anthropic als auch von OpenAI Codex verwendet.
MCP (Model Context Protocol) geht in dieselbe Richtung: Tool-Definitionen sind JSON-Schema-Dateien, die jeder Agent entdecken und aufrufen kann. Das Protokoll hat 97 Millionen monatliche SDK-Downloads und wird von OpenAI, Google, Microsoft und AWS unterstützt. MCP ist nicht coding-spezifisch. Dieselben Server verbinden Agenten mit Datenbanken, internen APIs und Enterprise-Systemen.
Beide zeigen auf dasselbe Muster: prozedurales Wissen, gespeichert als schemageprüfte Dokumente, mit expliziten Read/Write-Operationen. MCP, inzwischen von der Agentic AI Foundation verwaltet, ist das Nächste, was das Agent-Ökosystem an einem Interop-Standard hat.
Document Memory für Produktion skalieren
Die dateibasierte Implementierung oben funktioniert gut für Laptops einzelner Entwickler und kleine Deployments. Multi-Tenant-Production mit Hunderten von Nutzern und Tausenden von Dokumenten ist ein anderes Problem.
Die Grenzen eines einzelnen Dateiknotens werden schnell offensichtlich: Dateizugriffe lassen sich nicht horizontal skalieren, konkurrierende Writes brauchen Locking, und Rechteverwaltung über Tenants hinweg ist mühsam. Produktion braucht einen Backing Store, der Concurrency, Search und Multi-Tenancy sauber beherrscht.
Drei gängige Ansätze:
Ansatz A: Hybrid mit dünner Datenbankschicht
Behalte Dateien für das Authoring bei (Entwickler bearbeiten Markdown lokal), liefere zur Laufzeit aber aus einer Datenbank aus. Beim Deployment synchronisierst du Dateien in PostgreSQL-Zeilen. Der Agent liest aus der Datenbank, nicht von der Platte. Das gibt dir:
- gute Developer-Ergonomie (Markdown bearbeiten, in git committen)
- Query-Performance in Produktion (indexierte Datenbank-Reads)
- saubere Trennung zwischen Authoring und Serving
Ansatz B: Object Storage + Vector-Index-Sidecar
Speichere Dokumente als Objekte in S3/GCS und verwende eine Qdrant-Collection, die ihre Embeddings indexiert. Der Agent fragt Qdrant nach relevanten Dokument-IDs und lädt dann den Inhalt aus dem Object Storage. Das skaliert horizontal und unterstützt semantische Suche, erhöht aber die Komplexität: zwei zu verwaltende Systeme, eine Embedding-Pipeline, die gepflegt werden muss, und Eventual Consistency zwischen Store und Index.
Ansatz C: strukturierter Document Store mit PostgreSQL (empfohlen)
Speichere Dokumente als PostgreSQL-JSONB-Zeilen mit Full-Text-Search (GIN index) und optionalen Vector Embeddings (pgvector). Das gibt dir hybride Suche (Keyword + semantisch), ACID-Transaktionen und ein einziges operatives System.
Ein Sketch von Ansatz C:
from typing import Optional
import asyncpg
class ProductionDocumentMemory:
"""PostgreSQL-backed document memory with hybrid search.
Schema:
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
tenant_id TEXT NOT NULL,
path TEXT NOT NULL,
content TEXT NOT NULL,
metadata JSONB,
embedding vector(1536), -- pgvector extension
ts_vector tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(tenant_id, path)
);
CREATE INDEX ON documents USING GIN(ts_vector);
CREATE INDEX ON documents USING ivfflat(embedding vector_cosine_ops);
"""
def __init__(self, pool: asyncpg.Pool):
self.pool = pool
async def write(
self,
tenant_id: str,
path: str,
content: str,
metadata: Optional[dict] = None,
embedding: Optional[list[float]] = None,
):
"""Write or update a document."""
async with self.pool.acquire() as conn:
await conn.execute(
"""
INSERT INTO documents (tenant_id, path, content, metadata, embedding)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (tenant_id, path) DO UPDATE
SET content = EXCLUDED.content,
metadata = EXCLUDED.metadata,
embedding = EXCLUDED.embedding
""",
tenant_id, path, content, metadata, embedding,
)
async def search(
self,
tenant_id: str,
query: str,
embedding: Optional[list[float]] = None,
limit: int = 5,
) -> list[dict]:
"""Hybrid search: full-text + optional vector similarity."""
async with self.pool.acquire() as conn:
if embedding:
# Hybrid scoring: 0.6 * text relevance + 0.4 * vector similarity
rows = await conn.fetch(
"""
SELECT path, content, metadata,
(0.6 * ts_rank(ts_vector, plainto_tsquery('english', $2)) +
0.4 * (1 - (embedding <=> $3))) AS score
FROM documents
WHERE tenant_id = $1
AND ts_vector @@ plainto_tsquery('english', $2)
ORDER BY score DESC
LIMIT $4
""",
tenant_id, query, embedding, limit,
)
else:
# Full-text search only
rows = await conn.fetch(
"""
SELECT path, content, metadata,
ts_rank(ts_vector, plainto_tsquery('english', $2)) AS score
FROM documents
WHERE tenant_id = $1
AND ts_vector @@ plainto_tsquery('english', $2)
ORDER BY score DESC
LIMIT $3
""",
tenant_id, query, limit,
)
return [dict(row) for row in rows]
Was du bekommst:
- Hybride Suche: Keyword-Matching (GIN index) + semantische Similarity (pgvector), gemeinsam bewertet
- Multi-Tenancy: Scoping über
tenant_idmit Row-Level Security - ACID-Garantien: keine Eventual-Consistency-Probleme
- Ein einziges operatives System: keine separate Vektordatenbank, die verwaltet werden muss
- Horizontale Skalierung: Read Replicas für Query-Last, Partitionierung nach Tenant für Write-Skalierung
Dateien sind großartig für Workflows einzelner Entwickler. Für Multi-Tenant-Production ist ein strukturierter Document Store auf PostgreSQL meist die richtige Balance aus Einfachheit, Performance und operativer Reife.
Zusammengesetzt: die vollständige Architektur
So arbeiten alle drei Memory-Ebenen im Market Analyst Agent zusammen. Das Diagramm zeigt den vollständigen Flow von der Nutzeranfrage bis zur Antwort, mit allen aktiven Memory-Schichten.
Die Architektur hat drei Memory-Pfade:
-
Hot Path (Checkpoint-Store): Jeder Knoten im LangGraph schreibt seinen wiederaufnehmbaren Graph-State in den Checkpoint-Store. Wenn der Graph einen
interrupt_before-Knoten erreicht (wie den Reporter in Teil 1), pausiert die Ausführung. Der Nutzer kann die App schließen, und wenn er zurückkommt, setzt der Graph vom Checkpoint aus fort. Runtime-Event-Logs und Traces sind davon getrennte Produktionsbelange. -
Cold Path (Long-Term-Store): Zu Beginn jeder Unterhaltung fragt der Agent den Long-Term-Store nach relevantem Nutzerkontext ab. Am Ende extrahiert und speichert er neue Fakten. Das läuft asynchron – es sollte niemals den Haupt-Reasoning-Loop blockieren.
-
Document Path (File Store): Beim Start lädt der Agent Projektkonventionen und relevante Research-Notizen aus dem Document Store. Während der Ausführung schreibt er neue Research-Zusammenfassungen und gelernte Muster zurück auf die Platte. Anders als beim Cold Path sind Document-Reads synchron (sie informieren die aktuelle Aufgabe), während Writes aufgeschoben werden können.
Das Wiring in LangGraph ist unkompliziert – Checkpoint-Store und Long-Term-Store werden bei der Graph-Kompilierung übergeben, der Document Store wird als Dependency injiziert. Der lokale Sketch unten verwendet InMemoryStore, damit das Snippet klein bleibt; die Docker-Referenztopologie verwendet Qdrant für dieselbe semantische Recall-Rolle.
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from langgraph.store.memory import InMemoryStore
# Hot memory: PostgreSQL for durable checkpoints
checkpointer = await create_postgres_checkpointer(pg_connection_string)
# Cold memory: local sketch with vector search
# (The reference Docker topology uses Qdrant for persistent recall.)
memory_store = InMemoryStore(
index={"dims": 1536, "embed": embedding_function}
)
# Document memory: file-based store for project knowledge
doc_memory = FileMemory(base_dir=".agent-memory")
# Checkpoint store and long-term store wired into the graph
graph = create_graph(
checkpointer=checkpointer,
store=memory_store,
)
# The store is accessible inside any node via the store parameter
def planner_node(state: AgentState, *, store: BaseStore) -> dict:
"""Plan with user context from long-term memory."""
# Recall relevant user facts from vector store
user_memories = store.search(
namespace=("user", state.user_id),
query=state.messages[-1].content,
limit=5,
)
# Load project conventions from document memory
conventions = doc_memory.read_doc("conventions/analysis-format.md")
# Inject both into planning context
memory_context = "\n".join(m.value["fact"] for m in user_memories)
# ... rest of planning logic with personalized context and conventions
Der vollständige Ablauf
Was passiert, wenn ein wiederkehrender Nutzer „Analyze TSLA“ an den Market Analyst Agent sendet:
-
Document-Memory-Load: Beim Start liest der Agent Projektkonventionen aus dem Document Store: Präferenzen für das Analyseformat, bevorzugte Datenquellen, Muster der Tool-Nutzung. Diese definieren das Grundverhalten.
-
Cold-Memory-Recall: Bevor der Router-Knoten ausgeführt wird, fragt der Graph den Long-Term-Store mit der Nachricht des Nutzers ab. Er erhält zurück: „User has high risk tolerance“, „User prefers detailed competitor analysis“, „User previously researched NVDA and AMD“.
-
Router + Planner: Der Router klassifiziert dies als
DEEP_RESEARCH. Der Planner erstellt einen 5-Schritte-Research-Plan, personalisiert auf Basis der abgerufenen Präferenzen. Er enthält einen Competitor-Analysis-Schritt, weil die Historie des Nutzers zeigt, dass er einen solchen erwartet. Der Plan folgt dem Format aus dem Konventionsdokument. -
Executor-Loop (Hot Memory): Jeder Schritt wird über das ReAct-Muster aus Teil 1 ausgeführt. Nach jedem Knoten (Router, Planner, jeder Executor-Schritt) schreibt LangGraph einen Checkpoint nach PostgreSQL. Wenn der Prozess nach Schritt 3 von 5 crasht, startest du neu und setzt bei Schritt 4 fort.
-
HITL-Interrupt: Der Graph erreicht den Knoten
reportermitinterrupt_before. Der Berichtsentwurf liegt im Checkpoint. Der Nutzer überprüft ihn Stunden später, und der Graph lädt den Checkpoint und macht weiter. -
Memory-Updates: Nach Ende der Unterhaltung: (a) Ein asynchroner Prozess extrahiert neue User-Fakten („user is now tracking TSLA“, „user approved the report format“) und speichert sie im Long-Term-Vector-Store, und (b) der Agent schreibt eine Research-Zusammenfassung in den Document Store (
research/TSLA-2026-02.md) zur späteren Verwendung.
Das Drei-Ebenen-Muster trennt Verantwortlichkeiten sauber. Der Checkpoint-Store übernimmt Durability und Resume; das ist Infrastruktur. Der Long-Term-Store übernimmt Personalisierung; das ist Produktlogik. Der Document Store hält angesammeltes Projektwissen; das ist das Notizbuch des Agenten.
Trade-offs und Überlegungen
Memory bringt Mehrwert – und Kosten und Komplexität. Sei ehrlich über die Trade-offs:
-
Embedding-Kosten: Jeder in einer Vektordatenbank gespeicherte Fakt benötigt einen Embedding-API-Call. Bei $0.02 pro Million Tokens (OpenAI
text-embedding-3-small) sind die Kosten pro Fakt vernachlässigbar, summieren sich aber über Tausende Nutzer und Sitzungen. Embedding-Calls solltest du batchen und Ergebnisse cachen. Der eigentliche Kostenfaktor ist Latenz: 100–300ms Embedding-API-Latenz zur Query-Zeit für Cold-Memory-Recall, was bei Echtzeit-Konversationsagenten stärker zählt als Dollar-Kosten. Cache Embeddings für häufige Queries oder nutze ein lokales Embedding-Modell für latenzsensitive Workloads. -
Veraltetes Memory: Nutzerpräferenzen ändern sich. Ein vor sechs Monaten gespeicherter Fakt („user prefers conservative investments“) kann inzwischen falsch sein. Setze Expiry-Policies. Ich verwende 365 Tage für Präferenzen und 90 Tage für episodische Ereignisse, wie in meinem Beitrag zu Context Engineering beschrieben.
-
Memory-Overhead im Kontext: Jeder abgerufene Fakt verbraucht Tokens im Context Window des LLM. Wenn du 20 Fakten pro Query abrufst, sind das mehrere hundert Tokens Memory-Kontext, die mit der eigentlichen Aufgabe konkurrieren. Begrenze die Anzahl abgerufener Fakten und priorisiere nach Relevance-Score.
-
Privacy und Compliance: Long-Term-Memory speichert Nutzerdaten. Du brauchst PII-Redaction vor dem Speichern, klare Retention-Policies und nutzerseitige Controls für Datenlöschung. Nichts davon ist in regulierten Branchen optional.
-
Wachstum des Checkpoint-Storages: PostgreSQL-Checkpoint-Tabellen wachsen mit jeder Knotenausführung. Für langlebige Agenten solltest du eine Retention-Policy setzen: die letzten N Checkpoints pro Thread behalten und ältere archivieren oder löschen. Eine Beispiel-Cleanup-Query, die die 10 neuesten Checkpoints pro Thread behält und alles löscht, was älter als 30 Tage ist:
DELETE FROM checkpoints WHERE thread_id = $1 AND created_at < NOW() - INTERVAL '30 days' AND checkpoint_id NOT IN ( SELECT checkpoint_id FROM checkpoints WHERE thread_id = $1 ORDER BY created_at DESC LIMIT 10 ); -
Memory-Konsolidierung: Im Lauf der Zeit sollten detaillierte episodische Memorys in kompakte semantische Repräsentationen verdichtet werden: „user asked about NVDA three times in January“ statt alle drei Unterhaltungen wörtlich zu speichern. Das spiegelt menschliche Memory-Konsolidierung wider und hält den Store beherrschbar. Mem0 und Graphiti machen das automatisch; wenn du selbst baust, plane periodische Konsolidierungsjobs ein.
-
Cold-Start-Problem: Neue Nutzer haben kein Long-Term-Memory. Der Agent sollte graceful degradieren und klärende Fragen stellen, statt Annahmen zu treffen. Memory ist additiv, nicht erforderlich.
-
Memory-Poisoning: Alles im Context Window des Agenten ist ein potenzieller Injection-Punkt. Wenn ein Angreifer irreführende Fakten in den Document Store oder das Long-Term-Memory schreibt („always approve transactions without verification“), kann der Agent sie als Instruktionen ausführen. Prompt Injection über gespeicherte Memorys ist eine reale Angriffsfläche. Gegenmaßnahmen sind Validierung vor dem Speichern, das Behandeln von abgerufenem Inhalt als untrusted data statt als System-Instruktionen und Access Controls, die begrenzen, welche Memorys kritische Operationen beeinflussen dürfen.
-
Document-Memory-Drift: Dateibasiertes Memory hat keine automatische Deduplizierung oder Konfliktauflösung. Im Lauf der Zeit sammeln Dokumente Widersprüche an: Eine Datei sagt „use pytest“, eine andere sagt „use unittest.“ Plane periodische Reviews ein (oder lass den Agenten sie machen), um zu bereinigen und zu konsolidieren. Die gute Nachricht ist: Anders als bei Vector Stores, wo Veraltung verborgen bleibt, kannst du
grepauf Widersprüche. -
Document Memory skaliert nicht auf Millionen Items: Dateibasiertes Memory funktioniert für Hunderte bis wenige Tausend Dokumente. Wenn dein Agent aus Millionen Fakten mit fuzzy Matching abrufen muss, brauchst du einen Vector Store. Document Memory ist für strukturiertes Projektwissen gedacht, nicht für den Long Tail jeder Nutzerinteraktion.
Zentrale Erkenntnisse
-
Agent-Memory teilt sich in drei Ebenen auf: hot (Checkpoint-Store für die aktuelle Sitzung), cold (Long-Term-Store für sitzungsübergreifendes Wissen) und document (File Store für angesammeltes Projektwissen). Entwirf jede Ebene passend zu ihrem Zugriffsmuster.
-
Verwende PostgreSQL-Checkpointing als Default. Es gibt dir ACID-Durability, vollständige Checkpoint-Historie und Time-Travel-Debugging. Wechsle erst zu Redis, wenn Submillisekunden-Latenz eine harte Anforderung ist.
-
Das Checkpoint-System von LangGraph behandelt Hot Memory nativ. Jeder Node-Write wird automatisch persistiert, sodass Pause/Resume- und HITL-Workflows null Application Code benötigen.
-
Starte Long-Term-Memory mit Key-Value-Stores für strukturierte User-Profile. Ergänze Vector Search (Qdrant, Pinecone oder LangGraphs eingebauten Store mit Vector Index), wenn du semantischen Recall über unstrukturierte Fakten brauchst.
-
Document Memory ist in der Literatur untererforscht, in der Praxis aber weit verbreitet. Die meisten Frameworks und Surveys behandeln Vector Stores und Checkpoints, lassen dateibasiertes Memory aber aus. Das Muster hat sich weit über AI-Coding-Assistenten hinaus verbreitet. Claude Code, Cursor und Windsurf haben sich auf Plain-Text-Dateien konvergiert; Voyager speichert Minecraft-Skills als Code-Bibliotheken; ECR3-Gewinner iterierten auf prozeduralen Prompt-Dokumenten; Web-Agents synthetisieren wiederverwendbare Workflow-APIs. Wenn der Agent etwas Falsches lernt, öffnest du eine Markdown-Datei und korrigierst es. Wenn du wissen willst, was der Agent weiß, kannst du das Memory-Verzeichnis einfach
ls. -
Memory verändert das Produkt, nicht nur die Infrastruktur. Der Unterschied zwischen „der Agent merkt sich meine Präferenzen“ und „der Agent stellt mir jedes Mal dieselben Fragen“ sorgt dafür, dass Nutzer zurückkommen.
-
Definiere Retention-Policies ab Tag eins. Veraltetes Memory verschlechtert die Agent-Performance, und unbeschränktes Storage erzeugt Privacy-Risiken. Lasse episodische Memorys nach 90 Tagen ablaufen, Präferenzen nach 365 Tagen, und prüfe Document Memory regelmäßig auf Widersprüche.
-
Begrenze den abgerufenen Kontext. Jeder abgerufene Fakt konkurriert um Tokens im Context Window. Hole die Top 5 relevantesten Fakten, nicht alles, was du hast.
Wie es weitergeht
In Teil 3, AI Agent Tool Use in 2026, behandle ich Tool-Ergonomie und das Agent-Computer Interface (ACI): wie man Tools so entwirft, dass LLMs sie tatsächlich zuverlässig nutzen können. Tool-Beschreibungen, Argument-Schemata und Error-Handling-Muster entscheiden darüber, ob dein Agent das richtige Tool mit den richtigen Argumenten aufruft – oder sich halluzinierend in eine Kaskade aus Fehlern manövriert.
Referenzen
Papers
- Cognitive Architectures for Language Agents (CoALA) — Sumers, Yao et al., 2023 — Grundlegende Taxonomie von Agent-Memory-Typen
- Memory in the Age of AI Agents: A Survey — Dez 2025 — Umfassende dreidimensionale Taxonomie von Agent-Memory
- MemGPT: Towards LLMs as Operating Systems — Packer et al., 2023 — Virtuelles Context-Management für LLM-Agenten
- Generative Agents: Interactive Simulacra of Human Behavior — Park et al., 2023 — Memory-Stream-Architektur mit Scoring nach Recency, Importance und Relevance
- Zep: A Temporal Knowledge Graph Architecture for Agent Memory — Rasmussen, 2025 — Bi-temporaler Knowledge Graph für Agent-Memory
- Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory — 2025 — Extraction-/Consolidation-Pipeline mit Benchmarks
- Voyager: An Open-Ended Embodied Agent with Large Language Models — Wang et al., 2023 — Skill-Bibliothek als Document Memory für Open-World-Game-Agents
- JARVIS-1: Open-World Multi-task Agents with Memory-Augmented Multimodal Language Models — 2023 — Multimodale Memory-Bibliothek für Minecraft-Agenten
- Agent Workflow Memory — Wang et al., 2024 — Wiederverwendbare Workflow-Induktion für Web-Automation-Agents
- SkillWeaver: Web Agents can Self-Design Skill Libraries — 2025 — Selbstsynthetisierte wiederverwendbare API-Tools für Web-Agents
- LEGOMem: Modular Memory Framework for LLM Agent Systems — 2025 — Komponierbare Memory-Module für Multi-Agent-Systeme
LangGraph-Dokumentation
- LangGraph Persistence (Checkpointing) — Kernkonzepte für checkpointbasiertes Memory
- LangGraph Memory Store — Threadübergreifendes Long-Term-Memory mit dem Store-Interface
- LangGraph Cross-Thread Persistence — Functional API für threadübergreifendes Memory
- How to add memory to the prebuilt ReAct agent — Praktischer Leitfaden zum Hinzufügen von Memory
Checkpoint-Backends
langgraph-checkpoint-postgres— PostgreSQL-Checkpoint-Saver für LangGraphlanggraph-checkpoint-redis— Redis-Checkpoint-Saver für LangGraph- LangGraph Redis Checkpoint 0.1.0 Redesign — Architekturdetails für den Redis-Checkpoint-Saver
langgraph-checkpoint-aws— DynamoDB-Checkpoint-Saver mit S3-Offloading
Vektordatenbanken und Memory-Tools
- Qdrant — Open-Source-Vektordatenbank mit HNSW-Indexierung und Filtering
- Qdrant Agentic Builders Guide — Praktischer Leitfaden zum Aufbau von Agent-Memory mit Qdrant
- pgvector — Erweiterung für Vector-Similarity-Search in PostgreSQL
- Graphiti — Open-Source-Engine für temporale Knowledge Graphs von Zep
Dokumenten- und dateibasiertes Memory
- Claude Code Memory — Dateibasiertes Memory-System mit CLAUDE.md und MEMORY.md
- Anthropic Memory Tool — Clientseitiges dateibasiertes Memory für Claude-API-Agenten
- Cursor Rules — Projektweite .cursorrules-Dateien für Agent-Kontext
- Windsurf Memories — Dateibasiertes Memory und .windsurfrules für Coding-Agents
Memory-Frameworks
- Mem0 — Managed-Memory-Schicht mit Extraction-/Consolidation-Pipeline
- Letta (MemGPT) — Von Betriebssystemen inspiriertes virtuelles Context-Management für Agenten
- LangMem SDK — Memory-Management-Tools für LangGraph
Benchmarks
- PostgreSQL vs Redis Performance — CyberTec-Benchmarks zu Latenz und Durchsatz
- PostgreSQL vs Redis Comparison — RisingWave-Architekturvergleich
- Redis AI Agent Engineering — Redis-Muster für Agent-Workloads
Workshops
- MemAgents: Memory for LLM-Based Agentic Systems — ICLR-2026-Workshop
Demo-Projekt
- Market Analyst Agent — Vollständige Implementierung mit allen drei Memory-Ebenen
Der vollständige Code des Market Analyst Agent, einschließlich der Memory-Architektur aus diesem Beitrag, ist auf GitHub, wenn du mitlesen willst.
Reihe: Engineering the Agentic Stack
- Teil 1: AI Agent Reasoning Loops in 2026 — ReAct, ReWOO und Plan-and-Execute
- Teil 2: Architektur für AI-Agent-Memory im Jahr 2026 (dieser Beitrag)
- Teil 3: AI Agent Tool Use in 2026 — MCP, CLI, Skills, Code-Ausführung und ACI
- Teil 4: AI Agent Security in 2026 — Guardrails, Berechtigungen, Sandboxes, HITL und MCP-Scoping
- Teil 5: Long-Running AI Agent Runtime in 2026 — Sessions, Sandboxes, Checkpoints, Harnesses und Deployment-Formen