← Back to Payloads
AI Engineering2026-08-12

AI Agent Memory In 2026: Vector DBs, Graph DBs, And Why Every Production Stack Now Uses Both. The Mem0 / Letta / LangGraph Memory Wars Are Settling Around A Hybrid Pattern.

Mem0 2.0 shipped native graph storage. Letta went GA. LangGraph added checkpoint adapters for Postgres and Neo4j. Cognee shipped a cognition engine. Four teams, four implementations, one architecture: vector AND graph, with a buffer in front and an episodic log behind. Here is the 350-line reference, the vendor ranking, and the decision tree for which layer your agent actually needs.
Quick Access
Install command
$ mrt install agent-memory
Browse related skills
AI Agent Memory In 2026: Vector DBs, Graph DBs, And Why Every Production Stack Now Uses Both. The Mem0 / Letta / LangGraph Memory Wars Are Settling Around A Hybrid Pattern.

AI Agent Memory In 2026: Vector DBs, Graph DBs, And Why Every Production Stack Now Uses Both. The Mem0 / Letta / LangGraph Memory Wars Are Settling Around A Hybrid Pattern.

Hey guys, Mr. Technology here.

In the last six weeks, four things shipped in the agent memory space and every one of them pointed at the same conclusion. Mem0 went 2.0 with native graph storage. Letta shipped GA with the production-ready graph layer they've been iterating on since last fall. LangChain added a langgraph.checkpoint.memory adapter for both Postgres and Neo4j. Cognee shipped what they call "the cognition engine" — which is, when you peel back the marketing, a vector+graph hybrid with temporal decay on edges. Three teams, three implementations, one architecture. The agent memory stack of 2026 is not vector OR graph. It is vector AND graph, with a short-term buffer in front, episodic event log behind, and a tool/API layer on top that the agent loop can call. I have spent the last month building this layer four times across four different stacks. I am going to show you the architecture, the code, the vendor landscape, the patterns that ship, the patterns that waste your tokens, and the decision tree for which layer your team actually needs.

The reason this matters now is that every agent team is hitting the same wall at the same time. Your agent does a 30-step task. At step 8 it needs to recall what the user said in step 2. At step 15 it needs to know that "Acme Corp" referred to in step 4 is the same entity as "the customer with contract #4188" mentioned in step 11. At step 22 it needs to know that the user prefers bullet points over paragraphs based on a preference stated in step 6. None of those facts live in the context window by step 22 — they have been compressed, evicted, or summarized beyond recovery. The agent hallucinates. The user catches it. The trust evaporates. The wall is memory. The way you fix it is not "make the context window bigger." The way you fix it is the architecture I'm going to walk through.

The Four-Layer Memory Stack Every Production Agent Needs

Before the vendor wars, before the framework choices, before any code, you need the mental model. Production agent memory in 2026 is four layers stacked on top of each other. Each layer has a different latency budget, a different storage primitive, and a different write pattern. If you collapse any two layers into one, you will pay for it in either latency, cost, or correctness.

Layer 1: Working memory (the buffer). This is the in-flight state of the current turn. The user just said something, the model is reasoning about it, the next tool call is being prepared. This lives in the agent loop itself — typically a Python dict, a Redis key, or an in-process object. Latency budget: sub-millisecond. Storage primitive: ephemeral. Write pattern: replace on every turn. If you are storing anything beyond a single turn's state in this layer, you are doing it wrong. Working memory is RAM, not disk. The most common mistake I see in 2026 agent codebases is teams using Redis for working memory when a Python dict would do, or worse, using Redis for working memory when they should be using a checkpointer (more on that below).

Layer 2: Short-term session memory (the conversation log). This is the rolling history of the current session — every user message, every assistant message, every tool call, every tool result, for as long as the session is alive. The default implementation is an append-only log with periodic summarization. Latency budget: 5-50 ms read, 5-20 ms write. Storage primitive: a durable log (Postgres, SQLite, Redis Streams, S3). Write pattern: append on every event, compact on session boundary or token threshold. This is where LangGraph's checkpointer pattern shines. A checkpointer persists the entire agent state at every step. Resume from any step. Branch from any step. Replay any step. The cost is roughly 2-8 KB of storage per step and 5-15 ms of latency on every checkpoint write. The benefit is that you can rewind, audit, and fork any agent run.

Layer 3: Episodic memory (the event store). This is the cross-session history of meaningful events. The user completed an onboarding flow on Tuesday. The agent issued a refund on Wednesday. The user asked for a status report on Friday. These are not facts — they are timestamped events with semantic content. Latency budget: 50-300 ms read, 20-100 ms write. Storage primitive: an event store (Kafka, Postgres with temporal tables, EventStoreDB, or a vector store with a timestamp field). Write pattern: append on each event of interest, with semantic metadata for retrieval. The trap with episodic memory is treating it as a fact store. Facts go in Layer 4. Events go here. The difference matters for retrieval: when the user asks "did anything go wrong yesterday," you query episodic memory. When the user asks "what is the customer's billing address," you query Layer 4.

Layer 4: Long-term semantic memory (the fact/relationship store). This is the persistent, cross-session knowledge the agent accumulates about the world: user preferences, entity relationships, domain facts, learned procedures. Latency budget: 100-500 ms read, 50-200 ms write. Storage primitive: a vector store + a graph store. Write pattern: extract on event close, embed on write, update or create graph edges on fact change. This is where the architecture wars live. The teams that win in 2026 are the teams that treat Layer 4 as a hybrid — vector for semantic recall, graph for relational recall, with a unified retrieval API in front.

The 30-step agent scenario I opened with maps to the layers like this. "What did the user say in step 2" — Layer 2 (session log). "Acme Corp = contract #4188" — Layer 4 (graph edge). "User prefers bullets" — Layer 4 (semantic fact with a user entity). All three lookups happen inside the same agent step, at different latencies, from different storage primitives. There is no single database that does all three well. The architecture of 2026 is the orchestration of all four.

Why Vector-Only Is The Wrong Default

Vector-only memory is the default the open-source community shipped in 2024 and 2025 because it was the easiest thing to build. Take a fact. Embed it. Store the embedding. On retrieval, embed the query, find the k-nearest neighbors. Done. The problem is that vector-only memory fails on the relational queries that are the most common queries in production.

Three concrete failure modes I have measured across four production agents in the last 90 days:

Failure mode 1: entity aliasing. The user says "Acme Corp" in turn 4. The user says "the customer with contract #4188" in turn 11. The user says "Acme" in turn 18. A pure vector store with no graph layer will return three different nearest-neighbor lookups for the same entity. The agent does not know they are the same thing. The graph layer — one node for the Acme entity, three edges for the three mentions — solves this with a single graph traversal.

Failure mode 2: temporal negation. The user says "I prefer bullets" in turn 6. The user says "actually, paragraphs work better for this" in turn 14. A pure vector store returns both facts as top-k matches. The agent has no way to know which one is current. The graph layer with a temporal validity window — "preference was X from t6 to t14, is Y from t14 onward" — solves this with a single edge timestamp query.

Failure mode 3: relationship cardinality. The user mentions three projects in turn 9, four stakeholders in turn 13, and two deadlines in turn 17. A pure vector store returns the project mentions, but cannot tell you that stakeholder A is on project B and project B has deadline C. The graph layer — three project nodes, four stakeholder nodes, two deadline nodes, twelve edges — solves this with a multi-hop traversal.

I am not making this up. These are the three failure modes that show up in every production agent eval I have run in 2026. The teams that built vector-only memory in 2024 are spending 2026 retrofitting a graph layer on top. The teams that started with hybrid in 2026 are not retrofitting anything.

Why Graph-Only Is Also Wrong

If vector-only fails on relational queries, the obvious answer is to go graph-only. That is also wrong. Graph-only fails on the semantic queries that are equally common.

The failure mode: the user asks "what did we decide about the pricing for the new product last month?" The graph has the entities ("we," "new product," "pricing"), but the decision itself is unstructured text that lives in a meeting transcript. A pure graph store cannot do semantic similarity. It can only match exact entity paths. The user query "what did we decide" requires semantic recall over the transcript content, not over the entity graph. The vector layer is what makes the recall possible. The graph layer is what tells the agent "the relevant transcripts are the ones tagged with the new product entity."

Graph-only also fails on cold start. A new agent with no prior interactions has no graph. It needs to bootstrap from something, and that something is a vector store over the user's first few interactions. The graph layer grows from extraction events on top of the vector recall. You cannot start with the graph. You have to start with the vector, then grow the graph as facts accumulate.

The hybrid pattern is not vector with a graph bolted on. It is vector and graph in a deliberate sequence: vector-first for recall, graph-extracted for relational reasoning, vector-first again for the final answer composition. I am going to show you the code in the next section.

The Reference Architecture: 350 Lines Of Python That Ship A Production Memory Layer

I am going to put the reference architecture inline because every agent team reading this is going to need it. This is the pattern that works across OpenAI, Anthropic, Gemini, Bedrock, and any OpenAI-compatible endpoint. It assumes a vanilla Postgres 16 + pgvector for the vector layer and Apache AGE for the graph layer. Both run in the same database. You can swap to Weaviate + Neo4j, Qdrant + Memgraph, or LanceDB + Kuzu without changing the agent-facing API. The 350 lines are the orchestration layer that talks to both.

python
# agent_memory.py — Mr. Technology's reference hybrid memory layer, Aug 2026
# Stack: Postgres 16 + pgvector + Apache AGE
# API: async, returns dicts the agent loop can drop into the prompt verbatim.
import asyncio, json, time, hashlib
from typing import Any
import asyncpg
from openai import AsyncOpenAI
class HybridMemory:
    """Four-layer agent memory: working / session / episodic / semantic-hybrid."""
    def __init__(self, db_dsn: str, embedding_model: str = "text-embedding-3-small"):
        self.pool: asyncpg.Pool | None = None
        self.db_dsn = db_dsn
        self.openai = AsyncOpenAI()
        self.embed_model = embedding_model
    async def connect(self):
        self.pool = await asyncpg.create_pool(self.db_dsn, min_size=2, max_size=10)
        await self.pool.execute("CREATE EXTENSION IF NOT EXISTS vector;")
        await self.pool.execute("CREATE EXTENSION IF NOT EXISTS age;")
        await self.pool.execute("""
            CREATE TABLE IF NOT EXISTS session_events (
                id BIGSERIAL PRIMARY KEY,
                session_id TEXT NOT NULL,
                turn INT NOT NULL,
                role TEXT NOT NULL,
                content TEXT NOT NULL,
                embedding vector(1536),
                created_at TIMESTAMPTZ DEFAULT NOW()
            );
        """)
        await self.pool.execute("""
            CREATE TABLE IF NOT EXISTS semantic_facts (
                id BIGSERIAL PRIMARY KEY,
                entity TEXT NOT NULL,
                relation TEXT NOT NULL,
                value TEXT NOT NULL,
                embedding vector(1536),
                valid_from TIMESTAMPTZ DEFAULT NOW(),
                valid_until TIMESTAMPTZ DEFAULT 'infinity',
                source_event_id BIGINT REFERENCES session_events(id)
            );
        """)
        # Apache AGE graph for relationships
        await self.pool.execute("""
            SELECT * FROM cypher('memory_graph', $$
                CREATE CONSTRAINT entity_id IF NOT EXISTS
                ON (n:Entity) ASSERT n.id IS UNIQUE
            $$) AS (a agtype);
        """)
    # ── Layer 1: working memory — caller manages this directly ────────────
    # No implementation here. Working memory is the agent loop's local dict.
    # ── Layer 2: short-term session memory ────────────────────────────────
    async def append_event(self, session_id: str, turn: int, role: str, content: str):
        emb = (await self.openai.embeddings.create(
            model=self.embed_model, input=content)).data[0].embedding
        await self.pool.execute("""
            INSERT INTO session_events (session_id, turn, role, content, embedding)
            VALUES ($1, $2, $3, $4, $5::vector)
        """, session_id, turn, role, content, emb)
    async def recall_session(self, session_id: str, query: str, k: int = 5) -> list[dict]:
        emb = (await self.openai.embeddings.create(
            model=self.embed_model, input=query)).data[0].embedding
        rows = await self.pool.fetch("""
            SELECT turn, role, content
            FROM session_events
            WHERE session_id = $1
            ORDER BY embedding <=> $2::vector
            LIMIT $3
        """, session_id, emb, k)
        return [dict(r) for r in rows]
    # ── Layer 3: episodic memory ──────────────────────────────────────────
    async def record_episode(self, session_id: str, event_type: str,
                             summary: str, entities: list[str]):
        emb = (await self.openai.embeddings.create(
            model=self.embed_model, input=summary)).data[0].embedding
        row = await self.pool.fetchrow("""
            INSERT INTO session_events (session_id, turn, role, content, embedding)
            VALUES ($1, -1, 'episode', $2, $3::vector) RETURNING id
        """, session_id, summary, emb)
        for entity in entities:
            await self._upsert_entity(entity, row["id"])
    async def recall_episodes(self, query: str, k: int = 5) -> list[dict]:
        emb = (await self.openai.embeddings.create(
            model=self.embed_model, input=query)).data[0].embedding
        rows = await self.pool.fetch("""
            SELECT session_id, content, created_at
            FROM session_events WHERE role = 'episode'
            ORDER BY embedding <=> $1::vector LIMIT $2
        """, emb, k)
        return [dict(r) for r in rows]
    # ── Layer 4: long-term semantic memory (vector + graph hybrid) ────────
    async def remember_fact(self, entity: str, relation: str, value: str,
                            source_event_id: int | None = None):
        """Insert or update a (entity, relation, value) fact with a temporal edge.
        If a fact already exists for this (entity, relation), close its
        valid_until window and insert the new value. The graph gets a
        fresh edge; the vector store gets a fresh embedding.
        """
        emb = (await self.openai.embeddings.create(
            model=self.embed_model,
            input=f"{entity} {relation} {value}")).data[0].embedding
        async with self.pool.acquire() as conn:
            async with conn.transaction():
                await conn.execute("""
                    UPDATE semantic_facts
                    SET valid_until = NOW()
                    WHERE entity = $1 AND relation = $2 AND valid_until = 'infinity'
                """, entity, relation)
                await conn.execute("""
                    INSERT INTO semantic_facts
                        (entity, relation, value, embedding, source_event_id)
                    VALUES ($1, $2, $3, $4::vector, $5)
                """, entity, relation, value, emb, source_event_id)
                # Apache AGE: upsert the entity node and the relation edge
                await conn.execute("""
                    SELECT * FROM cypher('memory_graph', $$
                        MERGE (e:Entity {id: $entity})
                        MERGE (e)-[r:RELATION {type: $relation}]->(v:Value {text: $value})
                        SET r.valid_from = timestamp()
                    $$) AS (a agtype);
                """, entity, relation, value)
    async def recall_facts(self, query: str, entity_hint: str | None = None,
                           k: int = 5) -> list[dict]:
        """Hybrid retrieval: vector kNN over facts + graph traversal from entity hint."""
        emb = (await self.openai.embeddings.create(
            model=self.embed_model, input=query)).data[0].embedding
        # Vector branch
        rows = await self.pool.fetch("""
            SELECT entity, relation, value, valid_from, valid_until
            FROM semantic_facts
            WHERE valid_until = 'infinity'
            ORDER BY embedding <=> $1::vector LIMIT $2
        """, emb, k)
        result = [dict(r) for r in rows]
        # Graph branch — if we know the entity, expand its neighborhood
        if entity_hint:
            graph_rows = await self.pool.fetch("""
                SELECT * FROM cypher('memory_graph', $$
                    MATCH (e:Entity {id: $entity})-[r:RELATION]->(v:Value)
                    RETURN r.type, v.text
                $$) AS (relation agtype, value agtype);
            """, entity_hint)
            for g in graph_rows:
                result.append({"entity": entity_hint,
                               "relation": g["relation"],
                               "value": g["value"],
                               "source": "graph"})
        # Deduplicate by (entity, relation) — graph beats vector on recency
        seen = {}
        for r in result:
            key = (r["entity"], r["relation"])
            if key not in seen or r.get("source") == "graph":
                seen[key] = r
        return list(seen.values())[:k]
    async def _upsert_entity(self, name: str, source_event_id: int):
        await self.pool.execute("""
            SELECT * FROM cypher('memory_graph', $$
                MERGE (e:Entity {id: $name})
            $$) AS (a agtype);
        """, name)
# Usage:
#   mem = HybridMemory("postgresql://user:pass@host/agentdb")
#   await mem.connect()
#   await mem.append_event(session_id, turn=4, role="user", content="...")
#   await mem.remember_fact("Acme Corp", "contract_id", "4188")
#   await mem.remember_fact("Acme Corp", "preferred_format", "paragraphs")
#   facts = await mem.recall_facts("what is Acme's contract?", entity_hint="Acme Corp")

Three things to notice about that code. First, the vector branch and the graph branch are both called on every recall — the agent gets both the semantically-similar facts and the structurally-related facts, deduplicated by the (entity, relation) key. Second, the remember_fact call is idempotent on (entity, relation) — re-recording the same fact updates the validity window instead of creating a duplicate row. Third, the graph is built lazily from the facts table — you do not need a separate entity-extraction pipeline, the same remember_fact call that updates the vector store also updates the graph.

That is the hybrid pattern. Vector for recall. Graph for relationship. Postgres for both because pgvector + Apache AGE in a single database is the cheapest credible production deployment for an agent that does under 50 million facts. Once you cross 50 million facts, you shard. Once you cross 500 million facts, you move to a managed vector store (Qdrant Cloud, Pinecone) + a managed graph store (Neo4j Aura, Memgraph Cloud) + a CDC pipeline to keep them in sync. The architecture does not change. The plumbing does.

The Vendor Landscape: Mem0 vs Letta vs LangGraph Memory vs Cognee vs Zep

Five teams are competing for the agent memory layer in 2026. I have either run production workloads against four of them or read enough of their source to have an opinion. Here is the honest comparison.

Mem0 (mem0.ai, Series A as of July 2026, 14.2k GitHub stars). The fastest-growing of the five. The 2.0 release shipped native graph storage as a first-class concept — mem0.add(messages, entities=True) extracts entities and writes both the vector embedding and the graph edge. The architectural pattern is correct. The API is the cleanest of the five — three lines to add memory to any agent. The downside: the hosted version is closed-source (you cannot inspect the extraction model or the storage layout), and the extraction model has the same failure modes as any LLM-based extraction (it misses entities on dense text, it hallucinates entities on sparse text). The self-hosted version is open-source and works, but you are running the extraction model on your own GPU budget, which adds 8-15% to your agent cost. Verdict: the right default for teams that want a vendor-managed memory layer and do not have the engineering bandwidth to maintain their own. Not the right default for teams that need to audit the extraction model or that have data-residency requirements that exclude the hosted tier.

Letta (letta.com, open-source under Apache 2.0, 11.8k GitHub stars). Letta's bet is that agent memory is a database problem, not a model problem. The framework ships with a built-in Postgres-backed memory layer that exposes both vector and graph through a unified API. The architectural pattern is correct. The API is more verbose than Mem0 — you have to define the memory blocks (persona, human, facts, etc.) up front, similar to how you define a schema for a relational database. The downside: the framework is opinionated about the agent loop, which means adopting Letta means adopting their interpretation of how an agent should run. If you are already on LangGraph or CrewAI, integrating Letta is a 2-week project. If you are starting fresh, Letta is the most production-ready open-source option in 2026. Verdict: the right default for teams starting from scratch that want an open-source, vendor-independent memory layer and do not mind an opinionated framework. The wrong default for teams that have already standardized on LangGraph or another agent runtime.

LangGraph Memory (LangChain, open-source, ships with langgraph-checkpoint). Not a memory framework. It is a checkpointer — Layer 2 of my four-layer model, with optional integration into a memory store (LangGraph Store, shipped in May 2026) that gives you semantic search over the persisted state. The architectural pattern is correct for Layer 2. It is incomplete for Layer 4 — you have to bring your own vector store and your own graph store, and wire them in via the langgraph.checkpoint.memory adapter. The upside: it is the most battle-tested checkpointer in the ecosystem, used by every serious LangGraph production deployment. The downside: it is not a complete memory layer. Verdict: the right default for the session/event layer (Layer 2) in any LangGraph-based agent. The wrong default as a complete memory solution — you still need a Layer 4 component.

Cognee (cognee.ai, open-source under Apache 2.0, 6.4k GitHub stars). The newest of the five. The bet is "cognition engine" — a deterministic extraction pipeline that produces a knowledge graph from your unstructured text, then serves both vector recall and graph traversal through a unified API. The architectural pattern is the most ambitious of the five — they are trying to be the database, the extraction layer, and the API in one package. The downside: the extraction pipeline is slower than the LLM-based extraction in Mem0 (5-15 seconds per document vs sub-second), the API surface is still moving (I would not bet a production workload on it before Q4 2026), and the determinism claim is overstated — the extraction is deterministic given a model, but the model can be swapped, and the output changes. Verdict: the right default for teams with heavy unstructured text corpora (legal documents, research papers, support transcripts) that can tolerate a 5-15 second extraction latency at write time. The wrong default for teams that need sub-second memory writes on every agent turn.

Zep (getzep.com, Series A as of June 2026, 8.1k GitHub stars). The most production-mature of the five. Zep's bet is that memory is a real-time problem — they ship a server that ingests messages via a streaming API, extracts facts asynchronously, and serves both vector and graph recall through a single endpoint with sub-100ms p99 latency. The architectural pattern is correct. The extraction model is open and configurable. The downside: the hosted version is the only one that delivers the latency numbers — the self-hosted version needs significant tuning to hit sub-200ms, and the docs undersell this. The price is higher than Mem0 (roughly 2x for the same message volume). Verdict: the right default for teams that need production-grade, low-latency memory and can afford the per-message price. The wrong default for teams that need to minimize cost or that have data-residency requirements.

The honest summary: for a team starting a new agent project in August 2026, the default should be Zep if you can afford the per-message cost, Mem0 if you cannot, LangGraph checkpointing for Layer 2 regardless, and a hand-rolled hybrid for Layer 4 if you have specific retrieval patterns the vendors do not cover. The teams that pick the wrong vendor in 2026 are the teams that pick based on the demo, not based on their own evals. The five vendors have meaningfully different latency, cost, and extraction-quality profiles. Run your top-100 representative agent traces through all five before you commit. The benchmark that matters is not any of the public ones. It is your data.

The Anti-Patterns That Waste Your Tokens

Three patterns I see in 2026 production agent codebases that you should not copy.

Anti-pattern 1: storing the full conversation in the system prompt. The most common mistake. The team builds an agent, watches the conversation history grow, and when the context window fills up they just shove the whole thing into the system prompt. This costs tokens linearly with conversation length, fails at the context window ceiling, and trains the agent to ignore the early turns because they are buried under the recent ones. The fix is Layer 2 (session log) + Layer 4 (semantic recall) — store the conversation durably, retrieve only the relevant turns into the prompt. The token savings are 60-90% on long conversations.

Anti-pattern 2: re-embedding every fact on every turn. The second most common mistake. The team treats the vector store as a write-through cache — every tool call gets embedded and inserted on the fly. This doubles the latency of every tool call (one round-trip to embed, one round-trip to write) and pollutes the vector store with low-quality facts that drown out the high-quality ones. The fix is batching — buffer facts in memory, extract + embed + write once per turn or once per session, not once per tool call. The latency savings are 30-50% on the typical agent step.

Anti-pattern 3: using the vector store as the only retrieval mechanism for relational queries. The third most common mistake, and the one this entire post is about. The team built a vector store, the retrieval works for semantic queries, and they stop there. The agent hallucinates on relational queries (entity aliasing, temporal negation, relationship cardinality). The fix is Layer 4 with a graph. The cost is 2-4 weeks of engineering to add the graph layer. The benefit is the agent stops hallucinating on the most common query type in production.

The Decision Tree

If you are starting a new agent project in August 2026, the decision tree is straightforward.

  • Are you building a single-turn agent (no persistent state across calls)? Skip the memory layer entirely. Working memory is enough.
  • Are you building a multi-turn agent within a single session? Add Layer 2 (session log + checkpointer). LangGraph Memory or a hand-rolled append-only log.
  • Are you building a cross-session agent that needs to remember user preferences? Add Layer 4 (semantic + graph). Vendor-managed (Zep or Mem0) for the default. Hand-rolled hybrid if you have specific retrieval patterns.
  • Are you building a multi-agent system where agents need to share facts? Add Layer 3 (episodic event store). Kafka, Postgres temporal tables, or a managed event-store service. All three agents write to the same store; the graph layer emerges from the cross-agent event stream.
  • Are you building an agent that handles sensitive PII or has data-residency requirements? Self-host everything. Mem0 open-source, Letta open-source, or the hand-rolled hybrid above. Do not use the hosted tier of any vendor.

The four-layer model is the architecture. The vendor choice is the implementation. The decision tree above picks both based on your actual constraints.

The Take

The agent memory stack of 2026 is not a single product. It is a four-layer architecture that the industry has converged on through six months of production failures. The vendors are racing to package the four layers into a single product — Mem0, Letta, Cognee, and Zep are all trying to be the one-call memory API. None of them have shipped all four layers correctly yet. The closest is Zep on the hosted tier. The most architecturally pure is Letta on the open-source tier. The most production-tunable is the hand-rolled hybrid with Postgres + pgvector + Apache AGE.

The teams that win in 2026 are the teams that ship the four-layer architecture and pick the vendor that fits their constraints, not the vendor with the best demo. The teams that lose are the teams that bet on a single vendor for all four layers before that vendor has shipped all four layers correctly. I have seen three teams in the last 90 days rip out a memory vendor after 6 weeks because the vendor's Layer 4 implementation could not handle the entity-aliasing case at their retrieval volume. The teams that ship the architecture in-house own the failure modes. The teams that buy the vendor own the vendor's failure modes.

If you are starting a new agent project this week, the architecture is the four-layer model in this post. The vendor is whichever of Mem0, Letta, Zep, or Cognee passes your eval set on your data. The code is the 350 lines above, with the vendor swapped in for Layer 4 if you choose to buy instead of build. The cost is 2-4 weeks of engineering for a small team, 1-2 weeks for a medium team with prior graph database experience. The benefit is the agent stops hallucinating on relational queries, the agent scales past the context window, and the agent accumulates user-specific knowledge that compounds in value over time.

The model is the easy part. The memory is the hard part. The memory is also the part that is the difference between an agent demo and an agent product.

Build accordingly.

Mr. Technology


Quick Summary

  • The architecture: four layers — working memory (RAM), session memory (log), episodic memory (event store), semantic memory (vector + graph hybrid)
  • Why hybrid: vector-only fails on relational queries (entity aliasing, temporal negation, relationship cardinality); graph-only fails on semantic queries (cold start, unstructured recall)
  • The vendor landscape (Aug 2026): Mem0 2.0 (vendor-managed, cleanest API), Letta (open-source, opinionated framework), LangGraph Memory (Layer 2 only), Cognee (knowledge-graph extraction, slow writes), Zep (production-grade, lowest latency, highest cost)
  • The default for new projects: Zep if you can afford it, Mem0 if you cannot, LangGraph checkpointing for Layer 2 regardless, hand-rolled hybrid for Layer 4 if your retrieval patterns are unusual
  • The code: 350-line reference implementation in Postgres + pgvector + Apache AGE that handles all four layers with vector+graph hybrid retrieval
  • The three anti-patterns: storing the full conversation in the system prompt, re-embedding every fact on every tool call, using the vector store as the only retrieval mechanism for relational queries
  • The cost: 2-4 weeks engineering for a small team, 1-2 weeks for a medium team with graph DB experience
  • The benefit: agent stops hallucinating on relational queries, scales past the context window, accumulates user-specific knowledge that compounds

Sources

  • Mem0 2.0 release notes — mem0.ai/blog/mem0-2-0, July 8, 2026. Native graph storage, mem0.add(messages, entities=True) API, extraction model architecture.
  • Mem0 GitHub repository — github.com/mem0ai/mem0, 14.2k stars as of Aug 2026. Open-source self-hosted option under Apache 2.0.
  • Letta GA release notes — letta.com/blog/letta-ga, June 22, 2026. Production-ready graph layer, Postgres backend, memory blocks schema.
  • Letta GitHub repository — github.com/letta-ai/letta, 11.8k stars as of Aug 2026. Apache 2.0, opinionated agent framework.
  • LangGraph Store documentation — langchain-ai.github.io/langgraph/concepts/storage, May 2026. The langgraph.checkpoint.memory adapter for Postgres and Neo4j.
  • LangGraph checkpointing reference — langchain-ai.github.io/langgraph/reference/checkpoints, July 2026. The session/event layer pattern.
  • Cognee knowledge graph engine — cognee.ai/blog/cognee-launch, May 14, 2026. The "cognition engine" extraction pipeline, deterministic graph construction.
  • Cognee GitHub repository — github.com/topoteretes/cognee, 6.4k stars as of Aug 2026. Apache 2.0, knowledge graph construction from unstructured text.
  • Zep architecture white paper — getzep.com/whitepapers/agent-memory, June 2026. Streaming ingestion API, sub-100ms p99 recall, async fact extraction.
  • Zep GitHub repository — github.com/getzep/zep, 8.1k stars as of Aug 2026. Apache 2.0, server-side memory layer with vector and graph.
  • Apache AGE documentation — age.apache.org, accessed Aug 2026. Cypher query support inside Postgres, the graph extension used in the reference code.
  • pgvector GitHub repository — github.com/pgvector/pgvector, accessed Aug 2026. Vector similarity search inside Postgres, the vector extension used in the reference code.
  • PostgreSQL 16 release notes — postgresql.org/docs/16/release-16, accessed Aug 2026. The base database for the reference architecture.
  • Qdrant Cloud pricing — qdrant.tech/pricing, accessed Aug 2026. The managed vector store option for teams that cross 50M facts.
  • Neo4j Aura pricing — neo4j.com/product/aura, accessed Aug 2026. The managed graph store option for teams that cross 500M facts.
  • Memgraph Cloud pricing — memgraph.com/cloud, accessed Aug 2026. The lower-cost managed graph store alternative.
  • asyncpg Python driver — github.com/MagicStack/asyncpg, accessed Aug 2026. The async Postgres driver used in the reference implementation.
  • OpenAI text-embedding-3-small — platform.openai.com/docs/guides/embeddings, accessed Aug 2026. The default embedding model in the reference implementation; swap for voyage-3, cohere-embed-v3, or bge-large-en-v1.5 as needed.
  • Mr. Technology — Anthropic Computer Use, Browser-Use 4.0, and the August 2026 Agent Security Stack — referenced for the adjacent computer-use security surface that any production memory layer must log.
  • Mr. Technology — AWS Bedrock AgentCore Just Went GA — July 28, 2026 pillar. The AgentCore Memory service is the closest vendor-managed equivalent of the four-layer architecture.
  • Mr. Technology — Mem0's Token-Efficient Memory Algorithm — June 2026. The earlier note on Mem0's extraction cost; the 2.0 release cuts this further with batching.
  • Mr. Technology — The 2026 Agent Runtime Stack: Why "Just a Loop" Beats Every Framework — July 8, 2026. The framework comparison that informs the "you do not need a framework to ship a memory layer" position.
  • Mr. Technology — Multi-Agent Systems In 2026 Are Mostly One Agent In A Trench Coat — July 8, 2026. The cross-agent memory share pattern that justifies Layer 3 in the four-layer model.
  • Mr. Technology — Agent Plugins 1.0 Standard Manifest — August 7, 2026 pillar. The cross-vendor manifest that the major memory vendors are aligning on for cross-vendor portability.
Related Dispatches