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 →
Beginner Lifecycle

Importance-Based Forgetting

⏰ ~15 min to implement 📦 Requires: Dakera v0.12+

Assign every memory an importance score at write time and let low-value entries fade naturally. Critical facts survive indefinitely; resolved tickets, small talk, and ephemeral context drop out of recall automatically, with no manual pruning.

Start Free →
Prerequisites
  • Running Dakera server (Quickstart)
  • An agent ID to scope memories to
  • Basic understanding of 0.0–1.0 importance scale

The Problem: Noisy, Unbounded Memory Stores

Memory stores grow indefinitely by default. After six months, a customer support agent for a SaaS product accumulates thousands of memories: resolved bug reports, closed tickets, one-off workarounds, support greetings, and fleeting context from conversations long since closed. Without a forgetting mechanism, every recall query returns a mix of critical knowledge and stale noise.

The result: agents confidently surface outdated information, recall latency increases as candidate sets balloon, and storage costs grow linearly with agent lifespan. The solution is not to delete memories — it is to score them so the right ones surface and the wrong ones fade.

How Importance Decay Works

Decay never deletes a memory: a background pass (hourly by default) lowers the importance of memories that are not being used, on a half-life you configure (DAKERA_DECAY_HALF_LIFE_HOURS, default 168), and stops at a floor of 0.01 (DAKERA_DECAY_MIN_IMPORTANCE). A recall with min_importance leaves out every memory below that threshold. The memory stays in storage, so you can audit or raise it again, while recall stays clean. Only a forget or a TTL deletes.

Architecture

Every store_memory call accepts an importance field (0.0–1.0). After that, importance changes in three ways: decay lowers it over time when the memory is not used (each access restarts the clock, and the memory type sets the speed: working 3×, episodic 1×, semantic 0.5×, procedural 0.3×), feedback moves it (an upvote adds 0.05 plus 5% of the current value, a downvote takes away 0.15), and explicit update_importance calls set it. Since v0.12 a recall does not raise importance. At recall time, min_importance is a filter: memories below the threshold are not returned.

Diagram: What a Recall Threshold Lets Through

Diagram of a recall with min_importance=0.5 over the support agent taxonomy. Identity facts (1.0), escalations (0.9) and active tickets (0.6) pass the filter and are ranked. Resolved tickets (0.2) and greetings (0.1) fall below 0.5 and are left out of the results, but they are not deleted.Diagram of a recall with min_importance=0.5 over the support agent taxonomy. Identity facts (1.0), escalations (0.9) and active tickets (0.6) pass the filter and are ranked. Resolved tickets (0.2) and greetings (0.1) fall below 0.5 and are left out of the results, but they are not deleted.
The threshold decides what a recall returns; nothing below it is deleted.Full size ↗Full size ↗

Diagram: How a Memory Fades

State diagram of one memory. It is stored with an importance score and appears in recall while it is above the query's min_importance. As decay lowers it, or when update_importance does (a resolved ticket from 0.6 to 0.2), it falls below the threshold and is soft-deleted: hidden from recall but still stored and auditable. update_importance can raise it again (a P0 escalation from 0.5 to 0.9). Only batch_forget, for GDPR requests, test data or spam, hard-deletes it from storage.State diagram of one memory. It is stored with an importance score and appears in recall while it is above the query's min_importance. As decay lowers it, or when update_importance does (a resolved ticket from 0.6 to 0.2), it falls below the threshold and is soft-deleted: hidden from recall but still stored and auditable. update_importance can raise it again (a P0 escalation from 0.5 to 0.9). Only batch_forget, for GDPR requests, test data or spam, hard-deletes it from storage.
Falling below the threshold hides a memory; only a forget removes it.Full size ↗Full size ↗

Real-World Scenario: Customer Support Agent

Scenario: HelpDesk AI builds a support agent for a B2B SaaS product. The agent accumulates memories from thousands of support conversations: billing questions, bug reports, feature requests, and resolved tickets. After 12 months, the agent has 15,000+ memories. Recall quality degrades because closed tickets from last year surface alongside active incidents.

HelpDesk assigns importance by memory category: resolved tickets get 0.2, active bug workarounds get 0.6, product announcements get 0.85, and permanent account facts (plan tier, contract terms) get 1.0. The recall threshold is set to min_importance=0.5 by default, ensuring resolved tickets never surface in new conversations.

Result: new conversations recall account facts, announcements and open workarounds, while last year's closed tickets stay in storage for audit without reaching the prompt.

Step-by-Step Implementation

  1. Define your importance taxonomy
    Before writing code, map your memory categories to importance scores. A support agent might use: identity facts (1.0), escalations (0.9), active tickets (0.6), resolved tickets (0.2), greetings (0.1). This taxonomy is the most important design decision in the pattern.
  2. Store memories with scored importance
    At every store_memory call, pass the appropriate importance score. Use tags alongside importance for multi-dimensional filtering later (e.g., tag resolved tickets with "resolved" so you can also filter by tag if needed).
  3. Configure recall thresholds per query type
    Not all queries need the same threshold. A "what is this user's account tier?" query should use min_importance=0.8 (only the top of the taxonomy: identity facts, escalations, preferences). A "what bugs has this user reported?" query might use min_importance=0.4 to include recent working notes.
  4. Update importance when status changes
    When a ticket is resolved, call update_importance to drop its score from 0.6 to 0.2. This takes it below the 0.5 threshold at once, without deleting the memory. When a feature request is escalated to P0, raise its importance from 0.5 to 0.9.
  5. Batch forget truly irrelevant memories
    For memories you know are permanently useless (GDPR deletion, test data, spam), hard-delete them rather than letting them decay: forget for known IDs, batch_forget for everything matching a filter (a tag, an importance range). Decay is for gradual relevance loss; forget is for immediate removal.

Before & After: Recall Results

Before — no importance filter (8 results)
[
  { "content": "Hi there! How can I help?",
    "importance": 0.1, "age_days": 45 },
  { "content": "Ticket #1022 resolved: billing glitch",
    "importance": 0.2, "age_days": 38 },
  { "content": "Ticket #1104 resolved: login error",
    "importance": 0.2, "age_days": 30 },
  { "content": "User plan: Pro ($299/mo)",
    "importance": 1.0, "age_days": 180 },
  { "content": "API rate limit workaround documented",
    "importance": 0.55, "age_days": 14 },
  { "content": "Good morning, happy to assist!",
    "importance": 0.1, "age_days": 22 },
  { "content": "Ticket #982 resolved: CSV export bug",
    "importance": 0.2, "age_days": 60 },
  { "content": "User prefers async Slack updates",
    "importance": 0.8, "age_days": 90 }
]
// 5 of 8 results are stale noise
After — min_importance=0.5 (3 results)
[
  { "content": "User plan: Pro ($299/mo)",
    "importance": 1.0, "age_days": 180 },
  { "content": "User prefers async Slack updates",
    "importance": 0.8, "age_days": 90 },
  { "content": "API rate limit workaround documented",
    "importance": 0.55, "age_days": 14 }
]
// 3 signal memories, no stale noise
// All 3 directly relevant to new query

Implementation

# Store a permanent identity fact
curl -X POST http://localhost:3000/v1/memory/store \
  -H "Authorization: Bearer dk-..." \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "support-bot",
    "content": "User: Alex Kim, Pro plan, enterprise contract, API access",
    "importance": 1.0,
    "memory_type": "semantic",
    "tags": ["identity", "account", "alex-kim"]
  }'

# Store an active ticket (medium importance)
curl -X POST http://localhost:3000/v1/memory/store \
  -H "Authorization: Bearer dk-..." \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "support-bot",
    "content": "Ticket #2091 open: user cannot export to PDF, reproducible on Firefox",
    "importance": 0.6,
    "memory_type": "episodic",
    "tags": ["ticket", "open", "alex-kim"]
  }'

# When the ticket resolves: drop its importance below the recall threshold
curl -X PATCH http://localhost:3000/v1/memories/mem-id-123/importance \
  -H "Authorization: Bearer dk-..." \
  -H "Content-Type: application/json" \
  -d '{"agent_id": "support-bot", "importance": 0.2}'

# Recall with a threshold: resolved tickets are left out
curl -X POST http://localhost:3000/v1/memory/recall \
  -H "Authorization: Bearer dk-..." \
  -H "Content-Type: application/json" \
  -d '{"agent_id": "support-bot", "query": "PDF export issue", "min_importance": 0.5, "top_k": 10}'

# Hard-delete every memory about a user (e.g., GDPR request); a filter is required
curl -X DELETE http://localhost:3000/v1/memories/forget/batch \
  -H "Authorization: Bearer dk-..." \
  -H "Content-Type: application/json" \
  -d '{"agent_id": "support-bot", "filter": {"tags": ["alex-kim"]}}'
from dakera import DakeraClient, BatchForgetRequest, BatchMemoryFilter

client = DakeraClient(base_url="http://localhost:3000", api_key="dk-...")
AGENT = "support-bot"

# --- Importance taxonomy (recall uses min_importance=0.5 by default) ---
IMPORTANCE = {
    "identity":        1.0,   # account facts: always above the threshold
    "escalation":      0.9,
    "preference":      0.8,
    "active_ticket":   0.6,   # above 0.5 while open
    "resolved_ticket": 0.2,   # below 0.5: left out of default recalls
    "greeting":        0.1,
}

# Store permanent account fact
client.store_memory(
    agent_id=AGENT,
    content="User: Alex Kim, Pro plan $299/mo, enterprise contract, API access enabled",
    importance=IMPORTANCE["identity"],
    memory_type="semantic",
    tags=["identity", "account", "alex-kim"]
)

# Store active ticket
ticket_mem = client.store_memory(
    agent_id=AGENT,
    content="Ticket #2091 OPEN: cannot export to PDF, reproducible Firefox 124, macOS",
    importance=IMPORTANCE["active_ticket"],
    memory_type="episodic",
    tags=["ticket", "open", "pdf-export", "alex-kim"]
)

# When ticket resolves: drop importance below the recall threshold
def close_ticket(memory_id: str, resolution: str):
    client.update_importance(
        agent_id=AGENT,
        memory_ids=[memory_id],
        importance=IMPORTANCE["resolved_ticket"]
    )
    # Store resolution note below the threshold too, kept for audit
    client.store_memory(
        agent_id=AGENT,
        content=f"RESOLVED: {resolution}",
        importance=0.45,
        memory_type="semantic",
        tags=["ticket", "resolved", "alex-kim"]
    )

close_ticket(ticket_mem["id"], "Ticket #2091: PDF export fixed in v2.14.1, deployed 2026-05-15")

# Recall: only active, relevant memories
active_context = client.recall(
    agent_id=AGENT,
    query="Alex Kim account and open issues",
    min_importance=0.5,
    top_k=10
)
for m in active_context.memories:
    print(f"[{m.importance:.2f}] {m.content}")

# Hard-delete for GDPR compliance: every memory tagged with the user
def delete_user_memories(user_tag: str) -> int:
    resp = client.batch_forget(BatchForgetRequest(
        agent_id=AGENT,
        filter=BatchMemoryFilter(tags=[user_tag]),
    ))
    return resp.deleted_count
import { DakeraClient } from '@dakera-ai/dakera';

const client = new DakeraClient({ baseUrl: 'http://localhost:3000', apiKey: 'dk-...' });
const AGENT = 'support-bot';

const IMPORTANCE = {
  identity:       1.0,
  escalation:     0.9,
  preference:     0.8,
  activeTicket:   0.6,
  resolvedTicket: 0.2,
  greeting:       0.1,
} as const;

// Store permanent identity fact
await client.storeMemory(AGENT, {
  content: 'User: Alex Kim, Pro plan $299/mo, enterprise contract, API access',
  importance: IMPORTANCE.identity,
  memory_type: 'semantic',
  tags: ['identity', 'account', 'alex-kim'],
});

// Store active ticket
const { memory: ticketMem } = await client.storeMemory(AGENT, {
  content: 'Ticket #2091 OPEN: cannot export to PDF, reproducible Firefox 124',
  importance: IMPORTANCE.activeTicket,
  memory_type: 'episodic',
  tags: ['ticket', 'open', 'pdf-export', 'alex-kim'],
});

// On resolution: drop importance below the recall threshold
async function closeTicket(memoryId: string, resolution: string) {
  await client.updateImportance(AGENT, {
    memory_ids: [memoryId],
    importance: IMPORTANCE.resolvedTicket,
  });
  await client.storeMemory(AGENT, {
    content: `RESOLVED: ${resolution}`,
    importance: 0.45,
    memory_type: 'semantic',
    tags: ['ticket', 'resolved', 'alex-kim'],
  });
}

await closeTicket(ticketMem.id, 'Ticket #2091: PDF fix deployed in v2.14.1');

// Filtered recall: resolved tickets never appear
const { memories } = await client.recall(AGENT, 'Alex Kim account and open issues', {
  min_importance: 0.5,
  top_k: 10,
});

// GDPR hard delete: every memory tagged with the user (a filter is required)
const resp = await client.batchForget({
  agent_id: AGENT,
  filter: { tags: ['alex-kim'] },
});
console.log(`Deleted ${resp.deleted_count} memories`);
use dakera_client::{BatchForgetRequest, BatchMemoryFilter, DakeraClient, MemoryType,
    RecallRequest, StoreMemoryRequest};

let client = DakeraClient::builder("http://localhost:3000")
    .api_key("dk-...")
    .build()?;
let agent = "support-bot";

// Store permanent identity memory
client.store_memory(
    StoreMemoryRequest::new(agent, "User: Alex Kim, Pro plan, enterprise contract, API access")
        .with_type(MemoryType::Semantic)
        .with_importance(1.0)
        .with_tags(vec!["identity".into(), "account".into(), "alex-kim".into()]),
).await?;

// Store active ticket at medium importance
let ticket = client.store_memory(
    StoreMemoryRequest::new(agent, "Ticket #2091 OPEN: PDF export fails in Firefox 124")
        .with_type(MemoryType::Episodic)
        .with_importance(0.6)
        .with_tags(vec!["ticket".into(), "open".into(), "alex-kim".into()]),
).await?;

// On resolution: drop importance below the recall threshold
client.patch_memory_importance(&ticket.memory_id, agent, 0.2).await?;

// Recall with importance filter
let results = client.recall(
    RecallRequest::new(agent, "Alex Kim open issues")
        .with_min_importance(0.5)
        .with_top_k(10),
).await?;

// Hard delete every memory tagged with the user (GDPR)
let deleted = client.batch_forget(BatchForgetRequest::new(
    agent,
    BatchMemoryFilter::default().with_tags(vec!["alex-kim".into()]),
)).await?;
println!("{} memories deleted", deleted.deleted_count);
client := dakera.NewClientWithOptions(dakera.ClientOptions{
    BaseURL: "http://localhost:3000",
    APIKey:  "dk-...",
})
ctx := context.Background()
agent := "support-bot"
f32 := func(v float32) *float32 { return &v }

// Permanent identity fact
client.StoreMemory(ctx, agent, dakera.StoreMemoryRequest{
    Content:    "User: Alex Kim, Pro plan, enterprise contract, API access",
    Importance: f32(1.0),
    MemoryType: "semantic",
    Tags:       []string{"identity", "account", "alex-kim"},
})

// Active ticket at medium importance
ticket, _ := client.StoreMemory(ctx, agent, dakera.StoreMemoryRequest{
    Content:    "Ticket #2091 OPEN: PDF export fails in Firefox 124",
    Importance: f32(0.6),
    MemoryType: "episodic",
    Tags:       []string{"ticket", "open", "alex-kim"},
})

// On resolution: drop importance below the recall threshold
client.PatchMemoryImportance(ctx, ticket.Memory.ID, agent, 0.2)

// Recall with filter
results, _ := client.Recall(ctx, agent, dakera.RecallRequest{
    Query:         "Alex Kim open issues",
    MinImportance: f32(0.5),
    TopK:          10,
})

// GDPR: hard delete every memory tagged with the user
resp, _ := client.BatchForget(ctx, dakera.BatchForgetRequest{
    AgentID: agent,
    Filter:  dakera.BatchMemoryFilter{Tags: []string{"alex-kim"}},
})
fmt.Printf("Deleted %d memories\n", resp.DeletedCount)

Recall that stays focused as the store grows

Importance-based decay runs server-side automatically — zero maintenance required.

Start Building →

SDK Reference

MethodSDKPurpose
store_memory(agent_id, content, importance, memory_type, tags)PythonStore memory with importance score
storeMemory(agentId, {content, importance, memoryType, tags})TypeScriptStore memory with importance score
recall(agent_id, query, min_importance, top_k)PythonRetrieve memories above importance threshold
recall(agentId, query, {min_importance, top_k})TypeScriptRetrieve memories above importance threshold
update_importance(agent_id, memory_ids, importance)PythonReassign importance score to trigger/resist decay
updateImportance(agentId, {memory_ids, importance})TypeScriptReassign importance score to trigger/resist decay
forget(agent_id, memory_id)PythonHard-delete a single memory permanently
forget(agentId, memoryId)TypeScriptHard-delete a single memory permanently
batch_forget(request)PythonHard-delete every memory matching a filter (GDPR deletion)
batchForget(request)TypeScriptHard-delete every memory matching a filter (GDPR deletion)

Performance Considerations

168 h
Default decay half-life (DAKERA_DECAY_HALF_LIFE_HOURS)
0.01
Importance floor: decay never goes lower, never deletes
1 h
Default interval of the decay pass (DAKERA_DECAY_INTERVAL_SECS)

Edge Cases

Edge Case 1: Setting importance=1.0 by default

Developers often set importance=1.0 "to be safe," but this defeats the entire pattern. If everything is permanent, recall becomes noisy again. Reserve 1.0 strictly for facts that should never leave the store: user identity, account constants, irrevocable decisions. For everything else, use 0.5–0.9 and let decay do its job.

Edge Case 2: Forgetting to downgrade on state change

An active ticket stored at importance 0.6 keeps passing a 0.5 threshold after resolution, until decay brings it down, if you forget to call update_importance. Integrate the importance downgrade into your ticket-close webhook or event handler; treat it as part of the "mark resolved" workflow, not an afterthought.

Edge Case 3: Feedback inflating scores unexpectedly

Since v0.12 a recall does not raise importance, but upvotes do: each one adds 0.05 plus 5% of the current value (at most 1.0). A low-importance memory that users keep upvoting can climb back over your threshold. If you're seeing stale memories resurface, check their feedback history (GET /v1/memories/{id}/feedback) and use update_importance to set them back to 0.1 after investigation.

Edge Case 4: Using min_importance too aggressively

Setting min_importance=0.9 on all queries will return only identity facts — you'll miss active tickets and preferences. Use context-sensitive thresholds: high threshold (0.8) for factual lookups, lower threshold (0.4–0.5) for open-ended context queries. Tune per query intent.

Edge Case 5: Soft decay vs. hard delete for compliance

Soft decay (importance drops below threshold) does not physically remove the memory from storage; it only hides it from recall. For GDPR Article 17 right-to-erasure compliance, you must hard-delete: forget for known IDs, or batch_forget with a filter such as a per-user tag. Document which memories are subject to erasure obligations and automate hard deletion on user account closure.

Advanced Configuration: Decay Settings & Memory Policies

Server-Wide Decay (admin)

# Python: read and change the decay engine (needs an admin key with no namespace restriction)
decay = client.decay_config()
client.decay_update_config(strategy="exponential", half_life_hours=168, min_importance=0.01)

# TypeScript
const decay = await client.decayConfig();
await client.decayUpdateConfig({ strategy: 'exponential', half_life_hours: 168, min_importance: 0.01 });

Per-Namespace Memory Policy

An agent's memories live in the namespace _dakera_agent_{agent_id}. Its memory policy (PUT /v1/namespaces/{ns}/memory_policy, get_memory_policy() / set_memory_policy() in the SDKs) can give each memory type its own decay curve (working_decay, episodic_decay, semantic_decay, procedural_decay) and a default TTL. Read the policy, change the fields you need and write it back: the write replaces the stored policy.

Decay Settings Reference

SettingDefaultEffect
DAKERA_DECAY_HALF_LIFE_HOURS168Half-life of the decay curve
DAKERA_DECAY_STRATEGYexponentialServer-wide curve (exponential, linear or step)
DAKERA_DECAY_MIN_IMPORTANCE0.01Floor that decay never goes below
DAKERA_DECAY_INTERVAL_SECS3600How often the background decay pass runs
Try it live

Run this pattern against a real Dakera.

The Playground executes live API calls in your browser. No account, no install. Then run it on your own server in five minutes.