Every month someone publishes a new explainer on how AI agents manage memory, and most start inside the model: bigger context windows, retrieval tricks, summarization buffers. We run several agentic systems in production, and from that seat memory looks like a storage and versioning problem. The model brings one thing, a context window, and a context window is a cache. It gets evicted on every deploy and every crash. Anything the agent needs to know tomorrow has to live in storage with an explicit lifetime. In our systems those lifetimes come in three sizes: one run, one quarter, one corpus.

Three agent memory layers with different lifetimes, checkpoints, skills, and work memory, under a context window labeled as a cache

Checkpoints: memory that survives a worker death

Our internal multi-agent analytics platform persists LangGraph agent state with AsyncPostgresSaver checkpoints. Each state transition writes the graph state to Postgres, so when a worker dies mid-run, the run resumes from the last checkpoint instead of restarting from zero. Workers die more often than architecture diagrams admit. The demo's week-one incident log shows in public how routinely a small system gets interrupted; on a multi-agent platform every deploy and restart is an interruption, and without checkpointing each one throws away in-flight work.

The lifetime here is a single run. Checkpoints hold position rather than knowledge: which node the graph reached, what intermediate results exist. Nothing in a checkpoint should outlive the run that wrote it. We treat checkpoint reuse across runs as a bug, because replaying stale state into a fresh graph produces failures that look like model errors and debug like infrastructure errors.

Skills: expertise versioned like code

The second layer changes on the scale of quarters. We maintain about 85 SKILL.md files, versioned domain expertise that MCP servers and agents load before any tool call. On our IoT platform the energy and environmental domain frameworks load before the agent runs its first data query, so the model reasons with our methodology instead of whatever its training data happens to suggest for "energy efficiency."

The files matter less than the governance around them. Every skill carries an ADOPT / FORK / BUILD classification, so we know whether we adopted an external practice, forked and modified one, or built our own. Semver is mandatory. Per-skill version pinning surfaces stale dependencies in CI: if a workflow pins a skill at 2.x and the skill moves to 3.0, the build fails before an agent quietly reasons with mismatched assumptions. Skills get a quarterly review, and customer-facing skills live apart from team-internal ones, because a wrong sentence in front of a customer costs more than one in a runbook.

A skill without semver is a rumor. You cannot tell which agents consumed which version, whether a bad answer traces to v2.3 or v2.4, or what breaks when you edit it. Pinning turns "the agent got smarter, or dumber, and we are not sure why" into a diff you can read.

Work memory: what the system learned from doing the work

The third layer is a memory-pipelines system that accumulates as the platform works. Each enrichment stores the raw prompt and response payloads as JSONB, and every enrichment is versioned by a hash of the source it was computed from. When the source changes, the hash stops matching and the enrichment is identifiably stale. An enrichment without a source hash cannot be invalidated; it sits in the store looking exactly as trustworthy as a fresh one.

Eight consumers read the same memory, clustering, routing, evaluation, and a product surface among them. Each gets its own view instead of sharing one god-table. Views are cheap, and a schema shaped for clustering is a bad schema for routing; forcing them together turns every consumer change into a migration that risks the other seven.

One rule has paid for itself repeatedly: compute derived values at read time. Our cost-wedge metric is computed on read and never stored. When the formula changed, history repriced itself for free. Stored derived values freeze yesterday's formula into every historical row.

Underneath sits a two-truth data model. Operational data answers "what is happening now," analytical data answers "what patterns exist," and they live in separate stores with separate write paths. Mixing them is how dashboards end up querying live tables and agents end up reasoning from aggregates.

Sometimes the right amount of memory is none

Not every agent needs this stack. The doc-intel live demo is deliberately stateless per query: no conversation history, no user profile, no accumulated context. Its document index is its only memory, and for question answering over a fixed corpus that is the right amount. Statelessness buys cheap horizontal scaling and reproducible answers, which matter more for that job than continuity. Memory is a cost. Pay it where the lifetime of the information justifies it.

Versioning is what separates memory from contamination

Line the three layers up and the pattern repeats: checkpoints keyed to a run, skills keyed to a semver, enrichments keyed to a source hash. Every layer carries a key that says when its contents stop being true. That key is the difference between memory and contamination, because unversioned memory does not decay gracefully. It decays invisibly, and the agent keeps consuming it with full confidence.

So when an explainer tells you the model will remember, read that as an architecture with one layer and no versioning, which is to say a cache and a prayer. The systems behind this series are described in the operator brief; earlier posts covered a week of unattended operation and the eval pipeline that gates changes to all of this. Memory was the last piece I expected to spend real engineering time on. It has repaid that time better than any model upgrade.