Your Support Agent Still Remembers the Customer Who Was Furious in March. Decide What Memory Expires Before You Decide What It Stores.

Cross-session memory fails less often from forgetting than from remembering too long. A four-class expiry scheme for support agents, and the one field every stored memory needs.

Your Support Agent Still Remembers the Customer Who Was Furious in March. Decide What Memory Expires Before You Decide What It Stores.

A customer writes in about a billing question in September. The support agent opens with an apology for "the frustration you experienced earlier" and offers a goodwill credit. The customer is confused. The frustration was a shipping delay in March, it was resolved in March, and they were perfectly happy until this message made them wonder what the company keeps on file about their mood.

Nothing malfunctioned. The memory layer did what it was built to do: it stored a fact and retrieved it when the same customer came back. The design mistake happened earlier, when the team decided what to store and never decided how long each thing stays true.

Remembering is easy. Expiring is the decision.

Most cross-session memory designs start with a write policy: after each conversation, extract the useful facts and save them. Retrieval then pulls the most relevant ones into context next time. That covers half the lifecycle.

The missing half is that facts about a customer have very different shelf lives:

  • "Prefers email over phone" might hold for years.
  • "Is on the Pro plan" is true until the next billing event, and the billing system already knows it better than memory does.
  • "Was angry about a late delivery" is true for days.
  • "Is waiting on a replacement unit, tracking pending" is true until the unit arrives, and then it is actively harmful.

A memory store with one retention rule treats all four the same. Relevance-based retrieval makes it worse: the angry-customer note is semantically close to almost any complaint, so it keeps getting pulled in.

🔍 Four classes, four expiry rules

Before writing a single memory, sort what the agent might learn into classes, and give each class its own way of dying.

1. Stable preferences: long TTL, overwrite on contradiction

Language, contact channel, "please don't call before 10am". Keep these for a long time, but when a newer conversation contradicts one, replace it instead of appending. Two conflicting preferences in context means the model picks one, and it may pick the old one.

2. Facts owned by another system: don't store them at all

Plan tier, order status, account balance, open tickets. These have a source of truth that the agent can query with a tool. Storing a copy in memory creates a second, slower-updating version of the truth. Store a pointer if anything ("customer has had billing disputes, check the ledger"), never the value.

3. Episodic state: expire on an event, not a clock

"Waiting on a replacement." "Promised a callback from tier 2." These are true until something happens. A fixed TTL is wrong in both directions: too short and the agent forgets an open promise, too long and it asks about a replacement that arrived weeks ago. Tie them to a closing condition the agent or a system event can check.

4. Sentiment and tone: short TTL, or never store

How the customer felt is the most tempting thing to save and the most damaging to replay. If you keep it at all, give it a lifespan measured in days and scope it to the issue that caused it. In many support deployments the right answer is not to store it: the current message already tells you how the customer feels now.

What a memory record needs

Each stored item needs enough metadata for the expiry rule to run. A minimal shape:

{
  "customer_id": "c_1842",
  "class": "episodic",
  "content": "Awaiting replacement headset, RMA opened",
  "created_at": "2026-03-04",
  "expires": { "on_event": "rma_closed", "rma_id": "rma_5531" },
  "source_conversation": "conv_9920"
}

And the retrieval path filters before it ranks:

def load_memories(customer_id, now):
    items = store.fetch(customer_id)
    live = []
    for m in items:
        if m.cls == "system_owned":
            continue                      # never trust a copy
        if m.cls == "sentiment" and now - m.created_at > days(7):
            continue
        if m.cls == "episodic" and event_closed(m.expires):
            continue
        live.append(m)
    return rank_by_relevance(live)

The order matters. If you rank first and filter later, a stale but highly relevant memory wins the top slot and the filter is fighting retrieval instead of shaping it.

The decision, compressed

When an extraction step proposes a new memory, run it through this:

Question If yes
Does another system own this fact? Don't store. Give the agent a tool.
Does it stop being true when something happens? Store as episodic, expire on that event.
Is it about how the customer felt? Short TTL, scoped to the issue, or skip.
Would it still be true and useful in six months? Store as preference, overwrite on contradiction.
None of the above? It probably wasn't worth storing.

The last row does a lot of work. A large share of what extraction prompts produce ("customer asked about refunds", "customer used the mobile app") fits no class, and storing it just adds retrieval noise.

⚠️ Two traps when retrofitting

Backfilling classes onto an existing store. If you already have months of untyped memories, don't let a model classify them all in one pass and trust the result. Default unclassified items to the shortest lifespan, and let the ones that matter be re-learned from new conversations.

Expiring silently. When an episodic memory closes, log why. When a customer says "you told me someone would call back", you want to know whether the promise expired because the callback happened or because a timer ran out.

The point

The September apology wasn't a retrieval bug, and a better embedding model wouldn't have fixed it. It was a stored fact outliving the situation that made it true. For a support agent, deciding when each kind of memory stops being true matters as much as deciding what to remember, so make that decision first and the write policy gets much easier to get right.

Keep reading

Similar posts

Matched on shared tags and category — the more bars, the stronger the overlap with what you just read.