Engineering the Agentic Stack · Teil 2

Architektur der AI-Agent-Memory: Checkpoints und Vector Stores

Automatische Übersetzung Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.

Ein Reasoning Loop überlebt einen einzelnen Request nur, 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 jeder spätere Turn benötigt und wo dieser State liegen sollte.

Als Anker für die Diskussion über Hot Checkpoints verwende ich den Market Analyst Agent – einen kleinen LangGraph Agent, der Marktdaten abruft und einen Analystenbericht schreibt. Die Abschnitte zu Cold Vectors und Raw Markdown sind unabhängige illustrative Designs und zeigen Erweiterungen, die das aktuelle Projekt noch nicht implementiert. Anschließend behandle ich, 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 herum steuert. Das Harness entscheidet, welche Inhalte davon das Context Window erreichen; die Stores tun das nicht. Dieser Artikel behandelt, wo dieser State liegt, bevor das Harness darauf zugreift. Teil 3 und Teil 4 behandeln, was das Harness anschließend mit dem Prompt macht.


Was ist AI-Agent-Memory?

AI-Agent-Memory ist die State-Schicht, mit der ein Agent Task-Fortschritt bewahren, früheres Wissen abrufen und seinen Wissensstand über mehrere Runs hinweg aktualisieren kann. In Production ist das keine einzelne Vector Database, sondern eine Kombination aus Hot Checkpoints, Cold Semantic oder Structured Stores und menschenlesbarer Document Memory.

BedarfBeste StandardeinstellungWarum
Einen Run pausieren und fortsetzenPostgreSQL Checkpoint StoreDurable, abfragbar und gemeinsam mit App-Daten leicht zu betreiben
Transient State mit niedriger LatencyRedis Checkpoint StoreSchnelles Resume und kurzlebiger State, mit Trade-offs bei der Persistenz
Semantisches Recall über Threads hinwegQdrant oder pgvectorRuft Memories nach Bedeutung ab, nicht nur über exakte Keys
Strukturierte User-FaktenPostgreSQL oder Key-Value StoreDeterministische Updates sind für Präferenzen und IDs besser als Fuzzy Retrieval
Projektkonventionen und gelernte VerfahrenMarkdown- oder JSON-DateienMenschenlesbar, diffbar und für Agents leicht aktualisierbar
Memory für Beziehungen zwischen EntitätenKnowledge GraphSinnvoll, wenn Beziehungen wichtiger sind als einzelne Fakten

Beginnen Sie nicht mit Memory, nur weil es intelligent klingt. Beginnen Sie mit dem für den User sichtbaren Fehler: verlorener Fortschritt, vergessene Präferenz, wiederholte Recherche oder die fehlende Wiederverwendung einer Projektkonvention.

Fehlerbilder, die Memory erfordern

Ein Stateless Agent kann eine isolierte Frage beantworten, vergisst den Request aber, sobald der Call endet. Dieses Design scheitert, sobald das Produkt eines der folgenden Verhaltensmuster benötigt:

  • Pause und Resume: Ein User startet eine Recherche, schließt seinen Laptop und kommt am nächsten Tag zurück. Ohne Checkpointed State beginnt der Agent von vorn.
  • Kohärenz über mehrere Turns: In einer langen Conversation muss sich der Agent merken, welche Tools er aufgerufen, welche Daten er gesammelt und welche Plan-Schritte er abgeschlossen hat.
  • Personalisierung: Ein zurückkehrender 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 auf die Freigabe eines Menschen für den nächsten Schritt. Der „Waiting“-State muss Process-Restarts überleben.

Beim Market Analyst Agent aus Teil 1 erzeugt der Request „Analyze NVDA“ einen Plan, fünf Tool Calls, gesammelte Daten und einen Berichtsentwurf. Antwortet der User mit „sieht gut aus, aber füge eine Wettbewerbsanalyse hinzu“, kann ein Checkpoint Store den State des letzten abgeschlossenen Schritts laden und den Wettbewerbs-Schritt ergänzen. Ohne Checkpointed State kann der Agent nicht auflösen, worauf sich „sieht gut aus“ bezieht, und muss von vorn beginnen.

Long-Term Memory behandelt einen anderen Fall. Kommt der User eine Woche später zurück und fragt: „Update my NVDA analysis“, muss sich der Agent möglicherweise an eine Präferenz für konservative Risikobewertungen und ein Interesse an Semiconductor Stocks erinnern. Ein Vector-backed Memory Store kann diese Fakten über Sessions hinweg abrufen, ohne sie erneut abzufragen.

Die folgenden Implementierungsbeispiele verwenden LangGraph, LangChains Open-Source-Library zum Erstellen von Agents als explizite State Graphs; die von LangGraph gezogenen Storage-Grenzen lassen sich auf jedes Framework übertragen. LangGraph trennt Memory nach Scope. Jede Graph Execution läuft innerhalb eines Threads, also einer Conversation oder eines Tasks. Persistierter State innerhalb dieses Threads ist Short-Term Memory. Über Threads hinweg geteilter State ist Long-Term Memory. Im Folgenden sind „Thread“ und „Conversation“ austauschbar; ich vermeide „Session“ für diesen Scope, weil Teil 5 den Begriff für das Durable Log eines einzelnen Runs reserviert, von denen mehrere auf einem Thread anfallen können. Der aktuelle Model-Kontext und In-Process-Variablen bilden die Working-Memory-Schicht über beiden Stores.

Sechs Memory-Typen für Agents und die drei Storage-Tiers, auf die sie abgebildet werdenSechs Memory-Typen für Agents und die drei Storage-Tiers, auf die sie abgebildet werden


Eine Taxonomie von AI-Agent-Memory

Vor der Implementierung 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 Taxonomie, die auf Cognitive Science basiert. In meinem Context-Engineering-Beitrag habe ich Memory Scoping eingeführt; hier erweitere ich es auf sechs Kategorien:

Memory-TypScopeLebensdauerBeispielStorage Pattern
WorkingAktueller SchrittMillisekundenTool-Call-Argumente, aktuelle LLM-AntwortIn-Process (Python dict)
Short-TermAktueller ThreadMinuten–StundenConversation-History, Plan-Fortschritt, gesammelte DatenCheckpoint Store
EpisodicThread-übergreifendTage–Monate„Letzte Woche fragte der User nach NVDA-Earnings“Vector Store / KV Store
SemanticThread-übergreifendMonate–permanent„Der User bevorzugt konservative Investments“Vector Store / KV Store
DocumentThread-übergreifendTage–permanentProjektnotizen, Research-Summaries, gelernte MusterFile Store (Markdown/JSON)
ProceduralSystemweitPermanent„Bei der Aktienanalyse immer SEC-Filings prüfen“Config / System Prompt

Working Memory bezeichnet das, womit das LLM gerade aktiv Reasoning ausführt: Python-Variablen in der aktuellen Funktion, die Inhalte des Context Windows und Tool-Call-Argumente während der Ausführung. Das ist die schnellste und flüchtigste Schicht. Über den aktuellen Schritt hinaus wird nichts persistiert. Working Memory ist durch das Context Window des Models begrenzt, wodurch es zum Bottleneck wird. Alles, was der Agent zum Entscheidungszeitpunkt „weiß“, muss hier hineinpassen – unabhängig davon, ob es aus dem Checkpoint Store, einer Vector Query oder einem File stammt. Die anderen Tiers existieren, um zur richtigen Zeit die richtigen Informationen in Working Memory zu laden.

Short-Term Memory ist der Checkpoint, den LangGraph nach jeder Einheit der Graph Execution schreibt – einem Super-Step, der im nächsten Abschnitt definiert wird. Episodic und Semantic Memory persistieren über Threads hinweg. Document Memory speichert Projektnotizen, Research-Summaries und gelernte Konventionen in Dateien, die Menschen und Agents inspizieren können. Procedural Memory liegt in System Instructions und Tool Definitions und ändert sich nicht für jeden User.

Für die Implementierung werden fünf der sechs Kategorien auf drei Storage-Tiers abgebildet. 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 ist in der obigen Taxonomie dem Hot Tier zugeordnet, wird aber nie wirklich gespeichert: Es existiert für einen einzelnen Schritt im Prozess und ist das Context Window, in das die drei Storage-Tiers laden. Procedural Memory liegt außerhalb aller drei: Es lebt im System Prompt und in den Tool Definitions und wird daher zusammen mit dem Agent ausgeliefert, statt gespeichert und abgerufen zu werden.

CoALA klassifiziert Working, Episodic, Semantic und Procedural Memory. Die Studie „Memory in the Age of AI Agents“ betont Vector Stores und Knowledge Graphs, während LangGraph Checkpoints und seine Store-Schnittstelle dokumentiert. File-backed Project Knowledge liegt außerhalb dieser Taxonomien, obwohl Claude Code, Cursor und Devin Desktop persistente Projektdateien laden.

Dasselbe Storage Pattern erscheint auch in anderen Domänen. Ein Minecraft Agent (Voyager) speichert wiederverwendbare Game Skills als Code Libraries, Teams in einem Enterprise-Document-QA-Wettbewerb iterieren über Procedural-Prompt-Dokumente, und Web Agents leiten aus erfolgreichen Runs wiederverwendbare Browsing-Workflows ab. Auf alle drei komme ich später zurück; der Punkt ist hier, dass Dateien dieses Wissen ohne separaten Embedding Service inspizierbar und versionierbar machen.

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 dies gehen kann: Simulierte Agents speicherten, reflektierten über und riefen ihre eigenen Memories ab. Sein Memory Stream bewertete Kandidaten nach Recency, Importance und Relevance – ein Design, das weiterhin einen nützlichen Referenzpunkt für das Retrieval von Agent Memory bildet.


Short-Term Agent Memory: der Checkpoint Store

Jedes Mal, wenn LangGraph einen Super-Step abschließt – einen Node oder einen Batch parallel ausgeführter Nodes – serialisiert das Framework den vollständigen Graph State und schreibt ihn in einen Checkpoint Store. Das bildet die Grundlage für Pause/Resume, Time-Travel-Debugging und HITL-Workflows.

Hot Memory: ein bei jedem Super-Step geschriebener Checkpoint und der Recovery-Pfad, der ihn lädtHot Memory: ein bei jedem Super-Step geschriebener Checkpoint und der Recovery-Pfad, der ihn lädt

Ein Checkpoint enthält den zum Fortsetzen erforderlichen Graph State: den AgentState aus Teil 1 – Messages, Identity, User Profile, Plan-Schritte, Research-Daten und Execution Mode. Zusätzlich speichert LangGraph eigenes Bookkeeping: ID und Timestamp des Checkpoints, eine Version pro Channel (LangGraphs Bezeichnung für einen einzelnen State Key) sowie einen separaten Record darüber, welche Channel-Versionen jeder Node bereits gesehen hat. Die Step-Nummer liegt in den Metadaten des Checkpoints und nicht im Checkpoint selbst. Durch den Vergleich dieser beiden Informationen ermittelt der Graph, was als Nächstes ausgeführt wird. Nach einem HITL Interrupt oder einem Process-Restart lädt der Graph den Checkpoint, der an der letzten abgeschlossenen Grenze geschrieben wurde, und steigt in den nächsten Node ein. Er setzt die Ausführung nicht an einer beliebigen Python-Zeile fort. Ein Checkpoint unterscheidet sich außerdem von einem Append-Only Event Log oder Trace; Teil 5 trennt diese Runtime-Observability-Flächen ausdrücklich.

Funktionsweise des LangGraph-Checkpointing

LangGraphs BaseCheckpointSaver ist eine einfache Schnittstelle: put() schreibt einen Checkpoint, get_tuple() liest den neuesten Checkpoint eines Threads, list() gibt die History zurück. Jeder Checkpoint ist über (thread_id, checkpoint_ns, checkpoint_id) adressiert, wobei thread_id die Conversation identifiziert, checkpoint_ns das Subgraph-Namespace behandelt und checkpoint_id eine eindeutige Version ist.

Entscheidend ist, welcher Backend darunter liegt. PostgreSQL und Redis sind zwei verbreitete Produktionsoptionen.

PostgreSQL vs. Redis

Redis und PostgreSQL als Checkpoint-Backends, verglichen nach Latency, Durability und Query-ModellRedis und PostgreSQL als Checkpoint-Backends, verglichen nach Latency, Durability und Query-Modell

DimensionPostgreSQL (langgraph-checkpoint-postgres)Redis (langgraph-checkpoint-redis)
Durability-ModellACID-Transaktionen, WAL und ReplicationKonfigurierbare Persistenz: Append-Only-Command-Log (AOF) oder periodische Snapshots (RDB)
Checkpoint-HistoryDurable History für Resume und DebuggingRetention hängt von Saver- und Eviction-Settings ab
Primäre RestriktionDatabase-Write-Latency und TabellenwachstumRAM-Verbrauch, Eviction und Persistenzkonfiguration
Operativer FitTeams, die bereits relationale Datenbanken betreibenTeams, die Redis bereits mit hohem Throughput betreiben
Beste Standardeinstellung fürDurables Resume und reproduzierbares DebuggingLatency-sensitive, wiederherstellbarer Session-State

Allgemeine Database-Benchmarks sagen die Checkpoint-Performance nicht zuverlässig voraus. Messen Sie die serialisierte State-Größe, Write-Frequenz, Persistenz-Settings und Concurrency Ihres eigenen Graphs.

PostgreSQL: der Durable Default

PostgreSQL ist für die meisten Teams der sicherere Default. Checkpoints überleben Crashes, Sie erhalten vollständige Transaction-Semantics, und die Checkpoint-History macht Time-Travel-Debugging unkompliziert.

Eine vereinfachte Version des Checkpoint-Setups in memory/hot.py. Setzen Sie für Production LANGGRAPH_STRICT_MSGPACK=true oder konfigurieren Sie eine explizite allowed_msgpack_modules-Allowlist, damit die Checkpoint-Deserialisierung nur sichere oder deklarierte Types zulässt; der permissive Default warnt vor nicht registrierten Types, lässt sie aber zu.

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() -> 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(
            {"messages": [HumanMessage(content="Analyze NVDA")]}, config
        )

        # Resume later on the same thread_id — loads the latest checkpoint.
        result = await graph.ainvoke(
            {"messages": [HumanMessage(content="approved")]}, config
        )

asyncio.run(main())

Der AsyncPostgresSaver verwendet das Package langgraph-checkpoint-postgres, das vier Tabellen anlegt: checkpoints (der serialisierte State), checkpoint_blobs (große binäre Daten), checkpoint_writes (Pending Writes für Crash Recovery) und checkpoint_migrations (Schema-Version). Concurrent Writes werden durch den Primary Key (thread_id, checkpoint_ns, checkpoint_id) und Upserts getrennt, nicht durch Locking – zwei Worker auf demselben Thread beschädigen sich nicht gegenseitig, koordinieren sich aber ebenfalls nicht.

Redis: wenn Latency der Bottleneck ist

Wenn Checkpoint-Latency der Bottleneck ist, ist Redis eine Option für wiederherstellbaren State. Messen Sie vor der Entscheidung gegenüber PostgreSQL die serialisierte State-Größe, Persistenz-Settings und Concurrency.

Eine vereinfachte Version des Checkpoint-Setups in 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.
    Trade-off: less durable than PostgreSQL unless you enable AOF,
    Redis's append-only log — snapshots alone lose recent writes on a crash.
    """
    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())

Der 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 ersetzte mehrere Search-Operationen durch einen einzelnen JSON.GET-Call und reduzierte die Latency deutlich. Redis 8.0+ enthält RedisJSON und RediSearch standardmäßig – zusätzliche Module müssen nicht installiert werden.

Für Deployments mit begrenztem Speicher speichert ShallowRedisSaver nur den neuesten Checkpoint pro Thread – keine History, aber minimaler RAM-Verbrauch. Verwenden Sie dies, wenn Sie Pause/Resume benötigen, aber kein Time-Travel-Debugging.

Wann welche Option?

Verwenden Sie PostgreSQL, wenn:

  • Sie die vollständige Checkpoint-History für Time-Travel-Debugging oder reproduzierbares Resume benötigen
  • Durability nicht verhandelbar ist (Financial Services, Healthcare)
  • Sie PostgreSQL bereits in Ihrem Stack betreiben
  • Ihr Agent lange Tasks ausführt und der Verlust des State stundenlange Neuberechnung bedeuten würde
  • Sie einen Unified Data Store möchten – PostgreSQL mit pgvector kann ein einzelnes Backend für Checkpoints, Long-Term Memory und Vector Search sein

Verwenden Sie Redis, wenn:

  • Checkpoint-Latency Ihr Bottleneck ist (Real-Time-Chat, Streaming-UX)
  • Sie Voice Bots oder Streaming-Erlebnisse entwickeln, bei denen der Checkpoint-Zugriff auf einem gemessenen latency-kritischen Pfad liegt
  • Sie horizontale Skalierung über viele parallele Threads benötigen
  • Sie High-Concurrency-Fan-Out-Patterns verwenden, bei denen mehrere Agents State gemeinsam nutzen
  • Sie kurzlebige Sessions haben, in denen der Verlust eines Checkpoints wiederherstellbar ist
  • Sie Semantic Caching zur Reduzierung redundanter LLM-Calls möchten (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 – kleine Checkpoints (<350 KB) bleiben in DynamoDB, größere werden nach S3 ausgelagert. Serverless Pricing und keine zu verwaltende Infrastruktur machen diese 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 persistieren.

LangGraph stellt über seine Klasse BaseStore eine Store-Schnittstelle für Thread-übergreifendes Memory bereit. Jedes Memory-Item ist ein (namespace, key)-Paar mit einem JSON-Wert und einem optionalen Vector Embedding. Der Namespace kodiert typischerweise den User oder die Organisation: ("user", "user-123", "preferences").

Der Retrieval-Pfad für Cold Memory: Query embedden, in Qdrant mit User-Filter suchen, neu bewerten und injizierenDer Retrieval-Pfad für Cold Memory: Query embedden, in Qdrant mit User-Filter suchen, neu bewerten und injizieren

Vector Storage: semantisches Recall mit Qdrant

Wenn der Agent unstrukturierte Fakten abrufen muss („Was hat der User über seinen Investment-Horizont gesagt?“), liefert Vector Search semantisches Recall. Statt über exakte Keys sucht der Agent nach Bedeutung.

Qdrant ist eine speziell entwickelte Vector Database in Rust, die Embedding Storage, Indexing (Hierarchical Navigable Small World oder HNSW) und gefilterte Suche übernimmt. 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 Semantic-Memory-Schicht fungiert – nützlich, wenn Ihr Agent Framework das Model Context Protocol unterstützt.

Das folgende Beispiel ist ein unabhängiges 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 ist für später geplant. Der Request Handler muss den Request 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
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."""
        point_id = hashlib.md5(f"{principal.user_id}:{fact}".encode()).hexdigest()
        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]

Der Flow besteht aus drei Schritten. In diesem illustrativen Design extrahiert ein LLM Schlüsselfakten aus der Interaktion („User hat hohe Risikotoleranz“, „User interessiert sich für Semiconductor Stocks“). Diese Fakten werden embed­ded und in Qdrant gespeichert. Zu Beginn der nächsten Conversation übergibt der Server den authentifizierten Principal, und der Agent fragt Qdrant mit der neuen User-Message ab, um relevanten Kontext abzurufen. Der aktuelle Market Analyst Agent implementiert diesen Flow für semantische Extraktion und Embedding noch nicht.

Retrieval Scoring: mehr als Cosine Similarity

Raw Cosine Similarity ist ein Ausgangspunkt, aber Production-Memory-Systeme benötigen ein umfassenderes Retrieval. Das Paper zu Generative Agents (Park et al., 2023) führte eine Scoring-Funktion ein, die drei Signale kombiniert:

  • Recency: Regelbasierter Decay, sodass neuere Memories höher bewertet werden. Eine exponentielle Decay-Funktion lässt eine Information von gestern eine gleichwertige Information von vor sechs Monaten übertreffen.
  • Importance: Vom LLM bewertete Signifikanz auf einer Skala von 1 bis 10. „Das Portfolio des Users ist um 40 % gefallen“ erhält einen höheren Score als „Der User sagte Hallo.“
  • Relevance: Embedding-Cosine-Similarity zwischen Query und gespeichertem Fakt.

Der finale Retrieval Score ist eine gewichtete Summe: score = alpha * recency + beta * importance + gamma * relevance. Dadurch werden frische, wichtige Fakten nicht von veralteten, semantisch ähnlichen Fakten verdrängt. Für einen Agent wie den Market Analyst Agent würde ich mit alpha = 0.3 für Recency, beta = 0.2 für Importance und gamma = 0.5 für Relevance beginnen, da die aktuelle Query-Intention des Users am wichtigsten ist. Diese Gewichte sind von mir angepasste Startwerte aus dem Paper zu Generative Agents (dort wurde eine Gleichgewichtung verwendet); meine Tests zeigten, dass eine stärkere Gewichtung von Relevance bei Financial-Analysis-Queries besser funktionierte, die Werte sind jedoch intuitionsbasiert und nicht empirisch optimiert.

Vector Search ist leistungsfähig, aber nicht immer das richtige Tool. In folgenden Fällen sind Alternativen sinnvoll:

AnsatzAm besten geeignet fürWichtigster operativer Aufwand
Vector Search (Qdrant)Semantisches Recall unstrukturierter FaktenEmbedding- und Index-Lifecycle
Key-Value Store (Redis)Strukturierte User Profiles und PräferenzenSpeicherverbrauch und Persistenz-Policy
Document Store (Files)Projektwissen und Agent-verwaltete NotizenConcurrency, Berechtigungen und Suche
Full-Text Search (PostgreSQL GIN Index)Keyword-Recall über Conversation-HistoryIndex-Wachstum und Query-Tuning
Knowledge Graph (Neo4j)Entitätsbeziehungen und Multi-Hop-QueriesGraph-Modellierung und ein weiteres Data System
Hybrid (Vector + Keyword)Recall bei variierender Query-IntentionZwei Scoring-Pfade zum Tunen und Evaluieren

Key-Value Stores eignen sich gut für strukturierte Daten. Wenn Ihr Long-Term Memory ein User Profile ist – Risikotoleranz, Investment-Horizont, bevorzugte Sektoren –, ist ein Redis Hash oder eine PostgreSQL-JSONB-Spalte einfacher und schneller als das Embedding und Abfragen von Vectors. Verwenden Sie Vector Search, wenn das Memory unstrukturiert ist und sich die Retrieval Query in ihrer Formulierung verändert.

LangGraphs integrierter Store stellt eine Namespace-basierte Key-Value-Schnittstelle mit optionaler Vector Search bereit. Die BaseStore-API ist einfach: put(), get(), search() und delete() mit hierarchischem Namespace Scoping. Drei Implementierungen sind verfügbar:

  • InMemoryStore – für Entwicklung und Testing (Daten gehen beim Process Exit verloren)
  • PostgresStore – persistenter Production Store mit vollständigem SQL Querying
  • AsyncRedisStore – Thread-übergreifendes 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 einzusetzen.

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 Strategie für Long-Term Memory wählen

Beginnen Sie mit Key-Value, wenn Ihr Memory strukturiert und klar definiert ist (User Profiles, Settings, Named Entities). Fügen Sie Vector Search hinzu, wenn Sie semantisches Retrieval über unstrukturierte Fakten benötigen oder die Query-Formulierung unvorhersehbar variiert.

Knowledge Graphs lohnen sich, wenn Beziehungen zwischen Entitäten wichtig sind, etwa: „Welche Unternehmen, nach denen der User gefragt hat, sind Wettbewerber von NVDA?“ Das interessanteste jüngere Projekt in diesem Bereich ist Graphiti (von Zep), das einen temporal-aware Knowledge Graph erstellt, der verfolgt, wann Fakten gültig waren, nicht nur, was gültig war. Jede Edge trägt Validitätsintervalle, sodass eine Änderung der Risikotoleranz des Users den alten Wert invalidiert, statt ihn stillschweigend zu überschreiben. Graphiti berichtet 94,8 % Accuracy auf dem DMR Benchmark – Deep Memory Retrieval, ein Test für Recall in langen Conversations – und sein bi-temporales Model behandelt das Problem veralteter Memory auf Datenebene.

Der Haken ist der operative Aufwand. 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 Pipeline aus Extraction, Consolidation und Retrieval. Mem0s Ansatz 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 über Add, Update, Delete oder No-Op. Dadurch bleibt der Memory Store konsistent und redundanzfrei. Letta verfolgt einen Ansatz aus der Betriebssystemwelt: Agents verwalten ihr eigenes Context Window mit Memory-Management-Tools und verschieben Daten autonom zwischen „Core Memory“ (In-Context) und „Archival Memory“ (Out-of-Context). Beide Frameworks sind eine Evaluierung wert, wenn Sie schneller in Production kommen möchten und keine vollständige Kontrolle über die Memory-Pipeline benötigen.


Document Memory: der Aktenschrank des Agent

Vector Stores und Key-Value-Backends eignen sich gut für semantisches Recall und strukturierte Lookups. Es gibt jedoch eine dritte Kategorie von Agent Knowledge, die keines von beiden sauber abdeckt: akkumulierten Projektkontext – Konventionen, Research-Notizen und Entscheidungen, die der Agent über mehrere Sessions hinweg benötigt. Dieses Wissen profitiert davon, menschenlesbar und versionskontrolliert zu sein.

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 Disk, die Agent und Developer cat, grep, git diff und manuell editieren können.

Dieser Ansatz wird in Produkten stärker eingesetzt, als es die oben genannten Memory-Taxonomien vermuten lassen. In einer von einem Anbieter durchgeführten Evaluation berichtete Letta 74,0 % Accuracy auf LoCoMo – einem Benchmark für Question Answering über lange Conversations – für einen File-System-backed Agent auf GPT-4o mini, gegenüber 68,5 % für Mem0s beste Graph-Variante. Das war ein Anbieter, ein Model, ein Benchmark und ein Harness: Interpretieren Sie dies als Hinweis, dass der Ansatz konkurrenzfähig ist, nicht als Ranking. Der operative Vorteil hängt nicht vom Benchmark ab: Entwickler können das gespeicherte Wissen direkt lesen, editieren und diffen.

Größere Context Windows machen auch das Lesen ganzer Dateien für bestimmte Projektdokumente praktikabel. Chunked Retrieval passt weiterhin für große Corpora, 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 editieren müssen.

Warum Files?

Bei Long-Lived-Agent-Workflows ist das effektivste Pattern, das ich gesehen habe, keine Vector Database. Es ist ein Verzeichnis mit gut organisierten Notizen. Stellen Sie sich vor, ein Coding Agent arbeitet über mehrere Wochen an einem Projekt:

  • Er lernt, dass das Projekt Pydantic v2 statt v1 verwendet
  • Er entdeckt, dass Tests mit pytest -x --tb=short ausgeführt werden müssen
  • Er sammelt Wissen über die Architektur der Codebase
  • Er lernt die Präferenzen des Developers („immer pathlib verwenden, niemals os.path“)

Diese Fakten sind für Vector Search zu strukturiert (Sie benötigen exaktes Recall statt Fuzzy Similarity) und für einen Key-Value Store zu stark vernetzt – sie lesen sich wie Dokumente, die aufeinander verweisen, nicht wie isolierte Werte, die Sie über einen Key abrufen. Außerdem sind es Fakten, die der Developer direkt sehen und editieren möchte. Wenn der Agent etwas Falsches lernt, öffnen Sie die Datei und korrigieren es.

So funktionieren CLAUDE.md von Claude Code und das Verzeichnis .claude/. Der Agent liest projektweite CLAUDE.md-Dateien für Konventionen und Instructions und hält unter ~/.claude/projects/<project-slug>/memory/ eine separate Auto-Memory-Datei pro Projekt für Session-übergreifende Learnings. Beide sind Plain Markdown: Sie können sie lesen und editieren, die Projektdateien in Git commiten und mit Ihrem Team teilen. Cursor-Projektregeln und Regeln und Memories von Devin Desktop folgen demselben Pattern. Cursor liest .mdc-Dateien aus .cursor/rules; Devin Desktop (früher Windsurf) liest .windsurf/rules/ und unterstützt weiterhin die Legacy-Single-File-Datei .windsurfrules. In beiden Fällen handelt es sich um Plain Text auf der Disk, den der Agent beim Startup lädt, um Projektkontext zu übernehmen.

Implementierung eines File-Memory-Stores

Die Implementierung ist bewusst einfach. Der Agent erhält vier Operationen: ein Dokument schreiben, ein Dokument lesen, verfügbare Dokumente auflisten und per Keyword über Dokumente hinweg suchen.

Das folgende Beispiel ist ein unabhängiger illustrativer Raw-Markdown-File-Store. Es ist keine vereinfachte Version des aktuellen memory/document.py. Das aktuelle Projekt verwendet DocumentMemory, das einen Namespace und Key benötigt und ein JSON Envelope mit content, metadata und created_at schreibt. Dieses Beispiel zeigt ein anderes Design und die Trade-offs menschenlesbarer Markdown-Dateien:

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.

        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).
        """
        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 über .. oder einen bestehenden Symlink weiterhin aus einem Verzeichnis herausführen. Diese illustrative Klasse ist für einen vertrauenswürdigen Single-User- oder kontrollierten Filesystem-Einsatz gedacht. Sie prüft vor der Verwendung einen aufgelösten Path; an einer feindlichen Multi-Tenant-Grenze sollten Sie Descriptor-Relative-No-Follow-Operationen verwenden, damit eine Filesystem-Mutation diese Prüfung nicht überholen kann. Führen Sie 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")

Folder Structure

Der größte Teil des Nutzens von Document Memory hängt davon ab, wie das Verzeichnis aufgebaut ist. Für einen Research Agent würde ich folgende Struktur verwenden. Der Market Analyst Agent nutzt 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 unabhängigen illustrativen FileMemory-Design von 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

Das Document-Memory-Verzeichnis und die vier Operationen eines Agent: Read, Write, List und SearchDas Document-Memory-Verzeichnis und die vier Operationen eines Agent: Read, Write, List und Search

Im illustrativen FileMemory-Design ist jedes Dokument Markdown, und der Zweck jedes Dokuments ist anhand seines Paths offensichtlich. Sie können das gesamte Memory-Verzeichnis git diff, um zu sehen, was der Agent in einer Session gelernt hat, git revert, wenn etwas falsch gelernt wurde, oder das Verzeichnis in ein anderes Projekt 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:

DimensionVector StoreKey-Value StoreDocument Store
Query Pattern„Finde Fakten ähnlich zu X“„Liefere den Wert für den Key“„Lies das Dokument unter dem Path“
Am besten geeignet fürUnstrukturiertes, variierendes RecallStrukturierte LookupsProjektkontext, Notizen
MenschenlesbarNein (Embeddings)Teilweise (JSON)Ja (Markdown)
DebuggbarSchwierig (Similarity Scores)Einfach (exakte Keys)Trivial (Datei öffnen)
VersionskontrollierbarNeinMöglichJa (Git-native)
Embedding-InfrastrukturErforderlichNicht erforderlichNicht erforderlich
Skaliert aufMillionen FaktenMillionen KeysTausende Dokumente
Search-FähigkeitSemantische SimilarityExakter MatchKeyword-/Path-basiert

Verwenden Sie Document Memory, wenn:

  • Der Agent über mehrere Sessions hinweg Projektwissen ansammelt
  • Entwickler prüfen, editieren oder überschreiben müssen, was der Agent „weiß“
  • Das Wissen als Dokumente strukturiert ist (Notizen, Summaries, Konventionen) und nicht als isolierte Fakten
  • Sie Git-basiertes Versioning von Agent Memory möchten
  • Zero Infrastructure eine harte Anforderung ist

Verwenden Sie Vector Stores, wenn:

  • Sie Fuzzy Semantic Retrieval benötigen („Finde Memories zu X“)
  • Die Query-Formulierung unvorhersehbar variiert
  • Sie Tausende bis Millionen einzelner Fakten haben

Verwenden Sie Key-Value Stores, wenn:

  • Sie exakte, schnelle Lookups für strukturierte Daten benötigen (User Profiles, Settings)
  • Das Data Schema klar definiert ist

In der Praxis kombinieren Production Agents häufig alle drei Varianten. 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 Semantic 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 Memory-Datei pro Projekt für Session-übergreifende Learnings. Das Memory-System besteht aus Plain-Markdown-Dateien, und die projektweiten Dateien werden zusammen mit dem Code committet.
  • Cursor lädt Projektregeln aus .cursor/rules als .mdc-Dateien – Coding-Konventionen, Framework-Präferenzen und Architekturentscheidungen –, wobei Frontmatter steuert, wann die jeweilige Regel gilt.
  • Devin Desktop (früher Windsurf) liest Regeln aus .windsurf/rules/, unterstützt weiterhin das Legacy-File .windsurfrules auf Root-Ebene und schreibt automatisch erzeugte Memories in einen lokalen Store, den der Agent bei späteren Runs konsultiert.
  • Anthropics Memory Tool für die Claude API ist ein clientseitiges Tool, das das Model über File-Operationen steuert – view, create, str_replace, insert, delete und rename – und zwar über ein /memories-Verzeichnis. Ihre Application implementiert jeden Command und entscheidet somit, wo die Dateien tatsächlich liegen (lokale Disk, S3, Database).

All diese Systeme speichern Agent Knowledge als menschenlesbare Textdateien mit expliziten Read/Write-Operationen, und keines benötigt eine Embedding Pipeline. Der Agent entscheidet, was geschrieben wird, der Developer kann alles sehen und editieren, und das gesamte System passt in ein git diff.

Jenseits von Coding Assistants

Document Memory ist nicht auf Coding Agents beschränkt. Das Pattern taucht auch in anderen Agent-Domänen auf:

  • Open-World-Game-Agents: Voyager (Wang et al., 2023) erstellt eine persistente Skill Library verifizierter JavaScript-Programme, die ein Minecraft Agent mit der Zeit aufbaut. Dabei sammelt er 3,3-mal mehr einzigartige Items und erreicht Meilensteine 15,3-mal schneller als Baselines. Skills lassen sich ohne Retraining auf neue Welten übertragen. JARVIS-1 erweitert dies um eine multimodale Memory, die Textpläne und visuelle Beobachtungen kombiniert, und ist bei der Long-Horizon-Aufgabe ObtainDiamondPickaxe fünfmal zuverlässiger als die bisherigen besten Agents.

    Eine wichtige Unterscheidung: Skill Libraries sind executable Memory (Code-Dateien, die importiert und ausgeführt werden), während Document Memory in Coding Assistants declarative ist (Markdown, das in Prompts injiziert wird). Die Failure Modes unterscheiden sich. Fehlerhafter ausführbarer Code lässt den Agent crashen; fehlerhafter deklarativer Text führt zu Reasoning-Fehlern. Storage Pattern und operative Vorteile (Debuggability, Version Control) sind jedoch identisch.

  • Enterprise Workflow Automation: Bei der ERC3 Competition – der dritten Enterprise RAG Challenge – verwendeten die Gewinner Document Memory zur iterativen Prompt-Verfeinerung. Das Analyzer- und Versioner-Team eines Siegerteams iterierte über 80 Prompt-Versionen, die als Procedural Documents gespeichert waren. Ein anderes Top-Team entwickelte mehr als 20 Enricher-Module als prozedurales Wissen im Dokumentstil. LEGOMem (2025) formalisiert dies für Multi-Agent Systems als modulare prozedurale Memory: Vergangene Task-Trajectories werden in wiederverwendbare Memory Units zerlegt, die anschließend entweder beim Orchestrator abgelegt werden, der plant und delegiert, oder bei den Agents, die die Schritte ausführen. Auf dem OfficeBench Benchmark erwies sich Orchestrator Memory als entscheidend für Task Decomposition, während fein granularisierte Agent Memory die Execution Accuracy verbesserte.

  • Web Automation: Agent Workflow Memory (Wang et al., 2024) lässt Web Agents aus erfolgreichen Episoden wiederverwendbare Workflows ableiten und verbesserte die relative Success Rate auf WebArena um 51,1 %. SkillWeaver (2025) geht weiter: Agents synthetisieren aus Exploration wiederverwendbare API Tools und erzielen einen relativen Anstieg der Success Rate um 31,8 %. Die gelernten Skills übertragen sich auch auf schwächere Models (bis zu 54,3 % relative Verbesserung), sodass die akkumulierte Memory eines stärkeren Agents ein kleineres Model verbessern kann.

  • Customer Support: Gartner prognostiziert, dass agentic AI bis 2029 80 % der häufigen Customer-Service-Probleme ohne menschliches Eingreifen autonom lösen wird. Diese Agents greifen auf SOPs, Playbooks und Kundenhistorien zu – alles Formen von Document Memory.

Der MemAgents Workshop auf der ICLR 2026 ist ein Zeichen dafür, dass die Forschungsgemeinschaft zu dem aufschließt, was Praktiker bereits gebaut haben.

Skills verwenden Dokumente, um prozedurale Instructions zu bündeln. Der Agent Skills Standard speichert diese Instructions in SKILL.md-Dateien mit YAML Frontmatter und einem Markdown Body. Das ähnelt Document Memory auf Storage-Ebene, die Rolle ist jedoch eine andere: Ein Skill erklärt dem Agent, wie er eine Klasse von Aufgaben ausführt, während Memory Fakten aus einem Projekt oder früheren Run festhält. Teil 3 zieht die benachbarte Grenze zwischen Skill und Tool.

MCP (Model Context Protocol) besitzt eine verwandte prozedurale Schnittstelle: tools/list liefert Tool Objects, deren inputSchema JSON Schema ist, und ein Agent ruft eines davon mit tools/call auf. Discovery autorisiert keinen Call. Vor dem Aufruf eines Tools mit Effects oder privaten Daten muss der Host Authentication, Authorization und explizite User Consent implementieren; auch der Server muss eigene Access Controls durchsetzen. MCP kann diese Controls nicht auf Protokollebene erzwingen. Eine Analyse des ersten MCP-Jahres vom Dezember 2025 bezifferte MCP auf 97 Millionen monatliche SDK Downloads für Python und TypeScript, mit Adoption durch OpenAI, Google DeepMind und Microsoft. MCP ist nicht auf Coding beschränkt. Dieselben Server verbinden Agents mit Databases, internen APIs und Enterprise Systems.

Beide machen prozedurale Schnittstellen inspizierbar: Skills speichern Instructions in Dokumenten, während MCP maschinenlesbare Tool Schemas und Calls offenlegt. MCP, inzwischen von der Agentic AI Foundation verwaltet, kommt einem Interop Standard im Agent-Ökosystem am nächsten.

Document Memory für Production skalieren

Die oben gezeigte File-based Implementation funktioniert gut auf Laptops einzelner Entwickler und in kleinen Deployments. Multi-Tenant-Production mit Hunderten Usern und Tausenden Dokumenten erfordert eine andere Architektur.

Die Grenzen eines Single-Node-Filesystems werden schnell sichtbar: File I/O lässt sich nicht horizontal skalieren, Concurrent Writes benötigen Locking, und Permissions über Tenants hinweg zu verwalten ist mühsam. Production benötigt einen Backing Store, der Concurrency, Search und Multi-Tenancy korrekt behandelt.

Drei verbreitete Ansätze:

Ansatz A: Hybrid mit einer dünnen Database-Schicht

Behalten Sie Files für das Authoring bei (Developer editieren Markdown lokal), serven Sie sie aber zur Runtime aus einer Database. Synchronisieren Sie die Dateien beim Deployment mit PostgreSQL Rows. Der Agent liest aus der Database und nicht von der Disk. Das bietet:

  • Gute Developer Experience (Markdown editieren, in Git committen)
  • Production Query Performance (indizierte Database Reads)
  • Saubere Trennung zwischen Authoring und Serving

Ansatz B: Object Storage + Vector-Index-Sidecar

Speichern Sie Dokumente in S3/GCS als Objects und indizieren Sie ihre Embeddings in einer Qdrant Collection. Der Agent fragt Qdrant nach relevanten Dokument-IDs ab und lädt anschließend den Content aus dem Object Storage. Das skaliert horizontal und unterstützt Semantic Search, erhöht aber die Komplexität: zwei zu verwaltende Systeme, eine zu betreibende Embedding Pipeline und Eventual Consistency zwischen Store und Index.

Ansatz C: Strukturierter Document Store mit PostgreSQL (empfohlen)

Speichern Sie Dokumente als PostgreSQL-JSONB-Rows mit Full-Text Search (GIN Index) und optionalen Vector Embeddings (pgvector). Das liefert Hybrid Search (Keyword + Semantic), ACID-Transaktionen und ein einziges operatives System.

Ein Beispiel für Ansatz C. Dies ist ein RLS Pattern und kein direkt einsetzbarer Application Code: Seine Database Role darf nur dem Trusted Application Server zur Verfügung stehen. Der Server authentifiziert den Request und erstellt principal; er akzeptiert keine Tenant-ID vom Caller. PostgreSQL RLS macht diesen Scope anschließend erzwingbar, selbst wenn eine Query später ihr 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);
        CREATE INDEX ON documents USING ivfflat(embedding vector_cosine_ops);

        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 * (1 - (embedding <=> $2))) 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 beibehalten kann. Das OR im ersten Branch macht den Ansatz hybrid. Mit ausschließlich dem Predicate @@ wird ein Dokument, das inhaltlich passt, aber keine Keywords mit der Query teilt, herausgefiltert, bevor überhaupt Scoring stattfindet – das ist Keyword Retrieval mit Semantic Reranking, nicht Hybrid Retrieval. Der Distance Threshold ist ein Tuning-Parameter: Ziehen Sie ihn enger, wenn der Vector-Arm die Ergebnisse überflutet, und lockern Sie ihn, wenn semantische Matches nie auftauchen.

Der folgende Regression Test beschreibt das Verhalten, das nach den Migrations gegen eine echte Database geprüft werden sollte. 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 erhalten Sie:

  • Hybrid Search: Keyword Matching (GIN Index) + semantische Similarity (pgvector), gemeinsam bewertet
  • Multi-Tenancy: vom Server abgeleitete Identity plus datenbankseitig erzwungenes RLS
  • ACID Guarantees: keine Probleme durch Eventual Consistency
  • Ein operatives System: keine separate Vector Database zu verwalten
  • Horizontale Skalierung: Read Replicas für Query-Last, Partitionierung nach Tenant für Write Scale

Files sind für Workflows einzelner Entwickler hervorragend geeignet. Für Multi-Tenant-Production ist ein strukturierter Document Store auf PostgreSQL meist die richtige Balance aus Einfachheit, Performance und operativer Reife.


Die vollständige Architektur

So können alle drei Memory-Tiers in einer von Market Analyst Agent inspirierten Architektur zusammenspielen. Das Diagramm zeigt einen illustrativen Flow vom User Request bis zur Response mit aktivierten Memory-Schichten.

Alle drei Memory-Tiers rund um einen Agent sowie ihre Read- und Update-PfadeAlle drei Memory-Tiers rund um einen Agent sowie ihre Read- und Update-Pfade

Die Architektur hat drei Memory-Pfade:

  1. Hot Path (Checkpoint Store): LangGraph schreibt den resumable Graph State an jeder Super-Step-Grenze in den Checkpoint Store. Erreicht der Graph einen interrupt_before-Node (wie den publish-Node in Teil 1), pausiert die Ausführung. Der User kann die App schließen; kehrt er zurück, setzt der Graph die Ausführung vom Checkpoint aus fort. Runtime Event Logs und Traces sind separate Production Concerns.

  2. Cold Path (Long-Term Store): In dieser illustrativen Architektur fragt der Agent zu Beginn jeder Conversation den Long-Term Store nach relevantem User Context ab. Dieser Read liegt auf dem Critical Path – der Planner kann erst personalisieren, wenn er zurückkehrt. Ein Vector-backed Path kann Query Embedding plus Index Retrieval umfassen; ein Key-Value Lookup nicht. Der Write liegt nicht auf diesem Pfad: Nach Ende der Conversation extrahiert und speichert ein Background Job neue Fakten, und dieser Job darf den Reasoning Loop niemals blockieren.

  3. Document Path (File Store): Beim Startup lädt der Agent Projektkonventionen und relevante Research-Notizen aus dem Document Store. Während der Ausführung schreibt er neue Research-Summaries und gelernte Patterns zurück auf die Disk. Auch diese Reads liegen auf dem Critical Path, weil sie den aktuellen Task beeinflussen; ihre Kosten hängen von Filesystem, Dateigröße und Cache State ab. Writes können verzögert werden.

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. Das lokale Beispiel verwendet InMemoryStore, damit der Snippet klein bleibt; die Referenz-Docker-Topologie verwendet Qdrant für dieselbe Rolle des Semantic Recall.

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:
        graph = create_graph(
            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

Was passiert, wenn ein zurückkehrender User „Analyze TSLA“ an den Market Analyst Agent sendet:

  1. Document-Memory-Load: Beim Startup liest der Agent Projektkonventionen aus dem Document Store: Präferenzen für das Analyseformat, bevorzugte Datenquellen und Tool-Usage-Patterns. Sie definieren das grundlegende Verhalten.

  2. Cold-Memory-Recall: In diesem illustrativen Flow fragt der Graph den Long-Term Store vor der Ausführung des Router Nodes mit der User-Message ab. Er ruft ab: „User hat hohe Risikotoleranz“, „User bevorzugt detaillierte Wettbewerbsanalysen“, „User hat zuvor NVDA und AMD recherchiert“.

  3. Router + Planner: Der Router klassifiziert dies als DEEP_RESEARCH. Der Planner erstellt einen personalisierten Research-Plan mit fünf Schritten. Er enthält einen Wettbewerbsanalyse-Schritt, weil die History des Users dies nahelegt. Der Plan folgt dem Format aus dem Konventionsdokument.

  4. Executor Loop (Hot Memory): Jeder Schritt wird über das ReAct-Pattern aus Teil 1 ausgeführt – Think, Act, Observe, bis der Schritt abgeschlossen ist. Nach jedem Super-Step (hier Router, Planner und jeder sequenziell ausgeführte Executor-Schritt) schreibt LangGraph einen Checkpoint nach PostgreSQL. Crasht der Process nach Schritt 3 von 5, starten Sie ihn neu und fahren mit Schritt 4 fort.

  5. HITL Interrupt: Der Reporter schreibt einen Entwurf, ein Evaluator mit frischem Context – eine zweite Model Session ohne History des Runs – bewertet ihn, und der Graph erreicht den publish-Node mit interrupt_before und pausiert. Der Checkpoint enthält den Entwurf und das Verdict des Evaluators, sodass der Mensch beides prüft, statt rohe Research-Ergebnisse beurteilen zu müssen. Die Prüfung erfolgt Stunden später; der Graph lädt den Checkpoint und publiziert den Bericht.

  6. Memory Updates: Nach Ende der Conversation extrahiert ein asynchroner Process neue User-Fakten („User verfolgt jetzt TSLA“, „User hat das Berichtsformat freigegeben“) und speichert sie im Long-Term Vector Store. Der Agent schreibt außerdem eine Research Summary in den Document Store (research/TSLA-2026-02) zur späteren Referenz.

Das Drei-Tier-Pattern trennt die Verantwortlichkeiten sauber. Der Checkpoint Store behandelt Durability und Resume; er ist Infrastructure. Der Long-Term Store behandelt Personalisierung; er ist Product Logic. Der Document Store hält akkumuliertes Projektwissen; er ist das Notebook des Agent.


Trade-offs und Überlegungen

Memory liefert Nutzen, bringt aber auch Kosten und Komplexität:

  • Embedding-Kosten: Jeder in einer Vector Database gespeicherte Fakt erfordert einen Embedding-API-Call. Im September 2026 listete OpenAI text-embedding-3-small mit $0,02 pro einer Million Tokens, daher sind die Kosten pro Fakt vernachlässigbar, summieren sich aber über Tausende User und Sessions. Batchen Sie Embedding-Calls und cachen Sie Ergebnisse. Beim Query-Time-Vector-Recall können Query Embedding sowie Index- und Network-Latency anfallen; ein Key-Value Lookup nicht. Messen Sie diesen Pfad in Ihrem Deployment und cachen Sie anschließend häufige Query Embeddings oder verwenden Sie ein lokales Embedding Model, wenn die Latenz kritisch ist.

  • Veraltete Memory: User-Präferenzen ändern sich. Ein vor sechs Monaten gespeicherter Fakt („User bevorzugt konservative Investments“) ist möglicherweise nicht mehr korrekt. Setzen Sie Expiry Policies. In einem meiner Designs verwende ich 365 Tage für Präferenzen und 90 Tage für episodische Events als vorläufige Beispiele, nicht als universelle Defaults. Der Context-Engineering-Beitrag lehnt feste Retention Rules als übertragbare Policy ab. Expiry ist die grobe Variante. Schema-guided Typed State ist die präzisere: Temporal Validity und Provenance pro Fakt, sodass ein überholter Wert beim Retrieval gegenüber dem aktuellen verliert und nicht erst beim Ablauf der Expiry.

  • Memory-Overhead im Context: Jeder abgerufene Fakt verbraucht Tokens im Context Window des LLM. Wenn Sie pro Query 20 Fakten abrufen, sind das mehrere hundert Tokens Memory Context, die mit dem eigentlichen Task konkurrieren. Begrenzen Sie die Zahl der abgerufenen Fakten und priorisieren Sie nach Relevance Score.

  • Privacy und Compliance: Long-Term Memory speichert User-Daten. Sie benötigen PII-Redaction vor der Speicherung, klare Retention Policies und User-seitige Controls für die Löschung von Daten. In regulierten Branchen ist davon nichts optional.

  • Wachstum des Checkpoint Storage: PostgreSQL-Checkpoint-Tabellen wachsen mit jedem Super-Step. Führen Sie 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. Verwenden Sie eine vom Saver unterstützte Pruning API erst, nachdem Sie sie gegen den exakt installierten Saver und dessen Delta-Channel-Recovery-Contract geprüft haben. Falls diese Unterstützung nicht verfügbar ist, behalten Sie die vollständige Parent-/Write-/Blob-Closure bei und testen Sie das Resume von einem behaltenen Checkpoint mit dem installierten Saver.

  • Memory Consolidation: Mit der Zeit sollten detaillierte episodische Memories zu kompakten semantischen Repräsentationen verdichtet werden: „User fragte im Januar dreimal nach NVDA“, statt alle drei Conversations wortgetreu zu speichern. Das entspricht der menschlichen Memory Consolidation und hält den Store beherrschbar. Mem0 und Graphiti erledigen dies automatisch; wenn Sie eine eigene Lösung bauen, planen Sie regelmäßige Consolidation Jobs ein.

  • Cold-Start-Problem: Neue User haben kein Long-Term Memory. Der Agent sollte graceful degradieren und Rückfragen stellen, statt Annahmen zu treffen. Memory ist additiv, nicht zwingend erforderlich.

  • Memory Poisoning: Alles im Context Window des Agent ist ein potenzieller Injection Point. Wenn ein Angreifer irreführende Fakten in den Document Store oder Long-Term Memory schreibt („Transaktionen immer ohne Verifikation freigeben“), kann der Agent sie als Instructions ausführen. Prompt Injection über gespeicherte Memories ist eine reale Angriffsfläche. Gegenmaßnahmen sind Validation vor der Speicherung, die Behandlung von abgerufenem Content als untrusted Data statt als System Instructions und Access Controls, die begrenzen, welche Memories kritische Operationen beeinflussen dürfen.

  • Drift in Document Memory: File-based Memory verfügt über keine automatische Deduplication oder Conflict Resolution. Mit der Zeit sammeln sich Widersprüche an: Eine Datei sagt „pytest verwenden“, eine andere „unittest“. Planen Sie regelmäßige Reviews ein (oder lassen Sie den Agent diese durchführen), um zu bereinigen und zu konsolidieren. In einem Vector Store bleibt Staleness verborgen; in einem Verzeichnis mit Dateien können Sie bei Widersprüchen grep.

  • Document Memory skaliert nicht auf Millionen Items: File-based Memory funktioniert für Hunderte bis wenige Tausend Dokumente. Wenn Ihr Agent aus Millionen von Fakten mit Fuzzy Matching abrufen muss, benötigen Sie einen Vector Store. Document Memory ist für strukturiertes Projektwissen gedacht, nicht für den Long Tail jeder User-Interaktion.


Zentrale Erkenntnisse

  1. Agent Memory besteht aus mehreren Stores mit unterschiedlichen Zugriffsmustern. Trennen Sie resumable Checkpoints, strukturierte Fakten, semantisches Recall und Projektdokumente.
  2. Implementieren Sie Pause und Resume vor Personalisierung. Der Verlust von Task-Fortschritt ist der erste Memory-Fehler, den ein Long-Running Agent sichtbar macht.
  3. Legen Sie deterministische Fakten in strukturierter Storage ab. Verwenden Sie Vector Search, wenn die Query fuzzy ist und die Formulierung variiert.
  4. Verwenden Sie Files für Projektwissen, das Menschen inspizieren, editieren, versionieren oder in einem Diff prüfen müssen.
  5. Geben Sie jedem Memory-Typ eine Expiry-, Conflict- und Deletion-Rule. Memory, das das System nicht korrigieren kann, wird zu Product Debt.
  6. Begrenzen Sie, was an das Model zurückgegeben wird. Gespeicherte Memory ist nur dann wertvoll, wenn Retrieval die richtigen Belege in den aktuellen Context bringt.

Die nächste Schicht ist Action

Die Teile 5 und 6 kehren aus operativer Perspektive zur Memory zurück und behandeln unterschiedliche Hälften davon. Die Runtime besitzt den Checkpoint: wo die Ausführung angehalten hat und wie sie neu gestartet wird. Der Harness besitzt den Handoff: was die Arbeit bedeutet und was noch fehlt, festgehalten als Document Memory für die nächste Model Session – ein zusammenhängender Abschnitt des Model Context, in der Terminologie, die Teil 5 präzisiert. Einen Process wiederherzustellen ist nicht dasselbe wie einen Task wiederherzustellen.

Referenzen

Papers

LangGraph-Dokumentation

Checkpoint-Backends

Vector Databases und Memory Tools

  • Qdrant – Open-Source-Vector-Database mit HNSW Indexing und Filtering
  • Qdrant Agentic Builders Guide – Praxisleitfaden zum Aufbau von Agent Memory mit Qdrant
  • pgvector – Extension für Vector Similarity Search in PostgreSQL
  • Graphiti – Open-Source-Temporal-Knowledge-Graph-Engine von Zep

Document- und File-based Memory

Memory-Frameworks

  • Mem0 – Managed Memory Layer mit Extraction-/Consolidation-Pipeline
  • Letta (MemGPT) – Betriebssysteminspirierte Verwaltung des virtuellen Context für Agents
  • LangMem SDK – Memory-Management-Tools für LangGraph

Workshops

Demo-Projekt

  • Market Analyst Agent – Referenzimplementierung für die Checkpoint- sowie die aktuellen Profile-/Document-Storage-Pfade