cd /news/ai-agents/how-to-implement-context-decay-so-an… · home › topics › ai-agents › article
[ARTICLE · art-148152] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=· neutral

How to implement context decay so an AI agent forgets stale or incorrect instructions over time?

A developer outlines a three-operation approach to making AI agent memory forget stale or incorrect instructions: closing an entry when a newer instruction replaces it (recording the date and successor while keeping the old entry), decaying unused and unconfirmed entries by shrinking their retrieval score over time down to a floor above zero, and pushing down entries that misled the agent with an explicit negative mark. The writeup argues that vector stores rank only by embedding similarity, which has no notion of age, deprecation or replacement, so diligence in writing memory actually increases the odds of recalling retired instructions. Deletion is reserved for secrets, personal data on request, and noise nothing ever depended on.

by read7 min views18 publishedOct 9, 2026

A year into a project, one agent's memory holds three instructions that should no longer win. It says to install packages with npm, and since June it also says to use pnpm; both are still there, and either can come back first. It holds a workaround for a library bug that was fixed in the spring, and nothing has needed it for months. It says to retry a failing integration test until it passes, and the last time the agent did that, the retries hid a real regression. Each one matches the question it answers exactly as well as on the day it was written.

The short answer: forgetting stale or incorrect instructions takes three operations, and decay is only one of them. Close an instruction when a newer one replaces it: keep it, mark the date it stopped applying and what replaced it, and make reads prefer open instructions. Decay the ones that go unused and unconfirmed for a long time, lowering their ranking with time so they stop competing at full strength. Push down an instruction that misled when the agent acted on it, with an explicit negative mark. None of the three deletes anything. Deletion stays for secrets, personal data on request, and noise nothing ever depended on.

A vector store ranks by closeness in embedding space, and age, deprecation and replacement are not dimensions of that space. Similarity has no age and no status: last year's convention matches today's query exactly as well as it ever did, and nothing in cosine distance says deprecated. The store is not broken. It answers "what is most similar?", while the agent needs "what is currently true?".

Diligence makes this worse: the more carefully agents write memory, the more retired instructions the store holds, and the higher the odds that a recall surfaces one.

Context decay lowers an entry's recall priority as time passes without it being used or confirmed, so it drifts down the ranking instead of competing at full strength. It changes ranking, not storage. Each of the three operations answers a different reason an instruction should stop winning:

Operation When it applies What changes What it must not do
Close A newer instruction replaces it Marked closed, with the date and its successor; reads prefer open entries Overwrite or delete the old entry
Decay Nobody uses, confirms or contradicts it for a long time Its ranking weight falls with time since it was last used or confirmed Delete silently
Push down Acting on it failed An explicit negative mark ranks it below the alternatives Spread beyond the entry that misled
Delete A secret, personal data on request, noise with no history The entry is gone Touch anything a past decision depended on

Closing. When an instruction is replaced, do not overwrite it and do not delete it. Mark it closed, record when it stopped applying, and link its successor: "install with npm, true until 2026-06, replaced by pnpm". Reads prefer open instructions, but the closed one still exists, and that matters more than it seems: an agent asked why the January branch has a package-lock.json needs January's instruction to answer. Overwriting rewrites history; closing versions it.

Closing needs to know what replaced what, and that is known for certain only at the moment of the correction. So the correction should name the instruction it closes when it is written, rather than leave a later pass to guess from two similar texts.

Decay. Instructions that are never recalled, never confirmed and never contradicted should not keep competing at full strength forever. One way to build it on a store you control: keep a last-used and a last-confirmed date on each entry, both set when it is written. At read time, fetch more candidates than you need, multiply each retrieval score by a factor that shrinks with the time since the later of the two dates, and stop that factor at a floor above zero, so an old entry that matches a question closely can still come back. Decay should demote, not erase. Decay that quietly deletes cannot be told apart from data loss.

Pushing down. Some instructions are not old, just wrong, and you know they are wrong because acting on them failed. Those deserve an explicit negative mark that ranks them below the alternatives the next time the same question comes up. Push-down is targeted: it lands on the entry that misled. Decay is ambient: it touches everything that goes unused.

Deleting. Hard deletion keeps a narrow, real role. The test is simple: if nothing ever depended on the entry, delete it; if anything did, close it instead.

With plain files, all three are editorial habits: a status line on each entry (active, or closed, replaced by ...), a periodic review that produces a list for a person to approve instead of silent removals, and a rule never to rewrite an old entry in place. It works, and a person is the garbage collector.

With a memory system, the same three become properties to look for: statuses and validity windows on entries, decay that demotes rather than deletes, and a feedback channel for marking what misled.

The code uses mnemoverse 0.4.0 from PyPI, the Python SDK of Mnemoverse (the author's company), with setup on the Python SDK page. The example covers closing, on the write and on the read, plus a rating after the agent acts. The agent's own logic is left out.

from mnemoverse import MnemoClient

client = MnemoClient()  # reads MNEMOVERSE_API_KEY
DOMAIN = "project:web"

old = client.write(
    "Instruction (2026-01, repo: web): install packages with npm.",
    concepts=["instruction", "package-manager", "npm"],
    domain=DOMAIN,
)
if not old.stored:
    raise SystemExit(f"Not stored: {old.reason}")

new = client.write(
    "Instruction (2026-06, repo: web): install packages with pnpm, not npm. "
    "Replaces the 2026-01 npm instruction.",
    concepts=["instruction", "package-manager", "pnpm", "npm"],
    domain=DOMAIN,
    supersedes=[str(old.atom_id)],
)
if not new.stored:
    raise SystemExit(f"Not stored: {new.reason}")
print(new.superseded)  # the ids this write marked as replaced

result = client.read("how do I install packages in the web repo", domain=DOMAIN)
current = [item for item in result.items if item.superseded_by is None]
for item in current:
    print(item.content)

if current:
    client.feedback(
        atom_ids=[item.atom_id for item in current],
        outcome=1.0,
        query_concepts=result.query_concepts,
        domain=DOMAIN,
    )

history = client.read(
    "which package manager did the web repo use in January",
    domain=DOMAIN,
    include_history=True,
)
for item in history.items:
    print("replaced" if item.superseded_by else "current", item.content)

The June write names the January record in supersedes, and the API reference says what happens to a record named there: "A target is not deleted; it gains a superseded_by pointer to the new atom and stays fetchable by ID". Hiding replaced records from ordinary reads is an organisation setting, "Hide replaced memories" under Settings in the console, and it is off by default, so a read can return the January and the June instructions side by side. For that case the reference names the field to check: "while filtering is off, this field is how you tell a replaced revision from a live one". That is why the code filters on superseded_by itself, which gives the same result with the setting on or off. With it on, ordinary reads return current records only, and include_history=True makes replaced ones eligible again under the same ranking, as Reading history describes.

Ratings re-rank what comes back next. The overview states the limit of that: feedback "changes the order of the next read, not the stored text". The two stored checks are there for the write gate: a write that is too similar to the nearest memory in the same domain is not stored, and a refused write returns no id to pass to supersedes.

The library page has the longer version, including what to look for in a memory system before you trust it with instructions. Which retired instruction is your agent still following?

── more in #ai-agents 4 stories · sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-to-implement-con…] indexed:0 read:7min 2026-10-09 · —