Automatische vertaling
Dit artikel is automatisch vertaald vanuit de oorspronkelijke Engelse versie.
AI-agentgeheugenarchitectuur in 2026: checkpoints, vector stores en bestandsgebaseerd geheugen
Deel 2 van de serie Engineering the Agentic Stack
State is wat een chatbot onderscheidt van een AI-agent. Zonder agentgeheugen begint elke interactie vanaf nul. De agent kan niet pauzeren en hervatten, leren van eerdere sessies of personaliseren. Deel 1 behandelde redeneerlussen van AI-agents: hoe een agent denkt. Deze post gaat over de geheugenarchitectuur die bepaalt wat hij onthoudt.
Ik loop door de geheugenarchitectuur van de Market Analyst Agent, en laat zien hoe hot checkpoints, cold vector stores en bestandsgebaseerd documentgeheugen samenwerken voor langlopende agents. Daarna bespreek ik wanneer PostgreSQL, Redis, Qdrant, key-value stores en gewone Markdown-bestanden elk logisch zijn.
TL;DR: AI-agentgeheugen splitst in drie lagen. Hot memory is checkpoint-state op threadniveau in Redis of PostgreSQL voor pauzeren/hervatten. Cold memory is kennis over sessies heen in een vector store of key-value-backend voor personalisatie. Document memory bestaat uit leesbare bestanden die de agent leest en schrijft voor persistente projectkennis. Het checkpointsysteem van LangGraph handelt de hot layer native af. Voor cold memory geeft vector search met Qdrant je semantische recall; een key-value store is genoeg voor gestructureerde feiten. Voor document memory geeft een bestandsgebaseerde store je debugbaarheid en geen embedding-infrastructuur.
Waarom AI-agentgeheugenarchitectuur ertoe doet
Een stateless agent is luxe autocomplete. Hij neemt een request aan, retourneert een response en vergeet alles. Dat werkt voor single-turn Q&A. Het breekt zodra je het volgende nodig hebt:
- Pauzeren en hervatten: een gebruiker start een researchtaak, klapt zijn laptop dicht en komt morgen terug. Zonder checkpointed state start de agent opnieuw vanaf nul.
- Coherentie over meerdere turns: in een lang gesprek moet de agent onthouden welke tools hij heeft aangeroepen, welke data hij heeft verzameld en welke planstappen hij heeft afgerond.
- Personalisatie: een terugkerende gebruiker verwacht dat de agent zijn risicotolerantie, gewenste analysediepte en eerdere interacties kent.
- Human-in-the-loop (HITL): de agent maakt een rapportdraft en wacht op goedkeuring. Die "waiting"-state moet process restarts overleven.
Neem de Market Analyst Agent uit Deel 1. Een gebruiker vraagt "Analyze NVDA". De agent bouwt een plan, roept vijf tools aan, verzamelt data en schrijft een rapportdraft. De gebruiker antwoordt "looks good, but add competitor analysis." Zonder checkpointed state heeft de agent geen idee waar "looks good" naar verwijst en moet hij opnieuw beginnen. Met een checkpoint store laadt hij de state van de laatste node, inclusief het verzamelde onderzoek, en voegt hij alleen een competitor-stap toe.
Stel je nu voor dat dezelfde gebruiker een week later terugkomt: "Update my NVDA analysis." Zonder langetermijngeheugen weet de agent niet dat deze gebruiker de voorkeur geeft aan conservatieve risicobeoordelingen of geïnteresseerd is in semiconductor-aandelen. Met een vector-backed memory store haalt hij die feiten op en personaliseert hij de response zonder ze opnieuw te hoeven vragen.
LangGraph splitst dit netjes op. Elke graph-executie draait binnen een thread — één gesprek of taak. State binnen een thread is working memory. State die over threads heen blijft bestaan is long-term memory. De engineeringvraag is welke storage-backend je voor elk gebruikt.
Een taxonomie van AI-agentgeheugen
Voordat we naar de implementatie gaan, helpt het om te classificeren wat agents moeten onthouden. Het CoALA-framework (Sumers, Yao et al., 2023) is de standaardtaxonomie en leunt op de cognitieve wetenschap. Ik introduceerde memory scoping al in mijn post over context engineering; hier breid ik dat uit naar zes categorieën:
| Geheugentype | Scope | Levensduur | Voorbeeld | Storagepatroon |
|---|---|---|---|---|
| Working | Huidige stap | Milliseconden | Tool-callargumenten, huidige LLM-response | In-process (Python dict) |
| Short-term | Huidige thread | Minuten–uren | Gespreksgeschiedenis, voortgang van plan, verzamelde data | Checkpoint store |
| Episodic | Cross-thread | Dagen–maanden | "Vorige week vroeg de gebruiker naar NVDA earnings" | Vector store / KV store |
| Semantic | Cross-thread | Maanden–permanent | "Gebruiker geeft de voorkeur aan conservatieve investeringen" | Vector store / KV store |
| Document | Cross-thread | Dagen–permanent | Projectnotities, researchsamenvattingen, geleerde patronen | File store (Markdown/JSON) |
| Procedural | Systeembreed | Permanent | "Controleer bij aandelenanalyse altijd SEC filings" | Config / system prompt |
Working memory is waar het LLM op dit moment actief mee redeneert: Python-variabelen in de huidige functie, de inhoud van het context window, tool-callargumenten midden in de uitvoering. Het is de snelste en meest efemere laag. Niets blijft bestaan buiten de huidige stap. Working memory wordt begrensd door het context window van het model, en dat maakt het de echte bottleneck. Alles wat de agent op beslismomenten "weet" moet hierin passen, of het nu uit de checkpoint store, een vector query of een file read komt. De andere lagen bestaan om op het juiste moment de juiste informatie in working memory te krijgen.
Short-term memory is de checkpoint store waar LangGraph na elke node-executie naartoe schrijft. Episodic en semantic geheugen zijn langetermijnstores die over threads heen blijven bestaan. Document memory is gestructureerde kennis die de agent in de tijd opbouwt — projectnotities, researchsamenvattingen, geleerde conventies — opgeslagen in leesbare bestanden die zowel de agent als de developer kunnen inspecteren en bewerken. Procedural memory is gecodeerd in de system prompt en tooldefinities; dat verandert niet per gebruiker.
In de praktijk stort dit in tot drie lagen: hot memory (working + short-term) voor de huidige sessie, cold memory (episodic + semantic) voor recall over sessies heen, en document memory voor opgebouwde projectkennis die baat heeft bij leesbaarheid en directe bewerkbaarheid.
De meeste frameworks en surveys focussen op de hot/cold-splitsing en slaan document memory over, ondanks hoe breed het wordt toegepast. Het CoALA-framework classificeert geheugen als working, episodic, semantic en procedural; geen woord over bestanden. De survey Memory in the Age of AI Agents behandelt vector stores en knowledge graphs maar geen documentbestanden. De documentatie van LangGraph behandelt checkpoints en de Store-interface, maar heeft geen native concept van bestandsgebaseerd geheugen.
In de praktijk is document memory nu standaard in AI-coding assistants. Claude Code, Cursor, Windsurf en Devin behandelen bestandsgebaseerd geheugen allemaal als een core feature. En het patroon verspreidt zich buiten coding: open-world game agents slaan herbruikbare skills op als codebibliotheken (Voyager), winnende enterprise-agents in competities itereren over procedurele promptdocumenten over runs heen (ECR3 winning approaches), en webautomation-agents synthetiseren herbruikbare workflow-API's uit succesvolle episodes (Agent Workflow Memory). De voordelen — debugbaarheid, version control, geen infrastructuur — zijn niet specifiek voor coding.
Een nuttige observatie uit dezelfde survey: agentgeheugen is niet hetzelfde als RAG of context engineering. Het onderscheidende kenmerk is dat de agent zelf de read/write-operaties uitvoert. Hij beslist wat hij onthoudt en wat hij vergeet, in plaats van te vertrouwen op een vaste retrieval-pipeline.
De paper Generative Agents (Park et al., 2023) liet zien hoe ver dit kan gaan: gesimuleerde agents die hun eigen herinneringen opsloegen, reflecteerden en ophaalden, vertoonden opvallend mensachtig gedrag. Het architectuurpatroon — een memory stream met retrieval gescoord op recency, importance en relevance — is nog steeds het sjabloon waarop de meeste moderne agentgeheugensystemen bouwen.
Short-term agentgeheugen: de checkpoint store
Elke keer dat een LangGraph-node uitvoert, serialiseert het framework de volledige graph-state en schrijft die naar een checkpoint store. Dat is de basis voor pauzeren/hervatten, time-travel debugging en HITL-workflows.
Een checkpoint bevat de graph-state die nodig is om te hervatten: de AgentState uit Deel 1 (messages, identity, user profile, plan steps, research data, execution mode), plus LangGraph-metadata zoals welke node het produceerde en een monotonisch oplopend volgnummer. Wanneer de graph hervat — na een HITL-interrupt of een process restart — laadt hij het nieuwste checkpoint en gaat hij precies verder waar hij stopte. Een checkpoint is niet hetzelfde als een append-only event log of trace; Deel 5 scheidt die runtime-observability-oppervlakken expliciet.
Hoe LangGraph-checkpointing werkt
De BaseCheckpointSaver van LangGraph is een eenvoudige interface: put() schrijft een checkpoint, get_tuple() leest de nieuwste voor een thread, list() retourneert de geschiedenis. Elk checkpoint is gesleuteld op (thread_id, checkpoint_ns, checkpoint_id), waarbij thread_id het gesprek identificeert, checkpoint_ns subgraph-namespacing afhandelt en checkpoint_id een unieke versie is.
De beslissing die ertoe doet is welke backend je erachter zet. LangGraph levert twee productieopties: PostgreSQL en Redis.
PostgreSQL vs Redis
| Dimensie | PostgreSQL (langgraph-checkpoint-postgres) |
Redis (langgraph-checkpoint-redis) |
|---|---|---|
| Read-latency | ~0.65ms | ~0.095ms |
| Write-latency | ~2ms (unlogged) tot 10ms (met WAL) | ~0.095ms |
| Throughput | ~15K txn/s | ~893K req/s |
| Duurzaamheid | Volledige ACID, WAL + replicatie | Configureerbaar (AOF/RDB), risico op dataverlies |
| Checkpoint history | Volledige geschiedenis (resume, time-travel debugging) | Configureerbare retentie via maxcount |
| Operationele kosten | Gemiddeld (standaard RDBMS-operations) | Hoger (RAM-bound, geheugenbeheer) |
| Schaalpatroon | Verticaal + read replicas | Horizontaal (Redis Cluster) |
| Beste voor | Duurzaamheid-eerst voor resume en debugging | Lage latency, hoge throughput, real-time |
Latencybenchmarks uit vergelijkingen van CyberTec en RisingWave.
PostgreSQL: de duurzame standaard
PostgreSQL is voor de meeste teams de veiligste standaard. Checkpoints overleven crashes, je krijgt volledige transactiesemantiek en de checkpoint history maakt time-travel debugging eenvoudig.
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
async def create_postgres_checkpointer(connection_string: str) -> AsyncPostgresSaver:
"""Create a PostgreSQL-backed checkpoint store.
PostgreSQL gives us ACID guarantees — if a checkpoint write succeeds,
the state is durable even if the process crashes immediately after.
"""
checkpointer = AsyncPostgresSaver.from_conn_string(connection_string)
# Create the checkpoint tables if they don't exist.
# This is idempotent — safe to call on every startup.
await checkpointer.setup()
return checkpointer
# Usage: wire into the graph compilation
checkpointer = await create_postgres_checkpointer(
"postgresql://user:pass@localhost:5432/agent_memory"
)
graph = create_graph(checkpointer=checkpointer)
# Every invoke/stream call now persists state automatically
config = {"configurable": {"thread_id": "user-123-session-1"}}
result = await graph.ainvoke({"messages": [HumanMessage(content="Analyze NVDA")]}, config)
# Resume later — loads the latest checkpoint for this thread
result = await graph.ainvoke({"messages": [HumanMessage(content="approved")]}, config)
De AsyncPostgresSaver gebruikt het pakket langgraph-checkpoint-postgres, dat drie tabellen aanmaakt: checkpoints (de geserialiseerde state), checkpoint_blobs (grote binaire data) en checkpoint_writes (pending writes voor crash recovery). Het schema ondersteunt gelijktijdige toegang en gebruikt advisory locks om write-conflicten te voorkomen.
Redis: wanneer latency de bottleneck is
Wanneer sub-milliseconde checkpointlatency ertoe doet (real-time conversational agents, tool-lussen met hoge frequentie), is Redis de betere keuze.
from langgraph.checkpoint.redis.aio import AsyncRedisSaver
async def create_redis_checkpointer(redis_url: str) -> AsyncRedisSaver:
"""Create a Redis-backed checkpoint store.
Redis stores checkpoints in memory for sub-millisecond access.
Trade-off: less durable than PostgreSQL unless AOF is enabled.
"""
checkpointer = AsyncRedisSaver.from_conn_string(redis_url)
# Initialize Redis data structures
await checkpointer.setup()
return checkpointer
# Usage: same graph API, different backend
checkpointer = await create_redis_checkpointer("redis://localhost:6379")
graph = create_graph(checkpointer=checkpointer)
De AsyncRedisSaver uit langgraph-checkpoint-redis slaat checkpoints op als JSON-documenten, gesleuteld op thread-ID. De v0.1.0-redesign verving meerdere search-operaties door één enkele JSON.GET-call, wat de latency aanzienlijk verlaagde. Redis 8.0+ bevat RedisJSON en RediSearch standaard — geen extra modules om te installeren.
Voor deployments met weinig geheugen slaat ShallowRedisSaver alleen het nieuwste checkpoint per thread op — geen history, maar minimaal RAM-gebruik. Gebruik dit wanneer je pauzeren/hervatten nodig hebt maar geen time-travel debugging.
Wanneer gebruik je welke
Gebruik PostgreSQL wanneer:
- Je volledige checkpoint history nodig hebt voor time-travel debugging of reproduceerbare resume
- Duurzaamheid niet onderhandelbaar is (financiële dienstverlening, healthcare)
- Je al PostgreSQL in je stack draait
- Je agent langlopende taken uitvoert waarbij stateverlies uren aan herberekening betekent
- Je een unified data store wilt — PostgreSQL met pgvector kan dienen als één backend voor checkpoints, langetermijngeheugen en vector search, wat je infrastructuur vereenvoudigt
Gebruik Redis wanneer:
- Checkpointlatency je bottleneck is (real-time chat, streaming UX)
- Je voice bots bouwt — STT-to-LLM-to-TTS-pipelines hebben sub-milliseconde state access nodig
- Je horizontaal wilt schalen over veel gelijktijdige threads
- Je high-concurrency fan-out-patronen hebt waarbij meerdere agents state delen
- Je kortlevende sessies hebt waarbij verlies van een checkpoint herstelbaar is
- Je semantische caching wilt om redundante LLM-calls te verminderen (Redis LangCache cachet semantisch vergelijkbare queries om herhaalde LLM-calls te vermijden)
Andere opties: langgraph-checkpoint-sqlite werkt voor lokale development en single-process-deployments. Voor AWS-native stacks levert langgraph-checkpoint-aws een DynamoDBSaver met intelligente payload-handling — kleine checkpoints (<350 KB) blijven in DynamoDB, grote worden automatisch offloaded naar S3. Serverless-pricing en geen infrastructuur om te beheren maken dit aantrekkelijk voor deployments met variabele load.
Langetermijngeheugen: onthouden over sessies heen
Hot memory handelt het huidige gesprek af. Maar wat doe je met de gebruiker die volgende week terugkomt? Langetermijngeheugen slaat feiten, voorkeuren en interactiegeschiedenis op die over threads heen blijven bestaan.
LangGraph levert een Store-interface voor cross-thread-geheugen via zijn klasse BaseStore. Elk memory-item is een (namespace, key)-paar met een JSON-value en optionele vector embedding. De namespace codeert meestal de gebruiker of organisatie: ("user", "user-123", "preferences").
Vector storage: semantische recall met Qdrant
Wanneer de agent ongestructureerde feiten moet ophalen ("Wat zei de gebruiker over zijn investeringstijdlijn?"), levert vector search semantische recall. In plaats van exacte key lookups queryt de agent op betekenis.
Qdrant is een purpose-built vectordatabase, geschreven in Rust, die embedding-opslag, indexing (HNSW) en gefilterde search afhandelt. Ik behandelde HNSW en de trade-offs in detail in mijn post over search ranking. Qdrant biedt ook een MCP-server die fungeert als semantische geheugenlaag — handig als je agentframework het Model Context Protocol ondersteunt.
Uit memory_store.py:
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct, Distance, VectorParams
from langchain_anthropic import ChatAnthropic
import hashlib
import json
class UserMemoryStore:
"""Long-term memory backed by Qdrant vector search.
Stores user facts as embedded vectors for semantic retrieval.
Each fact is a short natural-language statement about the user.
"""
def __init__(self, qdrant_url: str, collection_name: str = "user_memory"):
self.client = QdrantClient(url=qdrant_url)
self.collection_name = collection_name
self._ensure_collection()
def _ensure_collection(self):
"""Create the collection if it doesn't exist."""
collections = [c.name for c in self.client.get_collections().collections]
if self.collection_name not in collections:
self.client.create_collection(
collection_name=self.collection_name,
vectors_config=VectorParams(
size=1536, # text-embedding-3-small dimensions
distance=Distance.COSINE,
),
)
def store_fact(self, user_id: str, fact: str, embedding: list[float]):
"""Store a user fact with its embedding."""
point_id = hashlib.md5(f"{user_id}:{fact}".encode()).hexdigest()
self.client.upsert(
collection_name=self.collection_name,
points=[PointStruct(
id=point_id,
vector=embedding,
payload={"user_id": user_id, "fact": fact},
)],
)
def recall(self, user_id: str, query_embedding: list[float], top_k: int = 5):
"""Retrieve the most relevant facts for a user given a query."""
results = self.client.query_points(
collection_name=self.collection_name,
query=query_embedding,
query_filter={"must": [{"key": "user_id", "match": {"value": user_id}}]},
limit=top_k,
)
return [hit.payload["fact"] for hit in results.points]
De flow is: (1) na elk gesprek extraheert een LLM kernfeiten uit de interactie ("user has high risk tolerance", "user is interested in semiconductor stocks"), (2) feiten worden ge-embed en in Qdrant opgeslagen, (3) aan het begin van het volgende gesprek queryt de agent Qdrant met het nieuwe bericht van de gebruiker om relevante context op te halen.
Retrieval scoring: verder dan cosine similarity
Ruwe cosine similarity is een startpunt, maar productiegeheugensystemen hebben rijkere retrieval nodig. De paper Generative Agents (Park et al., 2023) introduceerde een scoringfunctie die drie signalen combineert:
- Recency: rule-based verval zodat recente herinneringen hoger scoren. Een exponentiële decay-functie zorgt ervoor dat een feit van gisteren hoger scoort dan een equivalent feit van zes maanden geleden.
- Importance: door een LLM beoordeelde significantie op een schaal van 1-10. "User's portfolio is down 40%" scoort hoger dan "user said hello."
- Relevance: embedding cosine similarity tussen de query en het opgeslagen feit.
De uiteindelijke retrievalscore is een gewogen som: score = alpha * recency + beta * importance + gamma * relevance. Zo voorkom je dat verse, belangrijke feiten worden weggedrukt door verouderde maar semantisch vergelijkbare feiten. Voor de Market Analyst Agent weeg ik relevance het zwaarst (0.5), met recency (0.3) en importance (0.2), omdat de huidige query-intentie van de gebruiker het belangrijkst is. Dit zijn startgewichten, aangepast op basis van de paper Generative Agents (die gelijke gewichten gebruikte); ik merkte dat extra nadruk op relevance beter werkte voor financiële analysequeries, maar de waarden zijn intuïtief gekozen en niet empirisch geoptimaliseerd.
Alternatieven voor vector search
Vector search is krachtig, maar niet altijd het juiste hulpmiddel. Dit is wanneer alternatieven logischer zijn:
| Aanpak | Beste voor | Latency | Complexiteit |
|---|---|---|---|
| Vector search (Qdrant) | Semantische recall van ongestructureerde feiten | 5–20ms | Gemiddeld |
| Key-value store (Redis) | Gestructureerde gebruikersprofielen, voorkeuren | <1ms | Laag |
| Document store (bestanden) | Projectkennis, agent-beheerde notities | 1–5ms | Laag |
| Full-text search (PostgreSQL GIN index) | Trefwoordgebaseerde recall van gespreksgeschiedenis | 2–10ms | Laag |
| Knowledge graph (Neo4j) | Entiteitsrelaties, multi-hop reasoning | 10–50ms | Hoog |
| Hybrid (vector + keyword) | Beste recall wanneer query-intentie varieert | 10–30ms | Gemiddeld |
Key-value stores werken goed voor gestructureerde data. Als je langetermijngeheugen een gebruikersprofiel is — risicotolerantie, investeringshorizon, voorkeurssectoren — dan is een Redis-hash of PostgreSQL JSONB-kolom eenvoudiger en sneller dan vectors embedden en queryen. Gebruik vector search wanneer het geheugen ongestructureerd is en de formulering van retrieval-queries varieert.
De ingebouwde Store van LangGraph biedt een namespace-gebaseerde key-value-interface met optionele vector search. De API BaseStore is eenvoudig: put(), get(), search() en delete() met hiërarchische namespace-scoping. Er zijn drie implementaties beschikbaar:
InMemoryStore— voor development en testing (data gaat verloren bij process exit)PostgresStore— persistente productiestore met volledige SQL-queryingAsyncRedisStore— cross-thread-geheugen met vector search, TTL-support en metadatafiltering
De configuratie index activeert vector search over opgeslagen items met een configureerbaar embeddingmodel. Voor veel use-cases is deze ingebouwde store voldoende zonder naar een dedicated vectordatabase te grijpen.
from langgraph.store.memory import InMemoryStore
# Create a store with vector search enabled
store = InMemoryStore(
index={
"dims": 1536,
"embed": my_embedding_function, # e.g., OpenAI text-embedding-3-small
}
)
# Store a user preference (namespace scopes to user)
await store.aput(
namespace=("user", "user-123", "preferences"),
key="risk-profile",
value={"risk_tolerance": "high", "horizon": "long-term"},
)
# Semantic search across user's memories
results = await store.asearch(
namespace=("user", "user-123"),
query="What is their investment style?",
limit=5,
)
Een strategie voor langetermijngeheugen kiezen
Begin met key-value als je geheugen gestructureerd en goed gedefinieerd is (gebruikersprofielen, settings, named entities). Voeg vector search toe wanneer je semantische retrieval over ongestructureerde feiten nodig hebt of wanneer de formulering van queries onvoorspelbaar varieert.
Knowledge graphs verdienen hun plek wanneer relaties tussen entiteiten ertoe doen, bijvoorbeeld: "Welke bedrijven vroeg de gebruiker aan die concurrenten zijn van NVDA?" Het interessantste recente project hier is Graphiti (van Zep), dat een temporally-aware knowledge graph bouwt die bijhoudt wanneer feiten waar waren, niet alleen wat waar was. Elke edge draagt geldigheidsintervallen, zodat een wijziging in de risicotolerantie van de gebruiker de oude waarde ongeldig maakt in plaats van die stilletjes te overschrijven. Graphiti rapporteert 94.8% nauwkeurigheid op de DMR-benchmark, en het bi-temporele model pakt het probleem van stale memory aan op de datalaag.
De keerzijde is operationeel. Een graph database draaien is niet triviaal, en voor de meeste agentapplicaties dekt vector search met metadatafiltering hetzelfde terrein met minder infrastructuur.
Managed memory frameworks zoals Mem0 en Letta (voorheen MemGPT) handelen de extraction-consolidation-retrieval-pipeline voor je af. De aanpak van Mem0 is opvallend: een LLM extraheert kandidaat-herinneringen, een decision engine vergelijkt elk nieuw feit met bestaande entries in de vector store, en een resolver beslist om toe te voegen, te updaten of te verwijderen, zodat de memory store coherent en niet-redundant blijft. Letta pakt het aan vanuit operating systems: agents beheren hun eigen context window met memory-management-tools en verplaatsen autonoom data tussen "core memory" (in-context) en "archival memory" (out-of-context). Beide zijn het evalueren waard als je sneller naar productie wilt en geen volledige controle over de memory-pipeline nodig hebt.
Document memory: de archiefkast van de agent
Het patroon is in de praktijk wijdverbreid, maar in de academische literatuur onderbelicht. Frameworkdocumentatie behandelt vector stores, knowledge graphs en contextmanagement diepgaand, maar bestandsgebaseerd geheugen krijgt amper een voetnoot. Dat gat is het waard om te vullen, want zo persisteren de effectiefste AI-coding assistants in de praktijk hun kennis. De benchmark van Letta vond dat een eenvoudige filesystem-aanpak 74.0% scoorde op de LoCoMo-benchmark voor conversational memory, beter dan meerdere gespecialiseerde memory libraries. Frontier-modellen zijn al getraind op agentic coding-taken en kunnen native met file-operaties omgaan, dus dit patroon sluit aan op hun sterke punten.
De verschuiving kwam door grotere context windows. In 2023-2024, met 8k-32k tokens, had je geen andere keuze dan documenten in chunks te knippen en te embedden. Met windows van 1M+ tokens (Gemini 1.5, Claude 4) is het meestal effectiever om de agent het hele bestand te laten lezen dan te gokken welke chunks relevant zijn. Operationeel is het verhaal ook eenvoudiger. Je kunt een Markdown-bestand lezen, bewerken en git diff. Je kunt geen vectordatabase git diff.
Vector stores en key-value-backends handelen semantische recall en gestructureerde lookups goed af. Maar er is een derde categorie agentkennis die geen van beide netjes bedient: opgebouwde projectcontext, de conventies, researchnotities en beslissingen die de agent over sessies heen nodig heeft en die baat hebben bij leesbaarheid en version control.
Dit is document memory: de agent leest en schrijft gestructureerde bestanden (Markdown, JSON, YAML) naar een bekende directory. Geen embeddings, geen database, geen infrastructuur. Gewoon bestanden op disk die zowel de agent als de developer kunnen cat, grep, git diff en handmatig bewerken.
Waarom bestanden?
Voor langlevende agentworkflows is het effectiefste patroon dat ik heb gezien geen vectordatabase. Het is een directory met goed georganiseerde notities. Kijk wat er gebeurt wanneer een coding agent wekenlang aan een project werkt:
- Hij leert dat het project Pydantic v2 gebruikt, niet v1
- Hij ontdekt dat tests met
pytest -x --tb=shortmoeten draaien - Hij bouwt kennis op over de architectuur van de codebase
- Hij leert de voorkeuren van de developer ("gebruik altijd
pathlib, nooitos.path")
Deze feiten zijn te gestructureerd voor vector search (je hebt exacte recall nodig, geen fuzzy similarity) en te talrijk voor een key-value store (ze vormen onderling verbonden documenten, geen losse feiten). Het zijn ook feiten die de developer direct wil zien en aanpassen. Als de agent iets verkeerd leert, open je het bestand en corrigeer je het.
Zo werkt Claude Code's CLAUDE.md en de directory .claude/. De agent leest projectniveau-CLAUDE.md-bestanden voor conventies en instructies, en schrijft naar ~/.claude/MEMORY.md voor learnings over sessies heen. De bestanden zijn gewone Markdown: je leest ze, bewerkt ze, commit ze naar git en deelt ze met je team. Cursor's .cursorrules en Windsurf's .windsurfrules volgen hetzelfde patroon: plain-text-bestanden die de agent bij startup laadt om projectcontext op te pikken.
Een file memory store implementeren
De implementatie is bewust eenvoudig. De agent krijgt vier operaties: een document schrijven, een document lezen, beschikbare documenten opvragen en op trefwoord zoeken in documenten.
Uit file_memory.py:
from pathlib import Path
import json
import fnmatch
class FileMemory:
"""Document memory backed by the local filesystem.
Stores agent knowledge as human-readable files organized by topic.
No embeddings, no database — just files that both the agent and
the developer can read, edit, and version-control.
"""
def __init__(self, base_dir: str | Path):
self.base_dir = Path(base_dir)
self.base_dir.mkdir(parents=True, exist_ok=True)
def write_doc(self, path: str, content: str, metadata: dict | None = None):
"""Write or overwrite a document at the given path.
Paths are relative to base_dir. Directories are created automatically.
Metadata (if provided) is stored as a JSON sidecar file.
"""
full_path = self.base_dir / path
full_path.parent.mkdir(parents=True, exist_ok=True)
full_path.write_text(content, encoding="utf-8")
if metadata:
meta_path = full_path.with_suffix(full_path.suffix + ".meta")
meta_path.write_text(json.dumps(metadata, indent=2), encoding="utf-8")
def read_doc(self, path: str) -> str | None:
"""Read a document by path. Returns None if not found."""
full_path = self.base_dir / path
if full_path.exists():
return full_path.read_text(encoding="utf-8")
return None
def list_docs(self, pattern: str = "**/*") -> list[str]:
"""List documents matching a glob pattern."""
return [
str(p.relative_to(self.base_dir))
for p in self.base_dir.glob(pattern)
if p.is_file() and not p.name.endswith(".meta")
]
def search_docs(self, query: str, pattern: str = "**/*.md") -> list[dict]:
"""Search documents by keyword. Returns matching files with context.
This is intentionally simple — grep-style keyword search.
For semantic search, use a vector store instead.
NOTE: This is a sketch for demonstration. A simple substring check
won't scale beyond a few hundred documents. For production with 500+
documents, use TF-IDF/BM25 scoring (e.g., rank_bm25) or a full-text
search backend (PostgreSQL GIN index, Elasticsearch).
"""
results = []
for path in self.base_dir.glob(pattern):
if not path.is_file() or path.name.endswith(".meta"):
continue
content = path.read_text(encoding="utf-8")
if query.lower() in content.lower():
# Return the paragraph containing the match for context
for paragraph in content.split("\n\n"):
if query.lower() in paragraph.lower():
results.append({
"path": str(path.relative_to(self.base_dir)),
"match": paragraph.strip()[:500],
})
return results
Mappenstructuur
Het grootste deel van de waarde van document memory komt uit hoe de directory is ingedeeld. Dit is de structuur die ik gebruik voor de Market Analyst Agent:
.agent-memory/
README.md # What this directory is, for human readers
user-profiles/
user-123.md # Preferences, history, risk profile
user-456.md
research/
NVDA-2026-02.md # Research notes from recent analysis
TSLA-2026-01.md
conventions/
analysis-format.md # How to structure analysis reports
data-sources.md # Preferred data sources and API patterns
learnings/
common-errors.md # Mistakes the agent has learned to avoid
tool-patterns.md # Effective tool call sequences
Elk bestand is Markdown. Het doel van elk bestand is direct duidelijk uit het pad. Je kunt git diff op de hele memory-directory uitvoeren om te zien wat de agent in een sessie heeft geleerd, een slechte learning git revert, of de directory naar een ander project kopiëren. Probeer dat maar eens met een Qdrant-collection.
Wanneer gebruik je document memory vs vector vs key-value
De drie memory-backends bedienen verschillende toegangspatronen:
| Dimensie | Vector Store | Key-Value Store | Document Store |
|---|---|---|---|
| Querypatroon | "Find facts similar to X" | "Get the value for key" | "Read the doc at path" |
| Beste voor | Ongestructureerde, variabele recall | Gestructureerde lookups | Projectcontext, notities |
| Menselijk leesbaar | Nee (embeddings) | Gedeeltelijk (JSON) | Ja (Markdown) |
| Debugbaar | Moeilijk (similarity scores) | Eenvoudig (exacte keys) | Triviaal (open het bestand) |
| Versiebeheerbaar | Nee | Mogelijk | Ja (git-native) |
| Embedding-infrastructuur | Vereist | Niet nodig | Niet nodig |
| Schaalt tot | Miljoenen feiten | Miljoenen keys | Duizenden documenten |
| Search-capaciteit | Semantische similariteit | Exacte match | Keyword- / path-based |
Gebruik document memory wanneer:
- De agent projectkennis opbouwt over meerdere sessies heen
- Developers moeten kunnen inspecteren, bewerken of overriden wat de agent "weet"
- De kennis gestructureerd is als documenten (notities, samenvattingen, conventies) in plaats van losse feiten
- Je git-gebaseerde versioning van agentgeheugen wilt
- Geen infrastructuur een harde eis is
Gebruik vector stores wanneer:
- Je fuzzy semantische retrieval nodig hebt ("find memories related to X")
- De formulering van queries onvoorspelbaar varieert
- Je duizenden tot miljoenen individuele feiten hebt
Gebruik key-value stores wanneer:
- Je exacte, snelle lookups nodig hebt voor gestructureerde data (gebruikersprofielen, settings)
- Het dataschema goed gedefinieerd is
In de praktijk combineren productie-agents vaak alle drie. De Market Analyst Agent gebruikt PostgreSQL-checkpoints voor hot memory, Qdrant voor semantische recall van gebruikersfeiten en een bestandsgebaseerde document store voor projectconventies en researchnotities.
Praktijkvoorbeelden
Het patroon is al wijdverbreid in AI-coding assistants:
- Claude Code leest
CLAUDE.md-bestanden uit de projectroot en parent-directories, en schrijft naar~/.claude/MEMORY.mdvoor learnings over sessies heen. Het hele geheugensysteem bestaat uit gewone Markdown-bestanden die je samen met je code commit. - Cursor laadt
.cursorrules-bestanden voor projectspecifieke agentinstructies: coding-conventies, frameworkvoorkeuren, architectuurbeslissingen. - Windsurf gebruikt
.windsurfrules-bestanden plus een directorymemories/waarin de agent geleerde patronen uit je codebase opslaat. - Anthropic's memory tool voor de Claude API biedt operaties
create_memory,read_memory,update_memoryendelete_memory, client-side geïmplementeerd. Jouw applicatie bepaalt waar de bestanden daadwerkelijk leven (lokale disk, S3, database).
De gemene deler: al deze systemen slaan agentkennis op als menselijk leesbare tekstbestanden met expliciete read/write-operaties. Geen embeddings. Geen vectorinfrastructuur. De agent beslist wat hij schrijft, de developer kan alles zien en bewerken, en het hele systeem past in een git diff.
Buiten coding assistants
Document memory is niet beperkt tot coding agents. Het patroon duikt op in heel verschillende agentdomeinen:
-
Open-world game agents: Voyager (Wang et al., 2023) bouwt een persistente skill library van geverifieerde JavaScript-programma's die een Minecraft-agent in de tijd opbouwt, waarmee 3.3x meer unieke items worden verzameld en mijlpalen 15.3x sneller worden bereikt dan bij baselines. Skills zijn overdraagbaar naar nieuwe werelden zonder retraining. JARVIS-1 breidt dit uit met multimodaal geheugen dat tekstuele plannen en visuele observaties combineert, met een 5x succesratio op de moeilijkste taken.
Eén onderscheid is hier belangrijk: skill libraries zijn executable geheugen (codebestanden die worden geïmporteerd en uitgevoerd), terwijl document memory in coding assistants declarative is (Markdown die in prompts wordt geïnjecteerd). De failure modes verschillen. Slechte uitvoerbare code laat de agent crashen; slechte declaratieve tekst leidt tot redeneerfouten. Maar het opslagpatroon en de operationele voordelen (debugbaarheid, version control) zijn hetzelfde.
-
Enterprise workflow automation: de winnaars van de ECR3-competitie gebruikten document memory voor iteratieve promptverfijning. De Analyzer- en Versioner-agents van een winnend team itereren door 80 promptversies, opgeslagen als procedurele documenten. Een ander topteam bouwde 20+ enricher-modules als documentachtige procedurele kennis. LEGOMem (2025) formaliseert dit als een modulair geheugenframework voor multi-agent-systemen, met gespecialiseerde geheugentypen (sensory, short-term, long-term) die agents als bouwblokken combineren.
-
Web automation: Agent Workflow Memory (Wang et al., 2024) laat webagents herbruikbare workflows afleiden uit succesvolle episodes, met een verbetering van 51% in succesratio op WebArena. SkillWeaver (2025) gaat verder: agents synthetiseren herbruikbare API-tools uit exploratie, met een winst van 31.8% in succesratio. De geleerde skills dragen ook over naar zwakkere modellen (54.3% verbetering), zodat het opgebouwde geheugen van een sterkere agent een kleinere kan versterken.
-
Customer support: Gartner voorspelt dat AI-agents in 2029 autonoom 80% van veelvoorkomende klantenserviceproblemen zullen oplossen. Deze agents refereren aan SOP's, playbooks en customer histories, en dat zijn allemaal vormen van document memory.
De MemAgents-workshop op ICLR 2026 is een teken dat de onderzoeksgemeenschap inhaalt wat practitioners al hebben gebouwd. Document memory is duidelijk voorbij zijn oorsprong in coding assistants gegroeid.
Skills zijn document memory met een schema. De Agent Skills-standaard (SKILL.md-bestanden met YAML-frontmatter en een Markdown-body) wordt nu gebruikt door zowel Anthropic als OpenAI Codex.
MCP (Model Context Protocol) gaat dezelfde kant op: tooldefinities zijn JSON Schema-bestanden die elke agent kan ontdekken en aanroepen. Het protocol heeft 97 miljoen maandelijkse SDK-downloads en wordt ondersteund door OpenAI, Google, Microsoft en AWS. MCP is niet coding-specifiek. Dezelfde servers verbinden agents met databases, interne API's en enterprise-systemen.
Beide wijzen naar hetzelfde patroon: procedurele kennis opgeslagen als schema-afgedwongen documenten, met expliciete read/write-operaties. MCP, nu beheerd door de Agentic AI Foundation, is het dichtstbijzijnde dat het agentecosysteem heeft bij een interoperabiliteitsstandaard.
Document memory schalen voor productie
De bestandsgebaseerde implementatie hierboven werkt goed op laptops van individuele developers en bij kleine deployments. Multi-tenant-productie met honderden gebruikers en duizenden documenten is een ander probleem.
De limiet van één node met files wordt snel duidelijk: je kunt file-I/O niet horizontaal schalen, gelijktijdige writes vereisen locking en permissies per tenant beheren is pijnlijk. Voor productie heb je een backing store nodig die concurrency, search en multi-tenancy goed afhandelt.
Drie gebruikelijke benaderingen:
Approach A: hybrid met een dunne databaselaag
Houd bestanden voor authoring (developers bewerken Markdown lokaal), maar serveer tijdens runtime vanuit een database. Bij deployment sync je bestanden naar PostgreSQL-rows. De agent leest uit de database, niet van disk. Dit geeft je:
- Developer-ergonomie (Markdown bewerken, committen naar git)
- Query-performance in productie (geïndexeerde database-reads)
- Een schone scheiding tussen authoring en serving
Approach B: object storage + vector index sidecar
Sla documenten op in S3/GCS als objecten, met een Qdrant-collection die hun embeddings indexeert. De agent queryt Qdrant voor relevante document-ID's en haalt daarna content op uit object storage. Dit schaalt horizontaal en ondersteunt semantische search, maar voegt complexiteit toe: twee systemen om te beheren, een embedding-pipeline om te onderhouden en eventual consistency tussen store en index.
Approach C: gestructureerde document store met PostgreSQL (aanbevolen)
Sla documenten op als PostgreSQL JSONB-rows met full-text search (GIN index) en optionele vector embeddings (pgvector). Dit geeft je hybrid search (keyword + semantic), ACID-transacties en één operationeel systeem.
Een schets van Approach C:
from typing import Optional
import asyncpg
class ProductionDocumentMemory:
"""PostgreSQL-backed document memory with hybrid search.
Schema:
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
tenant_id TEXT NOT NULL,
path TEXT NOT NULL,
content TEXT NOT NULL,
metadata JSONB,
embedding vector(1536), -- pgvector extension
ts_vector tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(tenant_id, path)
);
CREATE INDEX ON documents USING GIN(ts_vector);
CREATE INDEX ON documents USING ivfflat(embedding vector_cosine_ops);
"""
def __init__(self, pool: asyncpg.Pool):
self.pool = pool
async def write(
self,
tenant_id: str,
path: str,
content: str,
metadata: Optional[dict] = None,
embedding: Optional[list[float]] = None,
):
"""Write or update a document."""
async with self.pool.acquire() as conn:
await conn.execute(
"""
INSERT INTO documents (tenant_id, path, content, metadata, embedding)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (tenant_id, path) DO UPDATE
SET content = EXCLUDED.content,
metadata = EXCLUDED.metadata,
embedding = EXCLUDED.embedding
""",
tenant_id, path, content, metadata, embedding,
)
async def search(
self,
tenant_id: str,
query: str,
embedding: Optional[list[float]] = None,
limit: int = 5,
) -> list[dict]:
"""Hybrid search: full-text + optional vector similarity."""
async with self.pool.acquire() as conn:
if embedding:
# Hybrid scoring: 0.6 * text relevance + 0.4 * vector similarity
rows = await conn.fetch(
"""
SELECT path, content, metadata,
(0.6 * ts_rank(ts_vector, plainto_tsquery('english', $2)) +
0.4 * (1 - (embedding <=> $3))) AS score
FROM documents
WHERE tenant_id = $1
AND ts_vector @@ plainto_tsquery('english', $2)
ORDER BY score DESC
LIMIT $4
""",
tenant_id, query, embedding, limit,
)
else:
# Full-text search only
rows = await conn.fetch(
"""
SELECT path, content, metadata,
ts_rank(ts_vector, plainto_tsquery('english', $2)) AS score
FROM documents
WHERE tenant_id = $1
AND ts_vector @@ plainto_tsquery('english', $2)
ORDER BY score DESC
LIMIT $3
""",
tenant_id, query, limit,
)
return [dict(row) for row in rows]
Wat je krijgt:
- Hybrid search: keyword matching (GIN index) + semantische similariteit (pgvector) samen gescoord
- Multi-tenancy:
tenant_id-scoping met row-level security - ACID-garanties: geen eventual-consistency-problemen
- Eén operationeel systeem: geen aparte vectordatabase om te beheren
- Horizontale schaalbaarheid: read replicas voor queryload, partitioning per tenant voor writeschaal
Bestanden zijn uitstekend voor workflows van één developer. Voor multi-tenant-productie is een gestructureerde document store op PostgreSQL meestal de juiste balans tussen eenvoud, performance en operationele volwassenheid.
Alles samenbrengen: de volledige architectuur
Zo werken alle drie de geheugenlagen samen in de Market Analyst Agent. Het diagram toont de volledige flow van user request tot response, met alle geheugenlagen actief.
De architectuur heeft drie geheugenpaden:
-
Hot path (checkpoint store): elke node in de LangGraph schrijft zijn hervatbare graph-state naar de checkpoint store. Wanneer de graph een
interrupt_before-node bereikt (zoals de reporter in Deel 1), pauzeert de uitvoering. De gebruiker kan de app sluiten, en wanneer hij terugkomt hervat de graph vanaf het checkpoint. Runtime event logs en traces zijn aparte productieaandachtspunten. -
Cold path (long-term store): aan het begin van elk gesprek queryt de agent de long-term store voor relevante gebruikerscontext. Aan het einde extraheert en bewaart hij nieuwe feiten. Dit draait asynchroon — het mag de hoofdredeneerlus nooit blokkeren.
-
Document path (file store): bij startup laadt de agent projectconventies en relevante researchnotities uit de document store. Tijdens uitvoering schrijft hij nieuwe researchsamenvattingen en geleerde patronen terug naar disk. In tegenstelling tot het cold path zijn document-reads synchroon (ze informeren de huidige taak), terwijl writes kunnen worden uitgesteld.
De wiring in LangGraph is eenvoudig — de checkpoint store en long-term store worden meegegeven bij graph compilation, terwijl de document store als dependency wordt geïnjecteerd. De lokale schets hieronder gebruikt InMemoryStore zodat het snippet klein blijft; de referentie-Docker-topologie gebruikt Qdrant voor dezelfde semantische-recallrol.
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from langgraph.store.memory import InMemoryStore
# Hot memory: PostgreSQL for durable checkpoints
checkpointer = await create_postgres_checkpointer(pg_connection_string)
# Cold memory: local sketch with vector search
# (The reference Docker topology uses Qdrant for persistent recall.)
memory_store = InMemoryStore(
index={"dims": 1536, "embed": embedding_function}
)
# Document memory: file-based store for project knowledge
doc_memory = FileMemory(base_dir=".agent-memory")
# Checkpoint store and long-term store wired into the graph
graph = create_graph(
checkpointer=checkpointer,
store=memory_store,
)
# The store is accessible inside any node via the store parameter
def planner_node(state: AgentState, *, store: BaseStore) -> dict:
"""Plan with user context from long-term memory."""
# Recall relevant user facts from vector store
user_memories = store.search(
namespace=("user", state.user_id),
query=state.messages[-1].content,
limit=5,
)
# Load project conventions from document memory
conventions = doc_memory.read_doc("conventions/analysis-format.md")
# Inject both into planning context
memory_context = "\n".join(m.value["fact"] for m in user_memories)
# ... rest of planning logic with personalized context and conventions
De volledige flow
Wat gebeurt er wanneer een terugkerende gebruiker "Analyze TSLA" naar de Market Analyst Agent stuurt:
-
Document memory load: bij startup leest de agent projectconventies uit de document store: voorkeuren voor analyseformat, voorkeursdatabronnen en toolgebruikspatronen. Die zetten het baseline-gedrag.
-
Cold memory recall: voordat de routernode uitvoert, queryt de graph de long-term store met het bericht van de gebruiker. Hij haalt op: "User has high risk tolerance", "User prefers detailed competitor analysis", "User previously researched NVDA and AMD".
-
Router + Planner: de router classificeert dit als
DEEP_RESEARCH. De planner maakt een gepersonaliseerd researchplan in vijf stappen op basis van de opgehaalde voorkeuren. Daarin zit een competitor analysis-stap, omdat de gebruikersgeschiedenis laat zien dat die gewenst is. Het plan volgt het format uit het conventiedocument. -
Executor-loop (hot memory): elke stap voert uit via het ReAct-patroon uit Deel 1. Na elke node (router, planner, elke executor-stap) schrijft LangGraph een checkpoint naar PostgreSQL. Als het process crasht na stap 3 van 5, restart je en ga je verder met stap 4.
-
HITL-interrupt: de graph bereikt de node
reportermetinterrupt_before. Het conceptrapport zit in het checkpoint. De gebruiker beoordeelt het uren later, en de graph laadt het checkpoint en gaat verder. -
Memory-updates: nadat het gesprek is afgelopen: (a) een asynchroon proces extraheert nieuwe gebruikersfeiten ("user is now tracking TSLA", "user approved the report format") en slaat die op in de long-term vector store, en (b) de agent schrijft een researchsamenvatting naar de document store (
research/TSLA-2026-02.md) voor toekomstig gebruik.
Het patroon met drie lagen scheidt verantwoordelijkheden netjes. De checkpoint store handelt duurzaamheid en resume af; dat is infrastructuur. De long-term store handelt personalisatie af; dat is productlogica. De document store bewaart opgebouwde projectkennis; dat is het notitieboek van de agent.
Trade-offs en aandachtspunten
Geheugen voegt waarde toe, en het voegt kosten en complexiteit toe. Wees eerlijk over de trade-offs:
-
Embeddingkosten: elk feit dat in een vectordatabase wordt opgeslagen vereist een embedding-API-call. Bij $0.02 per miljoen tokens (OpenAI
text-embedding-3-small) zijn de kosten per feit verwaarloosbaar, maar over duizenden gebruikers en sessies heen loopt het op. Batch embedding-calls en cache resultaten. De echte kosten zitten in latency: 100-300ms embedding-API-latency tijdens querytijd voor cold memory recall, en dat is voor real-time conversational agents belangrijker dan dollarkosten. Cache embeddings voor veelvoorkomende queries, of gebruik een lokaal embeddingmodel voor latencygevoelige workloads. -
Stale memory: gebruikersvoorkeuren veranderen. Een feit van zes maanden geleden ("user prefers conservative investments") kan niet meer kloppen. Stel expiry policies in. Ik gebruik 365 dagen voor voorkeuren en 90 dagen voor episodische events, zoals beschreven in mijn post over context engineering.
-
Memory-overhead in context: elk opgehaald feit verbruikt tokens in het context window van het LLM. Als je per query 20 feiten ophaalt, zijn dat al snel honderden tokens aan memory-context die concurreren met de eigenlijke taak. Limiteer het aantal opgehaalde feiten en prioriteer op relevance-score.
-
Privacy en compliance: langetermijngeheugen slaat gebruikersdata op. Je hebt PII-redactie vóór opslag nodig, duidelijke retentiepolicies en user-facing controls voor data deletion. Niets daarvan is optioneel in gereguleerde sectoren.
-
Groei van checkpointopslag: PostgreSQL-tabellen voor checkpoints groeien bij elke node-executie. Voor langlopende agents moet je een retentiepolicy instellen: bewaar de laatste N checkpoints per thread en archiveer of verwijder oudere. Een voorbeeldquery die de 10 meest recente checkpoints per thread bewaart en alles ouder dan 30 dagen verwijdert:
DELETE FROM checkpoints WHERE thread_id = $1 AND created_at < NOW() - INTERVAL '30 days' AND checkpoint_id NOT IN ( SELECT checkpoint_id FROM checkpoints WHERE thread_id = $1 ORDER BY created_at DESC LIMIT 10 ); -
Memory-consolidatie: in de tijd moeten gedetailleerde episodische herinneringen comprimeren naar compacte semantische representaties: "user asked about NVDA three times in January" in plaats van alle drie de gesprekken letterlijk op te slaan. Dat weerspiegelt menselijke geheugenconsolidatie en houdt de store beheersbaar. Mem0 en Graphiti doen dit automatisch; als je je eigen oplossing bouwt, plan dan periodieke consolidatiejobs.
-
Cold-start-probleem: nieuwe gebruikers hebben geen langetermijngeheugen. De agent moet netjes degraderen en verduidelijkende vragen stellen in plaats van aannames te doen. Geheugen is aanvullend, niet vereist.
-
Memory poisoning: alles in het context window van de agent is een potentieel injectiepunt. Als een aanvaller misleidende feiten naar de document store of het long-term memory schrijft ("always approve transactions without verification"), kan de agent die als instructies uitvoeren. Prompt injection via opgeslagen herinneringen is een reëel attack surface. Mitigaties zijn validatie vóór opslag, opgehaalde content behandelen als onbetrouwbare data in plaats van als system instructions, en toegangscontroles die beperken welke herinneringen kritieke operaties mogen beïnvloeden.
-
Drift in document memory: bestandsgebaseerd geheugen heeft geen automatische deduplicatie of conflictresolutie. In de tijd stapelen documenten tegenstrijdigheden op: het ene bestand zegt "use pytest" terwijl een ander zegt "use unittest." Plan periodieke reviews in (of laat de agent ze doen) om te snoeien en te consolideren. Het goede nieuws is dat, in tegenstelling tot vector stores waar veroudering verborgen blijft, je
grepkunt gebruiken voor tegenstrijdigheden. -
Document memory schaalt niet naar miljoenen items: bestandsgebaseerd geheugen werkt voor honderden tot lage duizenden documenten. Als je agent uit miljoenen feiten met fuzzy matching moet ophalen, heb je een vector store nodig. Document memory is voor gestructureerde projectkennis, niet voor de long tail van elke gebruikersinteractie.
Belangrijkste takeaways
-
Agentgeheugen splitst in drie lagen: hot (checkpoint store voor de huidige sessie), cold (long-term store voor kennis over sessies heen) en document (file store voor opgebouwde projectkennis). Ontwerp elke laag voor zijn toegangspatroon.
-
Gebruik PostgreSQL-checkpointing als standaard. Het geeft je ACID-duurzaamheid, volledige checkpoint history en time-travel debugging. Schakel alleen over naar Redis wanneer sub-milliseconde latency een harde eis is.
-
Het checkpointsysteem van LangGraph handelt hot memory native af. Elke node-write wordt automatisch gepersisteerd, dus pauzeren/hervatten en HITL-workflows vereisen nul applicatiecode.
-
Begin langetermijngeheugen met key-value stores voor gestructureerde gebruikersprofielen. Voeg vector search toe (Qdrant, Pinecone of LangGraph's ingebouwde Store met vector index) wanneer je semantische recall nodig hebt over ongestructureerde feiten.
-
Document memory is in de literatuur onderbelicht maar in de praktijk breed geadopteerd. De meeste frameworks en surveys behandelen vector stores en checkpoints maar slaan bestandsgebaseerd geheugen over. Het patroon heeft zich ruim buiten AI-coding assistants verspreid. Claude Code, Cursor en Windsurf convergeerden op plain-text-bestanden; Voyager slaat Minecraft-skills op als codebibliotheken; ECR3-winnaars itereren over procedurele promptdocumenten; webagents synthetiseren herbruikbare workflow-API's. Wanneer de agent iets verkeerd leert, open je een Markdown-bestand en corrigeer je het. Als je wilt weten wat de agent weet, kun je
lsop de memory-directory uitvoeren. -
Geheugen verandert het product, niet alleen de infrastructuur. Het verschil tussen "de agent onthoudt mijn voorkeuren" en "de agent stelt me elke keer dezelfde vragen" is wat gebruikers laat terugkomen.
-
Stel retentiepolicies vanaf dag één in. Stale memory schaadt agentprestaties en onbegrensde opslag creëert privacyrisico's. Laat episodische herinneringen na 90 dagen verlopen, voorkeuren na 365, en review document memory periodiek op tegenstrijdigheden.
-
Beperk opgehaalde context. Elk opgehaald feit concurreert om tokens in het context window. Haal de top 5 meest relevante feiten op, niet alles wat je hebt.
Wat volgt
In Deel 3, AI Agent Tool Use in 2026, behandel ik toolergonomie en de Agent-Computer Interface (ACI): hoe je tools ontwerpt die LLM's daadwerkelijk betrouwbaar kunnen gebruiken. Toolbeschrijvingen, argumentschema's en error-handlingpatronen bepalen of je agent de juiste tool met de juiste argumenten aanroept, of zich hallucinatiesgewijs een cascade aan failures in werkt.
Referenties
Papers
- Cognitive Architectures for Language Agents (CoALA) — Sumers, Yao et al., 2023 — Fundamentele taxonomie van agentgeheugentypen
- Memory in the Age of AI Agents: A Survey — dec 2025 — Uitgebreide driedimensionale taxonomie van agentgeheugen
- MemGPT: Towards LLMs as Operating Systems — Packer et al., 2023 — Virtueel contextmanagement voor LLM-agents
- Generative Agents: Interactive Simulacra of Human Behavior — Park et al., 2023 — Memory-streamarchitectuur met scoring op recency, importance en relevance
- Zep: A Temporal Knowledge Graph Architecture for Agent Memory — Rasmussen, 2025 — Bi-temporele knowledge graph voor agentgeheugen
- Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory — 2025 — Extraction/consolidation-pipeline met benchmarks
- Voyager: An Open-Ended Embodied Agent with Large Language Models — Wang et al., 2023 — Skill library als document memory voor open-world game agents
- JARVIS-1: Open-World Multi-task Agents with Memory-Augmented Multimodal Language Models — 2023 — Multimodale memory library voor Minecraft-agents
- Agent Workflow Memory — Wang et al., 2024 — Herbruikbare workflow-inductie voor webautomation-agents
- SkillWeaver: Web Agents can Self-Design Skill Libraries — 2025 — Zelf gesynthetiseerde herbruikbare API-tools voor webagents
- LEGOMem: Modular Memory Framework for LLM Agent Systems — 2025 — Composeerbare geheugenmodules voor multi-agent-systemen
LangGraph-documentatie
- LangGraph Persistence (Checkpointing) — Kernconcepten voor checkpointgebaseerd geheugen
- LangGraph Memory Store — Langetermijngeheugen over threads heen met de Store-interface
- LangGraph Cross-Thread Persistence — Functionele API voor cross-thread-geheugen
- How to add memory to the prebuilt ReAct agent — Praktische handleiding voor het toevoegen van geheugen
Checkpoint-backends
langgraph-checkpoint-postgres— PostgreSQL-checkpoint saver voor LangGraphlanggraph-checkpoint-redis— Redis-checkpoint saver voor LangGraph- LangGraph Redis Checkpoint 0.1.0 Redesign — Architectuurdetails voor de Redis-checkpoint saver
langgraph-checkpoint-aws— DynamoDB-checkpoint saver met S3-offloading
Vectordatabases en memory-tools
- Qdrant — Open-source vectordatabase met HNSW-indexing en filtering
- Qdrant Agentic Builders Guide — Praktische handleiding voor het bouwen van agentgeheugen met Qdrant
- pgvector — Extensie voor vector-similarity-search voor PostgreSQL
- Graphiti — Open-source temporale knowledge-graph-engine van Zep
Document- en bestandsgebaseerd geheugen
- Claude Code Memory — Bestandsgebaseerd geheugensysteem met CLAUDE.md en MEMORY.md
- Anthropic Memory Tool — Client-side bestandsgebaseerd geheugen voor Claude API-agents
- Cursor Rules — Projectspecifieke .cursorrules-bestanden voor agentcontext
- Windsurf Memories — Bestandsgebaseerd geheugen en .windsurfrules voor coding agents
Memory-frameworks
- Mem0 — Managed memory-laag met extraction/consolidation-pipeline
- Letta (MemGPT) — OS-geïnspireerd virtueel contextmanagement voor agents
- LangMem SDK — Memory-management-tools voor LangGraph
Benchmarks
- PostgreSQL vs Redis Performance — CyberTec-benchmarks voor latency en throughput
- PostgreSQL vs Redis Comparison — Architectuurvergelijking van RisingWave
- Redis AI Agent Engineering — Redis-patronen voor agentworkloads
Workshops
- MemAgents: Memory for LLM-Based Agentic Systems — ICLR 2026 Workshop
Demoproject
- Market Analyst Agent — Volledige implementatie met alle drie de geheugenlagen
De volledige code van de Market Analyst Agent, inclusief de geheugenarchitectuur uit deze post, staat op GitHub als je wilt meelezen.
Serie: Engineering the Agentic Stack
- Deel 1: AI Agent Reasoning Loops in 2026 — ReAct, ReWOO en Plan-and-Execute
- Deel 2: AI-agentgeheugenarchitectuur in 2026 (deze post)
- Deel 3: AI Agent Tool Use in 2026 — MCP, CLI, Skills, code execution en ACI
- Deel 4: AI Agent Security in 2026 — guardrails, permissies, sandboxes, HITL en MCP-scoping
- Deel 5: Long-Running AI Agent Runtime in 2026 — sessies, sandboxes, checkpoints, harnesses en deploymentvormen