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
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.

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.
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:
| Type | Use for | Decay speed |
|---|---|---|
episodic | Specific events, user actions, conversation history | 1× (the default type) |
semantic | Facts, knowledge, learned patterns | 0.5× |
procedural | Instructions, skills, how-to knowledge | 0.3× |
working | Short-term scratchpad, current task context | 3× |
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.)
| Score | When to use | Examples |
|---|---|---|
0.9 – 1.0 | Critical facts that must never be lost | User identity, API credentials context, safety rules, core preferences |
0.7 – 0.8 | Important context worth preserving long-term | Project goals, learned skills, key decisions, recurring patterns |
0.5 | Standard observations (the default) | Routine conversation facts, task completions, general notes |
0.3 – 0.4 | Ephemeral or low-value state | Current task scratchpad, temporary context, status updates |
0.1 – 0.2 | Noise; below 0.3 a memory is a candidate for AutoPilot consolidation | Greeting 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.
| Provider | How it works | Configuration |
|---|---|---|
| Rule-based pre-pass | Regular expressions for dates, URLs, UUIDs, emails and IP addresses | Always runs when extraction is on |
| GLiNER | Zero-shot named-entity recognition with a local ONNX model, no external calls | The 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, Ollama | LLM-based extraction | Per namespace with PATCH /, or server-wide with DAKERA_ |
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 type | Created when | Use case |
|---|---|---|
related_to | Two memories have a cosine similarity of at least 0.85 | Semantic clustering and association |
shares_entity | Two memories carry the same entity:* tag | Entity-centric retrieval ("everything about Alice") |
precedes | A related memory was created before another | Timelines and event chains |
linked_by | You link two memories with POST / | Custom associations |
supersedes | A newer memory is very similar (cosine of at least 0.92) to an older one and shares an entity with it | Recall 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:
| Method | How it works | When to use |
|---|---|---|
| Manual | Pass the memory_ids to merge to POST / | When you know which memories overlap |
| Deduplication | POST / 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 _. Refuses agents with more than 5,000 memories to compare (narrow with memory_type); dry_run: true only reports | After bulk imports or on high-volume agents |
| AutoPilot | Background deduplication and consolidation on a schedule; see AutoPilot | Always-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.

| Strategy | Behavior |
|---|---|
exponential (default) | Importance halves every half-life. |
linear | Importance drops by a fixed amount per half-life (0.5 per half-life), down to zero. |
step_function | Importance halves at each whole half-life, in steps. |
power_law | Importance falls as 1 / (1 + t/: fast at first, then slower. |
logarithmic | Importance falls slowly at first, then faster, reaching zero at one half-life. |
flat | No 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:
- 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. - 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
semanticmemory.
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:
upvoteraises importance by 0.05 plus 5% of the current importance (at most 1.0) and counts as an access.downvotelowers importance by 0.15 (not below 0).flagmarks the memory as irrelevant (decay_flag) without changing its importance.
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:

| Step | What happens | Details |
|---|---|---|
| 1. Classify | The query is classified | A rule-based classifier (an embedding-based one with DAKERA_) 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. Embed | The query is embedded locally | The 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 search | Nearest-neighbor retrieval | An exact scan up to DAKERA_ (default 1000) vectors, HNSW above it, returning a candidate pool larger than top_k. |
| 4. BM25 search | Full-text keyword match | A per-namespace BM25 index catches exact terms (IDs, error codes, names) that vectors miss. |
| 5. Entity vector search | A second vector pass filtered by entity | On by default (DAKERA_): person names found in the query select memories tagged with those entities. |
| 6. Fusion | Vector and BM25 results are merged | Min-max score normalization by default; "fusion": "rrf" uses Reciprocal Rank Fusion with k = DAKERA_RRF_K (default 60). |
| 7. Scoring | Relevance, importance and recency are combined | The score is w_. 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. Rerank | A cross-encoder rescores the top candidates | The ONNX cross-encoder bge- rescores them for precision ("rerank": false skips it); rerank_ 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.