{"id":34750,"date":"2026-05-11T15:08:16","date_gmt":"2026-05-11T15:08:16","guid":{"rendered":"https:\/\/www.europesays.com\/ai\/34750\/"},"modified":"2026-05-11T15:08:16","modified_gmt":"2026-05-11T15:08:16","slug":"why-your-ai-agent-doesnt-actually-remember-anything","status":"publish","type":"post","link":"https:\/\/www.europesays.com\/ai\/34750\/","title":{"rendered":"Why your AI agent doesn&#8217;t actually remember anything"},"content":{"rendered":"<p>A few months ago, I was reviewing a customer support agent who had a strange failure pattern. A user would chat with it on Monday about a billing issue, get partway through the resolution, and come back on Wednesday to continue. The Wednesday conversation would start from zero. The agent had no idea who the user was, what the previous issue had been, what had already been tried, or what had been promised.<\/p>\n<p>The team had built the agent on a solid foundation. Idempotency keys on action endpoints. Workflow state machines for multi-step processes. Transactional writes for anything that touched billing. The infrastructure for correctness was there. What they didn\u2019t have was a way for the agent to recall and reason about its own history, to know that this user, two days ago, had been promised a refund that hadn\u2019t yet been issued.<\/p>\n<p>That\u2019s the problem I want to talk about. Not the parts of agent reliability that good systems engineering already solves. The harder layer above them: the agent\u2019s ability to remember.<\/p>\n<p>First, what memory isn\u2019t<\/p>\n<p>Too many <a href=\"https:\/\/thenewstack.io\/ai-agent-memory-architecture\/\" data-wpil-monitor-id=\"3840\" class=\"local-link\" rel=\"nofollow noopener\" target=\"_blank\">architectural conversations about agent memory<\/a> get derailed because people use the word in different ways. Let me clear three of them away.<\/p>\n<p>Memory is not idempotency. If your agent takes the same action twice because the same request hit your endpoint twice, the fix is an idempotency key. The agent doesn\u2019t need to remember; the system needs to recognize the duplicate. Memory is not a workflow state. If your agent is mid-process, the fix is a state machine that records the current step and gates the legal transitions. Memory is not transactional consistency. If two agents are about to act on the same data, the fix is database-level isolation.<\/p>\n<p>All three are necessary. None of them is memory. They keep individual actions correct; they don\u2019t give the agent a sense of history.<\/p>\n<p>What memory actually is<\/p>\n<p>I\u2019ve come to think of agent memory as having five capabilities that have to work together. Persistent storage is one of them, and it\u2019s the easy one. The others are where most production agents fall short.<\/p>\n<p>Persistence: the agent\u2019s history survives session ends, process restarts, and deployments. Solved by writing to a database.<\/p>\n<p>Selection: the agent decides what is worth remembering. Storing every token of every conversation forever is both expensive and counterproductive. It dilutes the signal at recall time.<\/p>\n<p>Compression: raw history is summarized into something useful. A two-hour conversation becomes a paragraph plus structured facts. Without compression, retrieval cost grows linearly with interaction time.<\/p>\n<p>Decay and forgetting: old memories matter less than recent ones, and some should be forgotten. Without decay, stale information weighs the same as fresh information, which is exactly how RAG pipelines lie to you, <a href=\"https:\/\/thenewstack.io\/rag-pipeline-hybrid-search\/\" class=\"local-link\" rel=\"nofollow noopener\" target=\"_blank\">the topic of my last article<\/a>.<\/p>\n<p>Contamination prevention: incorrect memories are worse than no memories. Bad facts, once stored, pollute every future decision. A real memory system flags uncertain memories, downgrades them when they are contradicted, and quarantines them when they are proven wrong.<\/p>\n<p>Most discussions of agent memory collapse all five into the first. They argue about which database to use to store agent state, and call it solved. But persistence without selection gives you a slow agent. Without compression, an expensive one. Without decay, a confidently wrong one. Without contamination prevention, an agent that gets dumber over time. All five are necessary, and the substrate beneath determines which architectures you can build.<\/p>\n<p>\u201cPersistence without selection gives you a slow agent. Without compression, an expensive one. Without decay, a confidently wrong one. Without contamination prevention, an agent that gets dumber over time.\u201d<\/p>\n<p>A useful taxonomy<\/p>\n<p>Memory has structure. Borrowing from cognitive science gives me a vocabulary for diagnosing where <a href=\"https:\/\/thenewstack.io\/serverless-cloud-architecture-is-failing-modern-ai-agents\/\" data-wpil-monitor-id=\"3841\" class=\"local-link\" rel=\"nofollow noopener\" target=\"_blank\">agent architectures fail<\/a>.<\/p>\n<p>Working memory is the context window: current task, current turn. Fast, ephemeral. This is what most agents have today and confuse with memory in general.<\/p>\n<p>Episodic memory is the history of specific past interactions, with rich metadata: who, what, when, and outcome.<\/p>\n<p>Semantic memory is distilled knowledge. \u201cThis customer prefers morning flights.\u201d \u201cThe finance API returns amounts in cents.\u201d Queried by meaning, not by time.<\/p>\n<p>Procedural memory is learned behavior: which tool sequences work better than others. Almost nobody has this in production yet. It\u2019s a topic for another article.<\/p>\n<p>Working memory is a prompt string. Episodic memory needs structured time-series queries. Semantic memory <a href=\"https:\/\/thenewstack.io\/why-developers-need-vector-search\/\" data-wpil-monitor-id=\"3839\" class=\"local-link\" rel=\"nofollow noopener\" target=\"_blank\">needs vector search<\/a>. The rest of this article focuses on episodic and semantic, where the real production gap lies.<\/p>\n<p>Why single-purpose stores fall short<\/p>\n<p>The first thing most teams reach for is Redis. Fast, familiar, perfect for working memory. The problem is that key-value stores give you exactly one access pattern: get by key. That fails for episodic recall, which needs queries like \u201cevery successful refund I\u2019ve issued for users in this region in the last 30 days.\u201d That\u2019s a relational query, not a key lookup.<\/p>\n<p>The second thing teams reach for is a vector database. Embed past interactions, search by similarity. Useful for semantic memory, but it falls short for episodic recall, which often needs exact predicates: a specific user, a specific time window, a specific outcome. Cosine similarity doesn\u2019t help when you need WHERE clauses. And vector stores typically can\u2019t join against your application data, which means the agent can\u2019t reason about memory in the context of who the user is.<\/p>\n<p>Then there\u2019s contamination handling. When a memory is found to be wrong, you need to update or invalidate it. In a vector store, that\u2019s re-indexing. In a key-value store, mass cache invalidation. In a relational database with vector support, it\u2019s an UPDATE. Memory systems live or die by how reliably bad memories get cleaned up.<\/p>\n<p>The schema, with the honest caveats<\/p>\n<p>Here\u2019s a reference schema for a layer that handles episodic and semantic memory in a single database.<\/p>\n<p>&#8212; Episodic memory: what happened, with outcomes<br \/>\nCREATE TABLE episodic_memory (<br \/>\n  id           BIGINT PRIMARY KEY AUTO_INCREMENT,<br \/>\n  agent_id     VARCHAR(64) NOT NULL,<br \/>\n  user_id      BIGINT NOT NULL,<br \/>\n  session_id   BIGINT NOT NULL,<br \/>\n  action_type  VARCHAR(100) NOT NULL,<br \/>\n  summary      TEXT,           &#8212; compressed form<br \/>\n  raw_payload  JSON,           &#8212; full detail, kept for audit<br \/>\n  outcome      ENUM(&#8216;success&#8217;,&#8217;failure&#8217;,&#8217;pending&#8217;),<br \/>\n  confidence   FLOAT DEFAULT 1.0,<br \/>\n  superseded_by BIGINT NULL,   &#8212; contamination invalidation<br \/>\n  created_at   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,<br \/>\n  embedding    VECTOR(1536),<br \/>\n  INDEX idx_agent_user_time (agent_id, user_id, created_at),<br \/>\n  INDEX idx_embedding USING HNSW (embedding)<br \/>\n);<br \/>\n&#8212; Semantic memory: distilled knowledge, with decay metadata<br \/>\nCREATE TABLE semantic_memory (<br \/>\n  id           BIGINT PRIMARY KEY AUTO_INCREMENT,<br \/>\n  agent_id     VARCHAR(64) NOT NULL,<br \/>\n  user_id      BIGINT,<br \/>\n  fact         TEXT NOT NULL,<br \/>\n  confidence   FLOAT DEFAULT 1.0,<br \/>\n  source_count INT DEFAULT 1,<br \/>\n  last_confirmed_at DATETIME NOT NULL,<br \/>\n  contradicted_at   DATETIME NULL,<br \/>\n  created_at   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,<br \/>\n  embedding    VECTOR(1536),<br \/>\n  INDEX idx_embedding USING HNSW (embedding)<br \/>\n);<\/p>\n<p>A few details that the simpler schema I\u2019d have written a year ago didn\u2019t have:<\/p>\n<p>Both summary and raw_payload columns. Compression isn\u2019t optional. The agent reads summary at recall time; raw_payload is kept for audit and re-summarization.<\/p>\n<p>confidence and superseded_by. Memories aren\u2019t immutable. superseded_by is a soft-delete pointer for contamination. When memory A is contradicted by memory B, A points to B and is excluded from recall. The original isn\u2019t deleted; audit matters.<\/p>\n<p>source_count and last_confirmed_at. A fact confirmed by ten interactions is more reliable than one confirmed once. A fact last confirmed two years ago should weight less than one confirmed yesterday. These feed a decay function applied at recall time, not at write time.<\/p>\n<p>The data stays; its weight changes. This is the principle running through all of the above. Memory records aren\u2019t rewritten or deleted. They\u2019re annotated, decayed, or superseded.<\/p>\n<p>Recall queries that reflect real memory behavior<\/p>\n<p>With this schema, recall queries express harder behaviors than \u201cfind similar things.\u201d This includes measures such as similarity weighted by recency and confidence, while excluding contaminated memories.<\/p>\n<p>&#8212; Semantic recall with decay and contamination filtering<br \/>\nSELECT fact,<br \/>\n       confidence<br \/>\n       * EXP(-DATEDIFF(NOW(), last_confirmed_at) \/ 90.0) AS effective_weight,<br \/>\n       VEC_COSINE_DISTANCE(embedding, @task_vec) AS distance<br \/>\nFROM semantic_memory<br \/>\nWHERE (user_id = @user_id OR user_id IS NULL)<br \/>\n  AND contradicted_at IS NULL<br \/>\n  AND VEC_COSINE_DISTANCE(embedding, @task_vec) &lt; 0.30<br \/>\nORDER BY distance, effective_weight DESC<br \/>\nLIMIT 10;<\/p>\n<p>The decay term EXP(-days\/90) is a half-life of about 60 days. Customer preferences decay slowly. Inventory facts decay quickly. A real memory system tunes this per memory type.<\/p>\n<p>These queries are doing real memory work. They\u2019re not just retrieving rows. They\u2019re modeling how confidence decays, how contamination propagates, how recency interacts with relevance. The database is the substrate. The intelligence lives in the query, not the row.<\/p>\n<p>Updating memory: optimistic and pessimistic patterns<\/p>\n<p>Memory isn\u2019t write-once. Confidence updates as new evidence arrives. Memories get superseded. These updates can occur concurrently, so we need to be deliberate about concurrency control. Two patterns are common, and the difference matters because they\u2019re often conflated.<\/p>\n<p>Pessimistic locking takes the lock first and holds it for the duration of the operation. Use this when contention is expected, and the operation is short:<\/p>\n<p>&#8212; Pessimistic: lock the row, then update<br \/>\nBEGIN;<\/p>\n<p>SELECT confidence, source_count<br \/>\nFROM semantic_memory<br \/>\nWHERE id = @memory_id<br \/>\nFOR UPDATE;<\/p>\n<p>&#8212; Application computes new confidence<\/p>\n<p>UPDATE semantic_memory<br \/>\nSET confidence = @new_confidence,<br \/>\n    source_count = source_count + 1,<br \/>\n    last_confirmed_at = NOW()<br \/>\nWHERE id = @memory_id;<\/p>\n<p>COMMIT;<\/p>\n<p>Optimistic concurrency control takes a different approach. No lock at read time; instead, you record the version of the row you read, and at write time you assert the version hasn\u2019t changed. If it has, the update fails and the application retries. Use this when contention is rare and readers vastly outnumber writers:<\/p>\n<p>&#8212; Optimistic: read with version, write conditionally<\/p>\n<p>SELECT confidence, source_count, version<br \/>\nFROM semantic_memory<br \/>\nWHERE id = @memory_id;<\/p>\n<p>UPDATE semantic_memory<br \/>\nSET confidence = @new_confidence,<br \/>\n    source_count = source_count + 1,<br \/>\n    last_confirmed_at = NOW(),<br \/>\n    version = version + 1<br \/>\nWHERE id = @memory_id<br \/>\n  AND version = @read_version;<\/p>\n<p>&#8212; If 0 rows affected, another transaction won the race; retry.<\/p>\n<p>Both are valid. Memory updates from human reviewers (low-frequency, high-care) fit pessimistic locking. Memory updates from background summarization jobs across millions of records (high frequency, low contention) fit optimistic concurrency. Mixing them carelessly within a single transaction (FOR UPDATE plus a version check on the same row you just locked) does redundant work and signals confusion about which model you\u2019re committing to.<\/p>\n<p>The point isn\u2019t which to use. It\u2019s that the database supports both, that they\u2019re both ACID-correct, and that the agent doesn\u2019t implement either in application code.<\/p>\n<p>State persistence is not memory, but memory needs it<\/p>\n<p>Let me close with a distinction that matters more than most architecture discussions acknowledge.<\/p>\n<p>What I\u2019ve described in most of this article is a state-and-knowledge persistence layer. It\u2019s the substrate. By itself, it\u2019s not memory in the full sense. Memory is what happens when an intelligent system uses that substrate well: choosing what to remember, summarizing what to keep, weighing old against new, recognizing when something it believed was wrong, and bringing the right recollection to the right moment. The substrate doesn\u2019t do those things. The agent does. But the agent can\u2019t do those things without a substrate that supports them.<\/p>\n<p>\u201cThe substrate doesn\u2019t do those things. The agent does. But the agent can\u2019t do those things without a substrate that supports them.\u201d<\/p>\n<p>The question I\u2019d ask any team building production agents is not \u201cwhich vector database\u201d or \u201cshould we use Redis.\u201d It\u2019s whether the substrate underneath your agent supports the full set of memory behaviors you\u2019ll need within twelve months. Persistence, yes. But also: structured queries against episodic history. Vector recall over compressed summaries. Confidence decay computed at read time. Contamination invalidation as a first-class operation. Both pessimistic and optimistic concurrency. Horizontal <a href=\"https:\/\/thenewstack.io\/cloud-native-and-open-source-help-scale-agentic-ai-workflows\/\" data-wpil-monitor-id=\"3842\" class=\"local-link\" rel=\"nofollow noopener\" target=\"_blank\">scale as the agent<\/a> fleet grows.<\/p>\n<p>This is what we\u2019ve been thinking about as we\u2019ve evolved <a href=\"https:\/\/www.pingcap.com\/\" class=\"ext-link\" rel=\"external  nofollow noopener\" onclick=\"this.target=&#039;_blank&#039;;\" target=\"_blank\">TiDB<\/a> to support agent workloads. Not \u201ca vector database that also does SQL,\u201d and not \u201ca relational database with vectors bolted on.\u201d But a substrate that handles all the storage patterns a real memory system needs, with the consistency and scale properties of distributed SQL. The schema is the easy part. The patterns the substrate enables are where the agent\u2019s actual memory lives.<\/p>\n<p>The workload is new. The discipline is older than that.<\/p>\n<p>\t<a class=\"row youtube-subscribe-block\" href=\"https:\/\/youtube.com\/thenewstack?sub_confirmation=1\" target=\"_blank\" rel=\"nofollow noopener\"><\/p>\n<p>\n\t\t\t\tYOUTUBE.COM\/THENEWSTACK\n\t\t\t<\/p>\n<p>\n\t\t\t\tTech moves fast, don&#8217;t miss an episode. Subscribe to our YouTube<br \/>\n\t\t\t\tchannel to stream all our podcasts, interviews, demos, and more.\n\t\t\t<\/p>\n<p>\t\t\t\tSUBSCRIBE<\/p>\n<p>\t<\/a><\/p>\n<p>    Group<br \/>\n    Created with Sketch.<\/p>\n<p>\t\t<a href=\"https:\/\/thenewstack.io\/author\/ed-huang\/\" class=\"author-more-link\" rel=\"nofollow noopener\" target=\"_blank\"><\/p>\n<p>\t\t\t\t\t<img decoding=\"async\" class=\"post-author-avatar\" src=\"https:\/\/www.europesays.com\/ai\/wp-content\/uploads\/2026\/05\/00b4347d-cropped-44729a2f-ed-huang.png\"\/><\/p>\n<p>\n\t\t\t\t\t\t\tEd Huang is co-founder and CTO of TiDB powered by PingCAP. While he was at Wandou Labs, he worked on clustering Redis and created and open sourced Codis, a proxy based high performance Redis cluster solution. Ed then decided to&#8230;\t\t\t\t\t\t<\/p>\n<p>\t\t<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"A few months ago, I was reviewing a customer support agent who had a strange failure pattern. A&hellip;\n","protected":false},"author":2,"featured_media":34751,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[405,7537,22152,12100],"class_list":["post-34750","post","type-post","status-publish","format-standard","has-post-thumbnail","category-agentic-ai","tag-ai-agents","tag-artificial-intelligence-agents","tag-pingcap","tag-post-contributed"],"_links":{"self":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/posts\/34750","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/comments?post=34750"}],"version-history":[{"count":0,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/posts\/34750\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/media\/34751"}],"wp:attachment":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/media?parent=34750"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/categories?post=34750"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/tags?post=34750"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}