OS-style paging (default)

Hierarchical Memory diagramUser, Agent, Core, Recall, Archival, Summarizer. Step captions follow the diagram.UserAgentCorealways in contextRecallfull message logArchivalsearchable storeSummarizerHierarchical Memory diagramUser, Agent, Core, Recall, Archival, Summarizer. Step captions follow the diagram.UserAgentCorealways incontextRecallfullmessage logArchivalsearchablestoreSummarizer

All steps

Recall an old fact, save a new one

The context window overflows

Parts of this pattern
User Input / output
The person chatting with the long-running assistant, e.g. 'Remind me when my Lisbon trip is, and I'm vegetarian now.'
Agent LLM call
The memory-aware agent. Searches recall and archival, edits core (core_memory_append, core_memory_replace), moves facts out with archival_insert, then replies.
Core Memory
Small block of stable facts about the user, always in the prompt; core_memory_append adds facts here and core_memory_replace edits them.
Recall Memory
Verbatim log of evicted messages, kept so old turns stay searchable via recall_search (a tool call, not drawn as an edge).
Archival Memory
Long-term store of documents and facts, written with archival_insert, searched with archival_search and paged into context on demand.
Summarizer LLM call
Folds evicted messages into the running summary at the top of context, keeping names, decisions and numbers.
User → Agent
A new user message is appended to the context queue and starts the agent's turn.I'm vegetarian now. Also, when is that Lisbon trip again?
Agent → Archivalsearch
The agent's tool call for facts that are no longer in view.{"tool": "archival_search", "query": "Lisbon trip dates"} (the trip was saved to archival memory earlier)
Archival → Agentfacts
The matching facts are appended to the agent's context for its next step.[{"text": "Lisbon trip booked 12-19 May, hotel near Alfama"}]
Agent → Coresave fact
A durable fact is written into core memory so it appears in every future prompt.{"tool": "core_memory_append", "text": "User is vegetarian."}
Agent → Recallraw messages
When the context passes the token limit, the oldest messages are stored verbatim in recall memory.recall.store(["user: Can you book the Lisbon hotel?", "assistant: Booked, 7 nights."])
Agent → Summarizersummarize
The same evicted messages go to the summarizer along with the previous running summary.previous summary + evicted: "user: Can you book the Lisbon hotel?" ...
Summarizer → Agentnew summary
The updated running summary replaces the old one at the top of the context.Summary: User planning a Lisbon trip (12-19 May); hotel booked; prefers window seats.
Agent → User
The agent's final message to the user once it needs no more tool calls.Your Lisbon trip is 12-19 May. I've saved that you're vegetarian.

Rolling summary

No tiers and no memory tools: after each session the model folds the new dialogue into one running summary, which opens every later session.

Hierarchical Memory: Rolling summary diagramUser, Agent, Summarizer, Summary. Step captions follow the diagram.UserAgentSummarizerSummaryone running textHierarchical Memory: Rolling summary diagramUser, Agent, Summarizer, Summary. Step captions follow the diagram.UserAgentSummarizerSummaryone runningtext

All steps
Parts of this pattern
User Input / output
The person chatting with the assistant across many sessions.
Agent LLM call
Chats normally, with the running summary at the top of its prompt; it has no memory tools.
Summarizer LLM call
At the end of each session, folds the new dialogue into the previous summary, within a word limit.
Summary Memory
A single summary per user, rewritten after every session and prepended to the next one.
Summary → Agentprepend
When a session starts, the running summary goes at the top of the agent's prompt.Summary: User is planning a Lisbon trip, 12-19 May; hotel booked near Alfama; prefers window seats.
User → Agent
Each user message joins the session as in a normal chat.I'm vegetarian now. Any dinner ideas for Lisbon?
Agent → User
The agent replies using the summary and the current session.For your 12-19 May trip, try the vegetarian places near Alfama, close to your hotel.
Agent → Summarizersession log
When the session ends, its full log goes to the summarizer with the previous summary.previous summary + session: "user: I'm vegetarian now..."
Summarizer → Summarynew summary
The rewritten summary replaces the old one, ready for the next session.Summary: User is vegetarian (since October). Lisbon trip 12-19 May; hotel near Alfama; prefers window seats.

Fact store (Mem0)

After each exchange, an extractor pulls out candidate facts, compares each with similar stored facts, and applies ADD, UPDATE, DELETE or NOOP.

Hierarchical Memory: Fact store (Mem0) diagramUser, Agent, Extractor, Reconcile, Facts. Step captions follow the diagram.UserAgentExtractorReconcilevs similar factsFactsHierarchical Memory: Fact store (Mem0) diagramUser, Agent, Extractor, Reconcile, Facts. Step captions follow the diagram.UserAgentExtractorReconcilevs similarfactsFacts

All steps
Parts of this pattern
User Input / output
The person chatting with the assistant, e.g. one who says 'I'm vegetarian now'.
Agent LLM call
Replies with the most relevant stored facts in its prompt; it never manages memory itself.
Extractor LLM call
After each exchange, pulls out short, standalone facts worth remembering.
Reconcile Evaluator
Compares each candidate with the most similar stored facts and picks ADD, UPDATE, DELETE or NOOP.
Facts Memory
The fact store: one short fact per row, searchable by similarity, with a history of changes.
User → Agent
A user message starts the agent's turn.I'm vegetarian now, so no more seafood suggestions.
Agent → User
The agent's reply, sent before any memory work starts.Noted. I'll stick to vegetarian places from now on.
Agent → Extractorexchange
The latest exchange goes to the extractor after the reply is sent.user: I'm vegetarian now, so no more seafood suggestions. assistant: Noted...
Extractor → Reconcilecandidates
The candidate facts, each compared with the stored facts most similar to it.{"facts": ["User is vegetarian"]}
Reconcile → Factsoperation
The operations chosen for each candidate are applied to the store.DELETE fact 17 ('User loves Portuguese seafood'); ADD 'User is vegetarian (since October)'
Facts → Agentfacts
On the next message, the stored facts most similar to it go into the agent's prompt.Relevant facts: User is vegetarian. Lisbon trip 12-19 May.

← → neighboursesc atlas

Knowledge & Memory

Hierarchical Memory

Also known as MemGPT · Virtual context management · Tiered memory

Treat the context window like RAM. The agent uses tool calls to page facts between core, recall and archival memory.

OS-style paging: Introduced by Packer et al. (2023) · UC Berkeley

MemGPT, which framed the context window as main memory and the agent as its own memory manager.

Rolling summary: Introduced by Wang et al. (2023) · HKUST, CAS IIE and others

Recursively Summarizing Enables Long-Term Dialogue Memory in Large Language Models (Neurocomputing).

Fact store (Mem0): Introduced by Chhikara et al. (2025) · Mem0

Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory.

Measured over one run

OS-style paging

Nodes
6. Boxes in the diagram: models, tools, stores, people and plain code.
LLM calls
1. Model invocations in the first scenario: each time a model or agent node starts thinking, plus each branch it decides.
Tool calls
0. Messages sent to a tool node in the first scenario.
Steps
4. Steps in the first scenario, with repeated steps counted every time they run.
Scenarios
2. Distinct example runs you can switch between on the diagram.
Loop
no. Whether the flow cycles back on itself until a condition is met.
Parallel
no. Whether two or more messages are in flight at the same time.
Branch
no. Whether the flow takes one path out of several, chosen at run time.
Human in the loop
no. Whether a person appears as a node, to approve, correct or answer.
Autonomy
high. How much the model decides the control flow itself, from fixed code paths (low) to open-ended (high).
Cost
medium. Relative token spend per request.
Latency
medium. Relative wall-clock time per request.

Rolling summary

Nodes
4. Boxes in the diagram: models, tools, stores, people and plain code.
LLM calls
2. Model invocations in the first scenario: each time a model or agent node starts thinking, plus each branch it decides.
Tool calls
0. Messages sent to a tool node in the first scenario.
Steps
3. Steps in the first scenario, with repeated steps counted every time they run.
Scenarios
1. Distinct example runs you can switch between on the diagram.
Loop
no. Whether the flow cycles back on itself until a condition is met.
Parallel
no. Whether two or more messages are in flight at the same time.
Branch
no. Whether the flow takes one path out of several, chosen at run time.
Human in the loop
no. Whether a person appears as a node, to approve, correct or answer.
Autonomy
high. How much the model decides the control flow itself, from fixed code paths (low) to open-ended (high).
Cost
medium. Relative token spend per request.
Latency
medium. Relative wall-clock time per request.

Fact store (Mem0)

Nodes
5. Boxes in the diagram: models, tools, stores, people and plain code.
LLM calls
3. Model invocations in the first scenario: each time a model or agent node starts thinking, plus each branch it decides.
Tool calls
0. Messages sent to a tool node in the first scenario.
Steps
4. Steps in the first scenario, with repeated steps counted every time they run.
Scenarios
1. Distinct example runs you can switch between on the diagram.
Loop
no. Whether the flow cycles back on itself until a condition is met.
Parallel
no. Whether two or more messages are in flight at the same time.
Branch
no. Whether the flow takes one path out of several, chosen at run time.
Human in the loop
no. Whether a person appears as a node, to approve, correct or answer.
Autonomy
high. How much the model decides the control flow itself, from fixed code paths (low) to open-ended (high).
Cost
medium. Relative token spend per request.
Latency
medium. Relative wall-clock time per request.

Counted from the first scenario's timeline when the site is built.

Problem

A context window is finite, and long-running agents outgrow it: a personal assistant that has talked to you for months has far more history than any prompt can hold. Truncating the oldest messages makes it forget your name. Stuffing everything in is impossible, and retrieving blindly misses what matters.

How it works

Borrow the operating-system idea of virtual memory. The prompt is the fast, small tier, and slower tiers sit behind it. The agent itself manages the paging by calling memory tools.

  • Core memory is a short block that is always in the prompt: the user’s name, standing preferences, the agent’s persona.
  • Recall memory is the full, searchable log of past messages, including everything evicted from the window.
  • Archival memory is a separate, large store of facts and documents the agent chooses to save.

Take a message from the user that mentions a trip they planned months ago. The agent notices the details are missing from view and calls a search tool, and archival memory returns the dates. If the user then says they have gone vegetarian, the agent calls a save tool and the fact lands in core memory, so it appears in every later prompt.

When the window fills, the oldest messages are written verbatim to recall memory, and a summarizer folds them into a running summary that stays at the front of the prompt. The summary is lossy, but the raw messages are not: the agent can search recall for the exact wording when the summary is not enough.

Two simpler designs appear as tabs. A rolling summary drops the tiers and tools entirely and keeps one running summary per user. A fact store (Mem0) extracts facts after every exchange and reconciles each with what is already stored.

When to use

  • Conversations span days or months, far beyond one context window.
  • The agent must remember user facts, decisions and commitments.
  • Personalization matters more than raw speed.

When to avoid

  • Sessions are short and fit comfortably in the context window.
  • Every conversation is independent and stateless by design.
  • You cannot afford extra model calls for eviction and summarization.

Trade-offs

Memory management is spent from the same model budget as the task: every search, save and summary is another call, and the agent can burn turns on housekeeping. The running summary is lossy by construction, so the agent must notice that it needs the original and search recall for it. In return, effective memory is limited by storage and not by the window size.

Failure modes

  • Forgetting to look: the agent answers “I don’t know” without searching. Instruct it to search before admitting ignorance.
  • Core bloat: everything gets saved as a core fact until the always-on block crowds out the task. Keep it small and prune it.
  • Lossy summaries: a key number disappears from the running summary. Tell the summarizer what to preserve, and let the agent search recall for the original.
  • Stale or wrong facts: an outdated preference lives forever in core memory. Give the agent a way to edit and delete, not only append.

Build it with your AI

OS-style paging

Paste this into Claude Code, Cursor, ChatGPT or any AI coding assistant.

Implement the Hierarchical Memory pattern in this codebase, using the LLM client and language we already use.

## Goal

Give a long-running chat agent memory beyond its context window by treating the window like RAM: a small always-visible core, a log of evicted messages and a searchable archive, paged with tool calls.

## How it works

1. Build every prompt from the Memory-aware system prompt: the core memory block, then the running summary, then the recent messages.
2. Give the agent five tools: core_memory_append(text) and core_memory_replace(old, new) for core memory, recall_search(query) over the evicted-message log, and archival_insert(text) and archival_search(query) for the long-term store.
3. Run the agent loop: execute each tool call it makes, append the result, and call it again until it replies without a tool call.
4. If a tool call fails or its arguments do not parse, return the error text as the tool result so the agent can retry or move on.
5. After each turn, count the prompt's tokens. Above the threshold, evict the oldest messages: store them verbatim in recall memory and fold them into the running summary with the Running summary prompt.

## Prompts

### Memory-aware system prompt

```markdown
You are {{agent_name}}, a long-running assistant with limited context.

<core_memory>{{core}}</core_memory>
Core memory is always visible. Keep it short: stable facts about the user and yourself.

Tools:
- core_memory_append(text): save a durable fact to core memory.
- core_memory_replace(old, new): rewrite or shorten a fact in core memory.
- recall_search(query): find exact past messages that were evicted from view.
- archival_insert(text): save a fact or document to the long-term store.
- archival_search(query): look up documents and facts saved to the long-term store.
When core memory is full, move older facts out with archival_insert.
Evicted messages are saved to recall memory automatically, and a running
summary of them stays at the top of your context.

When the user states a lasting preference, save it. When you lack a fact
you should know, search before you say you don't know.
```

### Running summary prompt

```markdown
The context window is full and these messages are being evicted. Update the
running summary so it still covers them.

<previous_summary>{{previous}}</previous_summary>
<evicted_messages>{{evicted}}</evicted_messages>

Keep names, decisions, numbers and open questions. Drop pleasantries.
Write at most {{max_words}} words.
```

## Stop rule

Each turn stops when the agent replies without a tool call, or after 6 tool calls; at the cap, ask for a reply with tools disabled. The memory itself persists across sessions and is never reset.

## Requirements

- Make the eviction threshold (default 70% of the context window), the core memory limit (default 2,000 characters) and the per-turn tool cap (default 6) parameters.
- Reject a core_memory_append that would pass the limit, with an error telling the agent to move older facts out with archival_insert or shorten them with core_memory_replace.
- Store recall and archival memory outside the process, keyed by user, so they survive restarts.
- Log every memory write and every eviction, with token counts before and after.
- Add a test with a stub token counter that crosses the threshold on turn 3; assert the oldest messages are in recall memory verbatim, the summarizer was called once, and those messages are no longer in the prompt.

Rolling summary

Paste this into Claude Code, Cursor, ChatGPT or any AI coding assistant.

Implement the Hierarchical Memory pattern (Rolling summary variant) in this codebase, using the LLM client and language we already use.

## Goal

Give a chat agent long-term memory with no memory tools: keep one running summary per user, rewritten after each session and placed at the top of every later prompt.

## How it works

1. When a session starts, load the user's running summary (empty for a new user) and put it at the top of the system prompt.
2. Run the session as a normal chat. The agent reads the summary but has no memory tools.
3. When the session ends (after 30 minutes idle), send the previous summary and the full session log to the summarizer with the Session summary prompt.
4. Save the returned text as the user's new running summary, keeping the previous version.
5. If the summarizer fails or returns more than the word limit, keep the old summary and fold both session logs in at the end of the next session.

## Prompts

### Session summary prompt

```markdown
Update the running summary of what you know about this user, using
the session that just ended.

<previous_summary>{{previous}}</previous_summary>
<session>{{session_log}}</session>

Keep names, dates, preferences, decisions and open tasks. If the user
corrected an earlier fact, replace it rather than keeping both.
Write at most {{max_words}} words.
```

## Stop rule

One summary update per session. The summary is never reset; it stays under the word limit because the summarizer must drop the least important facts to fit.

## Requirements

- Make the word limit (default 400) and the idle time that ends a session (default 30 minutes) parameters.
- Keep the last 5 summary versions per user, so a bad rewrite can be rolled back.
- Log each update with its word count and the session id it came from.
- Add a test with a stub summarizer that returns 'User is vegetarian.'; assert the first prompt of the next session contains 'User is vegetarian.' above the first user message.

Fact store (Mem0)

Paste this into Claude Code, Cursor, ChatGPT or any AI coding assistant.

Implement the Hierarchical Memory pattern (Fact store (Mem0) variant) in this codebase, using the LLM client and language we already use.

## Goal

Keep a de-duplicated store of facts about each user: extract candidate facts after every exchange, reconcile each with similar stored facts, and retrieve the relevant ones into later prompts.

## How it works

1. After each exchange, send the user message, the reply and a short running summary to the extractor with the Fact extraction prompt, and parse its JSON list of candidate facts.
2. For each candidate, find the 5 most similar stored facts by embedding similarity.
3. Call the model with the Memory update prompt, passing the candidate and those facts, and parse its list of operations: ADD (new text), UPDATE (a fact id and the text with added detail), DELETE (the id of a contradicted fact) or NOOP.
4. Apply the operations in order to the fact store, keeping each fact's id, text, timestamps and source exchange.
5. Before each agent turn, retrieve the 5 stored facts most similar to the new message and put them in the prompt.
6. If the extractor or the reconcile reply does not parse, skip the memory update for that exchange and log it; the chat carries on.

## Prompts

### Fact extraction prompt

```markdown
Extract lasting facts about the user from this exchange.

<summary>{{summary}}</summary>
<exchange>{{user_message}} / {{assistant_reply}}</exchange>

Include preferences, plans, relationships and corrections. Skip small talk.
Respond in JSON: {"facts": ["short standalone fact", "..."]}
```

### Memory update prompt

```markdown
Decide how a new fact changes the user's stored memory.

<new_fact>{{candidate}}</new_fact>
<similar_facts>{{similar}}</similar_facts>

List the operations to apply:
- ADD the new fact if no stored fact covers it.
- UPDATE a stored fact only to add complementary detail to it
  (for example "Lisbon trip in May" becomes "Lisbon trip 12-19 May").
- DELETE every stored fact the new fact contradicts, then ADD the new fact.
- NOOP if the fact is already known.
Respond in JSON:
{"ops": [{"op": "ADD|UPDATE|DELETE|NOOP", "id": "<fact id or null>", "text": "<new text or null>"}]}
```

## Stop rule

Memory updates run once per exchange, after the reply is sent, and never block it. There is no loop to stop; the store persists across sessions.

## Requirements

- Make the similar-fact count (default 5) and the retrieved-fact count (default 5) parameters.
- Run extraction and reconciliation after the reply is sent, so the user never waits for them.
- Keep updated and deleted facts in a history table, so a wrong operation can be reverted.
- Log every operation with its candidate, the matched fact and the result.
- Add a test with the stored fact 'User loves seafood' and a stub that returns DELETE for it plus ADD 'User is vegetarian'; assert the store holds the single fact 'User is vegetarian' and the history holds the deleted text.

Prompt templates

OS-style paging

Memory-aware system prompt
You are {{agent_name}}, a long-running assistant with limited context.
<core_memory>{{core}}</core_memory>
Core memory is always visible. Keep it short: stable facts about the user and yourself.
Tools:
- core_memory_append(text): save a durable fact to core memory.
- core_memory_replace(old, new): rewrite or shorten a fact in core memory.
- recall_search(query): find exact past messages that were evicted from view.
- archival_insert(text): save a fact or document to the long-term store.
- archival_search(query): look up documents and facts saved to the long-term store.
When core memory is full, move older facts out with archival_insert.
Evicted messages are saved to recall memory automatically, and a running
summary of them stays at the top of your context.
When the user states a lasting preference, save it. When you lack a fact
you should know, search before you say you don't know.
Running summary prompt
The context window is full and these messages are being evicted. Update the
running summary so it still covers them.
<previous_summary>{{previous}}</previous_summary>
<evicted_messages>{{evicted}}</evicted_messages>
Keep names, decisions, numbers and open questions. Drop pleasantries.
Write at most {{max_words}} words.

Rolling summary

Session summary prompt
Update the running summary of what you know about this user, using
the session that just ended.
<previous_summary>{{previous}}</previous_summary>
<session>{{session_log}}</session>
Keep names, dates, preferences, decisions and open tasks. If the user
corrected an earlier fact, replace it rather than keeping both.
Write at most {{max_words}} words.

Fact store (Mem0)

Fact extraction prompt
Extract lasting facts about the user from this exchange.
<summary>{{summary}}</summary>
<exchange>{{user_message}} / {{assistant_reply}}</exchange>
Include preferences, plans, relationships and corrections. Skip small talk.
Respond in JSON: {"facts": ["short standalone fact", "..."]}
Memory update prompt
Decide how a new fact changes the user's stored memory.
<new_fact>{{candidate}}</new_fact>
<similar_facts>{{similar}}</similar_facts>
List the operations to apply:
- ADD the new fact if no stored fact covers it.
- UPDATE a stored fact only to add complementary detail to it
(for example "Lisbon trip in May" becomes "Lisbon trip 12-19 May").
- DELETE every stored fact the new fact contradicts, then ADD the new fact.
- NOOP if the fact is already known.
Respond in JSON:
{"ops": [{"op": "ADD|UPDATE|DELETE|NOOP", "id": "<fact id or null>", "text": "<new text or null>"}]}

Related patterns

Composes well with:

Sources

  1. MemGPT: Towards LLMs as Operating Systems (Packer et al., 2023)

From the Agentic Atlas by Joran Kikke · Edit on GitHub · Suggest a correction