Agent Memory

Agents that rely on context windows for memory forget everything when the window closes. Agents that write to a plain vector store have no governance: no audit trail, no access control, no way to know who stored what or why, and no way to correct a wrong belief without losing its history.

Relata is the governed memory layer: cognitive verbs that store, retrieve, and manage beliefs as bi-temporal, provenance-tracked rows — the same storage model as everything else in the database. Every memory item carries who wrote it, when it was believed to be true, what purpose was declared, and a confidence score. Old beliefs are not deleted — they are superseded, so the full belief history is always queryable.

What lives where. This page is the concept — why governed memory, what the model is, and how recall ranks results. For every verb, argument, response shape, and curl/JSON example, see the Agent memory reference; for the tool schemas, the MCP tools reference.

The verb model (conceptual)

The surface is a small set of cognitive verbs rather than free-form CRUD. Conceptually they fall into three groups:

  • Store & correctremember a belief, consolidate (supersede) a belief that changed, forget under a retention policy (never a hard delete — obeys legal holds).
  • Retrieve & inspectrecall by hybrid BM25 + vector search (optionally AS OF a past moment), recognize whether an identity is known, justify the full provenance chain behind a belief.
  • Relate & summariseassociate two items, resolve conflicting beliefs by policy, summarise a session/topic, and episodes_in to walk a session's narrative.

Five canonical types back them: MemoryItem (one belief), AgentSession, ToolCall, DecisionRecord, and Episode — all ordinary bi-temporal rows. For the full verb/argument/type reference, see Agent memory reference.

Why governed memory (vs. convenience-memory)

Most agent-memory products (Mem0, RushDB, Zep) optimize for convenience: push JSON, get semantic recall, schema-free. Relata optimizes for accountability — the memory must answer "what did the agent know, when, and why — and who was allowed to see it?"

ConcernRelata answer
Bi-temporal recallEvery memory carries valid_from/to (when it was true) and system_from/to (when Relata learned it). Query "what did the agent believe at T?" exactly.
Tamper-evident auditHash-chained commit manifests — any deletion or mutation leaves a forensic trail.
Provenance chainEvery MemoryItem links back to the ToolCall and AgentSession that produced it; justify replays exactly how a decision was reached.
Governed accessCedar-inspired ABAC + PURPOSE restrict which agents can read which memories; multi-tenant isolation is enforced at the query-planner level.
Finite context windowrecall injects only a bounded, ranked slice per turn (LIMIT N BUDGET T) — unbounded memory, bounded prompts.
Hallucination / memory poisoningPROV-O per row + hash chain; no source = not a memory. Every result is justify-able.

How recall ranking works

recall runs three steps:

  1. Hybrid retrieval — BM25 over text fields, vector similarity over the stored embedding, fused via reciprocal-rank fusion (RRF). Results that score in both signals rank higher than results that score in only one.
  2. Temporal filter — if as_of is supplied, only beliefs that were valid at that point in time are considered. This lets an agent reconstruct what it "knew" at a given past moment.
  3. Re-scoring — the fused relevance is blended additively with recency and the forgetting curve (0.70·relevance + 0.15·recency + 0.15·forget), then multiplicatively gated by confidence × class_weight. The additive blend prevents a single near-zero lifecycle factor from collapsing the whole score — a high-confidence belief from yesterday outranks a low-confidence belief from a minute ago, but an old but highly-relevant memory is no longer dropped just because its recency is near zero.

Unlimited memory, bounded prompts

Relata gives an agent unbounded memory with bounded prompts by separating what the agent knows (unlimited) from what it sees per turn (bounded):

  • Capacity is unbounded — durable storage is object-store (S3 / self-hosted S3-compatible), not RAM-bound like a vector DB. You run out of bucket, not memory.
  • The agent never loads it all — each turn, recall returns only the small, relevant, ranked slice, capped by LIMIT N BUDGET T. A 10-year, billion-row memory and a 1 MB memory cost the same prompt budget.
  • Cold vs hot — cold history lives on object storage; active-case data is promoted into RAM/SSD via the tiered cache.

Fast on constrained hardware (no GPU)

  • No LLM on the hot path. Ingest canonicalizes + validates declared identities (deterministic, cheap). Since v1.1 embeddings are caller-supplied — see LLM & Embedding configuration — so the ingest hot path is pure throughput.
  • Early-pruned retrieval. IdentityIndex bloom filters + graph pushdown + a BM25 shortlist mean the vector path scans a tiny candidate set.
  • Single binary, embedded free profile — one process alongside the agent's own loop; no separate vector server, Redis, and graph DB to operate.

Agent-framework adapters

Drop-in governed memory backends for every major Python agent framework. Each wraps the memory verbs, so purpose tracking and ACL stay on. None imports its framework at module load, so they are safe to install without the framework present.

FrameworkImport pathInterface shape
LangChainrelata_adapters.langchain.RelataMemoryBaseMemory
LlamaIndexrelata_adapters.llamaindex.RelataMemoryBaseMemory
CrewAIrelata_adapters.crewai.RelataStorageStorage
AutoGen v0.2relata_adapters.autogen.RelataMemoryMemory (async)
AG2 (v0.4+)relata_adapters.ag2.RelataAG2MemoryMemoryProtocol
Pydantic-AIrelata_adapters.pydantic_ai.RelataMemoryBackendmemory backend
smolagentsrelata_adapters.smolagents.RelataTooltool callable
LangGraphrelata_langgraph.RelataCheckpointercheckpointer
Auto-detectrelata_adapters.registry.get_memory_adapter()picks installed framework
from relata_adapters.langchain import RelataMemory
 
mem = RelataMemory(
    base_url="http://localhost:9090",
    bearer_token=token,
    purpose="customer-support-agent",
)
# chain = ConversationChain(llm=llm, memory=mem)

Querying memory as SQL

Because memory items are ordinary rows, you can query them with SQL — including bi-temporal operators:

-- All beliefs about "Alice" stored in the last 30 days, newest first
SELECT content, confidence, valid_from
FROM MemoryItem
WITH PROVENANCE
WHERE MATCH(content, 'Alice')
  AND system_from >= now() - INTERVAL '30 days'
ORDER BY confidence DESC, system_from DESC
LIMIT 20
 
-- What did the agent believe about Alice on 1 June?
SELECT content, confidence
FROM MemoryItem
AS OF '2026-06-01T00:00:00Z'
WHERE MATCH(content, 'Alice')
ORDER BY confidence DESC

See also