Agent Memory: The Bottleneck Killing Multi-Agent Systems
Most multi-agent systems don't fail because the models are bad. They fail because the agents can't remember what just happened — and then act on stale, contradictory, or completely missing context. Memory architecture is the unglamorous decision that determines whether your system compounds knowledge across tasks or resets to zero on every invocation.
This pattern shows up consistently in production LLM systems: the demo works because the context window is fresh, the task is narrow, and the evaluator is patient. The moment you run parallel agents, long-horizon tasks, or anything with real state — it falls apart at the memory seam.
Why "Just Use the Context Window" Is a Production Anti-Pattern
The default instinct is to shove everything into the context window. It's the path of least resistance — no external storage, no retrieval logic, no consistency headaches. For a single-turn chatbot or a short pipeline, it's fine. For a multi-agent system running tasks that span minutes, hours, or days, it's a slow-motion disaster.
Here's what degrades badly as context grows:
- Latency: Every token you send costs inference time. A 128K context hitting a frontier model adds real latency per call, multiplied by every agent in your graph.
- Cost: Token costs are linear. If five agents each get a 50K-token context to share state, you've just multiplied your input token bill by five. This compounds with every hop.
- Attention degradation: Frontier models lose fidelity on facts buried in long contexts — the "lost in the middle" problem is well-documented in research from Stanford and others. A 128K context is not a 128K guarantee.
- No persistence: Every invocation starts fresh unless you explicitly serialize and restore state. Stateless agents cannot learn from a session, adapt to a user's preferences, or avoid repeating the same mistake twice.
The fix is not one memory type — it's knowing which of the four memory layers your system actually needs, and wiring only those.
The Four Memory Layers (and When to Use Each)
Think of agent memory the way you'd think about memory in a computer: there are fast, small, expensive layers and slow, large, cheap ones. Each has a job.
1. In-Context Memory (Working Memory)
What it is: The live context window — everything the model can see right now.
Use it for: The immediate task. Current tool outputs, the last 2-3 turns of conversation, the active plan step.
Don't use it for: Cross-agent shared state, session history, or anything that persists beyond a single invocation.
Rule of thumb: Keep in-context memory under 20K tokens for most production workloads unless you've profiled that longer contexts are justified by task accuracy gains.
2. External Episodic Memory (Session/Thread State)
What it is: A structured store — Redis, a Postgres JSON column, a purpose-built store like Zep or Mem0 — where you write and read session-scoped facts.
Use it for: Remembering what happened earlier in this user session, tracking sub-task results as agents hand off to each other, accumulating partial outputs in a long-horizon workflow.
Implementation pattern: Write a structured summary after each significant agent action, not raw transcripts. Raw transcripts balloon. Structured summaries compress.
python# After each agent step, persist a structured event memory_store.append({ "session_id": session_id, "agent": "research_agent", "step": "web_search", "result_summary": "Found 3 relevant sources on X; top result is Y", "timestamp": utcnow(), "tokens_produced": step_token_count })
Don't use it for: Facts that need to survive beyond the session lifetime, or knowledge that should be shared across all users/sessions (that's semantic memory).
3. Semantic / Retrieval Memory (Long-Term Knowledge)
What it is: A vector store or hybrid search index — Qdrant, Weaviate, pgvector — where you embed and retrieve relevant knowledge chunks.
Use it for: Domain knowledge the agent needs to answer questions, past interaction summaries that inform future sessions, learned preferences.
Don't use it for: Structured state or sequential workflow logs — retrieval is probabilistic, not transactional. You don't want to retrieve "did the payment step complete?" from a vector store.
Advanced note on similarity math: Cosine similarity requires L2-normalized vectors. A raw dot product equals cosine similarity only when both vectors are unit-normalized — this is a correctness issue, not just a performance one. Most embedding APIs (OpenAI, Cohere) return normalized vectors, but verify before you ship retrieval logic. If you're not certain, use the safe form:
np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))rather than a bare dot product.
4. Procedural Memory (Learned Behavior)
What it is: Stored prompt templates, tool routing rules, few-shot examples, and fine-tuned model weights that encode how the agent should behave.
Use it for: Encoding hard-won lessons — which tool tends to fail on what input, which response format users prefer, domain-specific reasoning patterns.
Don't use it for: Fast-changing operational state. Procedural memory is slow to update. It's the agent's instinct, not its inbox.
The Consistency Problem Nobody Talks About
Multi-agent systems introduce a consistency challenge that single-agent systems don't have: two agents can read and write to shared memory concurrently, and without coordination, they corrupt each other's state.
Imagine a travel booking system where a research agent and a pricing agent both update the same session record simultaneously. Agent A reads the session at t=0 (say, 50ms into a 200ms pricing call) and writes its hotel options at t=200ms. Agent B reads at t=10ms, fetches live rates, and writes at t=190ms — overwriting a field Agent A was about to reference. Neither agent errors. The planner downstream just sees a silently inconsistent snapshot: hotel options from one write, rates from another, with no guarantee they correspond to the same inventory window. In a system generating 10–20 tool calls per session at 100–300ms each, this race is not theoretical.
The cheaper fix is to make shared memory append-only and have a reconciliation agent that periodically reduces the log into a consistent state snapshot. This is the event-sourcing pattern applied to agent state, and it's underused:
code[Research Agent] → append(event: "found hotel options", payload: {...}, t=200ms) [Pricing Agent] → append(event: "fetched live rates", payload: {...}, t=190ms) [Planner Agent] → read_snapshot() → reduce(events by timestamp) → plan next step
No locks. No mutations. Append-only writes are safe under concurrency. The planner reads a materialized view built from an ordered event log — not raw concurrent state where the last writer wins.
Memory-Layer Selection: A Decision Table
| What you need to remember | Lifespan | Consistency req | Use |
|---|---|---|---|
| Current tool output / active step | Seconds | N/A | In-context |
| This session's thread of actions | Minutes–hours | Ordered | Episodic (Redis/Postgres) |
| Cross-session user preferences | Days–months | Eventually consistent | Semantic (vector store) |
| How agents should behave | Permanent | Strong | Procedural (prompts / fine-tune) |
| Workflow completion flags | Hours–days | Strong (transactional) | Relational DB — not a vector store |
Note the last row: workflow state — whether a step completed, whether payment was confirmed — must live in a transactional store with ACID guarantees. This is the most common mistake in agentic architectures. Teams reach for the vector store because it's already there. Then they wonder why their workflow occasionally re-runs a completed step.
The Token Budget as a Memory Discipline
Memory design and cost control are the same problem viewed from different angles. Every byte you push into context is a token you pay for. Every retrieval call that returns 20 chunks when you needed 3 is waste. The engineers who get this right treat the context window as a scarce resource and design retrieval to be surgical.
Concrete rules that hold up in production:
- Top-k retrieval ≤ 5 for most RAG-in-agent patterns. If your agent needs more than 5 chunks to answer a question, the chunking strategy is wrong, not the k.
- Summarize, don't replay: When passing session history between agents, pass a 200-token structured summary, not the full transcript.
- TTL everything: Episodic memory should expire. A session summary from 6 months ago is usually noise, not signal.
- Profile before optimizing: Measure actual context sizes in production before assuming you have a bloat problem. Sometimes the context is lean and the latency culprit is tool call latency or model cold starts.
If you haven't modeled your per-session memory overhead as part of your unit economics, you're flying blind. Instrument token counts per agent role from day one — retrofitting observability into a live agentic system is painful.
The Contrarian Take
Most "memory problems" in agentic systems are actually task decomposition problems in disguise. If your agent needs a 200K-token context to do its job, the task is too large for one agent — not the context window too small. Break the task, not the bank.
What to Actually Do
-
Audit your current context sizes in production. If you're not logging token counts per agent invocation, start today. You cannot optimize what you haven't measured.
-
Map your memory needs to the four-layer framework above. For each type of state your system manages, assign it explicitly to a memory layer. If something is in the context window by default, ask whether it should be.
-
Switch workflow completion flags to a transactional store. If you're using a vector store to track whether an agent step is done, migrate that to Postgres or Redis with proper TTLs. Do this before you scale.
-
Implement append-only episodic writes with a reconciliation agent if you have more than two agents writing to shared state. The concurrency bugs from mutable shared memory will find you in production, not in testing.
-
Set a context budget per agent role — a hard ceiling on tokens-in that triggers a summarization pass before the agent is invoked. Treat it like a memory pressure limit, not a suggestion.
Memory is the difference between an agent that compounds value across time and one that's permanently amnesiac. The architecture isn't glamorous. Neither is the foundation of a building — until it fails.
Working on something like this? I take on a few fractional-CTO and AI engagements at a time.
Get my AI playbooks — straight to your inbox
Practical notes on shipping production AI, scaling teams, and the calls a CTO actually has to make. A few times a month. No spam, no fluff.