Memory Architectures
What the agent keeps and what it must forget — four kinds of memory, the write policy that decides everything, and how memory gets poisoned.
In this illustrative incident, Reference Agent starts giving consistent wrong answers six weeks after its memory system ships. The stored claim is: “reconciliation cutoff is 18:00 UTC.” An engineer originally said, “we usually cut off around six.” The agent converted one team’s informal habit into a global configuration claim. Later reconciliations retrieve and trust it. Authoritative configuration shows a separate cutoff for each tenant, with none set to 18:00 UTC.
The memory captured, stored and retrieved the statement as designed. It had a read policy and no write policy.
10.1Memory is not a database#
Storage alone does not determine memory quality. Three policies govern what is retained and reused:
The write policy decides what is worth remembering. This is where quality is won or lost, and it is systematically under-designed because writing is easy and the cost of a bad write is deferred.
The read policy decides what surfaces for a given task, how much, and how it is presented.
The forget policy decides what leaves — through expiry, contradiction, decay, or eviction. Without one, stale and unsupported entries can accumulate and make later answers less reliable.
The asymmetry between these is the key insight of the chapter. A bad read can damage the current decision, including a consequential action. A bad write costs every future run that retrieves it, and it does so with the full confidence of a stored fact. Writes persist until revoked or expired; reads propagate that stored risk into new work. Design accordingly.
Design Rule
Memory is three policies and a store, and the store is the least of them. A wrong read can cause immediate harm; a wrong write can repeatedly expose future runs. Make writing hard and reading cheap — the opposite of what feels natural.
10.2The four kinds, and why the distinction is operational#
Borrowing a taxonomy from cognitive psychology is only useful if the categories have different engineering consequences. These four do: they differ in lifetime, in who may write them, in how they are retrieved, and in what happens when they are wrong.
Working memory is the transcript and plan, scoped to the run, with checkpoints retained for recovery (Chapter 9). Persisting it indiscriminately across runs can introduce stale plans into new tasks.
Episodic memory is what happened, timestamped. “Run 4471 for tenant acme ended as escalated because the billing lead did not respond within the window.” Derive entries from recorded events and preserve their source. A model-generated episode summary can misstate what happened, so an episodic record is not automatically true or harmless. Its value is in patterns across many entries, which makes it the substrate for Chapter 25’s error analysis and Chapter 27’s trajectory harvesting.
Semantic memory stores assertions about the world as evidence for future runs. The cutoff incident involved this kind. False assertions can affect many later tasks, so writes need provenance, confidence, contradiction detection and gating.
Procedural memory is how to do things: skills, runbooks, learned procedures. It gets its own chapter (26) because the write path involves verification rather than mere validation. Its saving grace is that it is self-correcting in principle: a procedure with meaningful verification can fail visibly and be demoted; an unverified procedure may silently return the wrong result, which is a property semantic facts do not have.
10.3The write policy is the whole design#
Here is the minimal teaching store. It would not have prevented the ledger incident: length and lexical deduplication do not validate truth, scope or authority.
def write(self, text: str, kind: MemoryKind, *, source: str = "agent",
confidence: float = 0.6, scope: str = "user") -> MemoryItem | None:
text = " ".join(text.split())
if len(text) < 12: # toy length filter
return None
for existing in self.items:
if existing.kind is kind and _similar(existing.text, text):
existing.confidence = max(existing.confidence, confidence)
existing.touch() # reinforce, don't duplicate
return existing
item = MemoryItem(text=text, kind=kind, source=source,
confidence=confidence, scope=scope)
self.items.append(item)
self._evict()
return itemA short valid fact can fail that arbitrary length filter, and a long false claim can pass. The reference implementation’s gate is deliberately minimal, and a production semantic write policy needs four more properties.
Provenance is mandatory, not optional. Every semantic entry records how it came to be believed: which tool call, which document, which human, at what time. The ledger entry’s provenance would have been “inferred from a user’s conversational remark,” and that alone would have been enough to distrust it. A fact without provenance cannot be re-verified, cannot be attributed, and cannot be revoked when its source turns out to be wrong.
Confidence must reflect the source, not the phrasing. A fact read from a configuration file is not the same as a fact inferred from a sentence someone typed. Use source categories and verification status; numeric values such as 0.95 or 0.3 are illustrative policy scores, not calibrated probabilities. A configuration can be stale and an authorized human can be mistaken. If low-evidence claims must not be used, enforce an explicit eligibility filter before ranking; a low confidence multiplier alone does not prevent retrieval.
Only claims with explicit scope and validity qualify. Ask whether a claim will remain true next month and where it applies. “The cutoff is 18:00” does not establish tenant scope or a configured value. A current tenant value needs an observation timestamp and validity interval. Its pointer to billing.yaml may remain useful after the value changes.
Contradiction is a first-class event. When a new fact conflicts with a stored one, the system must do something other than store both. Something means: use domain-authoritative sources and explicit supersession within the same scope and validity period; do not let a confidence score or retrieval recency invent authority; mark the older as superseded rather than deleting it, so the change is auditable; and — for high-confidence conflicts — surface it to a human rather than choosing silently.
The semantic write gate
Do not let the agent write semantic memory as a side effect of conversation. Route every candidate through a gate, and make the gate cheap enough to run always.
- Shape check. Is it a single, declarative, self-contained claim? Multi-clause narrative is not a fact.
- Durability check. Would this be true next month? Session-scoped observations go to episodic, not semantic.
- Generality check. Is it true beyond this one situation? If it is about one run, it is episodic.
- Provenance check. Can we name how we know? No provenance, no write.
- Confidence assignment from the source ladder, not the model’s certainty. Models are confident about inferences.
- Contradiction check against existing entries. Resolve only where authoritative metadata establishes precedence; preserve and escalate unresolved conflicts.
- Deduplication against near-identical entries — reinforce rather than duplicate.
A model may propose structured claims, but code must enforce scope, evidence requirements and allowed transitions. Open-ended contradiction detection may itself require retrieval or model assistance; deterministic code can resolve only the conflicts its schema and domain rules capture. Measure gate cost and false acceptance rather than assuming a fixed price.
10.4Put numbers on the write gate#
A gate feels over-engineered until you count the writes it prevents. Consider an illustrative month for Reference Agent: 10,000 completed runs, each producing eight plausible memory candidates. That is 80,000 opportunities to turn something said or observed once into something the system may believe for months.
The first job is classification, not scoring. Suppose 62 per cent of those candidates describe what just happened or what the current run is doing. They belong in episodic or working memory, so 49,600 candidates leave the semantic path immediately. That is not lost knowledge. The run record remains available for audit and later error analysis; it simply does not masquerade as truth about the world.
The remaining 30,400 candidates face the semantic gate. Use round illustrative rates to make the capacity decision visible:
| Stage | Candidates entering | Rejected or merged | Candidates leaving |
|---|---|---|---|
| Classify by kind | 80,000 | 49,600 records routed elsewhere | 30,400 |
| Shape, durability and generality | 30,400 | 24,320 transient or compound claims | 6,080 |
| Provenance required | 6,080 | 1,520 with no re-verifiable source | 4,560 |
| Deduplicate | 4,560 | 3,192 near-duplicates reinforced, not appended | 1,368 |
| Contradiction gate | 1,368 | 41 held for review | 1,327 accepted |
Only 1.7 per cent of the original candidates become new semantic entries. That is an illustrative acceptance rate, not a target. A memory team that reports the other 98.3 per cent as a lost-write rate has chosen the wrong objective. The objective is not to preserve every potentially useful sentence. It is to minimise false beliefs while retaining enough durable facts to improve later work.
The same arithmetic sets an operational review budget. The illustrative contradiction rate above is 3 per cent of unique candidates: about 41 reviews a month, or two per working day. If a reviewer needs ninety seconds to inspect the candidate, its source and the conflicting entry, the queue costs roughly one hour a month. You can now argue about policy with a number. If the queue grows to 400, do not silently lower the gate. First ask whether candidate generation is too eager, whether one noisy source is producing most conflicts, or whether the underlying domain genuinely changes that often.
Measure errors per accepted write, not acceptance alone. Suppose a sample audit finds that eight of the 1,327 accepted entries are unsupported. The false-accept rate is 8 / 1,327 = 0.60%. That looks small until exposure enters the calculation. If each wrong entry is retrieved twelve times before correction, those eight bad writes create 96 contaminated recall events. One bad acceptance is not one bad outcome; it is a multiplier. Record both numbers:
- false-accept rate — unsupported semantic entries divided by accepted semantic writes;
- poison exposure — downstream retrievals of entries later revoked or superseded.
The first tells you whether the write gate is discriminating. The second tells you the blast radius created by the mistakes that passed it. A falling false-accept rate with rising poison exposure means the remaining errors are concentrated in high-retrieval entries, where confidence, scope or ranking is amplifying them.
There is an equally important false-reject question. Sample candidates the gate rejected and ask whether later runs had to rediscover the same durable fact. If 600 rejected claims caused repeated tool calls, the gate is wasting work even if it is perfectly safe. Track rediscovery cost as repeated authoritative look-ups attributable to absent memory. The useful operating point is not maximum retention or maximum rejection. It is the lowest combined cost of false acceptance, human review and rediscovery, measured against task success with memory disabled.
You can make that trade explicit with a simple loss function. Give each false acceptance a weight of 20, because one poison can affect many runs; each false rejection a weight of 1, because Reference Agent can usually look the fact up again; and each human review a weight of 0.25. If one candidate threshold produces 8 false accepts, 110 false rejects and 41 reviews, its illustrative loss is (8 × 20) + (110 × 1) + (41 × 0.25) = 280.25. A stricter threshold that produces 3 false accepts, 260 false rejects and 18 reviews scores 324.5. It is safer on the headline metric and worse overall under these weights. Change the weights for your domain — a mistaken bank destination may deserve 1,000 while a redundant configuration lookup may cost almost nothing — but write them down before tuning. Otherwise the team will optimise whichever error is easiest to count.
Do this calculation by memory kind and action consequence. A wrong preference such as date format creates annoyance; a wrong tenant policy can direct an action. They should not share one global confidence threshold. Route high-consequence claims to authoritative re-verification at read time even after they pass the write gate. Memory may tell Reference Agent where to look for a payment rule; it should not be the final authority for moving money.
11Candidate volume is a workload characteristic, not a success metric. A release that halves semantic writes while keeping task success flat has probably improved the memory system: fewer persistent claims, fewer contradiction checks and less future surface to audit.10.5Retrieval, decay, and the ranking that matters#
Score on more than similarity. Pure semantic similarity ranks by topical relatedness and ignores everything else that determines usefulness. The reference implementation combines three terms:
def score(self, query_terms: set[str], now: float | None = None) -> float:
now = time.time() if now is None else now
terms = set(re.findall(r"[a-z0-9]{3,}", self.text.lower()))
overlap = (len(terms & query_terms) / math.sqrt(len(terms))) if terms else 0.0
age_days = max(0.0, (now - self.last_used_at) / 86_400)
recency = 0.5 ** (age_days / 30.0) # 30-day half-life
return overlap * self.confidence * (0.4 + 0.6 * recency)The recency term matters more than it looks. Recency changes ranking, but neither it nor similarity establishes truth. This implementation refreshes last_used_at on retrieval, so a frequently recalled false claim can remain highly ranked. Corrections need explicit supersession and source verification. The 0.4 + floor prevents old facts from disappearing entirely, which matters because some facts are genuinely stable.
Cap hard, and present with provenance. Three to five entries, not twenty. The recalled block should be small, low-priority, and volatile in the assembler’s terms (Chapter 8) — the first thing dropped under pressure, because a memory that displaces the current task’s evidence is a net loss. And present each with its source and confidence:
- (semantic, conf 0.9, via ops-wiki) ledger_prod is the payments database for tenant acme
- (episodic, conf 0.9, via run:4471) Goal 'reconcile January' ended as escalated: billing lead did not respondShowing confidence and source lets the model weigh entries rather than treating all recalled text as equally authoritative. It also makes the failure legible in a trace: an operator reading the transcript can see that the agent acted on a 0.3-confidence conversational inference, which is a far better diagnostic than seeing a bare sentence.
10.6Read the reference store as policy, not infrastructure#
The reference implementation is intentionally small enough to inspect in one sitting. That makes it useful for understanding the policy boundary, but dangerous if you mistake a teaching implementation for a production memory service. Read each mechanism for the guarantee it actually provides.
Start with deduplication. write() compares a candidate only with entries of the same kind and calls _similar():
def _similar(a: str, b: str, threshold: float = 0.8) -> bool:
ta = Counter(re.findall(r"[a-z0-9]{3,}", a.lower()))
tb = Counter(re.findall(r"[a-z0-9]{3,}", b.lower()))
if not ta or not tb:
return a.strip().lower() == b.strip().lower()
inter = sum((ta & tb).values())
union = sum((ta | tb).values())
return (inter / union) >= thresholdThis is token-multiset Jaccard similarity. It catches repeated wording cheaply and deterministically: “tenant acme uses ledger_prod for payments” and a lightly reordered version share most tokens. It does not establish semantic equivalence. “Tenant acme permits scheduled external transfers from the settlement account after review” and the same sentence with “blocks” instead of “permits” score 11 / 13 = 0.846, above the 0.8 threshold, even though they are opposites. Conversely, “payments data lives in ledger_prod” may fail to match a longer sentence expressing the same fact.
That limitation defines the boundary: deduplication is not contradiction detection. The former asks whether two strings are similar enough to avoid duplicate storage. The latter asks whether two claims can both be true under the same scope and time interval. You can keep the first as a cheap local check; the second needs structured fields, a constrained comparison, or an authoritative re-read. Raising the Jaccard threshold does not solve negation, time or scope.
The reinforcement path contains another deliberate choice:
if existing.kind is kind and _similar(existing.text, text):
existing.confidence = max(existing.confidence, confidence)
existing.touch()
return existingmax, rather than an average, prevents a low-confidence repeat from diluting a fact already established by a stronger source. It also means repetition never makes a claim more trustworthy on its own — forty copies of the same rumour remain no better than the best source among them. That is the right default. But provenance is stored as one source string on the existing item, so the teaching store does not preserve the new corroborating source. Production code needs a set of evidence records, each with its own source, timestamp and status. Otherwise you cannot distinguish independent corroboration from forty documents copied from one original.
Now inspect retrieval. retrieve() tokenises the query, filters by kind, drops scores below min_score, returns at most k, and calls touch() on each result. Two consequences follow. First, retrieval is state-changing: merely surfacing an entry increments uses and resets last_used_at. A frequently recalled memory stays fresh even if nobody verifies it. Recency here measures attention, not truth. Name the field accordingly in a production schema — last_retrieved_at — and keep last_verified_at separate.
Second, the score is lexical. It is predictable, offline and easy to debug, which is exactly what the reference harness needs. It will miss synonyms and paraphrases. Replacing it with embeddings changes the recall mechanism, but it does not change the contract around it: filter scope before ranking, retain source and confidence, enforce k, and trace every inclusion and exclusion. A better similarity model cannot compensate for a missing tenant predicate.
The missing predicate is visible in the code. MemoryItem carries scope, and write() accepts it, but retrieve() has no scope argument and filters only on memory kind. That is a deliberate simplification for the single-process reference harness, not multi-tenant isolation. Copying this class behind an enterprise endpoint would make the chapter’s worst scope defect possible: every item in the process becomes a ranking candidate regardless of who owns it.
Do not repair that by adding scope: str = "user". A default merely converts an obvious omission into a plausible wrong query. Require an explicit scope object containing tenant and, where applicable, user identity; reject calls that omit it; apply the predicate before computing any score; and include the resolved scope in the trace. The negative test matters more than the happy path: seed a highly relevant tenant-A entry, query as tenant B with identical wording, and assert an empty result. Then query without a scope and assert an error, not a global search. Isolation is a candidate-set property, so no later reranker, prompt instruction or output filter can restore it reliably.
Finally, read eviction literally:
self.items.sort(key=lambda m: (m.kind is MemoryKind.SEMANTIC,
m.score(set(), now), m.uses))
overflow = len(self.items) - self.max_items
del self.items[:overflow]The first key protects semantic entries because False sorts before True: non-semantic items reach the eviction end first. The empty query makes lexical overlap zero for every item, so the score term is also zero for every item. In this implementation, eviction therefore distinguishes kind and then use count; it does not, in practice, order entries by confidence or age. That is acceptable for a bounded teaching store because the behaviour is deterministic and the comment states the intended direction. It is not a production retention policy.
A production sort key should be explicit rather than smuggling retention through a retrieval score. For example: protect legally or operationally required records; exclude entries under investigation from use but not audit; evict expired episodic entries; then rank remaining candidates by verified confidence, retrieval utility and age. Test each ordering with fixtures. A retention decision must be explainable as “expired and never useful,” not “its composite float was 0.017 lower.”
Design Rule
Keep three clocks for every persistent claim: when it was written, when it was last retrieved, and when its source was last verified. Retrieval refreshes attention, never truth. Only re-verification may extend a fact’s validity.
10.7Forgetting is a feature#
A store that only grows becomes slower and less accurate at exactly the moment it matters most. Four mechanisms, and you want all four.
Eviction under pressure. The reference implementation first protects semantic entries, then evicts by use count; because it scores against an empty query, it does not order entries by confidence or age. That deterministic rule suits the bounded teaching store. A production policy should explicitly expire entries first, then consider low confidence, no retrieval utility and age — subject to audit and retention requirements — rather than implying that the reference code already does so.
Expiry by nature. Some facts have known lifetimes. An on-call rotation is true for a week. A quarter’s targets are true for a quarter. Recording an expiry at write time is nearly free and prevents a whole class of confidently-stale answers.
Supersession on contradiction. Mark superseded entries rather than deleting them, so the audit trail survives.
Explicit revocation. A human must be able to say “forget that, it was wrong,” and the system must comply — including removing it from any derived artefacts. This is both an operational necessity and, for personal data, frequently a legal one (Chapter 30). Build the delete path early; retrofitting it into a system that has propagated facts into summaries and skills is genuinely hard.
22The under-appreciated case: forgetting must propagate. A revoked fact that has already been baked into a skill's steps, a compaction summary, or a knowledge-graph edge is still operative. Anything derived from memory needs a link back to its sources, which is one more argument for provenance being mandatory rather than nice.10.8Memory poisoning#
The ledger incident was an accident. The same mechanism is an attack surface, and it is a serious one because memory is persistent, trusted, and cross-session.
The attack: get a false fact into semantic memory, and every future run retrieves and acts on it. Unlike an injection contained to one run (Chapter 21), a poisoned memory affects every run until someone notices — and nobody notices, because a retrieved fact looks exactly like a correct retrieved fact.
| Vector | How it works | Defence |
|---|---|---|
| Conversational injection | A user asserts a false “fact” that the agent records | Source-based confidence: user statements are not system truth; require corroboration before acting |
| Document injection | A document in the corpus contains instruction-shaped text that becomes a memory | Never write memory from untrusted content without a gate; treat retrieved text as data (Ch 21) |
| Cross-tenant bleed | Tenant A’s fact retrieved for tenant B | Scope as a required parameter on every read and write; a test that asserts empty rather than cross-tenant |
| Slow drift | Many small plausible writes gradually shift the agent’s model of the world | Periodic audit of high-retrieval-count entries; alert on write-rate anomalies |
The last one is the hardest and the most interesting. No single write is suspicious; the aggregate is. One useful diagnostic is review of the highest-exposure entries: list the twenty semantic memories retrieved most often, and have a human read them. Scale review effort to risk, exposure and write volume; sampling alone cannot establish security. The ledger entry would have been at the top of that list.
Failure Mode
Do not let the model write semantic memory in the same turn it answers the user. The write becomes a side effect of a fluent conversation, unexamined and unaudited, and its confidence inherits the model’s conversational assurance rather than its evidential basis. Make memory writing a separate, explicit, gated operation — ideally reviewed asynchronously — so that “the agent said something plausible” never silently becomes “the system believes something.”
10.9The poison chain, turn by turn#
You debug memory poisoning by reconstructing the chain, not by staring at the final wrong answer. Consider an illustrative Reference Agent incident inside one tenant.
Day 0, ingestion. A finance analyst asks Reference Agent to summarise a newly uploaded reconciliation guide. The guide contains an outdated sentence: “Exception reports are sent to the regional settlement mailbox.” It also contains a current instruction to use the case-management queue. The summary is broadly correct, but the memory-candidate call extracts the first sentence as a clean declarative claim.
Day 0, promotion. The candidate is labelled semantic, scoped to the tenant, and assigned confidence 0.7 because it came from documentation. The pipeline records source="reconciliation-guide.pdf", but not the document version, page, quoted span, owner or validity interval. The shape, durability and provenance checks all pass because provenance has been reduced to “a filename exists.” No contradiction is found: the case-management instruction is expressed as a procedure, so a lexical comparison does not recognise the conflict. The entry is accepted.
Day 4, first exposure. Another user asks where exception reports go. Retrieval finds the memory because the words overlap exactly. Reference Agent answers with the mailbox, and touch() increments the entry’s use count and updates its last-used time. The answer is wrong, yet the retrieval has made the memory look more useful and more recent.
Day 9, reinforcement. A transcript summary contains the wrong answer from day 4. Candidate generation sees a near-duplicate. Deduplication returns the existing item and calls touch() again. It does not create an obviously suspicious second fact. The poison is now self-reinforcing: retrieval produced text that later became evidence of repetition, even though no independent source corroborated it.
Day 16, visible symptom. Operations notices that Reference Agent keeps recommending a retired mailbox. The team first suspects the model, then the retrieval ranking. Switching models changes the phrasing but not the answer. Increasing k makes matters worse because the same stored claim still appears, now beside more irrelevant context. The symptom is at read time; the defect occurred at write time.
Day 16, incomplete correction. An operator writes a new memory: “Exception reports use the case-management queue.” Both entries now rank for the same question. The older poison has more uses; the newer correction has more recency. Depending on wording, either may win. This is why adding the right fact is not a revocation mechanism. A contradiction must supersede the old entry and prevent it from appearing in normal recall.
The containment sequence starts with evidence preservation. Freeze semantic writes for the affected scope, but do not delete the suspect entry yet. Capture its identifier, retrieval history, source record and every memory or procedure derived from it. You need that graph both for diagnosis and for complete revocation.
Next, quarantine the entry so normal retrieval cannot return it. Mark it under_review or revoked; do not express quarantine by lowering confidence, because a sufficiently close lexical match can still outrank weaker evidence. Locate descendants: summaries that quote it, semantic entries produced from those summaries, generated skills that embed the mailbox, and queued actions whose arguments came from it. Cancel pending effects and rebuild derived artefacts from sources that remain valid.
Then install the correction from an authoritative source. Store the case-management rule with a precise source location, owner, scope, valid_from and, where known, valid_until. Mark the retired-mailbox entry as superseded by the new identifier. Keeping that edge preserves the audit trail and teaches evaluation: every trace that retrieved the old entry becomes a regression case.
Finally, repair the boundary that admitted the poison. “Better prompts” is not enough. Require a source locator that can be fetched again, classify document freshness separately from source type, reject semantic candidates derived from the agent’s own prior answers, and run contradiction checks over normalised subject-predicate-object fields where the domain supports them. For this incident, both claims normalise to the subject exception_report_destination; the conflicting objects become obvious even though one source says “mailbox” and the other says “case-management queue.”
The general lesson is sharper than “documents may be stale.” Provenance without re-verifiability is decoration. A filename cannot tell you whether the source changed, whether the quoted sentence was superseded elsewhere in the same document, or whether a derivative came from independent evidence. Store enough to fetch the exact evidence again, and make freshness a property of that evidence rather than of how often the memory has been recalled.
| Incident signal | What it reveals | Immediate response |
|---|---|---|
| Retrieval count rises after wrong answers | Attention is refreshing poison | Stop treating last_retrieved_at as freshness |
| Near-duplicate arrives from a transcript | Circular reinforcement | Reject agent-generated derivatives as corroboration |
| Correction coexists with old claim | No supersession state | Quarantine old entry; add an explicit replacement edge |
| Source cannot be fetched at the recorded location | Provenance is not re-verifiable | Block the write or require human review |
| Same revoked claim appears in a skill or summary | Revocation did not propagate | Traverse and rebuild all derivatives |
10.10Scope, and the multi-tenant hazard#
Every memory entry has a scope, and getting this wrong is the most serious defect class in a multi-tenant agent.
User scope is the default: preferences, working style, the person’s own context.
Tenant scope is shared across users in an organisation: systems, conventions, policies.
Global scope is genuinely universal and should be nearly empty — most things that feel global are actually tenant-scoped, and a fact promoted to global is a fact that will be wrong for someone.
The engineering rule is that scope is a required argument on every read and write, never a default and never an optional filter. A retrieval function whose signature permits omitting scope will eventually be called without it, and the result is a data-protection incident that is silent, invisible in quality metrics, and discovered by a customer. The test to write: call retrieval without scope and assert an explicit error, not a global query.
There is also a subtler scope question: whose memory is it? A fact the agent learned while working for user A, in tenant T, about a system owned by tenant T — is that user A’s memory or tenant T’s? The defensible answer is tenant-scoped for facts about systems and user-scoped for facts about people, with promotion from user to tenant requiring an explicit act. The indefensible answer is whatever your code happens to do, which is what most systems have.
10.11How shipped systems do it#
Documented products illustrate several bounded memory designs.
Hermes stores bounded memory in MEMORY.md and USER.md, with respective caps of 2,200 and 1,375 characters. It loads a frozen snapshot into the system prompt at session start.1 The current guide requires a separate profile for another agent sharing that installation. The files are small, human-readable and editable. A session snapshot favors stability and caching over immediate freshness.
Devin’s Knowledge feature previously stored trigger-scoped tips and instructions. Its current guide marks Knowledge deprecated and describes gradual automatic migration to Skills in Plugins.2 The new representation changes storage and activation. It does not remove the need for scope, provenance, verification or revocation.
OpenHands uses SKILL.md files with frontmatter triggers and paths, having deprecated an earlier “microagents” format.3 Cline ships a “memory bank” alongside rules and skills.4
These examples use small, human-editable, scoped artifacts, often combined with retrieval; they are not a census of production architectures. Larger learned stores and self-editing context still need strong write policies, verification and revocation.
The pragmatic architecture that follows: a small curated memory in context always (a few hundred tokens, human-reviewable), plus a larger retrieved store queried on demand, with the retrieval treated as evidence to verify rather than fact to trust. Chapter 11 is about doing the second part well.
10.12Current research: experience and persistent attacks#
The original LongMemEval (ICLR 2025) tests extraction, cross-session reasoning, temporal reasoning, updates and abstention. LongMemEval-V2, published as a preprint in May 2026, shifts toward experience in web environments: static and dynamic state, workflows, recurring mistakes and invalid assumptions. Its authors provide 451 questions with long multimodal action histories. These benchmarks motivate separate tests for recall and useful continuation; a question-answering score is not proof of safe autonomous action. LongMemEval, LongMemEval-V2, checked 8 September 2026.
Memory injection is also directly studied. MINJA examines malicious memory created through query-only interaction; the July 2026 GhostWriter preprint studies poisoning in tool-using personal agents. Their threat models and measured results are specific to the evaluated systems. Add cross-session tests in which hostile source text becomes a candidate memory, is recalled later, and must not acquire instruction authority. MINJA, GhostWriter, checked 8 September 2026.
10.13Measuring memory#
Evaluate memory across sessions to capture both its benefits and delayed harms:
Retrieval precision at k. Of the entries recalled for a task, what fraction were actually relevant? Label a couple of hundred by hand once; it is tedious and it gives direct evidence of whether the read policy works.
Write rate and its composition. Semantic writes per hundred runs, split by source. A rising rate is not growth, it is usually a loosening gate. A composition dominated by model-inferred entries is a system about to have a ledger incident.
Contradiction rate. How often does a new candidate conflict with a stored entry? Near-zero can mean a stable domain or missed conflicts; high can mean domain change or a noisy writer. Inspect examples before judging direction.
The A/B that actually matters. Run the same eval suite with memory on and off. This directly tests “is this helping,” and the results are frequently uncomfortable — memory can be neutral or harmful on a workload while adding cost and risk. Better to know.
| Symptom | Likely cause | Fix |
|---|---|---|
| Confidently wrong stable answers | Bad semantic write, high confidence | Tighten the source ladder; audit top-retrieved entries |
| Recall returns nothing useful | Store bloated with low-value entries | Tighten the write gate; strengthen decay |
| Corrections do not take effect | No decay, or no supersession | Add recency decay and contradiction handling |
| Stale answers about changing things | No expiry at write time | Record expiry for facts with known lifetimes |
| Memory helps in demos, not in evals | Read policy tuned on anecdote | Run the memory-on/memory-off A/B and believe it |
Definition
Semantic memory — deduplicated assertions about the world, retrieved as evidence in future runs; the kind that requires a write gate.
Provenance — the record of how a stored fact came to be believed: which source, which call, which human, when.
Memory poisoning — the introduction of false persistent facts, accidentally or adversarially, so later runs may reuse misleading or hostile content.
Key Takeaways
- Memory is three policies and a store, and the store matters least. A bad read can misdirect a run; a bad write can affect many later runs. Validate writes and scope reads.
- The four kinds differ operationally, and the split that matters is records versus claims: all kinds need scope and provenance, while reusable claims and procedures need additional validation.
- A semantic write gate requires shape, durability, generality, mandatory provenance, source-based confidence, contradiction handling and deduplication. Confidence must reflect the source, not the model’s fluency — exclude unsupported conversational inferences through an explicit eligibility rule before ranking.
- Score retrieval on relevance × confidence × recency decay, not similarity alone. Recency is a ranking signal; authoritative supersession handles corrections.
- Forgetting needs four mechanisms: eviction, expiry by nature, supersession on contradiction, and explicit revocation that propagates to derived artefacts.
- Memory poisoning can spread false claims or hostile instructions across sessions. Review high-exposure entries, require source verification and test poisoning across sessions.
- Several documented systems use small files, human editing or trigger-scoped skills; these are design examples, not a census of production memory architectures. Run the memory-on/memory-off A/B before believing your own system helps.
- Track false acceptance and poison exposure separately. A small number of bad writes can create many contaminated recalls, while a falling write rate may be a sign of a healthier gate.
- Retrieval refreshes attention, not truth. Keep written, retrieved and verified timestamps separate, and revoke a bad claim through every summary, skill and queued action derived from it.
Hands-on
Audit your memory before it audits you 1. List your twenty most-retrieved semantic entries and read them. Mark each as correct, stale, over-general, or wrong. Trace questionable entries back to their original evidence. 2. For each entry, try to answer “how do we know this?” Any entry without an answer is an entry to delete and a gate to build. 3. Implement source-based eligibility checks before ranking, and keep unsupported conversational inference out of action-authorising evidence. Re-run the audit and see what falls out of scope. 4. Make scope a required argument on every read and write. Write the test that calls retrieval without scope and asserts an explicit error. 5. Run your eval suite with memory enabled and disabled. If the difference is not clearly positive, you have a cost and risk with no benefit — and you now know before your customers do. 6. Sample fifty rejected semantic candidates. Count how many later caused an authoritative fact to be rediscovered, then compare that cost with the exposure created by accepted entries that were later revoked. 7. Pick one revoked entry and follow its identifiers into summaries, skills and queued actions. If you cannot enumerate every derivative, your delete path is a local operation rather than a forgetting policy.
-
Memory — Hermes Agent user guide. Nous Research. https://hermes-agent.nousresearch.com/docs/user-guide/features/memory Checked 9 November 2026. ↩
-
Knowledge — Devin. Cognition. Deprecation and Skills migration notice, checked 9 November 2026. https://docs.devin.ai/product-guides/knowledge ↩
-
Skills — OpenHands. https://docs.openhands.dev/overview/skills ↩
-
Configuration — Cline. https://docs.cline.bot/getting-started/config ↩