· UPDATED · ARJUN · WRITING

When an Agent Forgets, Look Beyond the Model

When an Agent forgets yesterday's instruction, the model may not be at fault. The missing piece is often how the system stores and retrieves memory.

You tell a coding Agent, “Use the Go standard library wherever possible in this project. Do not add a framework without a reason.” It follows the instruction for the rest of the conversation. The next day, you open a new thread and it recommends another framework.

The obvious conclusion is that the model has a poor memory.

But the model may never have had a chance to “remember.” When the previous conversation ended, the preference was not written to persistent storage. When the new thread began, the system did not retrieve it. The failure was not that the model forgot. The application never completed the read/write loop that makes memory possible.

This is where explanations of Agent Memory often become muddled. Context windows, chat logs, session state, vector databases, and model weights are all called “memory,” as if connecting a database automatically gave an Agent a durable past.

Separate those concepts first. The rest of the system becomes much easier to reason about.

Three Concepts That Are Often Blurred Together

A practical design starts by separating three scopes:

Layer What it is What it solves What it does not guarantee
Context Window The input actually provided to one model call Gives the model the material needed for the immediate task Does not automatically persist information across calls
Thread State Persisted state scoped to one conversation or task thread Stores messages, tool results, task progress, and intermediate artifacts Does not automatically share information across threads
Long-Term Memory Data stored across conversations and retrieved within an explicit scope Preserves user preferences, facts, experiences, or rules Writing something does not guarantee that it will later be retrieved correctly

The CoALA paper makes an important distinction: language models are stateless across calls, while language Agents can maintain Working Memory and Long-Term Memory in external data structures. It also separates the model’s Context from the Agent’s Working Memory. Working Memory can persist across model calls; each call receives only a subset of it.

In other words, Context is the material handed to the model now. State is the situation the Runtime currently knows about. Memory is a record that may become useful again later.

They are connected, but they are not interchangeable.

The relationship among the Context Window, Thread State, and Long-Term Memory
Context is the current input, State preserves the thread's working situation, and Long-Term Memory keeps reusable information across threads. Data moves among them only through explicit selection, retrieval, and writing.

A Context Window Is Not a Self-Updating Archive

The Context Window is an input budget. The system prompt, current request, message history, tool definitions, tool results, and retrieved documents all compete for the same space.

A longer window can hold more text, but it does not guarantee that every part of that text will be used equally well. In the multi-document question-answering and key-value retrieval tasks studied in Lost in the Middle, several models performed worse when relevant information appeared near the middle of a long context. The result came from specific models and tasks, so it should not be stretched into “every long-context model ignores the middle.” It does show that fitting information into the window and using it reliably are different problems.

Continually appending the entire conversation to the prompt is therefore a weak memory strategy. It creates three problems at once:

  1. Irrelevant history consumes space needed by the current task.
  2. Latency and model cost increase as the input grows.
  3. Important constraints become buried in old messages and may not be used consistently.

A better approach is to persist the complete State, then let a Context Builder select the information needed for the current step. The model receives a working set, not the whole archive.

Short-Term Memory Is Better Understood as Thread State

Current LangChain and LangGraph documentation defines Short-Term Memory as thread-scoped State. It belongs to one conversation thread, can be persisted through a Checkpointer, and can be restored between graph invocations or execution steps.

Message history is usually part of that State, but it need not be the whole thing. A code-editing Agent might also store:

{
  "goal": "Fix duplicate charges on payment retries",
  "constraints": ["Do not change the public API", "The change must be reversible"],
  "current_step": "Reproduce and locate the broken idempotency path",
  "evidence": ["trace-8f21", "test_payment_retry"],
  "artifacts": ["diagnosis.md", "patch.diff"]
}

These fields are easier to persist, inspect, and restore than a long paragraph of natural language. After a failure or restart, the Runtime knows where the task stopped. It does not have to ask the model to reread the entire conversation and guess.

When message history grows too long, applications can trim, delete, or summarize earlier messages. Summarization saves tokens, but it is lossy compression. A constraint omitted by the summarizer may be gone from every later model call. Goals, permission boundaries, acceptance criteria, and tool results are safer as structured State than as details entrusted only to an automatic summary.

Long-Term Memory Is Not Another Name for a Vector Database

A vector database solves one kind of retrieval problem: it can find semantically similar text even when the query uses different wording. It does not decide what is worth remembering, whether an old fact is still valid, or which record wins when two memories conflict.

Long-Term Memory can be divided by the kind of information it contains:

This taxonomy comes from cognitive architectures. It is not a prescription for three databases. The data structure should follow the query:

LangGraph’s Long-Term Store illustrates another valid design. It can organize memories as JSON documents by Namespace and Key, then add filtering or semantic search where needed. Data ownership and update behavior come first. The retrieval index comes later.

The First Hard Problem: Deciding What to Write

Not every sentence deserves to become Long-Term Memory.

“Use Python for this script” describes the current task. “Use Python by default in future examples” sounds like a preference that should cross conversation boundaries. Persisting the first sentence as a permanent preference may cause the system to recommend Python in the middle of a Go repository next week.

Every candidate write should answer at least five questions:

Memory can be written in the hot path of a request or consolidated later in the background. Hot-path writes make new information immediately available, but add latency and more work to the current request. Background writes keep the response path lean, but introduce scheduling and synchronization questions. Both are real engineering choices. Neither is the universal answer.

I would also keep explicit statements separate from system inferences. “Keep future answers concise” can become a candidate preference. Inferring “this user dislikes explanations” from two short conversations should not silently become a fact. Once an uncertain interpretation is stored with false confidence, every future retrieval can amplify the mistake.

The Agent Memory write path
Writing begins with whether the information deserves durable storage. If Scope, Type, Source, and Lifetime are unclear, the database merely preserves ambiguity for longer.

Retrieval Is More Than Dumping the Top K Into a Prompt

An Agent can still “remember” incorrectly after the storage layer works. Sometimes the right record exists, but the retriever surfaces something else. Sometimes a useful record is buried under unrelated results.

A more complete read path usually includes:

  1. Derive a retrieval intent from the current task instead of copying only the user’s last sentence.
  2. Apply hard filters for user, project, memory type, and validity status.
  3. Choose exact lookup, keyword search, vector search, or a hybrid based on the data.
  4. Rerank and deduplicate candidates, then detect conflicts.
  5. Place only the information needed for the current step into Context, with provenance attached.

The Generative Agents research prototype combined Recency, Importance, and Relevance in its retrieval score. That is an influential design example, not a universal formula. Customer preferences, code incident records, and the experiences of a simulated character require very different definitions of “important.” The weights have to be evaluated against the actual task.

There is another distinction worth keeping: knowledge-base RAG and user memory may use similar retrieval technology, but they are not the same data. Product documentation answers “what does this system support?” User memory answers “what happened to this user or in this task?” Their permissions, update cadence, and authoritative sources are different.

The Agent Memory read path
Reading is a retrieval pipeline: constrain the scope, recall candidates, rerank and deduplicate them, check conflicts, and let the Context Builder control what the model actually sees.

Memory Must Be Correctable, and It Must Be Able to Forget

Suppose the system has stored this fact:

Deployment method: Docker Compose.

Two months later, the project moves to Kubernetes. If the application simply appends another memory, retrieval may return two incompatible answers.

Deleting all history is not the only option. The old record can be marked Superseded with the period and source for which it was valid, while the new record becomes Current. The Agent then uses Kubernetes when asked about the present deployment, but can still see that Docker Compose existed when investigating an older incident.

Forgetting is also more than lowering a vector-similarity score. Temporary State can expire through a TTL. Stale facts can be invalidated, duplicate episodes can be consolidated, and data the user asks to remove must actually be deleted from persistent storage. Excluding a record from the next prompt does not mean the system has deleted it.

Correcting, versioning, and deleting memory
Correcting a fact does not require erasing its history. Current and Superseded records can coexist, but data covered by a deletion request must be removed from durable storage.

A Minimal Architecture to Start With

You do not need to deploy Redis, a vector database, a graph database, and a memory framework on day one. For many personal Agents, five clear responsibilities are enough:

  1. A Checkpointer persists messages and task State for each Thread.
  2. A Profile Store holds a small set of explicit, replaceable user or project facts.
  3. An Episode Store records past events with sources and outcomes; add full-text or vector indexing when retrieval needs it.
  4. A Memory Policy decides when to write, update, expire, or delete.
  5. A Context Builder selects State and Memory for the current step and stays within the input budget.

The pseudocode is deliberately short:

def build_context(user_id, thread_id, request):
    state = checkpoints.load(thread_id)
    profile = profiles.get(user_id)
    episodes = memories.search(
        query=request,
        filters={"user_id": user_id, "status": "current"},
    )
    return context_builder.pack(state, profile, episodes)

The database persists. The retriever finds candidates. The Policy makes lifecycle decisions. The Context Builder decides what the model sees. Once those responsibilities are separate, a bad answer becomes much easier to trace to the failing layer.

When Long-Term Memory Is the Wrong Feature

Long-Term Memory is not what makes a system an Agent.

If a tool handles isolated requests and nothing should carry over after completion, there is no reason to retain user data merely to appear more agentic. A deterministic business process may need a reliable database state machine, not model memory. Public-document question answering may need a versioned knowledge index, not a user profile.

Ask one question first: what exactly will the next task lose if this information is unavailable? If the answer is vague, do not store it yet.

References

When an Agent appears to “remember,” a concrete data flow sits underneath: what was saved, where it was stored, when it was retrieved, which record wins a conflict, and when the data is truly deleted.

The model processes the input in front of it. Making the past useful in the next call is the system’s job.