AI Agents

Hoe je geheugen toevoegt aan een AI-agent: statuspersistentiepatronen voor productie

Alejandro Rioja
Alejandro Rioja
8 min lezen
TL;DR

Staatloze agents — degenen die alles vergeten wanneer de Worker stopt — zijn prima voor eenmalige taken. Op het moment dat een agent moet onthouden wat er gisteren is gebeurd, een terugkerende klant moet herkennen, of moet voortbouwen op eerdere output, heb je geheugen nodig. Er zijn drie patronen: werkgeheugen (context in vlucht, leeft in KV voor de duur van een run), episodisch geheugen (wat er is gebeurd en wanneer, een opvraagbaar logboek) en semantisch geheugen (wat je weet, opgehaald via vectorzoekopdrachten of gestructureerde gegevens). Koppel het juiste patroon aan de juiste taak.

Gratis nieuwsbrief

Elke woensdag. 28.400+ operators. Geen opvulling.

Inhoudsopgave

Bijgewerkt juni 2026.

TL;DR: Staatloze agents — degenen die alles vergeten wanneer de Worker stopt — zijn prima voor eenmalige taken. Op het moment dat een agent moet onthouden wat er gisteren is gebeurd, een terugkerende klant moet herkennen, of moet voortbouwen op eerdere output, heb je geheugen nodig. Er zijn drie patronen: werkgeheugen (context in vlucht, leeft in KV voor de duur van een run), episodisch geheugen (wat er is gebeurd en wanneer, een opvraagbaar logboek) en semantisch geheugen (wat je weet, opgehaald via vectorzoekopdrachten of gestructureerde gegevens). Koppel het juiste patroon aan de juiste taak.

[Operator-perspectief] Ik ben meer dan eens tegen de staatloze muur opgelopen. De social reply-agent die zichzelf bleef voorstellen aan klanten met wie hij al 20 keer had gepraat. De dagelijkse briefing-agent die hetzelfde probleem vier dagen achter elkaar meldde omdat hij geen herinnering had aan het melden ervan gisteren. Het toevoegen van het juiste soort geheugen heeft beide opgelost. Dit is wat ik gebruik.

Waarom staatloze agents blijven falen

Een staatloze agent begint elke run alleen met wat je hem expliciet doorgeeft: de systeemprompt, het gebruikersbericht en welke gegevens je op het moment van aanroep ophaalt. Hij heeft geen bewustzijn van eerdere runs, eerdere gebruikers of eerdere beslissingen.

Voor een eenmalige classificatietaak — een reactie lezen, een categorie teruggeven — is staatloze correct. Het is snel, goedkoop en voorspelbaar.

Het faaloppervlak verschijnt zodra je continuïteit nodig hebt:

  • Een klantgerichte agent die de geschiedenis van de klant niet herkent
  • Een contentagent die een artikel aanbeveelt dat hij vorige week al aanbeveelde
  • Een moderatieagent die een opgelost geval blijft escaleren
  • Een dagelijkse briefing die dezelfde verouderde melding voor onbepaalde tijd toont

Dit zijn allemaal symptomen van hetzelfde probleem: de agent heeft geen manier om context over runs heen te dragen.

Drie soorten geheugen

Het kader dat ik nuttig vind in productie:

  1. Werkgeheugen — wat de agent nu weet, tijdens een enkele run. Bewaard in KV of in het geheugen voor de levensduur van de aanroep.
  2. Episodisch geheugen — wat er is gebeurd en wanneer. Een gestructureerd logboek dat de agent aan het begin van elke run leest om zich te oriënteren.
  3. Semantisch geheugen — wat het weet over de wereld, klanten of een kennisbank. Opgehaald via gestructureerde query’s of vectorzoekopdrachten wanneer relevant.

Je hebt niet altijd alle drie nodig. De meeste agents die ik run hebben werkgeheugen + episodisch nodig. Semantisch geheugen is het moeilijkst te bouwen en verdient zijn plek pas wanneer de kennisbank te groot is om in het contextvenster te passen.

Werkgeheugen: context in vlucht

Werkgeheugen is een status die leeft gedurende de duur van één agent-run. De eenvoudigste vorm zijn variabelen in de functiescope. De interessantere vorm is een gedeelde KV-sleutel die subtaken binnen dezelfde run lezen en schrijven.

Mijn social reply-agent gebruikt werkgeheugen om context te accumuleren terwijl hij een batch reacties in één wachtrij-bericht verwerkt. Hij leest aan het begin de recente gespreksgeschiedenis voor elke klant uit KV, voegt nieuwe context toe tijdens de verwerking en schrijft aan het einde terug.

typescript
// workers/social-reply.ts

async function processComment(
  comment: SocialCommentEvent,
  env: Env
): Promise<void> {
  // Recente geschiedenis van deze klant laden uit KV (werkgeheugen)
  const historyKey = `customer:${comment.userId}:history`;
  const rawHistory = await env.AGENT_KV.get(historyKey);
  const history: ConversationTurn[] = rawHistory
    ? JSON.parse(rawHistory)
    : [];

  // Een contextbewuste systeemprompt opbouwen vanuit de geschiedenis
  const systemPrompt = buildSystemPrompt(history);

  const response = await anthropic.messages.create({
    model: "claude-opus-4-8",
    max_tokens: 512,
    system: systemPrompt,
    messages: [{ role: "user", content: comment.text }],
  });

  const reply =
    response.content[0].type === "text" ? response.content[0].text : "";

  // Geschiedenis bijwerken — de laatste 10 beurten bewaren, TTL 30 dagen
  const updatedHistory: ConversationTurn[] = [
    ...history.slice(-9),
    { role: "assistant", content: reply, timestamp: comment.timestamp },
  ];
  await env.AGENT_KV.put(historyKey, JSON.stringify(updatedHistory), {
    expirationTtl: 60 * 60 * 24 * 30,
  });

  await postReply(comment, reply, env);
}

Twee dingen om op te merken. De geschiedenis is beperkt tot 10 beurten — gebruik een schuifvenster, laat het niet onbeperkt groeien. En de TTL is 30 dagen: als een klant een maand zwijgt, verloopt de geschiedenis en begint de agent opnieuw. Beide zijn opzettelijk.

Episodisch geheugen: wat er is gebeurd en wanneer

Episodisch geheugen is het logboek van de agent. Een gestructureerd overzicht van vroegere runs dat de agent aan het begin van elke nieuwe run leest om herhaling te vermijden.

Mijn dagelijkse briefing-agent toonde elke dag dezelfde verouderde meldingen omdat elke run geen bewustzijn had van wat al was gemeld. De oplossing: een gestructureerd logboek van vroegere meldingen dat de agent leest vóór het genereren van de briefing.

typescript
// workers/daily-brief.ts

interface AlertLogEntry {
  id: string;
  surfacedAt: string; // ISO-tijdstempel
  resolvedAt?: string;
  summary: string;
}

async function buildDailyBrief(env: Env): Promise<void> {
  const [emails, calendar, tasks] = await Promise.all([
    fetchOvernightEmails(env),
    fetchTodayCalendar(env),
    fetchTopTasks(env),
  ]);

  // Episodisch geheugen laden: wat al is gemeld
  const rawLog = await env.AGENT_KV.get("brief:alert-log");
  const alertLog: AlertLogEntry[] = rawLog ? JSON.parse(rawLog) : [];

  // Filteren op alleen recente, onopgeloste meldingen
  const sevenDaysAgo = new Date(
    Date.now() - 7 * 24 * 60 * 60 * 1000
  ).toISOString();
  const recentAlerts = alertLog.filter(
    (e) => e.surfacedAt > sevenDaysAgo && !e.resolvedAt
  );

  const brief = await synthesizeBrief(
    { emails, calendar, tasks, recentAlerts },
    env
  );

  // Het logboek bijwerken met nieuwe meldingen die in deze run zijn gevlagd
  const newAlerts: AlertLogEntry[] = brief.newAlerts.map((a) => ({
    id: crypto.randomUUID(),
    surfacedAt: new Date().toISOString(),
    summary: a,
  }));

  const updatedLog = [...alertLog, ...newAlerts].slice(-100); // de laatste 100 bewaren
  await env.AGENT_KV.put("brief:alert-log", JSON.stringify(updatedLog));

  await writeToWorkspace(brief.content, env);
}

De agent weet nu wat hij al heeft gezegd. Dubbele meldingen blijven buiten de briefing totdat het onderliggende probleem verandert. Wanneer ik een melding als opgelost markeer, verdwijnt deze van de actieve lijst.

Dit patroon generaliseert: elke agent die beslissingen, vlaggen of aanbevelingen produceert, heeft baat bij een logboek. Het logboek is goedkoop (een paar KB in KV), de opbrengst is hoog (geen redundante output meer).

Semantisch geheugen: wat je weet

Semantisch geheugen is de kennisbank. Het beantwoordt “wat weet je over X?” op het moment van de query, in plaats van alles vooraf in de systeemprompt te proppen.

De eenvoudigste vorm is een gestructureerde zoekopdracht in KV of een database. Mijn Pickleland-boekingsagent raadpleegt klantprofielen en baanvoorkeuren voordat hij bevestigingen opstelt:

typescript
// workers/booking-agent.ts

interface CustomerProfile {
  userId: string;
  preferredCourts: string[];
  experienceLevel: "beginner" | "intermediate" | "advanced";
  specialNotes: string;
}

async function draftConfirmation(
  booking: BookingEvent,
  env: Env
): Promise<string> {
  // Klantprofiel ophalen uit KV (semantisch geheugen — feitelijke kennis)
  const profileKey = `customer:${booking.userId}:profile`;
  const rawProfile = await env.AGENT_KV.get(profileKey);
  const profile: CustomerProfile | null = rawProfile
    ? JSON.parse(rawProfile)
    : null;

  const systemPrompt = profile
    ? `Je stelt gepersonaliseerde boekingsbevestigingen op. Deze klant geeft de voorkeur aan ${profile.preferredCourts.join(", ")}, is een ${profile.experienceLevel}-speler. ${profile.specialNotes}`
    : "Je stelt boekingsbevestigingen op voor een pickleballfaciliteit.";

  const response = await anthropic.messages.create({
    model: "claude-haiku-4-5-20251001",
    max_tokens: 256,
    system: systemPrompt,
    messages: [
      {
        role: "user",
        content: `Stel een bevestiging op voor: ${JSON.stringify(booking)}`,
      },
    ],
  });

  return response.content[0].type === "text" ? response.content[0].text : "";
}

Voor grotere kennisbanken — productdocumentatie, een support-kennisbank, alles wat te groot is om in een contextvenster te passen — heb je een vectoropslag nodig. De workflow is: de query insluiten, de k meest relevante chunks ophalen, ze in de context injecteren. Cloudflare Vectorize handelt dit native af als je al op Workers zit. Voor grotere indexen heb ik Upstash Vector gebruikt. De keuze hangt af van de schaal, niet van het principe.

De eerlijke noot over semantisch geheugen: het is de moeilijkste van de drie om te bouwen en te onderhouden. De index moet actueel blijven. De kwaliteit van het ophalen varieert. Begin met gestructureerde zoekopdrachten — KV, een tabel in D1 — en grijp pas naar vectorzoekopdrachten wanneer de gestructureerde aanpak het kennisoppervlak dat je nodig hebt niet kan dekken.

Het geheugen-beslissingsraamwerk

Voordat je geheugen aan een agent toevoegt, beantwoord drie vragen:

  1. Moet de agent onthouden tussen runs? Als elke aanroep echt onafhankelijk is — een vertaling, een classificatie, een eenmalige generatie — sla dan geheugen over. Staatloos is eenvoudiger en goedkoper.

  2. Herhaalt de agent zichzelf of handelt hij blind voor zijn eigen geschiedenis? Zo ja, voeg dan eerst episodisch geheugen toe. Het is de oplossing met de minste moeite en dekt de meeste klachten over “de agent blijft X doen”.

  3. Behandelt de agent elke gebruiker of entiteit identiek wanneer dat niet zou moeten? Zo ja, voeg werkgeheugen toe (klantgeschiedenis, gebruikersprofiel) of semantisch geheugen (een zoek- of ophaal-systeem).

De fout die ik het vaakst zie: iemand voegt een enorme kennisbank (semantisch geheugen) toe aan een agent die eigenlijk faalde omdat hij geen episodisch geheugen had — geen logboek van wat hij al had gedaan. De complexiteit past niet bij het probleem.

Wat ik echt gebruik in productie

Bij 30+ agents:

  • Alle hebben minstens werkgeheugen — een of andere vorm van status binnen een run, al is het alleen het contextvenster zelf.
  • Ongeveer de helft heeft episodisch geheugen — een logboek van vroegere runs, beslissingen of vlaggen. Dit is bijna altijd de moeite waard om toe te voegen.
  • Drie of vier hebben echt semantisch geheugen ondersteund door een vectoropslag. Dit zijn de agents die vragen beantwoorden over een grote, dynamische kennisbank.

Cloudflare KV is mijn standaard opslag voor werk- en episodisch geheugen. Het is snel, goedkoop en native geïntegreerd in Workers — geen extra client, geen aparte credential. De beperking: KV is uiteindelijk consistent en niet geweldig voor schrijfacties met hoge frequentie. Voor agents die meerdere keren per seconde status schrijven, gebruik ik in plaats daarvan Durable Objects of een D1-database.

Voor semantisch geheugen ondersteund door vectoren gebruik ik Cloudflare Vectorize voor kleine tot middelgrote indexen (minder dan ~100K vectoren) en Upstash Vector voor alles wat groter is. Beide hebben eersteklas JavaScript-clients.

De conclusie van de operator

Voeg geheugen toe aan een agent alleen wanneer staatloos gedrag echte problemen veroorzaakt — herhaalde output, blinde vlekken in de klantgeschiedenis, onwetendheid over vroegere beslissingen. Kies dan de juiste laag: werkgeheugen voor context tijdens de run, episodisch voor wat er historisch is gebeurd, semantisch voor wat je weet. Begin met episodisch als je het niet zeker weet — het repareert het meest voorkomende faalpatroon met de minste complexiteit. Grijp niet naar een vectordatabase totdat je gestructureerde zoekopdrachten hebt uitgeput. Het beste geheugensysteem is het eenvoudigste dat de agent correct laat gedragen.


Gerelateerd: De agent-stack die ik gebruik voor 30+ productie-agents · Gebeurtenisgestuurde vs. geplande agents · Hoe ik meet of een AI-agent echt werkt

Hulp nodig bij het ontwerpen van agentgeheugen voor jouw gebruik? Neem contact op — ik ontwerp productie-agentsystemen voor operatorteams.

Lees verder

Gerelateerde berichten

Lees verder

Ontvang het AI-playbook in je inbox

Elke woensdag. 28.400+ operators. Geen opvulling.

↵ alle resultaten bekijken esc esc om te sluiten