Building a Memory System for AI Agents
AI agents are becoming better at reasoning.
They can call APIs.
They can browse the web.
They can write code.
They can execute tools.
They can decompose complicated tasks into smaller tasks and sometimes recover when something goes wrong.
But there is one strangely human problem that many AI agents still struggle with:
They forget.
Not because they cannot store information.
They can store enormous amounts of information.
The problem is that storage is not memory.
A database can remember everything and still produce an agent that remembers nothing.
That distinction is one of the most interesting engineering problems emerging in modern AI systems.
If we want agents that operate over days, months, or years, we need something more sophisticated than stuffing previous conversations into a prompt.
We need memory architecture.
And building memory for an AI agent turns out to be much closer to designing a miniature cognitive system than simply adding a vector database.
The interesting question is therefore not:
“How do I give my AI more context?”
The interesting question is:
“What should this agent remember, why should it remember it, when should it retrieve it, and when should it forget it?”
That is a systems problem.
The Context Window Is Not Memory
Let's start with the most common misconception.
Suppose an agent has a conversation:
User:
My name is Daniel.
Agent:
Nice to meet you, Daniel.
User:
I'm building an e-commerce platform.
Agent:
Interesting. What technology are you using?
User:
Rust and PostgreSQL.
The model can respond intelligently because the relevant information exists inside the current context.
But tomorrow:
User:
Continue working on my project.
What project?
What language?
What architecture?
What decisions were already made?
What does the user prefer?
What problems have already been solved?
If the previous conversation isn't available, the agent starts from zero.
This is the fundamental distinction:
Context
↓
Information currently available to the model
Memory
↓
Information intentionally retained across interactions
Context is temporary working material.
Memory is persistent state.
This is not unique to AI.
A CPU has registers.
A process has memory.
A filesystem has persistent storage.
A distributed system has durable state.
A human has working memory and long-term memory.
AI agents need something similar.
Memory Is a State Management Problem
One useful way to think about agent memory is:
Agent = Model + Tools + State + Memory
The model performs reasoning.
Tools allow the model to interact with the world.
State describes what is currently happening.
Memory contains information that remains useful beyond the immediate interaction.
That gives us a basic architecture:
┌───────────────┐
│ User │
└───────┬───────┘
│
▼
┌───────────────┐
│ Agent Loop │
└───────┬───────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌────────┐ ┌─────────┐ ┌─────────┐
│ Model │ │ Tools │ │ Memory │
└────────┘ └─────────┘ └─────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
Semantic Episodic Working
Memory Memory Memory
Now the problem becomes much more interesting.
What exactly belongs inside Memory?
Memory Is Not One Thing
Human memory isn't a single bucket.
AI systems shouldn't treat it as one either.
A practical agent architecture can separate memory into several layers.
For example:
Working Memory
↓
Current task and immediate context
Episodic Memory
↓
What happened
Semantic Memory
↓
What is known
Procedural Memory
↓
How something should be done
Profile Memory
↓
Stable information about the user
Environmental Memory
↓
Information about the external world
These memories have different lifetimes and retrieval patterns.
1. Working Memory
Working memory is the easiest layer.
It contains information needed for the current task.
Imagine an agent debugging a production system.
Its working memory might contain:
{
"task": "debug payment timeout",
"service": "payments-api",
"environment": "production",
"recent_error": "gateway timeout",
"suspected_dependency": "payment-provider"
}
This information doesn't necessarily need to survive forever.
Once the debugging session is complete, much of it can disappear.
Working memory is essentially the agent's scratchpad.
You can implement it using:
- conversation state
- Redis
- in-process state
- task databases
- workflow state machines
The important thing is that it is short-lived.
2. Episodic Memory
Episodic memory records events.
Not just facts.
Events.
For example:
On September 17, the agent helped configure PostgreSQL replication.
The user rejected Redis as the primary cache.
The deployment failed because port 5432 was blocked.
The issue was resolved by updating the firewall configuration.
These are experiences.
A future agent might need them.
Imagine asking:
Why did we configure the database this way?
A semantic database might know:
PostgreSQL replication is enabled.
But episodic memory might know:
We enabled replication after the production database experienced
read-heavy workloads during a previous deployment.
That difference is powerful.
One describes what is true.
The other describes what happened.
3. Semantic Memory
Semantic memory stores knowledge.
For example:
The user's application uses PostgreSQL.
The application exposes REST APIs.
The production environment runs on Linux.
The frontend communicates with the backend using JSON.
These are facts.
Semantic memory is usually what people imagine when they hear:
“AI memory.”
But semantic memory alone is insufficient.
Suppose the agent stores:
User prefers Rust.
That sounds useful.
But what does “prefers” mean?
Was it mentioned once?
Did the user explicitly say it?
Was it inferred?
Is it still true?
Memory needs provenance.
4. Procedural Memory
Procedural memory describes how the agent should perform tasks.
For example:
When deploying this application:
1. Run tests.
2. Build the Docker image.
3. Push the image.
4. Run migrations.
5. Deploy the service.
6. Verify health checks.
This is not merely knowledge.
It is a procedure.
Procedural memory can become extremely valuable for agents operating inside organizations.
Instead of asking:
“How does this company deploy applications?”
the agent can retrieve the established procedure.
Procedural memory essentially turns previous successful workflows into reusable behavior.
5. User Profile Memory
Some information is stable enough to deserve its own category.
For example:
{
"preferred_language": "English",
"timezone": "Africa/Lusaka",
"coding_style": "explicit",
"favorite_framework": "Rust"
}
But profile memory is dangerous if implemented carelessly.
The agent shouldn't permanently store every statement.
There is a difference between:
"I like Rust."
and:
"I used Rust for this project."
The first could be a preference.
The second is contextual information.
Memory systems need to understand this distinction.
The Memory Pipeline
A memory system can therefore be modeled as a pipeline:
Conversation
│
▼
Memory Candidate Detection
│
▼
Extraction
│
▼
Classification
│
▼
Scoring
│
▼
Storage
│
▼
Retrieval
│
▼
Ranking
│
▼
Context Injection
This is the core architecture.
And notice something important:
The LLM is not the memory system.
The LLM participates in the memory system.
The actual memory system is an engineered subsystem surrounding it.
Step One: Detect Memory Candidates
Not every sentence deserves to become memory.
Consider this conversation:
User:
I'm using Python for this prototype.
Potential memory:
User is currently using Python for a prototype.
But:
User:
Python is installed on my machine.
That may not be useful later.
And:
User:
I tried Python once in 2019.
Probably shouldn't become an active preference.
Therefore, we need a memory extraction layer.
Conceptually:
def extract_memory_candidates(message):
candidates = llm_extract(
message,
schema=MemoryCandidate
)
return [
candidate
for candidate in candidates
if candidate.is_memorable
]
The important word here is candidate.
Extraction shouldn't immediately mean persistence.
Step Two: Give Memories Structure
Instead of storing:
User likes PostgreSQL.
store something richer.
For example:
{
"id": "mem_83291",
"type": "preference",
"subject": "user",
"predicate": "prefers",
"object": "PostgreSQL",
"confidence": 0.91,
"source": "conversation",
"created_at": "2026-09-17",
"updated_at": "2026-09-17"
}
Now the memory is queryable.
But we can go further.
Add:
{
"importance": 0.72,
"recency": 1.0,
"confidence": 0.91,
"scope": "technical",
"expires_at": null
}
Suddenly memory starts looking less like a collection of notes and more like a proper data model.
The Memory Record
A robust memory object might look like this:
Memory
├── id
├── type
├── content
├── embedding
├── subject
├── source
├── confidence
├── importance
├── created_at
├── updated_at
├── last_accessed_at
├── access_count
├── expiration
├── scope
├── status
└── relationships
The embedding is useful.
But it isn't the memory.
This distinction matters.
The embedding is an index representation.
The actual memory is the structured record.
Why Vector Databases Are Not Enough
Modern AI applications frequently use vector databases.
That's useful.
You can embed:
"The user prefers Rust."
and later perform semantic similarity search.
But consider:
The user prefers Rust.
versus:
The user used Rust once.
They may have similar embeddings.
But they represent completely different knowledge.
Vector similarity answers:
“What information is semantically similar?”
It does not necessarily answer:
“What information is true, relevant, current, important, authorized, or still valid?”
That's why serious memory systems often combine:
Vector Search
+
Structured Metadata
+
Temporal Reasoning
+
Keyword Search
+
Relationship Graphs
+
Rules
Memory retrieval becomes a hybrid search problem.
Hybrid Retrieval
Suppose the user asks:
Why did we choose PostgreSQL for this project?
A vector search might return:
PostgreSQL is a relational database.
Technically relevant.
But not necessarily useful.
A better retrieval system might combine:
Semantic similarity
How closely does the memory relate to the question?
Recency
How recent is it?
Importance
How significant is it?
Frequency
How often has it been accessed?
Confidence
How reliable is the information?
Scope
Does it apply to this project?
Temporal validity
Is it still true?
Then we can define:
score =
α * semantic_similarity
+ β * recency
+ γ * importance
+ δ * confidence
+ ε * scope_match
This is where memory engineering starts becoming interesting.
We're no longer simply retrieving documents.
We're ranking pieces of an agent's past.
Time Changes Meaning
Memory has a temporal dimension.
Consider:
The user uses React.
Maybe that was true in 2023.
In 2026:
The user migrated to Vue.
Now we have conflicting memories.
A naive vector database may return both.
A proper memory system should reason about time.
Represent memories as:
Memory A
valid_from = 2023
valid_until = 2026
Memory B
valid_from = 2026
valid_until = null
Now retrieval can prioritize currently valid information.
This is a simple temporal database idea applied to AI memory.
And it solves an enormous class of problems.
Memory Contradictions
Agents will inevitably receive contradictory information.
Imagine:
User:
I don't use TypeScript.
Six months later:
User:
I'm building everything in TypeScript now.
If the system simply appends memories:
User does not use TypeScript.
User uses TypeScript.
the agent has created ambiguity.
Instead, the new memory should trigger reconciliation.
Conceptually:
def update_memory(new_memory):
conflicts = find_related_memories(new_memory)
for old_memory in conflicts:
if contradicts(new_memory, old_memory):
supersede(old_memory, new_memory)
store(new_memory)
This creates a temporal chain:
Old belief
↓
Superseded
↓
New belief
Memory becomes versioned knowledge.
Memory Consolidation
Human memory isn't simply a log of everything that happened.
Information gets compressed.
Patterns become knowledge.
Individual experiences become general principles.
AI agents need the same mechanism.
Suppose the agent has these episodes:
User asked for short answers.
User asked for short answers.
User asked for short answers.
User requested concise explanations.
User complained when responses were excessively long.
Instead of keeping every event forever, the system might consolidate them into:
User prefers concise explanations.
This is memory consolidation.
The architecture becomes:
Episodes
│
▼
Pattern Detection
│
▼
Knowledge Extraction
│
▼
Semantic Memory
That is much more powerful than simply storing transcripts.
Memory Decay
Another interesting concept is forgetting.
We often design databases around permanence.
But intelligent memory requires controlled forgetting.
Not everything remains relevant forever.
We can model memory strength:
strength =
importance
× confidence
× usage
× recency_decay
For example:
def memory_strength(memory, now):
age = now - memory.last_accessed_at
decay = exp(-age / memory.decay_rate)
return (
memory.importance
* memory.confidence
* memory.access_weight
* decay
)
Old memories aren't necessarily deleted.
They simply become less likely to surface.
This distinction matters.
A memory that hasn't been used for two years may still be useful if the user suddenly asks about the old project.
Forgetting Should Be Intentional
There are at least three different operations:
Ignore
Archive
Delete
They are not the same.
Ignore
The memory still exists but isn't relevant.
Archive
The memory is retained but removed from normal retrieval.
Delete
The memory is actually removed according to the system's deletion policy.
A production-grade memory system should make these states explicit.
Privacy Is Part of Memory Architecture
The moment an agent stores information about a person, memory becomes a privacy problem.
A good memory system should answer:
Who created this memory?
Why was it stored?
Where did it come from?
Who can access it?
How long should it live?
Can it be corrected?
Can it be deleted?
What other memories depend on it?
This means memory records should include provenance.
For example:
{
"content": "User prefers concise responses",
"source": {
"conversation_id": "conv_129",
"message_id": "msg_441"
}
}
Now the system can trace the memory back to its origin.
That's not just useful for debugging.
It's fundamental to trust.
Memory and Authorization
Not every memory should be available to every agent.
Imagine an organization with:
Personal Assistant
Engineering Agent
Finance Agent
HR Agent
Customer Support Agent
They might share some memory.
But not all memory.
Therefore:
Memory
│
├── User scope
├── Project scope
├── Organization scope
├── Agent scope
└── Restricted scope
A finance agent shouldn't automatically retrieve sensitive engineering information.
Memory therefore becomes an authorization problem.
The retrieval query should include identity and scope:
retrieve(
query=query,
user_id=user_id,
agent_id=agent_id,
project_id=project_id,
permissions=permissions
)
Memory is data.
Data needs access control.
The Memory Graph
Some memories are related.
Consider:
User
│
├── works_on → Project A
│ │
│ ├── uses → Rust
│ ├── uses → PostgreSQL
│ └── deployed_to → AWS
│
└── prefers → concise explanations
This is a graph.
Graph relationships allow agents to traverse concepts.
For example:
Project A
↓
PostgreSQL
↓
Previous deployment
↓
Database migration failure
That may be more useful than retrieving isolated chunks of text.
This suggests a hybrid architecture:
Memory System
│
┌────────────┼────────────┐
▼ ▼ ▼
Vector SQL Graph
Index Metadata Relations
Each system solves a different part of the problem.
Memory Retrieval Is a Reasoning Problem
Now we reach the most interesting part.
When should the agent retrieve memory?
Not every prompt requires it.
If the user asks:
What is 17 × 24?
retrieving their entire project history is pointless.
But:
Continue the API architecture we designed yesterday.
requires memory.
Therefore, retrieval itself should be intelligent.
We can introduce a memory gate:
def should_retrieve_memory(query):
return classifier(query).requires_memory
But even this can be improved.
The agent can classify memory intent:
NO_MEMORY
USER_PROFILE
PROJECT_CONTEXT
PAST_EVENT
PREFERENCE
PROCEDURE
FACT
RELATIONSHIP
Then retrieve only the relevant memory class.
Memory Should Be Contextual
Imagine the user says:
Fix the authentication bug.
The memory system should know:
Which project?
Which repository?
Which authentication system?
What happened previously?
What fixes were attempted?
Memory retrieval should therefore depend on current context.
Something like:
context = {
"user": user_id,
"project": project_id,
"task": current_task,
"conversation": conversation_id
}
memories = retrieve(query, context)
This prevents irrelevant memories from flooding the model.
The Context Budget Problem
Even if an agent has a million memories, the model cannot necessarily consume all of them.
Context is finite.
So memory retrieval becomes an optimization problem:
Millions of memories
↓
Candidate retrieval
↓
Re-ranking
↓
Compression
↓
Top relevant memories
↓
Model context
Suppose the system retrieves 50 memories.
Don't blindly insert all 50.
Instead:
50 candidates
↓
re-rank
↓
12 useful memories
↓
summarize
↓
4 compact memory units
This is where memory and context engineering meet.
Memory Compression
Suppose an agent has 100 conversations about the same project.
Sending all 100 conversations to the model is ridiculous.
Instead, the system can maintain a project summary:
Project: Commerce API
Architecture:
- Rust backend
- PostgreSQL
- Redis cache
- REST API
Authentication:
- JWT
- Refresh tokens
- Role-based authorization
Important decisions:
- PostgreSQL selected for transactional consistency.
- Redis is used only as a cache.
- Authentication remains inside the API gateway.
Known issues:
- Rate limiting still needs redesign.
This summary is a form of compressed memory.
But compression introduces another problem:
Information loss.
A good memory system should preserve the original episodes while maintaining compressed representations.
Raw Events
↓
Summaries
↓
Long-Term Knowledge
If the summary is wrong, the system should be able to trace back to the source.
Memory Hierarchy
A powerful architecture therefore looks like this:
┌──────────────────┐
│ Working Memory │
└────────┬─────────┘
│
┌────────▼─────────┐
│ Recent Episodes │
└────────┬─────────┘
│
┌────────▼─────────┐
│ Consolidated │
│ Knowledge │
└────────┬─────────┘
│
┌────────▼─────────┐
│ Archived Memory │
└──────────────────┘
This resembles a storage hierarchy.
Fast memory.
Recent memory.
Long-term memory.
Cold storage.
Different access costs.
Different retrieval policies.
Building the Storage Layer
A practical implementation might use PostgreSQL for durable structured memory.
Schema:
CREATE TABLE memories (
id UUID PRIMARY KEY,
user_id UUID,
project_id UUID,
type VARCHAR(50),
content TEXT NOT NULL,
confidence FLOAT,
importance FLOAT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
last_accessed_at TIMESTAMP,
expires_at TIMESTAMP,
status VARCHAR(20)
);
Then add vector search using an appropriate vector extension or vector database.
You might have:
CREATE TABLE memory_embeddings (
memory_id UUID PRIMARY KEY,
embedding VECTOR(...)
);
And relationship storage:
CREATE TABLE memory_relations (
source_id UUID,
target_id UUID,
relation VARCHAR(50)
);
Now we have:
SQL
↓
Metadata + durability
Vector index
↓
Semantic retrieval
Relations
↓
Graph reasoning
The Memory API
We can expose memory through a simple interface.
class MemoryStore:
def remember(self, memory):
...
def search(self, query, context):
...
def update(self, memory_id, changes):
...
def forget(self, memory_id):
...
def consolidate(self, scope):
...
The agent doesn't need to know how memory is stored.
That's an important architectural principle.
The agent interacts with an abstraction.
The memory subsystem owns:
storage
indexing
ranking
expiration
consolidation
conflict resolution
authorization
This makes the system replaceable.
The Agent Loop
Now put everything together.
A production agent loop might look like:
while True:
message = receive_input()
context = load_working_context(message)
candidates = memory.retrieve(
query=message,
context=context
)
memories = memory.rank(candidates)
prompt = build_prompt(
message=message,
context=context,
memories=memories
)
response = model.generate(prompt)
actions = extract_tool_calls(response)
execute(actions)
candidates = extract_memory_candidates(
message,
response,
actions
)
memory.process(candidates)
update_working_state()
This is the skeleton of a persistent agent.
There is an interesting design principle here.
Sometimes an agent shouldn't store a memory merely because the user said something.
It should wait until the information has been validated.
Imagine:
User:
I think the production database is PostgreSQL.
That's uncertain.
The agent checks the deployment configuration.
It discovers:
Database = PostgreSQL
Now the agent has stronger evidence.
Memory formation can therefore incorporate tool observations.
Conversation
↓
Hypothesis
↓
Tool verification
↓
Confirmed fact
↓
Memory
This makes agent memory more trustworthy.
Agents don't only learn from conversations.
They interact with systems.
An agent might discover:
Repository uses Rust.
Production runs Kubernetes.
Service has three replicas.
Redis is unavailable in staging.
Deployment succeeded at 14:32.
These are memories derived from the environment.
Therefore every tool execution can produce memory candidates.
Tool Call
↓
Observation
↓
Memory Candidate
↓
Validation
↓
Persistent Memory
Now the agent develops an internal model of its environment.
The World Model
This leads to something bigger.
A sufficiently sophisticated memory system becomes a world model.
The agent begins to understand:
Who am I interacting with?
What projects exist?
What systems exist?
What happened before?
What decisions were made?
What constraints exist?
What has changed?
What usually works?
What usually fails?
At that point, memory isn't just a feature.
It becomes infrastructure.
The model provides reasoning.
Memory provides continuity.
Tools provide interaction.
Together they form something much closer to an autonomous software system.
Memory Is an Interface Between Time and Intelligence
This might be the deepest way to think about the problem.
A language model is extremely capable inside a moment.
Memory connects that capability across time.
Without memory:
t1 → intelligent
t2 → intelligent
t3 → intelligent
But each moment is disconnected.
With memory:
t1 ───────► t2 ───────► t3
│ │ │
└───────────┴───────────┴── Memory
The agent develops continuity.
And continuity is what makes long-running agents fundamentally different from chatbots.
Failure Modes
Of course, memory systems can fail spectacularly.
Failure 1: Remembering everything
Result:
Huge memory
Low signal
Poor retrieval
Context pollution
Failure 2: Remembering too little
Result:
Agent feels forgetful.
Failure 3: Stale memories
Result:
Agent confidently uses outdated information.
Failure 4: Contradictory memories
Result:
Agent retrieves mutually incompatible facts.
Result:
Agent stores an inference as a fact.
Failure 6: Retrieval failure
The memory exists but never gets retrieved.
This is especially important.
A memory system can have perfect storage and still appear broken because retrieval is poor.
Measure Memory Like a Real System
We need metrics.
Not just:
Does it work?
We should measure:
Recall
Did the system retrieve the memory when needed?
Precision
Were retrieved memories actually relevant?
Freshness
How often did the system use stale information?
Contradiction rate
How frequently did conflicting memories reach the model?
Memory write rate
How many memories are created per interaction?
Memory utility
How often does retrieved memory materially improve the result?
Retrieval latency
How quickly can the system retrieve relevant memories?
Context cost
How many tokens are consumed by memory?
These metrics transform memory from an abstract AI feature into an engineering subsystem.
The Strange Economics of Memory
Memory also has a cost.
Every memory requires:
Storage
Embedding
Indexing
Retrieval
Ranking
Validation
Maintenance
And every retrieved memory consumes context.
Therefore the optimal memory system isn't the one that remembers the most.
It is the one that produces the greatest useful information per unit of memory cost.
You can think about:
Memory Utility =
Useful Information
------------------
Storage + Retrieval + Context Cost
That's an engineering optimization problem.
Memory Should Be Treated as Data, Not Magic
One of the biggest mistakes in AI engineering is treating memory as mysterious.
It isn't.
Memory can be modeled.
It can be indexed.
It can be versioned.
It can be authorized.
It can be compressed.
It can be tested.
It can be monitored.
It can be deleted.
It can be migrated.
It can be backed up.
It can have schemas.
It can have APIs.
In other words:
Memory is software.
And once you realize that, the architecture becomes much clearer.
A More Advanced Architecture
A serious production memory system might eventually look like this:
┌──────────────┐
│ User │
└──────┬───────┘
│
▼
┌─────────────────┐
│ Agent Runtime │
└────────┬────────┘
│
┌───────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Model │ │ Tools │ │ Working │
│ │ │ │ │ Memory │
└────────────┘ └────────────┘ └─────┬──────┘
│
▼
┌─────────────────┐
│ Memory Gateway │
└────────┬────────┘
│
┌────────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Semantic │ │ Episodic │ │ Procedural │
│ Memory │ │ Memory │ │ Memory │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└──────────────────────┼─────────────────────┘
│
▼
┌──────────────────┐
│ Retrieval Engine │
└────────┬─────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
Vector SQL Graph
Search Filters Relations
│ │ │
└────────────┼────────────┘
▼
Re-ranking
│
▼
Compression
│
▼
Context Builder
│
▼
Model
This architecture is much closer to what long-lived agents will require.
The Future: Agents That Remember Their Own History
Imagine an AI coding agent working on a project for two years.
It remembers:
Why the architecture was designed this way.
Which technologies were rejected.
Which bugs occurred previously.
Which deployment procedures are reliable.
Which services depend on which.
Which experiments failed.
Which solutions worked.
What the team prefers.
What technical debt exists.
Now imagine the agent onboarding a new developer.
The developer asks:
Why does this service use this strange caching strategy?
The agent doesn't merely inspect the code.
It retrieves the history.
Three months ago, the team introduced this cache
after database load increased during a traffic spike.
A previous implementation caused stale inventory data,
so the current design invalidates the cache after writes.
That is a different kind of software intelligence.
The code tells you what exists.
Memory tells you why it exists.
And “why” is one of the most valuable pieces of information in engineering.
The Real Challenge Isn't Storage
The industry has become very good at storing information.
We have:
SQL databases
NoSQL databases
Object storage
Vector databases
Search engines
Graphs
Caches
Logs
Data warehouses
The hard problem isn't:
“Where do we put the memory?”
The hard problem is:
“What deserves to become memory?”
Then:
“How do we know whether it is true?”
Then:
“When should it be retrieved?”
Then:
“How should conflicting memories be resolved?”
Then:
“When should memory decay?”
Then:
“Who is allowed to see it?”
And finally:
“How does memory change the agent's behavior?”
Those questions are architecture.
Memory Turns Agents Into Systems
A stateless LLM call is simple.
Input → Model → Output
An agent with memory is different:
Input
↓
State
↓
Memory Retrieval
↓
Reasoning
↓
Tool Execution
↓
Observation
↓
Memory Formation
↓
State Update
↓
Next Action
Now we have a feedback loop.
And feedback loops are where complex systems emerge.
The agent is no longer simply generating text.
It is maintaining state over time.
That makes memory one of the foundational architectural layers of serious agent systems.
Final Architecture
If I were designing a memory system from scratch, I would start with five principles.
1. Separate context from memory
Context is temporary.
Memory is persistent.
Do not confuse the two.
2. Separate memory types
Use different representations for:
working state
events
facts
preferences
procedures
relationships
3. Use hybrid retrieval
Combine:
semantic search
metadata filters
temporal logic
keyword search
relationships
No single retrieval mechanism is sufficient.
4. Make memory versioned
Facts change.
Preferences change.
Projects change.
The system must understand:
what was true
when it was true
what replaced it
5. Make memory controllable
Memory should have:
provenance
permissions
expiration
correction
archival
deletion
Because intelligent memory without control quickly becomes technical debt.
The Bigger Idea
We spent the first era of AI teaching machines how to generate.
Then we taught them how to reason.
Then we taught them how to use tools.
The next challenge is teaching them how to continue.
Because intelligence without continuity is strangely incomplete.
An agent can solve a problem today.
But a truly useful agent should remember what happened yesterday.
It should understand what changed this morning.
It should know why a decision was made three months ago.
It should recognize that an old assumption is no longer true.
It should learn from failures.
It should consolidate experiences.
It should retrieve the right knowledge at the right moment.
And, perhaps most importantly, it should know that not everything deserves to be remembered.
That is the difference between a database and memory.
A database asks:
“What did we store?”
A memory system asks:
“What matters now?”
That question is much harder.
It requires retrieval.
It requires time.
It requires structure.
It requires reasoning.
It requires forgetting.
And it requires architecture.
The future of AI agents may therefore depend less on making models remember everything and more on building systems that understand what remembering actually means.
Because the most intelligent agent isn't necessarily the one with the largest context window.
It may be the one that knows exactly which piece of its past matters right now.