Memory
Give your agent a brain that remembers — across turns, across sessions, across years. Different memory tiers for different jobs, plus the full Claude-native memory landscape.
10 concepts · tap to jump
- 1Why one memory type isn't enough
- 2Working memory — the scratchpad
- 3Episodic memory — past interactions
- 4Procedural memory — the skill library
- 5Summarization pipeline & persistence
- 6The persistence layer
- 7The managed memory tool
- 8Auto Memory — Claude's own notes
- 9Native memory landscape & cert patterns
- 10Memory Manager — orchestration
Each concept is a 5-card cluster: Big Idea · Analogy · How It Works · Pattern · Misconceptions.
One store can't carry three jobs
An agent juggles three completely different kinds of memory. Working: the scratchpad for the current task — user intent, extracted entities, intermediate tool results. Episodic: a searchable archive of past conversations and decisions. Procedural: a library of proven action sequences ("for X, do A then B then C").
Stuff all three into one bucket and retrieval gets polluted. A search for "did the user mention their address?" turns up grocery-list scratchpads, last week's billing chat, and the recipe for fixing TLS errors. Separate them and each retrieval stays focused, each prompt stays lean.
The architecture is what lets agents scale from one-session toys to multi-year production assistants. Without layering you eventually run out of either context or coherence.
The single-notebook brain
BEFORE: Imagine your brain stored everything in one notebook — today's grocery list, how to ride a bike, your wedding day, the recipe for pasta carbonara, every meeting you've ever had — all on the same pages, in arrival order.
PAIN: Every recall means flipping through every page. The notebook fills up fast. Important long-term memories get crowded out by ephemeral notes — you'd "forget" how to ride a bike because you scribbled over it with a shopping list.
MAPPING: Real brains don't work that way. They use working memory (what you're holding right now), episodic memory (specific past events), and procedural memory (skills and habits). Multi-layer memory architectures give agents the same separation — and the same scaling benefits.
Three stores, one prompt
- Per turn, load three slicesThe runtime grabs the current working-memory dict, the top 2–3 episodes from the vector DB, and any matching procedural template.
- Format as labelled blocksEach tier becomes a clearly-tagged block in the system prompt —
[Working],[Past Interactions],[Suggested Procedure]— so Claude knows which is which. - Claude reasons over all threeWorking anchors the now. Episodic provides past context. Procedural suggests the plan. Claude weights them and acts.
- Update working as you goEach tool result, decision, or partial answer gets written back to working memory in real time.
- Promote at session endSummarize the conversation, embed it, store it in episodic. If a plan succeeded enough times, promote it to procedural.
Which tier does each fact belong in?
| Lifespan | Tier | Example |
|---|---|---|
| This task only | Working | "Currently looking up order #4521" |
| Across sessions | Episodic | "User prefers email over Slack" |
| Across many users / always | Procedural | "Refund flow: verify → check policy → issue" |
| Stale after minutes | Working | API response just received |
| Stale after weeks | Episodic + TTL | "Q3 budget was $50K" |
Start with the tier that solves your real problem. A simple FAQ bot needs none of this.
Common misconceptions
Working · Episodic · Procedural. Three different stores for three different memory jobs. Separation is the design — mixing them pollutes retrieval and bloats prompts.
Working memory: the agent's clipboard
Working memory is a fast, mutable scratchpad for the current task. It holds the user's parsed intent, extracted entities (names, dates, IDs), intermediate tool results, and the running plan. Concretely, it's a Python dict (or Redis cache for distributed agents) that lives in RAM.
Every LLM call sees this scratchpad through a to_prompt() method that formats the whole dict as a labelled block in the system prompt. That's how the agent never "forgets" what it learned two tool calls ago in a multi-step task.
Lifetime matches the task — created when a request arrives, cleared (or archived to episodic) when the response is sent.
The doctor's clipboard
BEFORE: Imagine a doctor seeing a patient without a clipboard. They walk in, the patient describes three symptoms, the doctor orders a blood test, then walks to the lab.
PAIN: Without notes, the doctor has to ask the patient to repeat everything. They lose track of what they learned, repeat questions, order duplicate tests. The visit takes three times longer and the patient loses trust.
MAPPING: Working memory is the agent's clipboard. It rides along with every LLM call so the agent always knows the current intent, entities, and intermediate findings — no rediscovery, no duplicate tool calls, no looping.
Set, get, format, clear
- InitializeAt task start, create a fresh dict tagged with a session ID and timestamp.
- Set as you learnEach parsed intent, extracted entity, or tool result writes a key into the dict with an updated_at stamp.
- Inject every turnBefore each Claude call,
to_prompt()formats the whole dict into a structured text block prepended to the system prompt. - Read on demandWithin a turn, the agent's own code can
get()any prior value to pass to a tool. - Clear or archiveWhen the task ends, either drop the dict or hand it to the summarizer for promotion to episodic memory.
Pseudocode — WorkingMemory
CLASS WorkingMemory: state = {} # key → {value, updated_at} METHOD set(key, value): state[key] = {value, now()} METHOD get(key, default=null): RETURN state[key].value OR default METHOD clear(): state = {} METHOD to_prompt(): IF empty: RETURN "[Working: empty — new task]" lines = ["[Working — session " + id + "]"] FOR EACH (key, entry) IN state: lines.append(" " + key + ": " + format(entry.value)) RETURN JOIN(lines, newline)
The to_prompt() method is the load-bearing piece — it's how the dict becomes context Claude can reason over.
Common misconceptions
Working memory is the clipboard, not the diary. Structured dict · injected as a labelled prompt block · cleared at task end. Without to_prompt() riding along, multi-step tasks lose state between tool calls.
Episodic memory: a searchable diary
Episodic memory stores summarized records of past conversations in a vector database, indexed by semantic embeddings and timestamps. After each session ends, the conversation is summarized into a compact record (~200–400 tokens) and embedded into a vector DB like ChromaDB.
When a new message arrives, the agent embeds it and searches for the closest matches — not by keyword, but by meaning. Top 2–3 hits are injected into the prompt as "relevant past context." That gives the agent cross-session continuity without replaying entire transcripts.
This is the difference between a stateless chatbot and a persistent assistant who remembers your last visit.
The personal assistant's diary
BEFORE: Picture an assistant who keeps a detailed diary that's searchable by topic. They don't reread the whole diary every morning — that would take hours. When you ask "what did we decide about the budget last week?", they search and find the right entry in seconds.
PAIN: Without the diary, the assistant starts every day with amnesia. You re-explain your preferences, past decisions, and ongoing projects every single session. For a support agent, every returning customer feels like a stranger.
MAPPING: Episodic memory is that searchable diary. Conversations end, the agent writes a short summary, files it by meaning, and pulls back the right entry the next time it's relevant.
Summarize, embed, search, inject
- At session endSend the conversation to Claude with a structured prompt that extracts topics, decisions, preferences, and outcomes. Output: ~300 tokens of structured summary.
- EmbedConvert the summary text into a vector (often ~1,536 floats) using an embedding model.
- Store with metadataSave embedding + summary text + metadata (session_id, timestamp, user_id, topic tags) in ChromaDB / Pinecone / Weaviate.
- At new session startEmbed the user's message. Run a cosine-similarity search over stored episode vectors.
- Inject top 2–3Format the closest matches as a "Relevant Past Interactions" block in the system prompt. Cap at 3 to avoid noise.
Pseudocode — EpisodicMemory
vector_db = PERSISTENT ChromaClient(path="./chroma") FUNCTION store_episode(summary, metadata): vec = EMBED(summary.text) vector_db.ADD( id = uuid(), vector = vec, text = summary.text, meta = {session_id, user_id, timestamp, topics} ) FUNCTION retrieve(user_message, k=3, user_id=null): q_vec = EMBED(user_message) hits = vector_db.QUERY(q_vec, k=k, filter={user_id: user_id}) RETURN [{summary, when, score} FOR h IN hits] FUNCTION to_prompt(user_message): hits = retrieve(user_message, k=3) IF empty: RETURN "" RETURN "[Relevant Past Interactions]\n" + JOIN("- " + h.summary FOR h IN hits)
Use PersistentClient, not the default in-memory client — otherwise everything evaporates on restart.
Common misconceptions
Summaries in, semantic search out. Episodic memory turns conversations into searchable embeddings, then injects the 2–3 closest matches at the next session start. Persistence is non-negotiable — default in-memory mode loses everything on restart.
Procedural memory: the skill library
Procedural memory stores reusable action sequences — tool chains, proven workflows, task templates — as structured records. Each record has two parts: a trigger description ("user asks for a weekly sales report") stored as an embedding, and an ordered list of execution steps ("query_db → aggregate → chart_gen → format_pdf") stored as JSON.
At runtime, the agent embeds the current request, searches procedural memory for matching triggers, and — if a high-confidence match is found — loads the procedure as a suggested plan. Claude can then follow or adapt it instead of replanning from scratch.
This is how agents get faster and more consistent over time without retraining.
The chef's recipe book
BEFORE: An experienced chef who's cooked the same pasta dish 200 times doesn't re-read the recipe each time. They've internalized it: boil water, salt it, cook 8 minutes, sauté garlic, toss together. Automatic.
PAIN: A novice without recipes reasons through every step from first principles, makes mistakes, takes 3x longer. Worse, they solve the same problem differently each time. Customers ordering the same dish twice get different meals.
MAPPING: Procedural memory is the agent's recipe book. When a tool sequence works, the agent saves it as a template. Next time a similar request arrives, it retrieves the template instead of replanning — faster, cheaper, more consistent.
Trigger match → suggested plan
- Save successful plansWhen a multi-step task completes successfully, store the trigger description, the ordered tool calls, and a success counter.
- Embed the triggerConvert the trigger description into a vector and index it for semantic search.
- Match on each new requestEmbed the user's request, search procedural memory, get a similarity score for the best match.
- Threshold the matchBelow ~0.7 similarity, ignore the match and let Claude reason from scratch. Above, load the steps as a suggested plan.
- Inject as suggestion, not mandateFormat as
[Suggested Procedure (used 12x)]. Claude can adapt, skip, or override based on current context.
Pseudocode — ProceduralMemory
FUNCTION save_procedure(name, trigger, steps): vec = EMBED(trigger) proc_db.ADD( id = name, vector = vec, meta = {name, trigger, steps_json, success_count: 0} ) FUNCTION match(user_request, min_sim=0.7): q = EMBED(user_request) hit = proc_db.QUERY(q, k=1) IF hit.score < min_sim: RETURN null # let Claude plan from scratch RETURN hit.meta FUNCTION to_prompt(user_request): proc = match(user_request) IF proc IS null: RETURN "" RETURN "[Suggested Procedure: " + proc.name + " (used " + proc.success_count + "x)]\n" + format_steps(proc.steps_json)
The threshold matters. Match too eagerly and the agent forces wrong-shaped plans onto novel requests.
Common misconceptions
Procedural memory is the agent's recipe book. Trigger embedding for matching, JSON steps for execution, similarity threshold to avoid forcing wrong plans. Saves tokens on planning and stabilizes outputs across repeated tasks.
Summarization: the bridge to long-term memory
The summarization pipeline is what turns short-term conversation into durable cross-session memory. At session end, Claude condenses the full transcript (4,000+ tokens) into a compact structured record (~300 tokens) capturing topics, decisions, preferences, action items, and outcomes.
That record is then embedded and stored in episodic memory. At the next session, the new user message is embedded and matched against the stored summaries — closest hits get injected. That's how an agent on Tuesday remembers what was decided on Friday without replaying every word in between.
Without summarization, you'd hit storage and context limits within a week.
The end-of-day project recap
BEFORE: A project manager writes a brief summary at the end of each workday: key decisions, action items, blockers, what's pending for tomorrow. Files it in a searchable archive.
PAIN: Without the habit, Monday morning is chaos. Nobody remembers Friday's decisions. Action items fall through the cracks. The team re-discusses the same issues. Effective weekly amnesia.
MAPPING: The summarization pipeline automates this end-of-day habit. Conversation ends, Claude writes a structured recap, embeds it, files it. Next session loads it. The agent never starts from scratch.
Summarize → embed → store → compact
- SummarizeSend the conversation to Claude with a structured prompt extracting topics, decisions, preferences, action items, outcomes. Output: structured JSON, ~200–400 tokens.
- EmbedConvert the summary text into a vector embedding using an embedding model (Voyage AI, OpenAI, etc.).
- StoreSave embedding + summary + metadata (session_id, timestamp, user_id, topic tags) in the vector DB.
- Compact periodicallyMerge related episodes from the same week, drop entries older than the TTL, deduplicate near-identical summaries — keeps the store from growing unbounded.
- Trigger only at session endNot after every turn. Mid-conversation summarization wastes tokens and loses context that only makes sense in the full flow.
Pseudocode — summarize_conversation
FUNCTION summarize_conversation(messages): text = format_transcript(messages) prompt = "You are a conversation summarizer. " + "Return JSON with these fields: " + "summary, topics, decisions, " + "user_preferences, action_items. " + "Return ONLY valid JSON." reply = CALL Claude(system=prompt, user=text) record = PARSE_JSON(reply) IF parse_failed: record = {summary: reply[:500], topics: [], decisions: [], user_preferences: [], action_items: []} RETURN record # Pipeline at session end: record = summarize_conversation(log) vec = EMBED(record.summary) episodic.STORE(vec, record, meta={ts: now()}) working.clear()
Use a capable model for summarization. Cheap models drop nuances like "user seemed frustrated" or the difference between "discussed X" and "decided X."
Common misconceptions
Compress 4,000 tokens to 300, then embed and file. Summarization is the bridge between today's conversation and next week's recall — treat it as a pipeline (summarize → embed → store → compact), not a single step.
Persistence: memory that survives a crash
Memory that lives only in RAM disappears when your process restarts. For production agents, all three tiers need durable storage — data written to disk (or a managed DB) that survives crashes, deployments, and OS restarts.
Each tier has its own backing store. Working memory: Redis or SQLite for distributed setups, JSON files for simple ones. Episodic memory: vector DB with persistent client (ChromaDB on disk, Pinecone, Weaviate Cloud). Procedural memory: SQLite or PostgreSQL for the steps JSON, plus the trigger embeddings in the vector DB.
The tradeoff is operational complexity — you now own a database, a backup story, and a growth plan.
Session cookie vs database
BEFORE: If you've worked with web apps, you've seen the difference between storing user data in a session cookie and storing it in a database. Cookies vanish when the browser closes.
PAIN: Cookie-only state means your user re-logs-in every refresh, re-fills every form, re-explains every preference. The "session" feels broken every time it ends.
MAPPING: Without a persistence layer, your agent's memory is a session cookie. The dict, the vector index, the procedure templates — all evaporate on restart. Persistence adds the database underneath each tier so memory turns from ephemeral state into durable record.
One backing store per tier
- Working memory → Redis or SQLiteRAM-fast for read/write, periodically flushed to a backing store keyed by session_id. Survives brief disconnects.
- Episodic memory → vector DB on diskChromaDB
PersistentClient(path="./chroma_db")or Pinecone/Weaviate. Embedding + summary text persist together. Survives full restarts. - Procedural memory → SQL + vector DBSteps JSON in SQLite/Postgres for transactional safety. Trigger embedding in the same vector DB so semantic match is fast.
- BackupsSnapshot the DB directory daily. For managed vector DBs, enable provider-side backup. Test restore at least once.
- Growth planCompaction job + TTL on episodic. Cap procedural at top-N most-used templates. Without a plan, vector DB grows unbounded.
Local vs managed storage
| Need | Pick |
|---|---|
| Hobby project, single machine | SQLite + local ChromaDB |
| Single server, <100K episodes | Postgres + ChromaDB on disk |
| Multi-region, millions of episodes | Pinecone / Weaviate Cloud |
| Distributed agents, shared session state | Redis cluster for working memory |
| Strict data residency / HIPAA | Self-hosted, your VPC |
Start simple. Graduate to managed services only when traffic, scale, or compliance forces the move.
Common misconceptions
PersistentClient(path=…) or a managed cloud client. This single missed line is the #1 reason "the agent keeps forgetting."Each tier needs its own durable backing store. Use the persistent client variant. Plan for backups, compaction, and growth from day one — otherwise persistence buys you new failure modes instead of protecting against old ones.
The managed memory tool
Claude exposes a built-in memory tool in the API: a managed key/value-and-namespace store that persists across requests for a given user, project, or org. Instead of standing up Postgres, building a summarization pipeline, and writing your own retrieval prompts, you call memory.write and memory.read tools and Anthropic handles storage, retrieval, and quota.
Enable it by adding the tool to your tools array. Claude calls memory.write to store a fact under a namespaced key (e.g. user_prefs/timezone), memory.read to retrieve, and memory.list to enumerate. Storage and retrieval costs are metered separately from input/output tokens.
Best for prototypes, B2C agents, and simple personalization. Not for regulated data or complex retrieval policies.
Self-storage vs your own warehouse
BEFORE: You're moving to a new city and need to store your stuff. Two options: rent a self-storage unit at a managed facility, or buy a warehouse and run it yourself.
PAIN: The warehouse gives you total control — security, layout, climate — but takes weeks to set up and you own all maintenance. Self-storage is ready in an hour, but the operator picks the rules and you can't customize the building.
MAPPING: The memory tool is self-storage — ready in minutes, Anthropic handles everything, but you live within their retrieval policy and data residency. Building your own 3-tier system is the warehouse — total control, your VPC, your retrieval logic, but days of work and ongoing operational load.
Three tools, one managed store
- Enable the toolAdd the memory tool to your
toolsarray. Pass scoping headers (per-user / per-tenant) to isolate stores. - Write a factClaude decides when to call
memory.write(key, value). Keys are namespaced (e.g.user_prefs/timezone). - Read a factClaude calls
memory.read(key)when it thinks it needs context. Read decisions are model-driven, not developer-driven. - List for discoveryUse
memory.list(prefix)to enumerate all keys under a namespace — useful when Claude is exploring what it knows about a user. - Pay per operationStorage and reads/writes are metered separately from token costs. Cheap at low scale, gets meaningful at high read volume.
Memory tool vs DIY 3-tier
| Concern | Memory tool | DIY 3-tier |
|---|---|---|
| Setup time | Minutes | Days |
| Where data lives | Anthropic-managed | Your DB, your VPC |
| Retrieval policy | Claude decides when to read | You decide — eager / lazy / hybrid |
| Compliance | Anthropic's region/ZDR options | Whatever your infra supports |
| Cost model | Per-read/write metered | Your storage + compute |
| Best for | Prototypes, B2C, simple personalization | Regulated data, complex retrieval, multi-tenant SaaS |
Common production pattern: memory tool for short-lived per-user prefs; DIY for case-grade context and audit-relevant decisions.
Common misconceptions
Memory tool for ease, DIY for control. The managed tool gets you live in minutes for B2C personalization. The 3-tier system is the right answer the moment you need data residency, custom retrieval policy, or high-volume per-user facts.
Auto Memory: Claude writes its own notes
Auto Memory is a Claude Code feature where Claude itself decides what's worth remembering across sessions and writes it to disk — without you typing a single line of CLAUDE.md. Build commands it figured out, debugging quirks, your discovered preferences — all silently captured and reloaded next session.
It's on by default in Claude Code v2.1.59+. Most learners discover it the first time they see "Writing memory…" in the terminal and wonder what just happened.
CLAUDE.md is what you tell Claude. Auto Memory is what Claude tells itself. Complementary systems — not a replacement.
The pair-programmer who keeps a notebook
BEFORE: Imagine pairing with a colleague who silently keeps a running notebook of everything they've learned about your codebase — the build commands, the debug quirks, your stylistic preferences — and reads that notebook before every session.
PAIN: Without that notebook, every Monday they re-discover that the local DB is on port 5433, the test runner needs Redis, and you prefer underscores over camelCase. You spend 15 minutes re-explaining the same things every single session.
MAPPING: Auto Memory is exactly that notebook. Claude takes notes about your project as it works, stores them per repo, and re-reads them at the start of every future session in the same working tree.
The memory directory
- Per-repo directoryEach git repo gets its own folder at
~/.claude/projects/<project>/memory/. - MEMORY.md is the indexAlways loaded at session start. Capped at first 200 lines or 25KB — acts as a router pointing to topic files.
- Topic files load on demandFiles like
debugging.md,build-quirks.md,preferences.mdare read by Claude only when relevant. - Two natural triggers"Remember that…" writes to Auto Memory (machine-local). "Add to CLAUDE.md" writes to your committed config (team-shared).
- Machine-local by designNot synced across machines, cloud, or teammates. Captures local quirks (your Redis port, your sandbox URL) safely.
CLAUDE.md vs Auto Memory
| CLAUDE.md | Auto Memory | |
|---|---|---|
| Who writes | You | Claude |
| Contains | Rules, conventions | Discovered patterns & quirks |
| Scope | Project / user / org | Per working tree (git repo) |
| Loaded | Every session, in full | First 200 lines / 25KB of MEMORY.md |
| Sync | Committed via git | Machine-local only |
| Best for | Coding standards, "always do X" | Build quirks, local prefs Claude discovered |
Toggle from /memory in-session. Disable per-run with CLAUDE_CODE_DISABLE_AUTO_MEMORY=1.
Common misconceptions
You write CLAUDE.md. Claude writes Auto Memory. Both auto-load every session. CLAUDE.md captures rules everyone needs. Auto Memory captures quirks Claude would otherwise re-discover every Monday morning.
The full Claude memory landscape
Claude provides at least twelve distinct memory primitives across five scopes — from the conversation-level message array to file-based persistent memory in Claude Code. Knowing all twelve, and which scope each operates at, is what separates "I learned the API" from "I can architect a Claude system."
Two memory patterns from the outermost scope — crash recovery manifests and tool-output trimming — don't fit cleanly into the working/episodic/procedural taxonomy but show up repeatedly as Domain 5 cert questions. Both solve real production failures.
Most production agents use 3–4 of the five scopes simultaneously, because each scope solves a different memory problem.
The Russian-doll architecture
BEFORE: Picture a set of nested Russian dolls. Each shell wraps the previous one. Each adds a layer of capability.
PAIN: If you only know the innermost doll exists, you're stuck rebuilding everything the outer dolls already give you. Engineers who know only the messages array end up writing summarization for the third time, not realizing the runtime already does it.
MAPPING: Claude's memory is nested the same way. Innermost: a single API call (system prompt + messages + tool_use cycle). Each outer scope wraps it with more persistence. Pick the smallest scope that solves the problem — reaching outside it is wasted complexity.
Five scopes, twelve primitives
| Scope | Primitives |
|---|---|
| 1. Within a single API call Stateless — whatever you send |
system prompt · messages array · tool_use cycle |
| 2. Across calls (server-assisted) Re-send context cheaply |
prompt caching · memory tool · Files API · Citations |
| 3. Within a session (Claude Code / SDK) Runtime keeps state for you |
--resume · fork_session · /compact · PreCompact hook · subagent isolation |
| 4. Persistent across sessions (Claude Code) Files on disk, auto-loaded |
CLAUDE.md hierarchy · Auto Memory · Skills · Slash commands · Hooks |
| 5. External persistence (you build) Scale, compliance, custom retrieval |
RAG + vector DB · 3-tier memory · crash recovery manifests · tool-trim wrapper |
Reading the landscape: volatility → scope 1; reuse rate → scope 2; persistence → scopes 4–5.
Crash recovery & tool-trim
Crash recovery manifest: a small structured file (e.g. .claude/state/manifest.json) the agent writes after every meaningful step. Captures modified files, test status, plan, current step, open TODOs. Lives outside the context window so it survives crashes, compaction, and reboots. On restart, the resumed session reads it and picks up exactly where it left off — instead of replanning the whole 40-minute migration.
Tool-output trimming: a pre-context filter applied to tool results before they hit the message history. The wrapper writes the full result to a side log on disk for debugging, but returns only a trimmed slice to the model (top-N grep matches, head+tail of long stdout, failures-only test output). Solves the silent killer: an 8,000-line grep result floods context and quality drops — not because Claude got worse, but because too much input.
Cert tip: when context-quality drops, the right fix is often less input, not a bigger model.
Common misconceptions
Five scopes, twelve primitives, three signals. Pick by volatility, reuse rate, and persistence. Pair the 3-tier system with crash recovery manifests and tool-output trimming and you've covered the full Domain 5 cert toolkit.
Memory Manager: the orchestrator
The Memory Manager is the glue that makes the three tiers work as a coherent system. Individual memory classes are building blocks — the Manager is what wires them together with a clear lifecycle: start_session() creates fresh working memory and loads relevant episodes and procedures, build_memory_context() formats all three tiers into the system prompt for each turn, and end_session() summarizes the conversation, stores the episode, and clears working memory.
It's also where routing decisions live: which tier to consult first, how to handle conflicts (e.g. an episodic preference contradicting a procedural template), how to decay stale entries, and when to promote items between tiers.
This is the boundary between "I have three memory classes" and "I have a memory system."
The orchestra conductor
BEFORE: Picture an orchestra with three skilled sections — strings, brass, percussion — each rehearsed but playing on their own.
PAIN: Without a conductor, sections drift out of sync, enter at the wrong moments, drown each other out. The audience hears noise instead of music. Each section is technically excellent but the whole thing collapses.
MAPPING: The Memory Manager is the conductor. Working, episodic, and procedural each know their job, but the Manager decides the timing, mixes the volume, resolves conflicts, and routes each turn through the right combination. The output is a coherent system instead of three loud soloists.
Five-step lifecycle
- start_sessionCreate fresh working memory tagged with session_id and user_id. Reset the conversation log.
- build_memory_context (per turn)Combine
working.to_prompt()+episodic.to_prompt(user_msg)(top 3) +procedural.to_prompt(user_msg)into one labelled string for the system prompt. - log_turnAppend every user/assistant exchange to the conversation log for later summarization.
- resolve conflictsIf a procedural template contradicts a recent episodic preference (e.g. user said "no email" but procedure ends with "send email"), prefer the more specific recent fact and flag the procedure for review.
- end_sessionCall
summarize_conversation(log), embed the result, store as an episode in episodic memory, then clear working memory. Optionally promote the conversation's plan to procedural if it succeeded and matches no existing template.
Pseudocode — MemoryManager
CLASS MemoryManager: episodic = EpisodicMemory(persist_dir) procedural = ProceduralMemory(persist_dir) working = null log = [] METHOD start_session(session_id, user_id): working = WorkingMemory(session_id) working.set("user_id", user_id) log = [] METHOD build_memory_context(user_msg): RETURN JOIN([ working.to_prompt(), episodic.to_prompt(user_msg, k=3), procedural.to_prompt(user_msg) ], "\n\n") METHOD log_turn(role, content): log.append({role, content}) METHOD end_session(): record = summarize_conversation(log) vec = EMBED(record.summary) episodic.STORE(vec, record, meta={user_id, ts}) working.clear() RETURN record.id
The whole 3-tier system is roughly 30 lines of orchestration on top of three storage classes.
Common misconceptions
The Manager is the system. Three storage classes give you parts; the lifecycle (start_session → build_memory_context per turn → end_session) gives you a working agent that remembers across turns and across days.
Tap to reveal answers
PersistentClient(path="./chroma_db") so embeddings survive process restarts.Open the full module on desktop
The desktop version walks through all three tiers in code — persistent ChromaDB, summarization pipelines, procedural template matching, the Memory Manager that orchestrates them — in Python and Node.js, plus animated diagrams of the timeline, the cert-pattern internals, and the full Claude-native memory landscape.
Open M11 on Desktop → Full code · 3-tier build · cert patterns · ~80 minModule 11 of 30 · Track 3: Memory & Context