The Problem
An agent has 10,000 memories after 2 years of use. Many say the same thing: "User prefers Python", "User likes Python best", "User's language of choice is Python." Querying them surfaces redundant results. Five slots are spent on the same fact. The relevant but distinct memories — the ones that actually matter — get pushed out.
And some of those 10,000 memories are wrong. The user preferred Python 2 years ago, then switched to Go. Both memories are in the store. The system retrieves both, and the older one — just because it has more access count from the early days — sometimes wins.
Memory consolidation solves both problems: merging redundant memories into single authoritative records, and marking outdated memories as superseded so they don't pollute retrieval. Memory forgetting solves the third problem: old, never-accessed memories that are probably stale accumulate without limit unless explicitly decayed and archived.
These are not nice-to-haves. At production scale — thousands of users, years of operation — a memory store without maintenance becomes unusable. Query latency grows, relevance degrades, and the agent starts making decisions based on outdated information it can't forget.
Status-based memory management
Memories should never be deleted — they're an audit trail. Instead, use a status field: active (retrievable), superseded (contradicted by newer fact, kept for history), merged (replaced by a consolidated record), archived (decayed below importance threshold). Only active memories are returned in retrieval. This is the same principle as soft-deletes in database design.
Consolidation: merge and update
Merge near-duplicates: cluster all active memories by embedding similarity. For each cluster where cosine similarity > 0.90, merge the cluster into a single consolidated memory. The merged record takes the highest importance score from the cluster, combines unique content, and marks the originals as status="merged".
The merge algorithm:
- Compute embeddings for all active memories (if not already cached)
- Greedy clustering: assign each memory to the first cluster where similarity >= threshold
- For clusters of size > 1, create a new merged memory
- Mark originals as
merged — they persist for audit but are not retrieved
The merged memory's text combines the key content from all cluster members. In production this should be an LLM call: "Merge these 3 similar memories into one comprehensive, self-contained statement." The merged text should be more complete than any individual record.
Mark contradictions superseded: when new information contradicts an existing memory, call consolidator.supersede(old_memory). The old memory's status becomes superseded. It's retained in the store (for audit trail and compliance) but excluded from retrieval queries. The new, accurate memory is stored as active.
This is the pattern for contradiction handling: never delete, always supersede + replace.
A user told your agent "I prefer Python" in January and "I'm now focusing on Go" in October. How should the memory system handle this?
Explanation: Memories should never be deleted — they're an audit trail. You may need to know what the agent "believed" at a given point in time for debugging, compliance, or user trust explanations. But superseded memories shouldn't influence retrieval. The status field enables this: old information is preserved for history while only current information influences agent behavior. This is the same principle as soft deletes in database design — mark as inactive rather than destroy.
Forgetting curves for agent memory
Not all memories decay at the same rate, and not all should be kept forever. High-importance memories (explicit user decisions, stated preferences, critical facts) should persist indefinitely. Low-importance memories (casual mentions, transient questions, filler) should fade.
The Ebbinghaus forgetting curve, adapted for agent memory:
importance_score -= decay_rate * (days_since_last_access / days_per_cycle)
With decay_rate=0.1 and days_per_cycle=30, a memory that hasn't been accessed in 90 days loses 0.3 importance points. Over a year without access, it loses ~1.2 points. Starting from 3.0, it reaches the archive threshold of 0.1 after about 9 years of zero access — appropriate for a medium-importance memory.
High-importance memories (4.5–5.0) decay much more slowly: they'd need decades of zero access to archive. Low-importance memories (1.0–1.5) archive within a few months of no access.
Access boost: when a memory is retrieved and used, importance_score += access_boost. Frequently accessed memories stay prominent; dormant memories fade. This creates a natural "currently relevant" signal without requiring explicit user input.
Archival, not deletion: memories below the archive_threshold are set to status="archived". They're excluded from retrieval but remain in the store for audit purposes. Archived memories can be unarchived if new evidence suggests they're relevant again.
Memory importance decay (reducing scores over time for unaccessed memories) serves what purpose?
Explanation: Without decay, all memories compete equally regardless of age. A preference from 2 years ago ranks just as high as one from last week when importance scores are tied. Decay creates a natural recency bias in the importance score — frequently accessed and recently confirmed memories stay highly ranked, while dormant memories fade. This makes retrieval naturally prioritize current, relevant information without requiring manual curation.
Memory maintenance pipeline
The maintenance pipeline is a scheduled job — run weekly or monthly. It does three things:
- Consolidate: run the
MemoryConsolidator across all active memories. Merge near-duplicate clusters. - Decay: apply the
ForgettingCurve to all memories. Archive those below threshold. - Report: generate a
MemoryHealthReport with statistics.
The health report provides operational visibility:
- How many memories were consolidated this cycle?
- How many were archived?
- What's the average importance score of the active set?
- How many are critical (4–5), medium (2–4), low (<2)?
A healthy memory store should see its active count stabilize over time — new memories enter, old ones archive, near-duplicates consolidate. If active count grows unboundedly, maintenance frequency needs to increase.