Long-Term Memory
Writing facts back — this buyer prefers replacements, this seller always disputes damage claims — makes an agent feel remarkable in a demo and is the fastest way to build a system that is confidently wrong for months. A remembered fact is an unverified claim that will be re-read later as established truth, by a component that has no way of telling the difference between something it observed and something it once inferred.
So the design is not the storage. Any table will do. The design is what gets written, on what evidence, and when it stops counting. Sundry shipped this last of everything in this chapter, on the narrowest scope the team could define, and still had two incidents in the first month.
What Is Worth Remembering
One distinction carries the entire feature. "Chose a replacement twice, on tickets T-36540 and T-39177" is a fact: it has dates, evidence, and a person could confirm or deny it. "Prefers replacements" is a guess about somebody derived from two data points. The first is an observation. The second is a characterization, and characterizations are what become permanent context that shapes every later interaction with a person who never agreed to any of it.
Worth storing: dated outcomes and stable attributes. This address has failed delivery twice with the same carrier. This seller took nine days to answer each of the last three claims. This account is a business account. Not worth storing: anything phrased as what somebody is like. The test is whether the person described could read the record and call it factually wrong — "difficult customer" cannot be argued with, which is precisely the reason it must not be there.
Write Discipline
Extraction runs after the ticket closes, not during it, and it produces candidates rather than facts. Each candidate is validated against a schema — subject, a predicate from a closed set, a value, the ticket it came from, the date it was observed, the date it expires — and anything that fails validation is dropped and counted. The closed predicate set does more work than any prompt could: it makes "difficult" unrepresentable rather than discouraged, which is Chapter 5's lesson again in a different store.
# accepted: an observation, with evidence and a date {"subject": "buyer:41827", "predicate": "chose_replacement", # one of 14 allowed predicates "value": {"order_id": "SU-82605"}, "evidence": "T-39177", # the ticket this was read from "observed": "2026-03-10", "expires": "2027-03-10"} # rejected: an inference dressed as a fact, and nothing to cite {"subject": "buyer:41827", "predicate": "prefers_replacements", # not in the set — dropped "value": {"confidence": "high"}, "evidence": None}
The two records differ in one word and in everything that matters. The accepted one says what happened, on which order, read out of which ticket, and until when it counts. The rejected one states a conclusion about a person, offers a confidence score in place of evidence, and cites nothing — and note that it is rejected by the schema rather than by anyone's judgement, which is the only kind of rejection that still happens at 4,200 tickets a week.
Records are appended, never mutated. A third replacement in June adds a row; it does not overwrite March. That costs a little storage and buys the ability to remove exactly one wrong fact later without guessing what it replaced or what was built on top of it.
Compounding Error
Here is the chain that cost Sundry a month. In March an early version of the extractor wrote "prefers replacements" for a buyer who had accepted two. In April the agent read that fact, led with a replacement offer, and the buyer took it after some back and forth. In May the extractor read that April thread and wrote a second fact from it: declines refunds. By June a buyer who wanted their money back was offered a replacement twice before escalating, and the record justifying that behaviour cited a ticket in which the agent's own offer was the only evidence.
That is the shape to watch for: the system's output becomes the evidence for its next belief, and each generation looks better supported than the last. Provenance and expiry are what make it unwindable. Provenance lets you trace the May fact back to the April ticket back to the March fact and delete the root. Expiry means a wrong belief stops mattering eventually even if nobody notices it. With neither, the only remedy left is deleting the entire store — which Sundry did once, in June, and it was the right call.
Retrieval, Not Injection
Long-term facts are looked up when they are relevant, exactly like policy passages. The tempting alternative is a customer profile block pasted into every ticket's context, which is cost on 100% of the queue for value on the 14% that are repeat contacts within 90 days — and, worse, it puts stale claims about a person in front of the model on tickets where nobody asked.
Sundry's version is a lookup by subject that the agent calls when the ticket suggests history matters, returning at most three facts, each with its observed date and its evidence ticket, under the same citation rule as policy. The dates are load-bearing: a model that can see an observation is from 2024 treats it differently from one made last week, and a reply that acts on a remembered fact says which one it acted on.
What Never to Store
The never-store list is written before the feature ships, not after the first incident, and it is short: payment details, government identifiers, health information and anything from which a protected characteristic could be inferred, and speculative characterizations of a person. The first three sound too obvious to need writing down, right up until an extraction step is pointed at raw ticket text — buyers explaining why they need a refund type card numbers and medical reasons into support tickets every week.
The list is enforced in the schema and in a filter on extraction, never in a prompt asking the model to be careful. Deletion has to reach the store as well: a customer deletion request that clears the transcript and leaves the derived facts standing has deleted the evidence and kept the conclusion, which is the worst available outcome. Chapter 13 covers how that runs in production; the decision belongs here, while there is still nothing to delete.
Does It Help
Barely. On repeat contacts, resolution moved two points. Repeat contacts are 14% of Sundry's queue, so the queue-wide effect is under half a point — well inside the noise of a 120-ticket eval set, so it was measured on the repeat-contact slice specifically, and it could not have been measured at all if the eval set had not already existed.
Against those two points, two incidents in the first month. The compounding chain above, which took three months to appear and a day to diagnose once somebody looked at provenance. And a stored observation about a seller — disputed damage claims on six of the last eight — which the agent quoted almost verbatim into a message_seller call, where a person at that company read it. The fix for the second was excluding seller facts from anything outbound, and it is the kind of boundary Chapter 12 makes explicit.
Ship this last, after the eval set exists, scoped to the narrowest set of observations a support agent would actually write on a card. Expect the review conversation to take longer than the code, and expect the number to be small. It is the feature most likely to hurt without anybody noticing, and the only defence against that is having built everything else in this chapter first, so you can see two points move against a baseline you trust.
- Storing inferences as facts — "difficult customer" becomes permanent context that shapes every future interaction, and the person it describes can never see it or correct it.
- Writing a remembered fact with no provenance — it cannot be verified, cannot be traced when it turns out to be wrong, and cannot be explained to the customer it is about.
- Setting no expiry — a preference recorded two years ago outranks what the same buyer did last week, and nothing in the system ever notices the record aged.
- Injecting the whole profile into every ticket — cost on the entire queue for value on the 14% that are repeat contacts, plus stale claims in context on tickets that never needed them.
- Building it before the eval set exists — a two-point move cannot be distinguished from noise without one, and this is the feature most likely to come out negative.
- Store dated observations with the evidence ticket attached, and use a closed predicate set so a characterization has no field to live in.
- Retrieve long-term facts by subject when the ticket calls for them, and never inject a profile block into every context.
- Set an expiry on every record and build a deletion path that a real customer request can reach, including facts derived from a deleted transcript.
- Ship it after the eval set exists and measure it on repeat contacts specifically, where the effect is large enough to be visible at all.
Knowledge Check
Which of these belongs in long-term memory as written?
- "Prefers replacements over refunds" — a stable preference the buyer has shown twice now
- "Chose a replacement on order SU-82605, from ticket T-39177, on 10 March"
- "Likely to accept a replacement, confidence high" — an inference with its certainty recorded
- "Tends to argue about delivery charges" — a pattern seen across three separate tickets
A remembered fact turns out to be wrong. What makes it possible to unwind everything built on it?
- The confidence score written alongside each fact, which flags the least reliable records first of all
- Provenance on every record, which traces a later fact back through the ticket that produced it
- Overwriting each fact in place, so only the newest version of a claim ever survives
- A periodic summary of the store, which condenses old records into current conclusions
How does compounding error actually happen in a memory store?
- The store grows until retrieval returns too many facts and the model cannot weigh them
- Records degrade over time as each rewrite loses detail from the original observation
- Two facts contradict each other, and the model picks whichever appears first in context
- A wrong fact shapes behaviour, and the extractor later reads that behaviour as fresh evidence
Why is a customer profile block injected into every ticket the wrong design?
- It spends context on the whole queue for value on the 14% of tickets that are repeat contacts
- A profile block is large enough on its own to push a long ticket past the model's context window
- Injected profiles break prompt caching entirely, since the block changes on every single request
- The model treats injected facts as considerably less authoritative than facts a tool returned to it
You got correct