NewRelease 0.12.3 is out: Engine v0.12.3, Dashboard 0.5.2, Helm 0.12.3, the four SDKs, CLI 0.8.2 and MCP 0.12.3Release 0.12.3 is outSee what's new →

How memory works

This page explains the model behind the Dakera API: agents and namespaces, memory types, importance, sessions, decay, the knowledge graph, consolidation and the steps a recall goes through. Read it before you design how your agents store and retrieve memories; the REST API reference has the exact routes and fields.

Memory lifecycle

State diagram of a memory: Stored (importance 0.0 to 1.0), then Active while agents recall it, Decaying when it is not recalled (half-life 168 hours by default), and Dormant at the importance floor (0.01 by default). A recall returns a decaying or dormant memory to Active and restarts the decay clock. Consolidation (AutoPilot, deduplication or manual) merges an active memory into another, and an explicit forget or TTL expiry deletes a memory at any stage; decay alone never deletes.State diagram of a memory: Stored (importance 0.0 to 1.0), then Active while agents recall it, Decaying when it is not recalled (half-life 168 hours by default), and Dormant at the importance floor (0.01 by default). A recall returns a decaying or dormant memory to Active and restarts the decay clock. Consolidation (AutoPilot, deduplication or manual) merges an active memory into another, and an explicit forget or TTL expiry deletes a memory at any stage; decay alone never deletes.
Decay lowers importance but never deletes; only a forget or a TTL does.Full size ↗Full size ↗

Core primitives

Agents

An agent is the owner of a set of memories, identified by an agent_id string you choose. Every memory belongs to one agent, and its memories live in the namespace _dakera_agent_{agent_id}. Agents are isolated: a recall for agent-A never returns memories of agent-B.

Four memory modules side by side: a film reel (episodic), an open book (semantic), gears (procedural) and a workbench lamp (working)
Episodic · Semantic · Procedural · Working. Four memory types, one store.

Use as many agents as you need: one per user, per pipeline stage or per persona. There is no registration step: the agent's namespace is created by its first stored memory. From v0.12.2 you can also create an agent ahead of time with POST /v1/agents ({"agent_id": "…"}), for example so a dashboard or a scoped key can work with it before it stores anything. Agent ids are at most 241 bytes.

Across agents — To map the entities and related memories several agents share, use POST /v1/knowledge/network/cross-agent. See the Patterns Library.

Memory types

Every memory has a memory_type (default episodic). The type sets how fast decay works on it, relative to the configured half-life:

TypeUse forDecay speed
episodicSpecific events, user actions, conversation history1× (the default type)
semanticFacts, knowledge, learned patterns0.5×
proceduralInstructions, skills, how-to knowledge0.3×
workingShort-term scratchpad, current task context3×

A memory that has never been accessed decays faster, and each access slows it a little. A namespace's memory policy can also give each type its own decay curve and default TTL.

Importance scoring

Every memory has an importance score from 0.0 to 1.0. Importance is one term of the recall score, a filter for batch recall and batch forget (min_importance, below_importance), and the value decay lowers over time. A recall records access_count and last_accessed_at but, since v0.12, does not raise importance: only feedback (upvote), explicit importance updates and session promotion do. (v0.11 boosted the importance of every memory a recall returned, so one query's results crowded out more relevant memories in later ones.)

ScoreWhen to useExamples
0.9 – 1.0Critical facts that must never be lostUser identity, API credentials context, safety rules, core preferences
0.7 – 0.8Important context worth preserving long-termProject goals, learned skills, key decisions, recurring patterns
0.5Standard observations (the default)Routine conversation facts, task completions, general notes
0.3 – 0.4Ephemeral or low-value stateCurrent task scratchpad, temporary context, status updates
0.1 – 0.2Noise; below 0.3 a memory is a candidate for AutoPilot consolidationGreeting messages, filler content, duplicates pending consolidation

Memory decay

Memories decay over time when not accessed. Rarely-recalled information fades; frequently-retrieved knowledge stays sharp. Configure the half-life via DAKERA_DECAY_HALF_LIFE_HOURS (default 168, i.e. 7 days). Decay never deletes a memory: importance stops at a floor (DAKERA_DECAY_MIN_IMPORTANCE, default 0.01). To delete a memory at a deadline, set ttl_seconds or expires_at (a Unix timestamp in seconds).

Sessions

A session groups related memories under one ID, so you can scope a recall to a conversation or list everything stored during one interaction.

Session-scoped recall. Pass session_id in a recall to restrict results to memories stored in that session: what was discussed in the current conversation rather than the agent's whole history.

A session carries optional metadata (task type, user context) and an optional summary when it ends. When a session with more than three memories ends, Dakera also stores a semantic summary memory of it. List a session's memories with GET /v1/sessions/{id}/memories or dk session memories {id}.

Idle sessions end by themselves (v0.12.2). A session with no activity for DAKERA_SESSION_IDLE_TIMEOUT_SECS (default 4 hours) is ended by the server exactly as a client end would, summary memory included, and reads ended_reason: "idle". Storing, updating or importing a memory with the session, a session-scoped recall or search, and POST /v1/sessions/{id}/touch all count as activity. A session can set its own idle_timeout_secs when it starts (0 = never ended for inactivity). Storing into an ended session still succeeds, and the response says session_state: "ended".

Namespaces

Namespaces are the isolation unit for vector data, API keys and quotas. The memory API picks the namespace from the agent_id; you address namespaces directly only with the vector routes, namespace-scoped API keys and quotas.

Quotas. A namespace can carry a quota (max_vectors, max_storage_bytes, max_dimensions, max_metadata_bytes) set through /admin/quotas. With the default hard enforcement, a write over a limit is refused with 413 QUOTA_EXCEEDED before anything is written (soft logs a warning, none only tracks). v0.11 stored quotas but never checked them; v0.12 enforces them on every write path.

Attachments and records (opt-in). With DAKERA_ATTACHMENTS on, an agent's memories can reference content-addressed files (attachment_ref) that are counted by quotas, backed up and replicated; an audio attachment can be transcribed into a memory, and with DAKERA_VISION an image page can be indexed for visual recall. With DAKERA_RECORDS on, a record is one indexed vector plus named extra representations (dense, token multivector, patch multivector). See Multimodal memory and Records.

Language. Full-text analysis, date parsing and the embedding model can be set for a language: DAKERA_MODEL=bge-m3, DAKERA_FULLTEXT_LANGUAGE, DAKERA_QUERY_LANG and a per-request lang. See Multilingual search.

Entity extraction

Entity extraction is opt-in per namespace: turn it on with "extract_entities": true (and optionally entity_types) in PUT or PATCH /v1/namespaces/{ns}/config. Each memory stored into such a namespace is tagged entity:<type>:<value>; those tags feed the knowledge graph and entity-filtered retrieval.

ProviderHow it worksConfiguration
Rule-based pre-passRegular expressions for dates, URLs, UUIDs, emails and IP addressesAlways runs when extraction is on
GLiNERZero-shot named-entity recognition with a local ONNX model, no external callsThe default model provider when entity_types are set. The model (about 782 MB) downloads on first use; a store into an extracting namespace waits up to 10 s, then answers 503 with Retry-After and the download progress
OpenAI, OpenRouter, Anthropic, OllamaLLM-based extractionPer namespace with PATCH /v1/namespaces/{ns}/extractor, or server-wide with DAKERA_EXTRACTOR_PROVIDER

To test extraction without storing anything, send text to POST /v1/memories/extract. A stored memory's entities are at GET /v1/memory/entities/{id}.

Knowledge graph

Each agent's memories form a persistent graph. When a memory is stored, Dakera links it to similar memories of the same agent and, where entity tags exist, to memories that share an entity. There are five edge types:

Edge typeCreated whenUse case
related_toTwo memories have a cosine similarity of at least 0.85Semantic clustering and association
shares_entityTwo memories carry the same entity:* tagEntity-centric retrieval ("everything about Alice")
precedesA related memory was created before anotherTimelines and event chains
linked_byYou link two memories with POST /v1/memories/{id}/linksCustom associations
supersedesA newer memory is very similar (cosine of at least 0.92) to an older one and shares an entity with itRecall demotes the outdated fact

Traverse the graph from a memory with GET /v1/memories/{id}/graph, find the path between two memories with GET /v1/memories/{id}/path, and export an agent's graph with GET /v1/agents/{id}/graph/export or the whole graph as JSON or GraphML with GET /v1/knowledge/export. POST /v1/knowledge/network/cross-agent maps shared entities and related memories across several agents.

Consolidation

Agents accumulate redundant or overlapping memories. Dakera has three ways to merge them:

MethodHow it worksWhen to use
ManualPass the memory_ids to merge to POST /v1/memory/consolidateWhen you know which memories overlap
DeduplicationPOST /v1/knowledge/deduplicate compares memories pairwise and merges only true duplicates: the same content words in the same order and the same entity tags. The absorbed text is kept in _dakera_duplicates. Refuses agents with more than 5,000 memories to compare (narrow with memory_type); dry_run: true only reportsAfter bulk imports or on high-volume agents
AutoPilotBackground deduplication and consolidation on a schedule; see AutoPilotAlways-on maintenance; on by default

Decay engine

A background pass (every DAKERA_DECAY_INTERVAL_SECS, default 3600 s) lowers the importance of memories that are not being used, so stale information ranks lower while memories in use keep their weight.

A brass curve descending over black steps with memory chips fading from bright gold to dark along it
Importance decays on a curve you choose. A recall resets the decay timer.
StrategyBehavior
exponential (default)Importance halves every half-life.
linearImportance drops by a fixed amount per half-life (0.5 per half-life), down to zero.
step_functionImportance halves at each whole half-life, in steps.
power_lawImportance falls as 1 / (1 + t/half-life): fast at first, then slower.
logarithmicImportance falls slowly at first, then faster, reaching zero at one half-life.
flatNo decay.

The server-wide strategy is DAKERA_DECAY_STRATEGY (exponential, linear or step) or PUT /admin/decay/config, with the half-life in DAKERA_DECAY_HALF_LIFE_HOURS. A namespace's memory policy (PUT /v1/namespaces/{ns}/memory_policy) can set any of the six per memory type (working_decay, episodic_decay, semantic_decay, procedural_decay), and a default TTL per type.

Access restarts decay. A recall records access_count and last_accessed_at, and decay counts from the last access. Since v0.12 a recall does not raise importance, and a decay pass follows the configured curve from where the importance started instead of compounding.

Spaced repetition. With a memory policy, each recall of a memory that has a TTL extends its expiry by access_count × spaced_repetition_factor × spaced_repetition_base_interval_seconds (defaults 1.0 and one day).

AutoPilot

AutoPilot is the background task that keeps an agent's memory space clean. It is on by default (DAKERA_AUTOPILOT_ENABLED=false turns it off) and runs two jobs:

  1. Deduplication, every 6 hours (DAKERA_AUTOPILOT_DEDUP_INTERVAL_HOURS): near-duplicates above a cosine similarity of 0.93 (DAKERA_AUTOPILOT_DEDUP_THRESHOLD) are merged, keeping the memory with the higher retention score.
  2. Consolidation, every 12 hours: clusters of three or more memories that share at least two tags and all have an importance below 0.3 are merged into one semantic memory.

Decay is a separate background pass (see Decay engine). GET /admin/autopilot/status shows the last runs; POST /admin/autopilot/trigger starts a cycle now.

Memory feedback loop

Feedback is how your application tells Dakera which memories helped. After a recall, send a signal for a memory with POST /v1/memories/{id}/feedback:

GET /v1/memories/{id}/feedback returns a memory's feedback history, GET /v1/agents/{id}/feedback/summary aggregates an agent's signals, and GET /v1/feedback/health reports system-wide signal quality.

The retrieval pipeline

A POST /v1/memory/recall goes through these steps:

A gold beam and a white beam entering a prism and merging into one beam that lands on a ranked stack of tiles
BM25 and vector search fused into one ranking, then re-ranked by a cross-encoder.
StepWhat happensDetails
1. ClassifyThe query is classifiedA rule-based classifier (an embedding-based one with DAKERA_ML_CLASSIFIER=true) labels the query keyword, semantic, hybrid, temporal or multi-hop, which picks BM25, vector or a weighted blend. A request's routing (auto, vector, bm25, hybrid) overrides it.
2. EmbedThe query is embedded locallyThe ONNX embedding model (default bge-large; bge-m3 for multilingual, colbert-small for late interaction, or a smaller model such as minilm or e5-small) runs in the server, with no external API calls.
3. Vector searchNearest-neighbor retrievalAn exact scan up to DAKERA_ANN_THRESHOLD (default 1000) vectors, HNSW above it, returning a candidate pool larger than top_k.
4. BM25 searchFull-text keyword matchA per-namespace BM25 index catches exact terms (IDs, error codes, names) that vectors miss.
5. Entity vector searchA second vector pass filtered by entityOn by default (DAKERA_ENTITY_VECTOR_SEARCH): person names found in the query select memories tagged with those entities.
6. FusionVector and BM25 results are mergedMin-max score normalization by default; "fusion": "rrf" uses Reciprocal Rank Fusion with k = DAKERA_RRF_K (default 60).
7. ScoringRelevance, importance and recency are combinedThe score is w_vec·relevance + w_imp·importance + w_rec·recency. In v0.12 the access-count term is gone and recency counts from when a memory was created, so rankings follow the query more closely and smart_score values change scale. See Upgrading to v0.12.
8. RerankA cross-encoder rescores the top candidatesThe ONNX cross-encoder bge-reranker-v2-m3 rescores them for precision ("rerank": false skips it); rerank_candidates caps how many, and the response's rerank_report says what it did. The top top_k are returned.

Per request you can set the routing, turn reranking off, choose the fusion, set vector_weight, and filter by session_id, tags, memory_type, min_importance or a since / until window.

Deep dives: Temporal memory & decay strategies · Knowledge graphs for AI agents · How agent memory works · Quickstart →

Put the concepts to work

88.2% Recall@20 on LoCoMo across 1,536 evaluated questions, from hybrid vector and BM25 search, knowledge-graph traversal and local ONNX models in a single Rust binary.

Quickstart → Production Patterns →
Stay sharp on agent memory
Benchmark results, SDK releases, and production patterns. Under 500 words per issue.
✓ You're in. First issue lands soon — watch for Dakera in your inbox.