Architektur der AI-Agent-Memory: Checkpoints und Vector Stores
Automatische Übersetzung Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.
Artikel-Update
Ursprünglich veröffentlicht am 14. Februar 2026. Überarbeitet und aktualisiert am 6. September 2026. Das Update behandelt Context Compaction, Memory Benchmarks und Storage APIs und grenzt Working State, Checkpoints und Long-Term Memory klarer voneinander ab.
Ein Reasoning Loop überlebt nur eine Anfrage, wenn sein State außerhalb des Workers gespeichert wird. Ohne Agent Memory kann der Agent einen pausierten Plan nicht fortsetzen, sich nach einem Crash nicht wiederherstellen und sich nicht an eine Präferenz aus einer früheren Session erinnern. Teil 1 behandelte den Control Flow. Dieser Beitrag zeigt, welchen State spätere Turns benötigen und wo dieser State liegen sollte.
Ich verwende den Market Analyst Agent — einen kleinen LangGraph Agent, der Marktdaten abruft und einen Analystenbericht schreibt — als Grundlage für die Diskussion über Hot Checkpoints. Die Abschnitte zu Cold Vector und Raw Markdown sind eigenständige illustrative Designs und zeigen Erweiterungen, die das aktuelle Projekt noch nicht implementiert. Anschließend geht es darum, wann PostgreSQL, Redis, Qdrant, Key-Value Stores und einfache Markdown-Dateien jeweils sinnvoll sind.
Jeder unten beschriebene Store wird vom Harness gelesen, also von dem Code, der den Loop um das Model steuert. Der Harness entscheidet, welche Inhalte in das Context Window gelangen; die Stores tun das nicht. Dieser Artikel behandelt, wo dieser State liegt, bevor der Harness darauf zugreift. Teil 3 und Teil 4 behandeln, was der Harness anschließend mit dem Prompt macht.
Was ist AI-Agent-Memory?
AI-Agent-Memory ist die State-Schicht, mit der ein Agent den Task-Fortschritt bewahren, früheres Wissen abrufen und sein Wissen über mehrere Runs hinweg aktualisieren kann. Ein Design kann Checkpoints, semantische oder strukturierte Stores sowie menschenlesbare Dokumente verwenden. Wähle nur die Stores, die für die Recall- und Recovery-Anforderungen des Produkts notwendig sind.
| Bedarf | Beste Standardwahl | Warum |
|---|---|---|
| Einen Run pausieren und fortsetzen | PostgreSQL Checkpoint Store | Dauerhaft, abfragbar und leicht gemeinsam mit App-Daten zu betreiben |
| Transienter State mit niedriger Latenz | Redis Checkpoint Store | Schnelles Resume und kurzlebiger State, mit Trade-offs bei der Persistenz |
| Semantischer Recall über Threads hinweg | Qdrant oder pgvector | Ruft Memories nach Bedeutung ab, nicht nur über exakte Keys |
| Strukturierte User-Fakten | PostgreSQL oder Key-Value Store | Deterministische Updates sind für Präferenzen und IDs besser als fuzzy Retrieval |
| Projektkonventionen und gelernte Verfahren | Markdown- oder JSON-Dateien | Menschenlesbar, diffbar und einfach von Agents zu aktualisieren |
| Memory für Beziehungen zwischen Entitäten | Knowledge Graph | Sinnvoll, wenn Beziehungen wichtiger sind als einzelne Fakten |
Beginne nicht mit Memory, nur weil es intelligent klingt. Beginne mit dem für den User sichtbaren Fehler: verlorenem Fortschritt, vergessener Präferenz, wiederholter Recherche oder der fehlenden Wiederverwendung einer Projektkonvention.
Fehlerbilder, die Memory erfordern
Ein Stateless Agent kann eine isolierte Frage beantworten, vergisst die Anfrage aber, sobald der Call endet. Dieses Design scheitert, wenn das Produkt eines der folgenden Verhaltensweisen benötigt:
- Pause und Resume: Ein User startet einen Recherche-Task, schließt seinen Laptop und kommt am nächsten Tag zurück. Ohne checkpointeten State startet der Agent von vorn.
- Kohärenz über mehrere Turns: In einer langen Unterhaltung muss sich der Agent daran erinnern, welche Tools er aufgerufen, welche Daten er gesammelt und welche Schritte des Plans er abgeschlossen hat.
- Personalisierung: Ein wiederkehrender User erwartet, dass der Agent seine Risikotoleranz, die bevorzugte Analysetiefe und frühere Interaktionen kennt.
- Human-in-the-Loop (HITL): Der Agent sammelt seine Belege und wartet darauf, dass ein Mensch den nächsten Schritt freigibt. Der „wartende“ State muss Prozessneustarts überleben.
Im Market Analyst Agent aus Teil 1 erzeugt die Anfrage „Analyze NVDA“ einen Plan, fünf Tool Calls, gesammelte Daten und einen Berichtsentwurf. Wenn der User mit „sieht gut aus, aber füge eine Wettbewerbsanalyse hinzu“ antwortet, stellt ein Checkpoint den Plan und die Recherche ab dem letzten abgeschlossenen Schritt wieder her. Das Hinzufügen des Wettbewerbsanalyse-Schritts würde eine nachträgliche Interpretation und ein Replanning erfordern; die Begleitimplementierung unterstützt dieses Verhalten nicht. Der Checkpoint liefert den vorherigen State, während die Anwendung entscheiden muss, wie die neue Anfrage den Plan verändert.
Long-Term Memory behandelt einen anderen Fall. Wenn der User eine Woche später zurückkehrt und fragt: „Aktualisiere meine NVDA-Analyse“, muss sich der Agent möglicherweise an eine Präferenz für konservative Risikobewertungen und ein Interesse an Semiconductor Stocks erinnern. Ein Vector-gestützter Memory Store kann diese Fakten über Sessions hinweg abrufen, ohne erneut danach zu fragen.
Die folgenden Implementierungsbeispiele verwenden LangGraph, LangChains Open-Source-Library zum Erstellen von Agents als explizite State Graphs; die von ihr definierten Storage-Grenzen lassen sich auf jedes Framework übertragen. Stell dir die fortlaufende „Analyze NVDA“-Unterhaltung eines Users als einen Thread vor. Jeder Lauf des Graphs zur Beantwortung oder Fortsetzung ist ein Run innerhalb dieses Threads. Während ein Run aktiv ist, sind der Context des Models und die lokalen Variablen des Programms dessen Working Memory; sie verschwinden, wenn diese Arbeit endet. LangGraph nennt State, der für diesen einen Thread gespeichert wird, Short-Term Memory, und Fakten, die für andere Threads verfügbar sind, Long-Term Memory. Im Folgenden bedeuten „Thread“ und „Conversation“ dasselbe. Teil 5 verwendet „Session“ für das dauerhafte Log eines einzelnen Runs; daher vermeidet dieser Artikel den Begriff für die Conversation.
Eine Taxonomie von AI-Agent-Memory
Bevor wir zur Implementierung kommen, hilft es, zu klassifizieren, woran sich Agents erinnern müssen. Das CoALA-Framework — Cognitive Architectures for Language Agents (Sumers, Yao et al., 2023) — ist eine häufig zitierte, auf Kognitionswissenschaft basierende Taxonomie. In meinem Context-Engineering-Beitrag habe ich das Scoping von Memory eingeführt; hier erweitere ich es auf sechs Kategorien:
| Memory-Typ | Scope | Lebensdauer | Beispiel | Storage Pattern |
|---|---|---|---|---|
| Working | Aktueller Schritt | Millisekunden | Tool-Call-Argumente, aktuelle LLM-Antwort | In-Process (Python dict) |
| Short-Term | Aktueller Thread | Minuten–Stunden | Conversation History, Plan-Fortschritt, gesammelte Daten | Checkpoint Store |
| Episodic | Thread-übergreifend | Tage–Monate | „Letzte Woche fragte der User nach NVDA-Earnings“ | Vector Store / KV Store |
| Semantic | Thread-übergreifend | Monate–dauerhaft | „Der User bevorzugt konservative Investments“ | Vector Store / KV Store |
| Document | Thread-übergreifend | Tage–dauerhaft | Projektnotizen, Forschungszusammenfassungen, gelernte Muster | File Store (Markdown/JSON) |
| Procedural | Systemweit | Dauerhaft | „Bei Aktienanalysen immer SEC-Filings prüfen“ | Config / System Prompt |
Working Memory enthält die aktuellen Beobachtungen, abgerufenen Fakten und Zwischenergebnisse des aktiven Runs. Ein Teil davon liegt in Application-Variablen; die ausgewählten Messages und Tool Results bilden den Input des Models. Dieser Input muss in das Context Window des Models passen, während der Application State größer sein und über mehrere Schritte hinweg bestehen bleiben kann. Process Memory geht bei einem Crash verloren, sofern sie nicht explizit gespeichert wird. Die übrigen Tiers liefern Informationen für diesen Working State.
Short-Term Memory ist der Checkpoint, den LangGraph nach jeder Einheit der Graph-Ausführung schreibt — einem Super-Step, der im nächsten Abschnitt definiert wird. Episodic und Semantic Memory bleiben über Threads hinweg bestehen. Document Memory speichert Projektnotizen, Forschungszusammenfassungen und gelernte Konventionen in Dateien, die Menschen und Agents prüfen können. Procedural Memory umfasst System-Instruktionen, Tool-Definitionen und wiederverwendbare Verfahren, die für einen Task abgerufen werden können. Die Lebensdauern in der Tabelle sind Beispiele; die Aufbewahrung folgt der Policy der Anwendung, und Working State kann einen gesamten aktiven Run überdauern.
Für die Implementierung reduzieren sich fünf dieser sechs Kategorien auf drei Storage-Tiers. Short-Term Memory wird zu Hot Memory, dem Checkpoint für den aktuellen Thread. Episodic und Semantic werden zu Cold Memory, also Recall über Threads hinweg. Document Memory hält angesammeltes Projektwissen lesbar und direkt editierbar. Working Memory wird dem Hot-Tier zugeordnet, weil Checkpoints den State bewahren können, der zur Rekonstruktion eines aktiven Runs erforderlich ist. Ein Checkpoint ist nicht die vollständige interne Berechnung des Models. Procedures können mit dem Agent ausgeliefert oder aus Dateien beziehungsweise einem anderen Store gespeichert und abgerufen werden. Diese Tiers beschreiben die Implementierungsentscheidungen dieses Artikels; sie sind keine sich gegenseitig ausschließenden Memory-Typen.
CoALA klassifiziert Working, Episodic, Semantic und Procedural Memory. Die Survey „Memory in the Age of AI Agents“ organisiert Memory dagegen nach Form, Funktion und Dynamik und umfasst unter anderem Dokumente, Codebases und wiederverwendbare Workflows. Dateien können mehrere dieser Kategorien implementieren. Dieser Artikel führt Document Memory separat auf, damit seine Storage- und Maintenance-Verantwortlichkeiten sichtbar werden.
Dasselbe Storage Pattern findet sich in anderen Domänen. Ein Minecraft Agent (Voyager) speichert wiederverwendbare Game Skills als Code-Libraries, und Web Agents leiten aus erfolgreichen Runs wiederverwendbare Browsing Workflows ab. Auf beide komme ich später zurück. Inspectable Files und indexiertes Retrieval können koexistieren: Voyager ruft Programme über Embeddings ihrer Beschreibungen ab.
Von einer festen RAG Pipeline unterscheidet sich von Agents verwaltete Memory außerdem dadurch, wer den Write ausführt. Der Agent oder sein Harness wählt aus, was gespeichert, aktualisiert und gelöscht wird, und entscheidet später, wann es abgerufen wird.
Das Paper zu Generative Agents (Park et al., 2023) zeigte, wie weit das gehen kann: Simulierte Agents speicherten, reflektierten über und riefen ihre eigenen Memories ab. Sein Memory Stream rankte Kandidaten nach Recency, Importance und Relevance — ein Design, das weiterhin einen nützlichen Referenzpunkt für Agent-Memory-Retrieval bietet.
Compaction hält eine Conversation nutzbar
Ein größeres Context Window macht die Entscheidung, was erhalten bleibt, nicht überflüssig. Aktuelle APIs können eine ältere Conversation zusammenfassen, bevor sie das Window füllt. Claudes serverseitige Compaction, am 06.09.2026 weiterhin ein Beta-Feature, liefert einen compaction-Block zurück, den nachfolgende Requests anstelle früherer Inhalte verwenden. Das kann den Aufwand für clientseitige Summarization reduzieren, aber die Zusammenfassung kann einen später benötigten Fakt auslassen.
Bewahre den autoritativen Task State außerhalb dieser Zusammenfassung auf: abgeschlossene Effekte, Freigaben, Source References und exakte User Constraints. Ein Checkpoint stellt die Ausführung wieder her; Compaction verkürzt den Model Context; Long-Term Memory wählt Wissen für eine andere Conversation aus. Teste diese drei Verhaltensweisen separat. Erzwinge in einem Test zur Hälfte eine Compaction und prüfe, ob die nächste Aktion weiterhin eine frühere Constraint respektiert. Verwende ein compacted Transcript nicht als einzigen Nachweis dafür, was freigegeben wurde.
Short-Term Agent Memory: der Checkpoint Store
LangGraph checkpointet den Graph State an Super-Step-Grenzen — an einem Node oder einem Batch parallel ausgeführter Nodes. Mit dem Standardwert durability="async" kann der nächste Schritt laufen, während dieser Write abgeschlossen wird; durability="sync" wartet vor dem Fortfahren auf die Persistenz und fügt Write-Latency hinzu. Crash Recovery verwendet den zuletzt persistierten Checkpoint, nicht unbedingt den zuletzt abgeschlossenen Schritt. Das bildet die Grundlage für Pause/Resume, Time-Travel-Debugging und HITL Workflows.
Ein Checkpoint enthält den Graph State, der für ein Resume benötigt wird: das AgentState aus Teil 1 — Messages, Identity, User Profile, Plan Steps, Research Data und Execution Mode. Nach einem HITL Interrupt oder einem Prozessneustart stellt LangGraph den zuletzt gespeicherten State wieder her und verwendet seine Scheduling-Metadaten, um den nächsten Node auszuwählen. Der Resume erfolgt an einer abgeschlossenen Node-Grenze, nicht an einer beliebigen Python-Zeile. Zu den gespeicherten Details gehören eine Checkpoint-ID und ein Timestamp, eine Version für jeden Channel (LangGraphs Bezeichnung für einen State Key) sowie die Channel-Versionen, die jeder Node bereits gesehen hat. Die Step Number ist Metadaten für diesen Checkpoint. Ein Checkpoint unterscheidet sich außerdem von einem Append-Only Event Log oder Trace; Teil 5 trennt diese Runtime-Observability-Surfaces ausdrücklich.
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 History zurück. Jeder Checkpoint wird über (thread_id, checkpoint_ns, checkpoint_id) adressiert, wobei thread_id die Conversation identifiziert, checkpoint_ns das Subgraph-Namespaceing übernimmt und checkpoint_id eine eindeutige Version ist.
Entscheidend ist die Wahl des Backends dahinter. PostgreSQL und Redis sind zwei verbreitete Production-Optionen.
PostgreSQL vs. Redis
| Dimension | PostgreSQL (langgraph-checkpoint-postgres) | Redis (langgraph-checkpoint-redis) |
|---|---|---|
| Durability-Modell | ACID-Transaktionen, WAL und Replication | Konfigurierbare Persistenz: Append-Only Command Log (AOF) oder periodische Snapshots (RDB) |
| Checkpoint-History | Dauerhafte History für Resume und Debugging | Retention hängt von Saver- und Eviction-Settings ab |
| Primäre Constraint | Database Write Latency und Table Growth | RAM-Nutzung, Eviction und Persistenzkonfiguration |
| Operational Fit | Teams, die bereits relationale Datenbanken betreiben | Teams, die Redis bereits mit hohem Throughput betreiben |
| Beste Standardwahl für | Dauerhaftes Resume und reproduzierbares Debugging | Latency-sensitive, recoverable Session State |
Generische Database Benchmarks sagen die Checkpoint-Performance nicht voraus. Miss die serialisierte State-Größe, Write-Frequenz, Persistenz-Settings und Concurrency deines eigenen Graphs.
PostgreSQL: der Durable Default
PostgreSQL ist für die meisten Teams der sicherere Default. Checkpoints überleben Crashes, du erhältst vollständige Transaction Semantics, und die Checkpoint-History macht Time-Travel-Debugging unkompliziert.
Eine vereinfachte Version des Checkpoint-Setups aus memory/hot.py. Wenn ein Angreifer Checkpoints schreiben könnte, setze LANGGRAPH_STRICT_MSGPACK=true oder konfiguriere allowed_msgpack_modules. Dadurch wird die Deserialisierung auf sichere oder deklarierte Typen beschränkt; der permissive Default warnt bei nicht registrierten Typen, erlaubt sie aber weiterhin.
import asyncio
from contextlib import asynccontextmanager
from langchain_core.messages import HumanMessage
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
@asynccontextmanager
async def postgres_checkpointer(connection_string: str):
"""Yield 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.
`from_conn_string` is itself an async context manager: it owns the
connection and closes it on exit, so the graph has to run inside it.
"""
async with AsyncPostgresSaver.from_conn_string(connection_string) as checkpointer:
# Create the checkpoint tables if they don't exist.
# This is idempotent — safe to call on every startup.
await checkpointer.setup()
yield checkpointer
async def main(authenticated_user_id: str) -> None:
# The graph lives inside the context manager's scope.
async with postgres_checkpointer(
"postgresql://user:pass@localhost:5432/agent_memory"
) as checkpointer:
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(
{"user_id": authenticated_user_id,
"messages": [HumanMessage(content="Analyze NVDA")]}, config
)
# After the server authenticates the approver and validates approval
# of this exact draft, update the companion's approval field.
await graph.aupdate_state(config, {"report_approved": True})
# Continue the static interrupt_before pause; new input starts a new run.
result = await graph.ainvoke(None, config)
# Local fixture identity. A server supplies this only after authentication.
asyncio.run(main(authenticated_user_id="user-123"))
Das user_id im Graph-Input stammt aus dem authentifizierten Server Context; thread_id lokalisiert lediglich Checkpoints und stellt weder die Identity fest noch autorisiert es den Zugriff auf einen Thread. Das AsyncPostgresSaver verwendet das Paket langgraph-checkpoint-postgres, das vier Tabellen anlegt: checkpoints (den serialisierten State), checkpoint_blobs (große Binärdaten), checkpoint_writes (ausstehende Writes für Crash Recovery) und checkpoint_migrations (Schema-Version). Concurrent Writers werden durch den Primary Key (thread_id, checkpoint_ns, checkpoint_id) und Upserts getrennt, nicht durch Locking — zwei Worker im selben Thread korrumpieren sich nicht gegenseitig, koordinieren sich aber auch nicht.
Redis: wenn die Latenz der Bottleneck ist
Wenn Checkpoint-Latency der Bottleneck ist, ist Redis eine Option für Recoverable State. Miss vor der Entscheidung für Redis statt PostgreSQL die serialisierte State-Größe, Persistenz-Settings und Concurrency.
Eine vereinfachte Version des Checkpoint-Setups aus memory/hot.py:
import asyncio
from contextlib import asynccontextmanager
from langgraph.checkpoint.redis.aio import AsyncRedisSaver
@asynccontextmanager
async def redis_checkpointer(redis_url: str):
"""Yield a Redis-backed checkpoint store.
Redis keeps checkpoints in memory for low-latency access.
Durability depends on RDB snapshots, AOF fsync policy, and replication.
AOF with appendfsync everysec can still lose about one second of writes
after a crash; enabling AOF alone is not a no-loss guarantee.
"""
async with AsyncRedisSaver.from_conn_string(redis_url) as checkpointer:
# Initialize Redis data structures
await checkpointer.asetup()
yield checkpointer
async def main() -> None:
# Same graph API, different backend.
async with redis_checkpointer("redis://localhost:6379") as checkpointer:
graph = create_graph(checkpointer=checkpointer)
asyncio.run(main())
Das AsyncRedisSaver aus langgraph-checkpoint-redis speichert jeden Checkpoint als eigenes RedisJSON-Dokument unter demselben (thread_id, checkpoint_ns, checkpoint_id)-Key wie der Postgres Saver. Das Redesign von v0.1.0 band Checkpoint Values ein und ersetzte den Retrieval pro Channel durch einen JSON.GET-Pfad. Diese Änderung betrifft den Value Retrieval, nicht jede Persistence-Operation; die Latency-Messungen des Vendors hängen von dessen Workload ab. Redis 8.0+ enthält RedisJSON und RediSearch standardmäßig — es müssen keine zusätzlichen Module installiert werden.
Wähle die Redis-Persistenz- und fsync-Policy anhand des tolerierten Loss Windows. RDB kann Writes seit dem letzten Snapshot verlieren; die übliche AOF-Policy appendfsync everysec kann ungefähr eine Sekunde verlieren. always tauscht Write-Latency gegen stärkere Persistenz, während no das Flushen dem OS überlässt. Teste Recovery mit den tatsächlichen Disk- und Replication-Settings.
Für Deployments mit knappem Memory speichert ShallowRedisSaver nur den neuesten Checkpoint pro Thread — keine History, aber minimalen RAM-Verbrauch. Verwende dies, wenn du Pause/Resume brauchst, aber kein Time-Travel-Debugging.
Wann welche Option verwenden?
Verwende PostgreSQL, wenn:
- du vollständige Checkpoint-History für Time-Travel-Debugging oder reproduzierbares Resume benötigst
- Durability nicht verhandelbar ist (Finanzdienstleistungen, Gesundheitswesen)
- PostgreSQL bereits in deinem Stack läuft
- dein Agent lange Tasks ausführt, bei denen verlorener State stundenlange Neuberechnung bedeutet
- du einen Unified Data Store möchtest — PostgreSQL mit pgvector kann ein einzelnes Backend für Checkpoints, Long-Term Memory und Vector Search sein
Verwende Redis, wenn:
- Checkpoint-Latency dein Bottleneck ist (Real-Time-Chat, Streaming-UX)
- du Voice Bots oder Streaming-Erlebnisse entwickelst, bei denen der Checkpoint-Zugriff auf einem gemessenen latency-kritischen Pfad liegt
- du horizontale Skalierung über viele unabhängige Threads benötigst. Wenn mehrere Agents gemeinsamen State verändern, gib diesem State einen Owner und koordiniere außerhalb des Checkpoint Savers.
- es sich um kurzlebige Sessions handelt, bei denen der Verlust eines Checkpoints wiederherstellbar ist
- du Semantic Caching zur Reduktion redundanter LLM Calls möchtest (Redis LangCache cached semantisch ähnliche Queries, um wiederholte LLM Calls zu vermeiden)
Weitere Optionen: langgraph-checkpoint-sqlite eignet sich für lokale Entwicklung und Single-Process-Deployments. Für AWS-native Stacks bietet langgraph-checkpoint-aws einen DynamoDBSaver mit automatischem Payload-Offloading — der dokumentierte Saver lagert bei konfiguriertem S3-Bucket Payloads oberhalb seines 350-KB-Schwellwerts aus. Dieser Schwellwert ist eine Implementierungs-Policy, nicht das 400-KB-Item-Limit von DynamoDB. Serverless Pricing und keine zu verwaltende Infrastruktur machen die Option für Deployments mit variabler Last attraktiv.
Long-Term Memory: Erinnerungen über Sessions hinweg
Hot Memory behandelt die aktuelle Conversation. Long-Term Memory betrifft den User, der nächste Woche zurückkehrt: Es speichert Fakten, Präferenzen und Interaktionshistorie, die über Threads hinweg bestehen bleiben.
LangGraph stellt über seine BaseStore-Klasse ein Store-Interface für threadübergreifende Memory bereit. Jedes Memory Item ist ein (namespace, key)-Pair mit einem JSON Value und optionalem Vector Embedding. Das Namespace kodiert typischerweise den User oder die Organisation: ("user", "user-123", "preferences").
Vector Storage: semantischer Recall mit Qdrant
Wenn der Agent unstrukturierte Fakten abrufen muss („Was hat der User über seinen Investment-Horizont gesagt?“), bietet Vector Search semantischen Recall. Statt über exakte Keys abzufragen, fragt der Agent nach Bedeutung.
Qdrant ist eine speziell entwickelte Vector Database in Rust, die Embedding Storage, Indexing (Hierarchical Navigable Small World, kurz HNSW) und gefilterte Suche unterstützt. HNSW und seine Trade-offs habe ich ausführlich in meinem Beitrag zum Search Ranking 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.
Das Folgende ist ein eigenständiges illustratives Qdrant Design. Es ist keine vereinfachte Version des aktuellen memory/long.py. Das aktuelle Projekt speichert User Profiles mit exaktem user_id-Filtering und einem Zero-Vector-Placeholder. Eine echte Embedding-Integration bleibt zukünftige Arbeit. Der Request Handler muss die Anfrage authentifizieren und principal aus der verifizierten Identity erzeugen; der Client liefert diesen Wert niemals. Der Qdrant-Filter definiert den Retrieval Scope, nicht die Autorisierung.
from qdrant_client import QdrantClient
from qdrant_client.models import (
PointStruct, Distance, VectorParams, Filter, FieldCondition, MatchValue,
)
import hashlib
import json
from dataclasses import dataclass
@dataclass(frozen=True)
class AuthenticatedPrincipal:
"""Created by the server after authentication, never from request JSON."""
user_id: str
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, principal: AuthenticatedPrincipal, fact: str, embedding: list[float]
):
"""Store a user fact with its embedding."""
identity = json.dumps([principal.user_id, fact], ensure_ascii=False).encode()
point_id = hashlib.sha256(identity).hexdigest()[:32]
self.client.upsert(
collection_name=self.collection_name,
points=[PointStruct(
id=point_id,
vector=embedding,
payload={"user_id": principal.user_id, "fact": fact},
)],
)
def recall(
self,
principal: AuthenticatedPrincipal,
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=Filter(
must=[FieldCondition(
key="user_id", match=MatchValue(value=principal.user_id)
)]
),
limit=top_k,
)
return [hit.payload["fact"] for hit in results.points]
Die Point-ID hasht ein JSON-Array aus User-ID und Fact, sodass Delimiter innerhalb eines der beiden Werte nicht zwei Identitäten zusammenführen können. Beispielsweise muss sich User a:b mit Fact c von User a mit Fact b:c unterscheiden. Die 32 hexadezimalen Zeichen passen in Qdrants UUID-Repräsentation für Point-IDs.
Der Flow besteht aus drei Schritten. In diesem illustrativen Design extrahiert ein LLM zentrale Fakten aus der Interaktion („User hat hohe Risikotoleranz“, „User interessiert sich für Semiconductor Stocks“). Diese Fakten werden embeddet und in Qdrant gespeichert. Zu Beginn der nächsten Conversation liefert der Server den authentifizierten Principal, und der Agent fragt Qdrant mit der neuen User Message ab, um relevanten Context abzurufen. Der aktuelle Market Analyst Agent implementiert diesen semantischen Extraction- und Embedding-Flow noch nicht.
Retrieval Scoring: mehr als Cosine Similarity
Raw Cosine Similarity ist ein Ausgangspunkt, Production-Memory-Systeme benötigen jedoch ein umfangreicheres Retrieval. Das Paper zu Generative Agents (Park et al., 2023) führte eine Scoring Function ein, die drei Signale kombiniert:
- Recency: Regelbasierter Verfall, sodass aktuelle Memories höher bewertet werden. Eine exponentielle Decay-Funktion lässt einen Fakt von gestern einen gleichwertigen Fakt von vor sechs Monaten übertreffen.
- Importance: Vom LLM bewertete Relevanz auf einer Skala von 1 bis 10. „Das Portfolio des Users ist um 40 % gefallen“ erhält eine höhere Bewertung als „Der User sagte hallo.“
- Relevance: Embedding-Cosine-Similarity zwischen Query und gespeichertem Fact.
Das Paper normalisiert alle drei Signale vor der Kombination auf vergleichbare Skalen. Tue dasselbe, bevor du Weights tunst; ein Raw-Importance-Score von 1–10 würde sonst ein Signal von 0–1 dominieren. Der finale Retrieval Score ist eine gewichtete Summe: score = alpha * recency + beta * importance + gamma * relevance. Dadurch werden frische, wichtige Fakten nicht von veralteten, aber semantisch ähnlichen Fakten verdrängt. Für einen Prototyp zur Finanzanalyse würde ich mit alpha = 0.3 für Recency, beta = 0.2 für Importance und gamma = 0.5 für Relevance beginnen, weil normalerweise die aktuelle Query bestimmt, welcher ansonsten gültige Fakt in den Context gehört. Das Paper zu Generative Agents verwendete eine Gleichgewichtung; diese Werte sind ein vorgeschlagener Startpunkt, keine gemessene Verbesserung. Tune sie gegen Held-out Recall und Task-Quality Checks, bevor du dich auf sie verlässt.
Alternativen zu Vector Search
Vector Search ist leistungsfähig, aber nicht immer das richtige Tool. Hier ist, wann Alternativen sinnvoll sind:
| Ansatz | Am besten geeignet für | Wichtigster Operational Cost |
|---|---|---|
| Vector Search (Qdrant) | Semantischer Recall unstrukturierter Fakten | Embedding- und Index-Lifecycle |
| Key-Value Store (Redis) | Strukturierte User Profiles und Präferenzen | Memory-Nutzung und Persistenz-Policy |
| Document Store (Dateien) | Projektwissen und vom Agent verwaltete Notizen | Concurrency, Permissions und Search |
| Full-Text Search (PostgreSQL GIN Index) | Keyword Recall über Conversation History | Index-Wachstum und Query-Tuning |
| Knowledge Graph (Neo4j) | Entitätsbeziehungen und Multi-Hop-Queries | Graph-Modellierung und ein weiteres Data System |
| Hybrid (Vector + Keyword) | Recall bei variierender Query-Intention | Zwei Scoring-Pfade zum Tunen und Evaluieren |
Key-Value Stores funktionieren gut für strukturierte Daten. Wenn dein Long-Term Memory aus einem User Profile besteht — Risikotoleranz, Investment-Horizont, bevorzugte Sektoren — ist ein Redis Hash oder eine PostgreSQL JSONB Column einfacher und schneller als das Embeddieren und Abfragen von Vectors. Verwende Vector Search, wenn die Memory unstrukturiert ist und sich die Retrieval-Query in ihrer Formulierung ändert.
LangGraphs integrierter Store stellt ein Namespace-basiertes Key-Value-Interface mit optionaler Vector Search bereit. Die BaseStore API ist einfach: put(), get(), search() und delete() mit hierarchischem Namespace Scoping. Es gibt drei Implementierungen:
InMemoryStore— für Entwicklung und Tests (Daten gehen beim Prozessende verloren)PostgresStore— persistenter Production Store mit vollständigem SQL QueryingAsyncRedisStore— threadübergreifende Memory mit Vector Search, TTL-Support und Metadata Filtering
Die Konfiguration index aktiviert Vector Search über gespeicherte Items mit einem konfigurierbaren Embedding Model. Für viele Use Cases reicht dieser integrierte Store aus, ohne eine dedizierte Vector Database einzuführen.
import asyncio
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
}
)
async def main() -> None:
# 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 the user's memories.
# The namespace prefix is positional here — `search`/`asearch` declare it
# as positional-only `namespace_prefix`, unlike `aput`.
results = await store.asearch(
("user", "user-123"),
query="What is their investment style?",
limit=5,
)
asyncio.run(main())
Eine Long-Term-Memory-Strategie auswählen
Beginne mit Key-Value, wenn deine Memory strukturiert und klar definiert ist (User Profiles, Settings, Named Entities). Füge Vector Search hinzu, wenn du semantisches Retrieval über unstrukturierte Fakten benötigst oder sich die Query-Phrasierung unvorhersehbar ändert.
Knowledge Graphs lohnen sich, wenn Beziehungen zwischen Entitäten relevant sind, etwa bei: „Welche Unternehmen, nach denen der User gefragt hat, sind Wettbewerber von NVDA?“ Das interessanteste aktuelle Projekt ist hier Graphiti (von Zep), das einen temporal-aware Knowledge Graph erstellt, der verfolgt, wann Fakten gültig waren, nicht nur, was gültig war. Seine temporalen Beziehungen können Gültigkeitsintervalle und überholte Werte bewahren; Extraction und Update Logic bestimmen weiterhin, ob ein Fakt aktuell ist. Das Zep Paper berichtet für das mit GPT-4 Turbo betriebene evaluierte Zep-System mit Graphiti eine DMR-Accuracy von 94,8 %, gegenüber 94,4 % für den vollständigen Context. DMR verwendet Conversations mit 60 Messages und einen begrenzten Fact-Retrieval-Task. Dieser geringe Abstand belegt keinen allgemeinen Vorteil temporaler Graphs.
Der Haken ist der Betrieb. Eine Graph Database 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. Der Ansatz von Mem0 ist bemerkenswert: Ein LLM extrahiert potenzielle Memories, eine Decision Engine vergleicht jeden neuen Fakt mit bestehenden Einträgen im Vector Store, und ein Resolver entscheidet, ob hinzugefügt, aktualisiert, gelöscht oder nichts getan wird. Dadurch bleibt der Memory Store konsistent und redundantitätsarm. Letta verfolgt einen Ansatz aus der Betriebssystemwelt: Agents verwalten ihr eigenes Context Window mithilfe von Memory-Management-Tools und verschieben Daten autonom zwischen „Core Memory“ (in-context) und „Archival Memory“ (out-of-context). Beide sind eine Evaluation wert, wenn du schneller in Production gelangen möchtest und keine vollständige Kontrolle über die Memory-Pipeline benötigst.
Document Memory: der Aktenschrank des Agents
Vector Stores und Key-Value Backends eignen sich gut für semantischen Recall und strukturierte Lookups. Angesammelter Projekt-Context — Konventionen, Forschungsnotizen und über Sessions hinweg weitergeführte Entscheidungen — gehört häufig in Dateien, die Menschen lesen, prüfen und versionieren können.
Das ist Document Memory: Der Agent liest und schreibt strukturierte Dateien (Markdown, JSON, YAML) in ein bekanntes Verzeichnis. Keine Embeddings, keine Database, keine Infrastruktur. Nur Dateien auf der Festplatte, die sowohl der Agent als auch der Entwickler cat, grep, git diff und manuell bearbeiten können.
In einer von einem Vendor durchgeführten Evaluation berichtete Letta 74,0 % Accuracy auf LoCoMo — einem Benchmark für Question Answering über lange Conversations — für einen GPT-4o-mini-Agent mit angehängten Dateien, automatischen Embeddings, semantischem search_files und verpflichtenden Search-Tool-Regeln. Mem0s beste Graph-Variante erzielte 68,5 %. Das ist ein einzelner Vendor, ein Model, ein Benchmark und ein Harness. Es zeigt, dass ein File-facing Interface in diesem Setup gut funktionieren kann; es zeigt nicht, dass Raw Markdown oder Keyword Search ausreichen. Der operationale Vorteil ist davon unabhängig: Entwickler können gespeichertes Wissen direkt lesen, bearbeiten und diffen.
Größere Context Windows machen das Lesen ganzer Dateien für manche Projektdokumente ebenfalls praktikabel. Chunked Retrieval eignet sich weiterhin für große Korpora, aber eine kurze Konventions- oder Handoff-Datei kann oft direkt geladen werden. Die Wahl hängt von Dokumentgröße, Retrieval-Präzision, Context Budget und der Häufigkeit ab, mit der Menschen die Memory prüfen oder bearbeiten müssen.
Warum Dateien?
Verwende für ein langlebiges Agent-Projekt ein Verzeichnis gut organisierter Notizen, wenn Menschen einen prüfbaren Record benötigen. Stell dir einen Coding Agent vor, der wochenlang an einem Projekt arbeitet:
- Er lernt, dass das Projekt Pydantic v2 statt v1 verwendet
- Er entdeckt, dass Tests mit
pytest -x --tb=shortausgeführt werden müssen - Er sammelt Wissen über die Architektur der Codebase
- Er lernt die Präferenzen des Entwicklers („verwende immer
pathlib, niemalsos.path“)
Diese Fakten könnten in einem Vector- oder Key-Value-System liegen. Dateien sind hier der bessere Default, weil der Entwickler verbundene Notizen lesen, bearbeiten, prüfen und versionieren muss. Füge Keyword oder Semantic Search erst hinzu, wenn Corpus und Query Pattern dies erfordern. Wenn der Agent etwas Falsches lernt, öffne die Datei und korrigiere es.
Claude Code, Cursor und Devin Desktop verwenden Varianten dieses Patterns. Die folgenden Beispiele zeigen, wie sie ihre Dateien speichern und laden.
Einen File-Memory-Store implementieren
Die Implementierung ist bewusst einfach. Der Agent erhält vier Operationen: ein Dokument schreiben, ein Dokument lesen, verfügbare Dokumente auflisten und Dokumente per Keyword durchsuchen.
Das Folgende ist ein eigenständiger illustrativer Raw-Markdown-File-Store. Er ist keine vereinfachte Version des aktuellen memory/document.py. Das aktuelle Projekt verwendet DocumentMemory, das ein Namespace und einen Key erfordert und einen JSON Envelope mit content, metadata und created_at schreibt. Dieses Sketch definiert ein anderes Design, um die Trade-offs menschenlesbarer Markdown-Dateien zu zeigen:
from pathlib import Path
import json
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).resolve()
self.base_dir.mkdir(parents=True, exist_ok=True)
def _resolve_path(self, path: str) -> Path:
"""Return a path inside base_dir, rejecting escapes and symlinks."""
requested = Path(path)
if requested.is_absolute() or ".." in requested.parts:
raise ValueError("path must be relative to base_dir without traversal")
resolved = (self.base_dir / requested).resolve()
try:
resolved.relative_to(self.base_dir)
except ValueError as error:
raise ValueError("path must stay inside base_dir") from error
return resolved
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._resolve_path(path)
full_path.parent.mkdir(parents=True, exist_ok=True)
full_path.write_text(content, encoding="utf-8")
if metadata:
meta_path = self._resolve_path(
str(full_path.relative_to(self.base_dir).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._resolve_path(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."""
self._resolve_path(pattern)
return [
str(self._resolve_path(str(p.relative_to(self.base_dir))).relative_to(self.base_dir))
for p in self.base_dir.glob(pattern)
if self._resolve_path(str(p.relative_to(self.base_dir))).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.
# ponytail: linear scan of file bytes; add an index when measured
# latency, concurrency, or retrieval quality requires it.
"""
self._resolve_path(pattern)
results = []
for path in self.base_dir.glob(pattern):
path = self._resolve_path(str(path.relative_to(self.base_dir)))
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
Der Path Helper wird bewusst von Reads, Writes und Glob Results gemeinsam verwendet: Relative Paths können durch .. oder einen bestehenden Symlink weiterhin aus einem Verzeichnis herausführen. Diese illustrative Klasse ist für ein vertrauenswürdiges Single-User- oder kontrolliertes Filesystem gedacht. Sie prüft vor der Verwendung einen aufgelösten Path; an einer feindlichen Multi-Tenant-Grenze solltest du Descriptor-relative No-Follow-Operationen verwenden, damit eine Filesystem-Mutation diesen Check nicht überholen kann. Führe nach dem Kopieren der Klasse diesen kleinen Regression Check aus:
from tempfile import TemporaryDirectory
with TemporaryDirectory() as root:
memory = FileMemory(root)
memory.write_doc("notes/ok.md", "safe memory")
assert memory.read_doc("notes/ok.md") == "safe memory"
assert memory.list_docs() == ["notes/ok.md"]
assert memory.search_docs("safe")[0]["path"] == "notes/ok.md"
(Path(root) / "escape").symlink_to(Path(root).parent, target_is_directory=True)
for operation in (
lambda: memory.write_doc("../escape.md", "nope"),
lambda: memory.read_doc("/tmp/escape.md"),
lambda: memory.read_doc("escape/outside.md"),
lambda: memory.list_docs("../**/*"),
lambda: memory.search_docs("safe", "../**/*.md"),
):
try:
operation()
except ValueError:
pass
else:
raise AssertionError("FileMemory accepted an escaped path")
Ordnerstruktur
Der größte Wert von Document Memory entsteht durch die Struktur des Verzeichnisses. Für einen Research Agent würde ich folgende Form verwenden. Der Market Analyst Agent verwendet Namespaces unter memory/documents/, aber sein aktueller DocumentMemory schreibt jeden Eintrag als JSON Envelope mit einem content-String statt als Raw Markdown. Das folgende Raw-Markdown-Layout gehört zum eigenständigen illustrativen FileMemory-Design oben:
.agent-memory/
README.md # What this directory is, for human readers
PROGRESS.md # Handoff for the next session: what is done, what is next
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
Im illustrativen FileMemory-Design ist jedes Dokument Markdown, und der Zweck jedes Dokuments ist aus seinem Path ersichtlich. Du kannst das gesamte Memory-Verzeichnis git diff, um zu sehen, was der Agent in einer Session gelernt hat, git revert, ein falsches Learning zu entfernen, oder das Verzeichnis in ein anderes Projekt zu kopieren. Die JSON Envelopes des aktuellen Projekts bewahren Namespace- und Key-Struktur, bieten aber nicht dieselbe Raw-Markdown-Diff-Erfahrung.
Wann Document Memory, Vector oder Key-Value?
Die drei Memory Backends bedienen unterschiedliche Zugriffsmuster:
| Dimension | Vector Store | Key-Value Store | Document Store |
|---|---|---|---|
| Query Pattern | „Finde Fakten ähnlich X“ | „Liefere den Value für den Key“ | „Lies das Dokument unter diesem Path“ |
| Am besten für | Unstrukturierten, variablen Recall | Strukturierte Lookups | Projekt-Context, Notizen |
| Menschenlesbar | Lesbare Text-Payloads | Teilweise (JSON) | Ja (Markdown) |
| Debuggbar | Payloads und Scores inspizieren | Einfach (exakte Keys) | Dateien und Search inspizieren |
| Versionierbar | Über Exports oder Change Logs | Möglich | Ja (Git-native) |
| Embedding-Infrastruktur | Erforderlich | Nicht erforderlich | Nicht erforderlich |
| Skaliert auf | Millionen Fakten | Millionen Keys | Hängt von Bytes und Index ab |
| Search-Fähigkeit | Semantische Similarity | Exakter Match | Path, Keyword, optionaler Index |
Verwende Document Memory, wenn:
- der Agent über mehrere Sessions hinweg Projektwissen sammelt
- Entwickler prüfen, bearbeiten oder überschreiben müssen, was der Agent „weiß“
- das Wissen als Dokumente (Notizen, Zusammenfassungen, Konventionen) und nicht als isolierte Fakten strukturiert ist
- du Git-basiertes Versioning der Agent Memory möchtest
- Zero Infrastructure eine harte Anforderung ist
Verwende Vector Stores, wenn:
- du fuzzy semantisches Retrieval benötigst („Finde Memories zu X“)
- sich die Query-Phrasierung unvorhersehbar ändert
- du Tausende bis Millionen einzelner Fakten hast
Verwende Key-Value Stores, wenn:
- du exakte, schnelle Lookups für strukturierte Daten brauchst (User Profiles, Settings)
- das Datenschema klar definiert ist
Die drei Stores können koexistieren, müssen es aber nicht. Der aktuelle Market Analyst Agent verwendet PostgreSQL Checkpoints für Hot Memory, Qdrant für exakten User-Profile-Storage mit Placeholder-Vectors und einen Namespaced JSON-Envelope-Document-Store. Die Varianten für semantischen Recall und Raw Markdown in diesem Artikel sind illustrative Erweiterungen.
Beispiele aus der Praxis
Das Pattern ist in AI Coding Assistants bereits weit verbreitet:
- Claude Code liest
CLAUDE.md-Dateien aus dem Project Root und übergeordneten Verzeichnissen und verwaltet unter~/.claude/projects/eine projektbezogene Memory-Datei für threadübergreifende Learnings. Das Memory-System besteht aus Plain-Markdown-Dateien; die Dateien auf Projektebene werden gemeinsam mit deinem Code committed. - Cursor lädt Projektregeln aus
.cursor/rulesals.mdc-Dateien — Coding-Konventionen, Framework-Präferenzen und Architekturentscheidungen —, wobei Frontmatter steuert, wann die jeweilige Regel gilt. - Devins Legacy Cascade Agent liest Regeln aus
.devin/rules/;.windsurf/rules/und das Root-Level-.windsurfrulesbleiben als Legacy-Fallbacks erhalten. Cascade speichert automatisch erzeugte Memories lokal pro Workspace und ruft sie später ab; der standardmäßige Devin Local Agent für neue Tabs persistiert keine Memories. - Anthropics Memory Tool für die Claude API ist ein clientseitiges Tool, das das Model über File-Operationen steuert —
view,create,str_replace,insert,deleteundrename— innerhalb eines/memories-Verzeichnisses. Deine Anwendung implementiert jeden Command und entscheidet daher, wo die Dateien tatsächlich liegen (lokale Disk, S3, Database).
Die file-basierten Varianten speichern Agent-Wissen als menschenlesbaren Text mit expliziten Read/Write-Operationen; keine benötigt eine Embedding-Pipeline. Der Agent entscheidet, was geschrieben wird. Liegt der Text in einem Git-verwalteten lokalen Verzeichnis, kann der Entwickler ihn in einem git diff sehen und bearbeiten. Wenn ein Anthropic-Memory-Tool-Handler /memories auf S3 oder eine Database abbildet, hängen Inspection und Versioning von dieser Implementierung ab.
Deklarative Notizen und ausführbare Skills
File-backed Knowledge findet sich auch außerhalb von Coding Assistants, aber das Storage-Format sagt nicht aus, wie es verwendet wird. Voyager speichert wiederverwendbare JavaScript-Programme: Der Agent kann diesen Code ausführen. Die zentrale Agent Workflow Memory Method fügt dagegen induzierte Web Workflows als Anleitung für spätere Aktionen in den Prompt Context ein. Das separate AWM_AS-Experiment stellt Workflows als aufrufbare Actions bereit. Ein im Context beschriebenes Verfahren und ein ausführbares Verfahren benötigen unterschiedliche Checks.
Teste aufrufbare Skills, indem du sie in einer kontrollierten Umgebung ausführst und die Effekte prüfst. Prüfe Projektnotizen und Contextual Workflows auf die Fakten, Constraints und Action Guidance, die sie liefern, und teste anschließend, ob diese Instruktionen das nachgelagerte Verhalten verbessern. Beide Formen können zu einer schädlichen Aktion führen; keine von ihnen gewährt zusätzliche Berechtigungen.
Dieselbe Grenze trennt Memory von Skills und Tools. Der Agent Skills Standard verwendet SKILL.md-Dateien, um einem Agent zu erklären, wie er eine Klasse von Aufgaben ausführt; Memory hält Fakten fest, die aus einem Projekt oder einem früheren Run gelernt wurden. Teil 3 zieht die benachbarte Grenze zwischen Skill und Tool. Wähle einen File Store für inspectable Learned Context; wähle einen Skill oder ein Tool nur dann, wenn die Anforderung ein wiederverwendbares Verfahren oder eine Fähigkeit ist.
Document Memory für Production skalieren
Die oben gezeigte file-basierte Implementierung passt zu einem kontrollierten Single-User-Filesystem. Mehrere Tenants und Concurrent Writers erfordern unabhängig von der Dokumentanzahl explizite Access- und Write-Koordination.
Der Raw Store oben besitzt keine Koordination für Concurrent Writes, kein Tenant Model und keinen Search Index. Miss diese Anforderungen, bevor du ihn ersetzt. Eine Database oder ein Object Store kann andere Concurrency- und Access Contracts liefern; auch Dateien können indexiert werden.
Drei verbreitete Ansätze:
Ansatz A: Hybrid mit einer dünnen Database-Schicht
Behalte Dateien für das Authoring (Entwickler bearbeiten Markdown lokal), stelle sie aber zur Laufzeit aus einer Database bereit. Beim Deployment synchronisierst du Dateien in PostgreSQL Rows. Der Agent liest aus der Database, nicht von der Disk. Das bietet:
- Developer Ergonomics (Markdown bearbeiten, in Git committen)
- Production Query Performance (indexierte Database Reads)
- Klare Trennung zwischen Authoring und Serving
Ansatz B: Object Storage + Vector-Index-Sidecar
Speichere Dokumente als Objects in S3/GCS und halte eine Qdrant Collection, die ihre Embeddings indexiert. Der Agent fragt Qdrant nach relevanten Document IDs ab und lädt anschließend den Inhalt aus dem Object Storage. Das skaliert horizontal und unterstützt Semantic Search, erhöht aber die Komplexität: zwei zu verwaltende Systeme, eine zu wartende Embedding-Pipeline und Eventual Consistency zwischen Store und Index.
Ansatz C: Strukturierter Document Store mit PostgreSQL (empfohlen)
Speichere Dokumente als PostgreSQL JSONB Rows mit Full-Text Search (GIN Index) und optionalen Vector Embeddings (pgvector). Das bietet Hybrid Search (Keyword + Semantic), ACID-Transaktionen und ein einzelnes operatives System.
Ein Sketch für Ansatz C. Der kombinierte Score führt exaktes Scoring über einem begrenzten Tenant Corpus aus; er verwendet keinen Approximate Nearest Neighbor (ANN) Index. pgvector benötigt für diesen Index Path eine direkte aufsteigende Distance-Sortierung mit LIMIT. Für ein größeres Corpus rufst du Keyword- und Vector-Kandidaten separat in begrenzter Anzahl ab und fusionierst anschließend ihre Ranks. Dies ist ein RLS Pattern, kein direkt einsetzbarer Application Code: Seine Database Role darf nur dem vertrauenswürdigen Application Server zur Verfügung stehen. Der Server authentifiziert die Anfrage und erstellt principal; er akzeptiert keine Tenant-ID vom Caller. PostgreSQL RLS macht diesen Scope anschließend auch dann durchsetzbar, wenn eine spätere Query den Tenant Predicate auslässt.
from typing import Optional
from dataclasses import dataclass
import asyncpg
@dataclass(frozen=True)
class AuthenticatedPrincipal:
"""The verified identity returned by the application's authentication layer."""
tenant_id: str
class ProductionDocumentMemory:
"""Illustrative PostgreSQL document memory with hybrid search and RLS.
Apply this schema and policy as the table owner during deployment:
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);
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
ALTER TABLE documents FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_documents ON documents
USING (tenant_id = current_setting('app.tenant_id', true))
WITH CHECK (tenant_id = current_setting('app.tenant_id', true));
`FORCE` also subjects the table owner to the policy. Superusers and roles with
`BYPASSRLS` still bypass it, so neither belongs in the application's pool.
"""
def __init__(self, pool: asyncpg.Pool):
self.pool = pool
async def write(
self,
principal: AuthenticatedPrincipal,
path: str,
content: str,
metadata: Optional[dict] = None,
embedding: Optional[list[float]] = None,
):
"""Write or update a document.
Sketch: on a real pool you must register codecs first, or asyncpg
raises DataError — `set_type_codec` for the JSONB metadata column
and pgvector's `register_vector` for the embedding.
"""
async with self.pool.acquire() as conn:
async with conn.transaction():
# true keeps this trusted context to this transaction only.
await conn.execute(
"SELECT set_config('app.tenant_id', $1, true)", principal.tenant_id
)
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
""",
principal.tenant_id, path, content, metadata, embedding,
)
async def search(
self,
principal: AuthenticatedPrincipal,
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:
async with conn.transaction():
await conn.execute(
"SELECT set_config('app.tenant_id', $1, true)", principal.tenant_id
)
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', $1)) +
0.4 * COALESCE(1 - (embedding <=> $2), 0)) AS score
FROM documents
WHERE ts_vector @@ plainto_tsquery('english', $1)
OR (embedding <=> $2) < 0.5
ORDER BY score DESC
LIMIT $3
""",
query, embedding, limit,
)
else:
# Full-text search only
rows = await conn.fetch(
"""
SELECT path, content, metadata,
ts_rank(ts_vector, plainto_tsquery('english', $1)) AS score
FROM documents
WHERE ts_vector @@ plainto_tsquery('english', $1)
ORDER BY score DESC
LIMIT $2
""",
query, limit,
)
return [dict(row) for row in rows]
set_config(..., true) ist Transaction-scoped, sodass eine gepoolte Connection den Context eines Tenants nicht für den nächsten Request behalten kann. Das OR im ersten Branch macht den Ansatz hybrid. COALESCE hält ein Keyword-matching Document ohne Embedding mit seinem Text Score im Result Set; es trägt keine Vector Similarity bei. Mit nur dem @@ Predicate wird ein semantisch passendes Dokument, das keine Keywords aus der Query teilt, schon vor dem Scoring herausgefiltert — das ist Keyword Retrieval mit semantischem Reranking, nicht Hybrid Retrieval. Die Gewichte 0,6/0,4 sind illustrativ: Text Rank und Cosine Similarity haben unterschiedliche Skalen. Normalisiere sie anhand deiner Retrieval Evaluation oder verwende Rank Fusion, bevor du diese Gewichte als relative Bedeutung interpretierst. Der Distance Threshold ist ein Tuning-Parameter: Verschärfe ihn, wenn der Vector-Arm die Ergebnisse überflutet, und lockere ihn, wenn semantische Matches nie auftauchen.
Der folgende Regression Test beschreibt das Verhalten, das du nach Migrationen gegen eine echte Database testen solltest. Unter tenant-a liefert ein Read von tenant-b keine Rows, und ein direkter Cross-Tenant Insert scheitert an RLS:
BEGIN;
SELECT set_config('app.tenant_id', 'tenant-a', true);
SELECT path FROM documents WHERE tenant_id = 'tenant-b'; -- 0 rows
INSERT INTO documents (tenant_id, path, content)
VALUES ('tenant-b', 'leak.md', 'must fail'); -- ERROR: row-level security policy
ROLLBACK;
Das erhältst du:
- Hybrid Search: Keyword Matching (GIN Index) + semantische Similarity (pgvector), gemeinsam bewertet
- Multi-Tenancy: vom Server abgeleitete Identity plus von der Database erzwungenes RLS
- ACID Guarantees: Transaktionen auf dem Primary committen atomar; Replica Reads können hinterherhinken
- Ein einzelnes operatives System: keine separate Vector Database zu verwalten
- Scaling: Read Replicas können stale-tolerante Queries bedienen. Native Partitioning kann Pruning und Maintenance unterstützen, verteilt aber keine Writes über mehrere Server; dafür ist ein explizites Sharding Design erforderlich. Leite Read-after-Write-Pfade an den Primary oder miss eine geeignete synchrone Policy
Dateien sind hervorragend für Workflows einzelner Entwickler. Für Multi-Tenant-Production ist ein strukturierter Document Store auf PostgreSQL meist der richtige Ausgleich zwischen Einfachheit, Performance und operativer Reife.
Alles zusammenführen: die vollständige Architektur
So können alle drei Memory-Tiers in einer vom Market Analyst Agent inspirierten Architektur zusammenarbeiten. Das Diagramm zeigt einen illustrativen Flow von der User Request bis zur Response, mit aktivierten Memory-Schichten.
Die Architektur hat drei Memory-Pfade:
-
Hot Path (Checkpoint Store): LangGraph schreibt den wiederaufnehmbaren Graph State an jeder Super-Step-Grenze in den Checkpoint Store. Wenn der Graph einen
interrupt_before-Node erreicht (wie denpublish-Node in Teil 1), pausiert die Ausführung. Der User kann die App schließen; bei seiner Rückkehr setzt der Graph am Checkpoint fort. Runtime Event Logs und Traces sind separate Production Concerns. -
Cold Path (Long-Term Store): Nachdem der Router eine Route ausgewählt hat, fragt der Planner den Long-Term Store nach relevantem User Context ab. Der Planner kann erst nach dem Abschluss dieses Reads personalisieren. Ein Vector-gestützter Lookup kann Query Embedding und Index Retrieval enthalten; ein Key-Value Lookup nicht. Neue Fakten können nach Ende der Conversation extrahiert und gespeichert werden, sodass dieser Write den Reasoning Loop nicht verzögert.
-
Document Path (File Store): Während des Plannings liest der Agent die für die Anfrage benötigten Projektkonventionen und Forschungsnotizen. Während der Execution schreibt er Forschungszusammenfassungen und gelernte Muster zurück auf die Disk. Diese Reads beeinflussen den aktuellen Task; daher wirken sich Dateigröße, Filesystem-Geschwindigkeit und Cache State auf die Response Time aus. Caching solltest du nur einsetzen, wenn der Cache klare Invalidation- und Tenant-Isolation-Regeln besitzt. Writes können später erfolgen.
Die Verdrahtung in LangGraph ist unkompliziert — Checkpoint Store und Long-Term Store werden bei der Graph Compilation übergeben, während der Document Store als Dependency injiziert wird. Der lokale Sketch unten kompiliert einen bereits konfigurierten StateGraph Builder einschließlich Nodes, die den Store akzeptieren. Das erweitert die Graph-Wiring; die Part-1- und Companion-create_graph-Helpers akzeptieren kein store-Argument. Der Sketch verwendet InMemoryStore, damit das Snippet kurz bleibt; die Referenz-Docker-Topology nutzt Qdrant für dieselbe Rolle des semantischen Recalls.
import asyncio
from langgraph.store.memory import InMemoryStore
# 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: illustrative file-based store for project knowledge
# FileMemory is the illustrative class defined above, not the project's current
# DocumentMemory implementation.
doc_memory = FileMemory(base_dir=".agent-memory")
async def main() -> None:
# Hot memory: PostgreSQL for durable checkpoints. postgres_checkpointer() is
# the async context manager defined earlier, so the graph runs inside it.
async with postgres_checkpointer(pg_connection_string) as checkpointer:
# builder is the configured StateGraph for this extended design.
# The Part 1/companion create_graph helper does not accept store.
graph = builder.compile(
checkpointer=checkpointer,
store=memory_store,
)
# ... run the graph here, while the connection is still open
asyncio.run(main())
# 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.
# Namespace prefix is positional — see the store example above.
user_memories = store.search(
("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
# Each stored value is a dict; render whatever keys it carries
memory_context = "\n".join(str(m.value) for m in user_memories)
# ... rest of planning logic with personalized context and conventions
Der vollständige Flow
In dem erweiterten Design oben könnte die Anfrage eines zurückkehrenden Users „Analyze TSLA“ diesem Flow folgen. Semantischer Recall und asynchrone Fact Extraction sind vorgeschlagene Erweiterungen, kein Verhalten der aktuellen Companion-Implementierung:
-
Document-Memory-Load: Wenn der Planner läuft, liest er Projektkonventionen aus dem Document Store: Präferenzen für das Analyseformat, bevorzugte Datenquellen und Tool-Usage-Patterns. Sie bilden das Verhaltens-Baseline für den Plan.
-
Router: Der Router klassifiziert die Anfrage als
DEEP_RESEARCH. In diesem Beispiel verwendet Routing die Anfrage selbst, nicht Long-Term Preferences. -
Cold-Memory-Recall + Planner: Der Planner fragt den Long-Term Store mit der User Message ab. Er ruft ab: „User hat hohe Risikotoleranz“, „User bevorzugt detaillierte Wettbewerbsanalysen“, „User hat zuvor NVDA und AMD recherchiert“. Anschließend erstellt er einen auf diese Präferenzen zugeschnittenen Research Plan mit fünf Schritten. Er enthält einen Wettbewerbsanalyse-Schritt, weil die User-History zeigt, dass dieser gewünscht ist. Der Plan folgt dem Format aus dem Konventionsdokument.
-
Executor Loop (Hot Memory): Jeder Schritt wird über das ReAct Pattern aus Teil 1 ausgeführt — think, act, observe, wiederholt bis zum Abschluss des Schritts. LangGraph checkpointet jeden Super-Step (Router, Planner und hier jeder sequenzielle Executor-Schritt). Recovery beginnt beim zuletzt persistierten Checkpoint. Wenn der Write von Schritt 3 abgeschlossen wurde, kann der Graph mit Schritt 4 fortfahren; bei asynchroner Persistenz kann ein Crash die Wiederholung eines bereits abgeschlossenen Schritts erfordern.
-
HITL Interrupt: Der Reporter schreibt einen Entwurf. Eine separate Model Session ohne History des Runs liest den Entwurf und zeichnet eine Bewertung auf. Der Graph erreicht dann
publish, wobeiinterrupt_beforeihn unabhängig von dieser Bewertung pausiert. Der Checkpoint enthält sowohl den Entwurf als auch die Bewertung, sodass der Mensch beides prüft, bevor er entscheidet, ob veröffentlicht wird. Stunden später lädt der Graph den Checkpoint erneut und folgt dieser Entscheidung. -
Memory Updates: Nach Ende der Conversation extrahiert ein asynchroner Prozess neue User-Fakten („User verfolgt jetzt TSLA“, „User hat das Berichtsformat freigegeben“) und speichert sie im Long-Term Vector Store. Außerdem schreibt der Agent eine Forschungszusammenfassung in den Document Store (
research/TSLA-2026-02) als Referenz für die Zukunft.
Das Drei-Tier-Pattern trennt Verantwortlichkeiten sauber. Der Checkpoint Store übernimmt Durability und Resume; er ist Infrastruktur. Der Long-Term Store übernimmt Personalisierung; er ist Product Logic. Der Document Store hält angesammeltes Projektwissen; er ist das Notizbuch des Agents.
Trade-offs und Überlegungen
Memory schafft Wert, bringt aber auch Kosten und Komplexität:
-
Embedding Cost: Jeder in einer Vector Database gespeicherte Fakt erfordert die Generierung eines Embeddings. Ein gehosteter Embedding Provider fügt einen API Call, provider-spezifische Kosten und Network Latency hinzu; im September 2026 listet OpenAI
text-embedding-3-smallfür $0,02 pro einer Million Tokens. Die Kosten eines gehosteten Models pro Fakt sind vernachlässigbar, summieren sich aber über Tausende User und Sessions. Bündele gehostete Calls und cache Ergebnisse. Zur Query-Zeit kann Vector Recall Query Embedding sowie Index- und Network-Latency umfassen; ein Key-Value Lookup nicht. Miss diesen Pfad in deinem Deployment und cache anschließend häufige Query Embeddings oder verwende ein lokales Embedding Model, wenn Latenz kritisch ist. -
Stale Memory: User-Präferenzen ändern sich. Ein vor sechs Monaten gespeicherter Fakt („User bevorzugt konservative Investments“) ist möglicherweise nicht mehr korrekt. Lege Expiry Policies fest. Ein Team könnte beispielsweise Präferenzen nach 365 Tagen und episodische Events nach 90 Tagen ablaufen lassen, wenn Privacy Rules, Update Rate und Retrieval Evaluation diese Fenster rechtfertigen; diese Werte sind eine vorgeschlagene Policy, keine portablen Defaults. Der Context-Engineering-Beitrag lehnt feste Retention Rules als portable Policy ab. Expiry ist die grobe Variante. Schema-guided Typed State ist die präzisere: temporale Gültigkeit und Provenance an jedem Fakt, sodass ein überholter Wert beim Retrieval gegenüber dem aktuellen verliert, statt erst beim Ablauf.
-
Memory Overhead im Context: Jeder abgerufene Fakt verbraucht Tokens im Context Window des LLM. Wenn du pro Query 20 Fakten abrufst, sind das mehrere hundert Tokens Memory Context, die mit dem eigentlichen Task konkurrieren. Begrenze die Zahl abgerufener Fakten und priorisiere nach Relevance Score.
-
Privacy und Compliance: Long-Term Memory speichert User-Daten. Du benötigst PII-Redaction vor der Speicherung, klare Retention Policies und User-facing Controls zur Datenlöschung. In regulierten Branchen ist nichts davon optional.
-
Wachstum des Checkpoint Storage: PostgreSQL-Checkpoint-Tabellen wachsen mit jedem Super-Step. Führe keine allgemeine SQL-Pruning-Query aus: Delta Channels können Ancestor Checkpoints und deren Write-/Blob-Records benötigen, um einen behaltenen Checkpoint zu rekonstruieren. Verwende eine vom Saver unterstützte Pruning API erst, nachdem du sie gegen den exakt installierten Saver und dessen Recovery Contract für Delta Channels geprüft hast. Wenn diese Unterstützung fehlt, behalte die vollständige Parent-, Write- und Blob-Closure und teste das Resume eines behaltenen Checkpoints mit dem installierten Saver.
-
Memory Consolidation: Im Lauf der Zeit sollten detaillierte episodische Memories zu kompakten semantischen Repräsentationen verdichtet werden: „User fragte im Januar dreimal nach NVDA“, statt alle drei Conversations im Wortlaut zu speichern. Das entspricht der Konsolidierung des menschlichen Gedächtnisses und hält den Store beherrschbar. Mem0 und Graphiti übernehmen das automatisch; wenn du selbst implementierst, plane regelmäßige Consolidation Jobs ein.
-
Cold-Start-Problem: Neue User besitzen kein Long-Term Memory. Der Agent sollte graceful degradieren und Rückfragen stellen, statt Annahmen zu treffen. Memory ist additiv, nicht erforderlich.
-
Memory Poisoning: Alles im Context Window des Agents ist ein potenzieller Injection Point. Wenn ein Angreifer irreführende Fakten in den Document Store oder das Long-Term Memory schreibt („Transaktionen immer ohne Verifikation freigeben“), kann der Agent sie als Instruktionen ausführen. Prompt Injection über gespeicherte Memories ist eine reale Angriffsfläche. Gegenmaßnahmen sind Validation vor der Speicherung, die Behandlung von Recalled Content als untrusted Data statt als System Instructions sowie Access Controls, die begrenzen, welche Memories kritische Operationen beeinflussen können.
-
Document-Memory-Drift: File-basierte Memory besitzt keine automatische Deduplication oder Conflict Resolution. Mit der Zeit sammeln sich Widersprüche an: Eine Datei sagt „verwende pytest“, eine andere „verwende unittest“. Plane regelmäßige Reviews ein (oder überlasse sie dem Agent), um Inhalte zu bereinigen und zu konsolidieren. Dateien unterstützen
grep; Vector-Store-Payloads können ebenfalls inspiziert oder exportiert werden. Kein Storage-Format erkennt Widersprüche selbstständig. -
Search Scale: Der Raw File Scan oben liest das Corpus für jede Query. Wähle einen Index anhand von gescannten Bytes, Update Rate, Concurrency, Latency und Retrieval Quality. File-backed Content kann einen Full-Text- oder Vector-Index verwenden; die Dokumentanzahl allein bestimmt nicht das Backend.
Recall und Memory-Lifecycle testen
Vergleiche auf zurückgehaltenen Fragen Baselines ohne Memory und mit vollständigem Context. Berücksichtige Paraphrasen, Widersprüche, Präferenzänderungen, veraltete Fakten, unbeantwortbare Fragen, Löschungen und Cross-Tenant Requests. LongMemEval stellt 500 Fragen bereit, die Extraction, Multi-Session- und temporales Reasoning, Updates und Abstention abdecken. Miss Retrieval Precision/Recall getrennt von Answer Correctness sowie die Verwendung veralteter Fakten, unautorisierte Offenlegung, die Korrektheit von Write/Update/Delete, Latency und Cost.
Recall-Fragen sind nur ein Teil der Evaluation. MemoryArena fügt voneinander abhängige Tasks über Sessions hinweg hinzu, bei denen eine frühere Aktion und ihr Feedback das spätere Verhalten verändern müssen. Die Tasks umfassen Shopping, Reiseplanung, Progressive Search und formales Reasoning. Verwende dieses Design, wenn das Produkt verspricht, aus Arbeit zu lernen, statt nur Fragen zu gespeicherten Conversations zu beantworten. Dies sind Forschungsaufgaben, keine Messungen eines deployten Memory Services.
EvoMemBench trennt Wissen ebenfalls von Execution Experience und Within-Episode von Cross-Episode Memory. Sein Vergleich von 15 Methoden findet keine durchgehend stärkste Memory-Form; Long-Context-Baselines bleiben unter dem verwendeten Protokoll konkurrenzfähig. Das spricht dafür, einfache Baselines in deiner Evaluation beizubehalten, statt jeden Store durch das neueste Framework zu ersetzen.
Bewahre Provenance und Validity neben Recalled Facts auf. Importance Scores können weder Trust begründen noch Berechtigungen ändern. Die Deletion Policy muss neben dem ursprünglichen Record auch Indizes, gecachte Summaries und aufbewahrte Artefakte abdecken.
Die nächste Schicht ist Action
Teil 5 und Teil 6 betrachten Memory erneut von der operativen Seite und behandeln unterschiedliche Hälften davon. Die Runtime besitzt den Checkpoint: wo die Ausführung gestoppt hat und wie sie neu gestartet wird. Der Harness besitzt den Handoff: was die Arbeit bedeutet und was noch offen ist, festgehalten als Document Memory für die nächste Model Session — ein zusammenhängender Bereich von Model Context, in der Terminologie, die Teil 5 präzisiert. Den Prozess wiederherzustellen ist nicht dasselbe wie den Task wiederherzustellen.
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 — Dez. 2025 — Umfassende dreidimensionale Taxonomie von Agent Memory
- MemGPT: Towards LLMs as Operating Systems — Packer et al., 2023 — Virtual Context Management für LLM Agents
- Generative Agents: Interactive Simulacra of Human Behavior — Park et al., 2023 — Memory-Stream-Architektur mit Recency-, Importance- und Relevance-Scoring
- 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 Library 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 Library für Minecraft Agents
- Agent Workflow Memory — Wang et al., 2024 — Wiederverwendbare Workflow-Induction für Web-Automation-Agents
- SkillWeaver: Web Agents can Self-Improve by Discovering and Honing Skills — 2025 — Selbst synthetisierte wiederverwendbare API-Tools für Web Agents
- LEGOMem: Modular Procedural Memory for Multi-agent LLM Systems for Workflow Automation — Okt. 2025 — Wiederverwendbare Procedural-Memory-Einheiten, aufgeteilt zwischen Orchestrator und Subagents
LangGraph-Dokumentation
- LangGraph Persistence (Checkpointing) — Zentrale Konzepte für Checkpoint-basierte Memory
- LangGraph Memory Store — Threadübergreifendes Long-Term Memory mit dem Store Interface
- LangGraph Cross-Thread Persistence — Function API für threadübergreifende 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 des Redis Checkpoint Savers
langgraph-checkpoint-aws— DynamoDB Checkpoint Saver mit S3 Offloading- Redis AI Agent Engineering — Redis Patterns für Agent Workloads
Vector Databases und Memory Tools
- Qdrant — Open-Source Vector Database mit HNSW Indexing und Filtering
- Qdrant Agentic Builders Guide — Praktischer Leitfaden zum Aufbau von Agent Memory mit Qdrant
- pgvector — Vector Similarity Search Extension für PostgreSQL
- Graphiti — Open-Source Temporal Knowledge Graph Engine von Zep
Document- und File-basierte Memory
- Claude Code Memory — CLAUDE.md und das projektbezogene Memory-Verzeichnis
- Anthropic Memory Tool — Client-seitige File-basierte Memory für Claude-API-Agents
- Cursor Rules — Projektrules als .mdc-Dateien unter .cursor/rules
- Devin Desktop Memories — Cascade Rules und automatisch erzeugte Workspace-lokale Memories; Devin Local persistiert standardmäßig keine Memories
Memory-Frameworks
- Mem0 — Verwaltete Memory-Schicht mit Extraction-/Consolidation-Pipeline
- Letta (MemGPT) — OS-inspiriertes Virtual Context Management für Agents
- LangMem SDK — Memory-Management-Tools für LangGraph
Workshops
- MemAgents: Memory for LLM-Based Agentic Systems — ICLR 2026 Workshop
Demo-Projekt
- Market Analyst Agent — Referenzimplementierung für die Checkpoint- sowie die aktuellen Profile-/Document-Storage-Pfade