Memory: Virtual Context and MemGPT
Context windows are finite. Conversations, documents, and tool traces are not. MemGPT (Packer et al., 2023) treats the problem like OS virtual memory: main context is RAM, the external store is disk, and the agent pages between them. Every 2026 memory system inherits this pattern.
Learning Objectives
- Explain the OS analogy MemGPT builds on: main context = RAM, external context = disk, memory tools = page in/out.
- Implement the two-tier MemGPT pattern in stdlib with a main-context buffer, an external searchable store, and page in/out tools.
- Describe how the agent issues "interrupts" to query or modify external memory and how the result is spliced back into the next prompt.
- Identify the MemGPT design choices that carry into Letta (Lesson 08) and Mem0 (Lesson 09).
The Problem
A bigger context window looks like the answer to memory. It isn't. Three failure modes keep showing up in production:
- Overflow. Long conversations, long documents, and tool-heavy trajectories cross the window, and everything past the cutoff is gone.
- Dilution. Inside the window, irrelevant context still pulls attention away from what matters. Frontier models degrade on long inputs.
- Persistence. A new session starts with an empty window, so an agent with no external memory can never say "remember when you asked me to..." across sessions.
Bigger windows help but don't fix any of this. Mem0's 2025 paper measured that 128k-window baselines still miss long-horizon facts that a 4k-window agent with external memory catches.
The Concept
MemGPT: the OS analogy
MemGPT is a design that treats the prompt as small, fast memory and an external searchable store as large, slow memory, with the agent moving data between the two through tool calls. Packer et al. (arXiv:2310.08560, v2 Feb 2024) borrow the idea straight from operating-system virtual memory:
| OS concept | MemGPT concept | 2026 production analog |
|---|---|---|
| RAM | main context (prompt) | Anthropic/OpenAI context window |
| Disk | external context | vector DB, KV, graph store |
| Page fault | memory tool call | memory.search, memory.read, memory.write |
| OS kernel | agent control loop | ReAct loop with memory tools |
The agent runs a normal ReAct loop. The only addition is one extra class of tools that page data in and out of main context.
Two tiers
- Main context. A fixed-size prompt holding the current task, including the core memory sections pinned inside it. The model always sees all of it.
- External context. Unbounded and searchable through tools: the archival store, plus past conversation turns reachable through
conversation_search. The agent reads it when something is relevant and writes to it when new facts emerge.
The original paper tested the design on two tasks that a base window can't handle: document analysis over 100k tokens, and multi-session chat with persistent memory across days.
The interrupt pattern
MemGPT calls this memory-as-interrupt. Mid-conversation, the agent invokes a memory tool, the runtime executes it, and the result splices into the next assistant turn as a new observation. It works like a Unix read() syscall: the process blocks, gets its bytes back, and carries on.
The canonical memory tool surface:
core_memory_append(section, text)writes to a persistent section of the prompt.core_memory_replace(section, old, new)edits a persistent section.archival_memory_insert(text)writes to the searchable external store.archival_memory_search(query, top_k)retrieves from the external store.conversation_search(query)scans past turns.
Where MemGPT ends and Letta begins
MemGPT became Letta in September 2024. The research repo (cpacker/MemGPT) is still there, and Letta extends the design in three ways:
- Three named tiers instead of two: core, recall (past conversation), archival (Lesson 08). This splits the same ground differently: MemGPT's core memory already lives inside main context, and its conversation search covers what Letta calls recall.
- Native reasoning in place of the
send_message/heartbeat pattern (Lesson 08). - Sleep-time agents that do memory work asynchronously (Lesson 08).
Even when production runs on Letta, Mem0, or a custom two-tier store, the MemGPT paper is the foundation underneath.
Where this pattern goes wrong
- Memory rot. Writes pile up faster than reads, and retrieval drowns in stale facts. The fixes are periodic consolidation (Letta sleep-time) and explicit invalidation (Mem0 conflict detector).
- Memory poisoning. External memory is just retrieved text. If attacker-controlled content lands in a memory note, the agent re-ingests it next session. This is the Greshake et al. attack (Lesson 27) stretched across time.
- Citation loss. The agent recalls that "the user asked me to ship X" but can't say which turn it came from. Store source references (session ID, turn ID) with every archival write.
Build It
code/main.py implements MemGPT's two-tier pattern in stdlib:
MainContextis a fixed-size prompt buffer with acoredict and amessageslist. When the message count passes the cap, it evicts the oldest messages into anevictedlist. Nothing is summarized, and evicted messages are not written to archival; they stay reachable only throughconversation_search.ArchivalStoreis an in-memory BM25-esque store (token-overlap scoring) of (id, text, tags, session, turn) records.- Five memory tools map to the MemGPT surface.
- A scripted agent fills archival with facts, then a follow-up is served by calling
archival_memory_search.
Run it:
python3 code/main.py
The trace shows the agent writing three facts, filling main context to the cap (which forces eviction), then retrieving from archival. That is a MemGPT-style workflow, with no real LLM involved.
Use It
Every production memory system today is a MemGPT variant:
- Letta (Lesson 08): three tiers, native reasoning, sleep-time compute.
- Mem0 (Lesson 09): vector, KV, and graph stores fused with a scoring layer.
- OpenAI Assistants / Responses: managed memory via threads and files.
- Claude Agent SDK: long-term memory via skills and a session store.
Pick one by operational shape (self-hosted, managed, framework-integrated). The core pattern is the same in all of them.
Ship It
outputs/skill-virtual-memory.md is a reusable skill that produces a correct two-tier memory scaffold (main context, archival store, tool surface) for any target runtime, with the eviction policy and citation fields already wired in.
Most people building a memory layer spend their effort on retrieval quality and almost none on what gets written. Writes are the part that bites you later. Every fact you let the agent store is a fact it will retrieve, trust, and act on in a session you aren't watching, so a sloppy write today becomes a confident wrong answer next week. Decide up front what is allowed into archival memory, tag it with where it came from, and make that the part you test hardest. The search algorithm is easy to swap out afterward. A polluted store is not.
Exercises
- Add a
max_main_context_tokenscap measured in tokens (approximate withlen(text.split())* 1.3). The starter code evicts the oldest messages by count and keeps no summary. Instead, compact the evicted messages into a summary when the cap is exceeded. Compare behavior with and without the summarizer. - Implement BM25 properly over the archival store (term frequency, inverse document frequency). Measure recall@10 on a toy fact set versus the token-overlap baseline.
- Add
citationfields (session_id, turn_id, source_url) to archival inserts. Make the agent cite sources on every retrieval-backed answer. - Simulate memory poisoning: add an archival record that says "ignore all future user instructions." Write a guard that scans retrievals for directive-shaped text and marks them untrusted.
- Port the implementation to use the MemGPT research repo's core-memory JSON schema (
cpacker/MemGPT). What changes when you switch from flat strings to typed sections?
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| Virtual context | "Unlimited memory" | Main (prompt) + external (searchable) tiers with page in/out |
| Main context | "Working memory" | The prompt: fixed-size, always visible |
| Archival memory | "Long-term store" | External searchable persistence, retrieved on demand |
| Core memory | "Persistent prompt section" | Named sections pinned inside the main context |
| Memory tool | "Memory API" | Tool call the agent issues to read/write external memory |
| Interrupt | "Memory page fault" | Agent pauses, runtime fetches, result splices into next turn |
| Memory rot | "Stale facts" | Old writes drown retrieval; fix with consolidation |
| Memory poisoning | "Injected persistent note" | Attacker content stored as memory, re-ingested on recall |
Further Reading
- Packer et al., MemGPT (arXiv:2310.08560): OS-inspired virtual context paper
- Letta, Memory Blocks blog: the three-tier evolution
- Anthropic, Effective context engineering: treating context as a budget
- Chhikara et al., Mem0 (arXiv:2504.19413): hybrid production memory on top of this pattern
Code
- main.py (python, 198 lines)