Quick answer: AI agents have no built-in memory. The model forgets everything the moment a conversation ends. "Agent memory" is engineering you add on top: short-term memory is the context window you manage, long-term memory is facts written to a database (usually a vector store) and retrieved later, and episodic memory is a record of past runs the agent can learn from. Every memory system you've seen, from ChatGPT's memory feature to autonomous coding agents, is some combination of those three.
That surprises people, because agents feel like they remember. They don't. They re-read.
Why Agents Need Memory At All
An LLM call is stateless. You send tokens in, you get tokens out, and nothing persists on the model side. For a single question that's fine. For an agent that runs for hours, serves the same user for months, or performs the same task weekly, it breaks down in three specific ways:
- The context window fills up. A long agent run generates more tool output than fits in context, so early information falls off.
- Sessions end. The user tells the agent their deployment setup on Monday; on Tuesday the agent has no idea.
- Mistakes repeat. Without a record of past runs, the agent hits the same dead end every single time.
Each failure maps to a different memory type, which is why the taxonomy matters. It's not academic vocabulary; it's a debugging checklist.
The Three Types, Side by Side
| Type | What it stores | Where it lives | Lifespan |
|---|---|---|---|
| Short-term | The current conversation and tool results | The context window | One session |
| Long-term | Facts, preferences, learned knowledge | A database, usually a vector store | Indefinite |
| Episodic | What happened in past runs: actions, outcomes, errors | Logs or summarized run records | Indefinite |
Some frameworks add a fourth category, procedural memory (skills and how-to knowledge baked into prompts or fine-tunes), but in practice most teams fold that into the system prompt and move on.
Short-Term Memory Is Just Context Management
Short-term memory sounds trivial: it's the messages array. The engineering problem is that it's finite, and agents are voracious. A single fetched web page can be 50,000 tokens of raw HTML.
So short-term memory work is mostly compression:
- Summarize old turns. When the conversation passes a threshold, replace the oldest messages with a model-written summary.
- Truncate tool output aggressively. The agent needed the answer from that API call, not all 400 lines of it.
- Feed clean text, not markup. Converting pages to Markdown before they enter context cuts token usage dramatically, which we measured in token optimization for feeding web data to LLMs.
In my experience, teams reach for a vector database when their actual problem is that they're stuffing raw HTML into context. Fix short-term hygiene first. It's cheaper.
Long-Term Memory: The Vector Store Pattern
Long-term memory is the part people usually mean by "agent memory," and the standard implementation is embarrassingly simple: when something worth remembering happens, write it down; before responding, look up anything relevant.
Here's the whole pattern with Chroma:
import chromadb
client = chromadb.PersistentClient(path="./agent_memory")
memory = client.get_or_create_collection("user_facts")
def remember(fact: str, source: str):
memory.add(
documents=[fact],
metadatas=[{"source": source, "stored_at": "2026-07-19"}],
ids=[str(memory.count() + 1)],
)
def recall(query: str, n: int = 3) -> list[str]:
results = memory.query(query_texts=[query], n_results=n)
return results["documents"][0]
remember("User deploys with Docker Compose on a single OVH box", "chat")
recall("how does the user deploy?")
Retrieved memories get prepended to the prompt as context. That's it. The retrieval side is identical to RAG, and everything we wrote about building RAG pipelines with real-time web data applies here too.
The hard part isn't storage or retrieval. It's deciding what to store. Two approaches work:
- Explicit tool call. Give the agent a
save_memorytool and prompt it to use the tool when it learns something durable. Simple, auditable, and the agent under-uses it. - Background extraction. After each session, a separate model pass extracts candidate facts from the transcript. Better recall, but you'll store junk, so add deduplication.
Store facts as short, self-contained sentences. "User prefers TypeScript" retrieves well. A 2,000-token conversation chunk doesn't, for the same reasons covered in what chunkers do.
Episodic Memory: Remembering What Happened
Episodic memory records events rather than facts: the agent tried X, it failed with error Y, the fix was Z. For agents that repeat a task, this is the difference between improving and thrashing.
The lightweight version is a run journal. After each task, have the model write a structured post-mortem:
{
"task": "scrape pricing page for competitor A",
"outcome": "failed",
"lesson": "Site returns 403 to datacenter IPs; use stealth fetch tier",
"date": "2026-07-18"
}
Before the next similar task, retrieve past episodes for that task type and inject the lessons. Coding agents do exactly this when they maintain notes files across sessions, and it's the mechanism behind most "self-improving agent" demos you'll see. Nothing about the model changed. Its notes got better.
The Problem Nobody Warns You About: Stale Memories
Here's the failure mode that bites production systems. Long-term memory makes an agent confidently wrong over time, because facts about the world expire and vector stores don't know that.
A memory that says "Competitor A's Pro plan costs $49/month" was true in March. It's July. The agent will keep asserting $49 forever, and retrieval will keep surfacing it, because stale memories are exactly as retrievable as fresh ones.
Two defenses, and you want both:
Timestamp everything and treat old memories as claims, not facts. Store stored_at metadata (as in the code above) and prompt the agent: memories older than N days about changeable topics must be verified before use.
Give the agent a way to re-verify against the live web. When a memory is stale, the agent should fetch the current state of the source, compare, and update the record:
import requests
def refresh_memory(fact: str, source_url: str) -> str:
page = requests.post(
"https://api.link.sc/v1/fetch",
headers={"x-api-key": "YOUR_API_KEY"},
json={"url": source_url, "format": "markdown"},
).json()["content"]
# Hand `page` plus the old fact to the model:
# "Is this memory still accurate? If not, restate it."
return page
We use link.sc for the fetch step because the agent gets clean Markdown instead of HTML, and pages that block datacenter traffic still come back. The pattern is the same one that powers agents with live web access generally, covered in giving your AI agent internet access.
Memory without a refresh path is a cache with no invalidation. It degrades silently, which is the worst way to degrade.
Where to Start
Don't build all three at once. The order that works:
- Fix short-term memory first: summarization, truncation, Markdown-not-HTML.
- Add long-term memory only when users repeat themselves across sessions.
- Add episodic memory only when the agent repeats tasks and repeats mistakes.
- Add timestamps and web-refresh from day one of long-term memory, because retrofitting freshness onto a stale store is miserable.
Memory is what turns a chatbot into something that feels like a colleague. But it's plumbing, not magic: a context window you manage, a database you query, a journal you keep, and a fetch tool that keeps all of it honest.
Give your agent a fetch tool for keeping memories fresh: create a free link.sc account with 500 requests a month included.