Agent memory surface

RelataDB is the agentic-first ontological database — one governed, bi-temporal knowledge base that speaks every database protocol. This page covers its agent-memory surface: the governed memory layer where what an agent knew, when, and why is defensible, not just convenient.

Most agent-memory products (Mem0, RushDB, Zep) optimize for convenience: push JSON, get semantic recall, schema-free, with embeddings computed for you. Relata optimizes for accountability: every belief is bi-temporal, provenance-stamped, and access-controlled. Since v1.1 embeddings are caller-supplied — either pre-compute _emb_text in the row payload or run the embedder sidecar for async drain — so the ingest hot path is pure throughput (no per-row model call). If you need memory that feels smart, those are great; if the memory must answer "what did the agent know, when, and why — and who was allowed to see it?", that's Relata.

Relata vs. the convenience-memory cohort

Mem0 / RushDB / ZepRelata
Optimized forConvenience, fast onboardingAccountability, defensibility
HistoryCurrent stateBi-temporalAS OF valid + system time
ProvenanceNone / lightPROV-O per row + tamper-evident hash chain
Access controlACID onlyCell-level ACL (Cedar) + org isolation + per-tenant encryption/quotas/namespaces
SchemaSchema-freeSchema-as-code ontology (governed)
ModelOn Neo4j / hostedOwn Rust engine, object-store native, single binary
LicenseAGPL-3.0-only / hostedAGPL-3.0-only (source on request)
Best fitPersonalization, RAG, schema-free appsRegulated, audited, intel/LEA/FININT, court-grade

Why Relata for agent memory

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 mean any deletion or mutation leaves a forensic trail. Compliance and incident-response needs are met out of the box.
Provenance chainEvery MemoryItem links back to the ToolCall and AgentSession that produced it. Replay 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.

Problems we solve

ProblemRelata solution
Finite context window — agents forget anything that doesn't fit the promptExternal governed store + recall injects a bounded, ranked slice per turn (LIMIT N BUDGET T)
Memory bloat degrades recall — as memory grows, noise drowns signalHybrid retrieval (BM25 + vector + graph) with early pruning; consolidate supersedes stale facts instead of accumulating them
No "current truth" — facts change but old beliefs lingerBi-temporal supersession: old belief closed at valid_to=now, new inserted, both AS OF-reconstructable
Hallucination amplification / memory poisoning — retrieved "memories" have no sourcePROV-O provenance per row + tamper-evident hash chain; every result is justify-able; no source = not a memory
No time-travel — can't ask "what did the agent know at T?"Bi-temporal recall … AS OF '<ts>' reconstructs the agent's state at any moment
Multi-agent collision / privacy leakPer-agent/session scoping + cell-level ACL (Cedar) + org isolation + sub-tenant namespaces
No compliance — GDPR delete, audit, access logsforget with retention + legal-hold, hash-chained audit, PURPOSE recording
Retrieval is slow / expensiveLazy detection + deterministic canonical ops on the hot path (no LLM/GPU) + tiered cache (RAM/SSD/object-store)

Unlimited memory

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; retrieval is selective, not exhaustive.
  • Cold vs hot — cold history lives on object storage; active-case data is promoted into RAM/SSD via the tiered cache. Archive-scale capacity with interactive latency.

Fast and efficient on constrained hardware

Relata is RAG-on-memory, engineered to run on a laptop or edge box with no GPU:

  • No LLM on the hot path. Ingest canonicalizes + validates declared identities (deterministic, cheap). Auto-detection/extraction is lazy via materialized views — you pay detection cost only on the slice you query, not on 100% of rows at write time.
  • Early-pruned retrieval. IdentityIndex bloom filters + graph pushdown + a BM25 shortlist mean the vector path scans a tiny candidate set, not the whole corpus.
  • Single binary, embedded free profile — one process, in-memory + optional object-store, running alongside the agent's own loop. No separate vector server, Redis, and graph DB to operate.
  • Token-efficient — hybrid (keyword + vector + graph) returns higher-precision results, so fewer tokens are injected for the same answer quality, and the budget operator sets a hard ceiling.

Honest scope: single-node free/server with object-store + tiered cache is the shipped answer today. Multi-node cluster scale (deep partitioning) is the cluster profile and still maturing.

The five canonical memory types

TypeDescription
MemoryItemThe atomic unit. Holds content, session_id, confidence [0,1], and bi-temporal timestamps. Created by remember, consumed by recall.
DecisionRecordA choice an agent made, with the inputs it considered and the rationale it logged. Created by justify.
AgentSessionA bounded interaction window grouping related MemoryItems and ToolCalls. Created implicitly on first remember for a new session_id.
ToolCallOne invocation of a tool: name, arguments, result, latency. Linked from MemoryItems so the provenance chain is complete.
EpisodeA higher-order grouping of related sessions forming a coherent narrative arc. Retrieved by episodes_in.

The 10 cognitive verbs

VerbHTTP surfaceDescription
rememberPOST /memory/rememberStore a new MemoryItem. Returns id, confidence, valid_from.
recallGET /memory/recall?q=...Semantic + temporal search over MemoryItems. Returns ranked list with provenance.
recognizeGET /memory/recognize/:idFetch one MemoryItem by id with full provenance chain attached.
episodesGET /memory/episodes?session_id=...List Episodes for a session, ordered by valid_from.
justifyGET /memory/justify/:idTrace the decision provenance for a MemoryItem — returns the chain of ToolCalls and DecisionRecords that produced it.
consolidatePOST /memory/consolidateSupersede an existing MemoryItem with updated content. The old item is retained in history; a new one is created with higher confidence.
forgetDELETE /memory/forget/:idSchedule a MemoryItem for retention-policy deletion. Does not hard-delete immediately — the item remains queryable until the retention window expires.
associatePOST /memory/associateLink two memory items / entities with a typed, provenance-stamped association (from_id, to_id, relation).
resolveGET /memory/resolve/:idResolve a memory reference through its supersession chain to the canonical (live) MemoryItem.
summarisePOST /memory/summariseProduce a governed, provenance-stamped summary of a session or topic.

Batch helpers: POST /memory/remember/batch and POST /memory/associate/batch are high-throughput variants that amortise the per-record index flush across a whole batch; per-item errors are returned per element and valid items still commit.

Surface note: The cognitive verbs are MCP tools and REST endpoints — they are not SQL keywords. The LIMIT N BUDGET T retrieval knobs and recall … AS OF '<ts>' time-travel are expressed as verb/REST parameters (?top_k=, ?as_of= on GET /memory/recall). The underlying bi-temporal AS OF is available as real SQL over the base types (SELECT … FROM MemoryItem AS OF '<ts>').

Recall-quality knobs — tuning what comes back

Recall isn't a black box. Five keyword parameters (the "retrieval-quality operators") let you shape recall for your domain — confidence floors, memory decay, hard token budgets, Ebbinghaus forgetting, and early-cancel. All five are accepted by GET /memory/recall, the recall MCP tool, and every SDK's search(...) / search_detailed(...) method.

ParameterUnderlying operatorWhat it doesExample
min_confidenceCONFIDENCE(f)Drop memories below this confidence floor. Use to keep low-quality / speculative beliefs out of the prompt.min_confidence=0.7
recency_half_life_secsRECENCY(λ)Exponential score decay half-life, in seconds. Recent memories rank higher; old ones don't vanish, they just decay.recency_half_life_secs=604800 (1 week)
budget_tokensBUDGET(t)Hard ceiling on the cumulative token cost of returned memories. The server stops emitting once the budget is hit — your prompt literally cannot overflow.budget_tokens=2000
stability_daysFORGETTING_CURVE(d)Ebbinghaus stability parameter, in days. Mirrors human-memory reinforcement: memories that haven't been re-touched decay faster.stability_days=30
cancel_thresholdCANCEL_WHEN(threshold)Short-circuit the scan the moment a hit exceeds this score. Use when you want "the first great match, then stop."cancel_threshold=0.95

Reading the effect back — search_detailed

Two response fields let you observe the knobs' effect, not just set them:

  • recall_cost_tokens — the running token total under BUDGET (how much of the budget was consumed).
  • cancelled — whether CANCEL_WHEN short-circuited the scan (true) or the full ranking ran (false).

These appear on the detailed recall envelope (Memory.search_detailed(...) in all three SDKs; GET /memory/recall?detailed=true).

SDK examples

Python

from relata import Memory
 
mem = Memory("http://localhost:9090", bearer_token="<token>", purpose="agent")
 
# Tight budget, recency-weighted, stop at the first near-certain hit
result = mem.search_detailed(
    "how do we reset the IR sensor?",
    top_k=10,
    min_confidence=0.6,           # CONFIDENCE floor
    recency_half_life_secs=259200,  # 3-day half-life  (RECENCY)
    budget_tokens=1500,            # hard prompt budget (BUDGET)
    cancel_threshold=0.92,         # stop early on a great match (CANCEL_WHEN)
)
print(result["recall_cost_tokens"], result["cancelled"])

TypeScript

const detail = await mem.searchDetailed("how do we reset the IR sensor?", {
  topK: 10, minConfidence: 0.6, recencyHalfLifeSecs: 259200,
  budgetTokens: 1500, cancelThreshold: 0.92,
});
console.log(detail.recall_cost_tokens, detail.cancelled);

Go

res, _ := mem.SearchDetailed(ctx, "how do we reset the IR sensor?",
    relata.WithTopK(10),
    relata.WithMinConfidence(0.6),
    relata.WithRecencyHalfLife(259200),
    relata.WithBudgetTokens(1500),
    relata.WithCancelThreshold(0.92),
)
fmt.Println(res.RecallCostTokens, res.Cancelled)

Tips & takeaways

  • Start with budget_tokens. It's the single biggest win for agent loops — the prompt literally cannot overflow the model's context window. Pick a budget that leaves room for the system prompt + tool output + the response.
  • Pair recency_half_life_secs with stability_days only if it matters. For most RAG use cases recency_half_life_secs alone is enough; stability_days (Ebbinghaus) is for long-lived agents that should "remember" frequently-revisited facts.
  • cancel_threshold trades coverage for latency. Set it when one excellent match is enough (FAQ retrieval, lookups); leave it unset when you want a ranked slate (brainstorming, summarization).
  • min_confidence is a safety net, not a ranking signal. It filters; it doesn't sort. Combine with recency_* for the actual ranking shape you want.
  • Bi-temporal recall composes with all five. Add as_of='<ts>' to reconstruct what the agent believed at T under the same quality constraints — "what would we have recalled on Tuesday under a 1500-token budget?"

Cross-ref: Concepts: Agent Memory · MCP tools · Bi-temporal queries · Limits


Quick-start via MCP

Relata exposes all the cognitive verbs as MCP tools. Point your agent's MCP client at the server and call tools directly:

// MCP initialize
POST /mcp/initialize
{"protocolVersion":"2024-11-05","clientInfo":{"name":"my-agent","version":"1.0"}}
 
// MCP list tools
GET /mcp/tools
// → returns the memory tools: remember, remember_batch, recall, recognize, episodes_in, justify, consolidate, forget, associate, resolve, summarise
 
// MCP call — remember
POST /mcp/tools/call
{
  "name": "remember",
  "arguments": {
    "content": "User prefers concise summaries",
    "session_id": "sess_abc123",
    "confidence": 0.9,
    "purpose": "personalisation"
  }
}
// → {"isError": false, "content": [{"type":"text","text":"{\"id\":\"<uuid>\",\"confidence\":0.9,...}"}]}
 
// MCP call — remember_batch (high-throughput write path)
POST /mcp/tools/call
{
  "name": "remember_batch",
  "arguments": {
    "items": [
      {"content": "User prefers dark mode", "session_id": "sess_abc123", "confidence": 0.9},
      {"content": "User timezone is UTC+5:30", "session_id": "sess_abc123", "confidence": 0.85},
      {"content": "User speaks English and Hindi", "session_id": "sess_abc123", "confidence": 0.95}
    ],
    "purpose": "personalisation"
  }
}

Quick-start via HTTP REST

Step 1 — Remember a fact

POST /memory/remember
Content-Type: application/json
 
{
  "content": "User prefers dark mode",
  "session_id": "sess_abc123",
  "confidence": 0.9,
  "purpose": "personalisation"
}

Response:

{
  "isError": false,
  "content": [{
    "type": "text",
    "text": "{\"id\":\"550e8400-e29b-41d4-a716-446655440000\",\"confidence\":0.9,\"valid_from\":1750000000000000000}"
  }]
}
GET /memory/recall?q=user+preferences&top_k=5&purpose=personalisation

Response:

{
  "isError": false,
  "content": [{
    "type": "text",
    "text": "{\"memories\":[{\"id\":\"550e8400...\",\"content\":\"User prefers dark mode\",\"score\":0.97}],\"total\":1}"
  }]
}

Step 3 — Justify a memory (provenance)

GET /memory/justify/550e8400-e29b-41d4-a716-446655440000?purpose=audit

Returns the full ToolCall → MemoryItem → DecisionRecord provenance chain.

Step 4 — Consolidate (update with higher confidence)

POST /memory/consolidate
Content-Type: application/json
 
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "content": "User prefers dark mode (confirmed across three sessions)",
  "confidence": 0.98,
  "purpose": "personalisation"
}

Returns {"superseded": "<old-id>", "new_id": "<new-id>"}.

Step 5 — Schedule forgetting

DELETE /memory/forget/550e8400-e29b-41d4-a716-446655440000?retain_days=30&purpose=gdpr

Returns {"scheduled": true, "policy": "delete_after_30d"}.

Governance model

Every memory operation passes through three governance layers:

  1. PURPOSE — The purpose field (e.g. "personalisation", "audit", "gdpr") is recorded in the audit log and may be required by ACL policy. When PURPOSE is omitted, the operation is recorded as unpurposed but still allowed in open mode.

  2. ACL — Cedar-inspired ABAC rules control which agent identities can write, read, or delete memories in a given organisation. Cell-level masking applies to sensitive fields.

  3. Provenance chain — Every MemoryItem references the AgentSession and ToolCall that produced it. The hash-chained manifest makes the chain tamper-evident. justify replays the chain on demand.

Multi-agent isolation

When multiple agents share one Relata instance, pass a tenant_id header:

POST /memory/remember
X-Relata-Agency: agent-team-alpha

The planner enforces tenant_id keying so agents never see each other's memories without an explicit cross-organisation grant.

session_id is not a tenant boundary. The session_id (typically an agent's Ed25519 pubkey) groups a conversation — it is not an isolation key. Two organisations reusing the same session_id are kept apart only by their tenant (org). On a multi-tenant profile (server/cluster with more than one tenant, or RELATA_TENANCY_MODE=multi) memory writes with no tenant are rejected 403. Present a per-tenant credential — X-Organization-Id or a tenant-scoped bearer token — for every memory write.

See also