Read three agent-memory tutorials back to back and you will see the same word, "memory," pointed at three different things: the context window, a Postgres table of past chats, and a vector database of user facts. None of the authors are wrong. They are naming different points on two axes that almost nobody draws, and once you draw them the whole vendor menu — Mem0, Zep, Letta, LangMem, the platform-native tools — stops looking like a pile of competing products and starts looking like a set of slots you fill.

Here is the map, front-loaded, because it is the whole article: there are two axes. One is how long the memory lasts. The other is what kind of thing it is. Every product you have heard of is a choice on both.

Axis one: duration#

Sorted by how long it survives, there are three tiers.

Working (short-term) memory is the live context window plus whatever scratchpad the agent writes during a single run — the message buffer, the tool calls, the active state it is holding in its head. It is ephemeral; when the session ends it is gone. In LangGraph this is the checkpointer, and it is scoped to a single thread.

Session (persistent) memory is that same conversation state, but made durable so the thread can be paused and resumed. It is still one conversation — keyed by a thread_id — but it survives a crash or a week-long gap. This is what lets a user come back to a half-finished task; it is also, mechanically, a checkpoint snapshot you can reload by thread id.

Long-term memory is the one people mean when they say an agent "remembers you." It persists across threads, sessions, and users: the facts, preferences, and past experiences that should outlive any single conversation. In LangGraph this is the Store, and the tell that it is a different thing from the checkpointer is that it is keyed by a namespace — usually a user id — not a thread. LangChain's own line is the cleanest summary of the split: checkpointers save state between turns within a thread; stores save data across conversations.

In code, you wire both at once, and the shape of the two calls is the whole distinction:

from langgraph.store.memory import InMemoryStore
from langgraph.checkpoint.memory import InMemorySaver
from langchain.embeddings import init_embeddings

# long-term: semantic search over a namespaced store
store = InMemoryStore(index={
    "dims": 1536,
    "embed": init_embeddings("openai:text-embedding-3-small"),
    "fields": ["text"],
})
store.put(("users", "u-123", "memories"), "pref_1",
          {"text": "prefers email follow-ups, not calls"})
hits = store.search(("users", "u-123", "memories"),
                    query="how should I contact them?")

# compile the graph with BOTH memories attached
graph = builder.compile(checkpointer=InMemorySaver(), store=store)

Swap InMemorySaver/InMemoryStore for PostgresSaver/PostgresStore and nothing above changes except durability. The thread_id you pass at invoke time drives the checkpointer; the namespace tuple drives the Store. That is the entire short-term/long-term boundary, expressed as two different keys.

Axis two: type#

Duration tells you how long a memory lasts. It says nothing about what the memory is — and that is the second axis, borrowed straight from cognitive science and now the field-standard framing (LangMem's taxonomy, echoed in Anthropic's context-engineering guidance).

The reason the second axis matters is that it changes where a memory should live, not just how long it lasts. Semantic facts belong in a searchable store. Procedural rules belong in the prompt. Episodic examples belong in a retrieval index you pull the top-k from. Sorting a "memory" onto the type axis tells you which mechanism it wants — and it is why a single vector database is not, by itself, "agent memory." It is one backend for one type.

The build decision that actually bites: hot-path vs background#

Once you know a memory's duration and type, one operational question remains: when does it get written? LangMem draws the line as hot-path versus background, and it is the decision that most shapes how your agent feels.

Hot-path formation means the agent saves memory inside its loop, consciously, via a tool call — it decides "this is worth keeping" mid-task. You get immediate persistence, at the cost of extra latency and tokens on that turn.

Background formation means a separate process reads the finished conversation after the fact and extracts what is worth keeping, debounced so a burst of messages doesn't trigger a storm of writes. The main loop stays fast; the tradeoff is that a memory isn't available the instant it's mentioned.

LangMem exposes both directly:

from langmem import create_manage_memory_tool, create_search_memory_tool
from langmem import create_memory_store_manager, ReflectionExecutor

# hot path: the agent writes/reads memory in-loop, as a tool
tools = [
    create_manage_memory_tool(namespace=("memories", "{user_id}")),
    create_search_memory_tool(namespace=("memories", "{user_id}")),
]

# background: subconscious extraction after the turn, debounced
manager  = create_memory_store_manager("openai:gpt-4o",
                                       namespace=("memories",))
executor = ReflectionExecutor(manager)   # runs off the hot path

Most production agents use both: hot-path for the handful of facts the agent knows are load-bearing, background for the slow consolidation of everything else. If you would rather not run either yourself, the platform-native options do the same job from the other side — Anthropic's memory tool (memory_20250818, now GA) gives the agent a file-backed /memories directory it reads and writes on the hot path, and Google's Vertex AI Memory Bank does background extraction as a managed service (GA since December 2025, billed per stored event). The newest wrinkle is consolidation of memory: Anthropic's "Dreaming" research preview runs a scheduled background pass over an agent's past sessions to extract recurring patterns and prune the store — memory that curates itself, the same idea a background executor implements at small scale.

The part the tutorials skip: you may not need any of this#

The map is useful precisely because it keeps you honest about the top tier. A long-term memory layer is not free, and it is not automatically an accuracy win. On the memory benchmarks everyone cites, a memory layer buys you a roughly order-of-magnitude reduction in cost and latency — but a plain full-context baseline, one that just pastes the whole history in, often beats the memory pipeline on raw accuracy, sometimes by tens of points on the multi-hop questions where compression hurts most. That tension is the whole subject of full context vs a memory layer, and it is why you should read a memory benchmark suspiciously before you let one sell you a layer.

So the honest build order is: start with working and session memory, because you need them the moment you have more than one turn. Reach for long-term memory when the history is too big or too long-lived to keep passing whole — not before. And when you do, sort each thing you want to remember onto both axes first. Duration tells you whether it goes in the checkpointer or the Store. Type tells you whether it's a fact to search, an example to retrieve, or a rule to put in the prompt. Get those two answers and the vendor you pick is almost an implementation detail — which is exactly how much or how little machinery the job actually needs to keep the window clear while the memory lives outside it.