Back to Blog

Temporal Memory for AI Agents: How Decay and Importance Scoring Work

Memories aren't all created equal, and they shouldn't persist at full strength forever. Here's how temporal systems model the natural lifecycle of agent memory.

The Problem with Timeless Memory

Most memory systems treat every stored piece of information as permanently important. A vector database doesn't know — or care — whether a memory was created 5 minutes ago or 5 months ago. When you search, all memories compete equally regardless of age.

This creates two problems for AI agents:

Temporal memory solves this by treating time as a first-class dimension of retrieval. Memories have a lifecycle: they're created with full strength, can be reinforced by access, and gradually decay when no longer relevant.

The Scoring Formula

When Dakera retrieves memories, the final ranking score is a composite of multiple signals:

final_score = (relevance_weight * relevance_score)
            + (recency_weight * recency_score)
            + (importance_weight * importance_score)
            - decay_penalty

Each component contributes different information:

Memory Decay Strategies

Dakera implements 6 decay strategies, each modeling a different forgetting curve. The choice of strategy depends on the type of memory you're storing.

Exponential Decay

Memories lose strength rapidly at first, then more slowly. Models the classic Ebbinghaus forgetting curve. Best for working memory — scratchpads, intermediate reasoning steps, transient observations.

# strength(t) = initial_strength * e^(-lambda * t)
# Half-life of 24 hours: memory is at 50% after 1 day, 25% after 2 days

client.namespace.configure(
    namespace="agent-scratch",
    decay_strategy="exponential",
    decay_half_life_hours=24
)
TimeStrength
0 hours100%
24 hours50%
48 hours25%
72 hours12.5%
7 days0.8%

Logarithmic Decay

Slow initial decay that accelerates over time. Memories stay strong for a while, then fade quickly. Best for medium-term context — project notes, meeting summaries, weekly updates.

# strength(t) = 1 - log(1 + alpha * t) / log(1 + alpha * max_t)

client.namespace.configure(
    namespace="project-notes",
    decay_strategy="logarithmic",
    decay_half_life_hours=168  # ~1 week
)

Linear Decay

Constant decay rate — memories lose the same amount of strength per unit time. Simple and predictable. Best for time-boxed contexts where you want memories to expire after a known duration.

# strength(t) = max(0, 1 - t / max_lifetime)

client.namespace.configure(
    namespace="daily-standup",
    decay_strategy="linear",
    decay_half_life_hours=48
)

Step Decay

Discrete importance levels that downshift on schedule. Memory stays at full strength for a period, then drops to the next level. Models how we categorize memories into "recent", "relevant", "archive".

# strength(t) = step_levels[floor(t / step_interval)]
# Example: 1.0 for 7 days, 0.6 for 30 days, 0.2 after 90 days

client.namespace.configure(
    namespace="customer-interactions",
    decay_strategy="step",
    decay_steps=[
        {"until_hours": 168, "strength": 1.0},   # Fresh: 7 days
        {"until_hours": 720, "strength": 0.6},   # Relevant: 30 days
        {"until_hours": 2160, "strength": 0.2}   # Archive: 90 days
    ]
)

Adaptive Decay

Decay rate adjusts based on access patterns. Memories that are frequently retrieved strengthen (like spaced repetition). Memories that are never accessed decay faster. This is the most sophisticated strategy and models long-term potentiation in biological memory.

# Each access resets the decay clock and slightly strengthens the memory
# Unused memories decay at an accelerating rate

client.namespace.configure(
    namespace="user-preferences",
    decay_strategy="adaptive",
    decay_half_life_hours=720,       # Base: 30 days
    access_boost_factor=1.5,         # Each access extends effective half-life by 50%
    neglect_acceleration=1.2         # Unused memories decay 20% faster per period
)

No Decay

Some memories should never fade — user identity, critical facts, system configuration. Use no decay for truly permanent memories:

client.namespace.configure(
    namespace="core-identity",
    decay_strategy="none"
)

# These memories will always compete at full strength
client.memory.add(
    namespace="core-identity",
    content="The user's name is Alice Chen and she prefers to be called Alice",
    importance=1.0
)

Importance Scoring

Importance scoring determines how much weight a memory carries independent of its age or relevance to the current query. There are two approaches: explicit and inferred.

Explicit Importance

The agent or application assigns an importance score at write time:

# Critical operational knowledge — never let this decay below threshold
client.memory.add(
    namespace="ops",
    content="Production database password was rotated on 2026-05-16. New credentials in vault.",
    importance=1.0  # Maximum importance
)

# Casual observation — fine to forget
client.memory.add(
    namespace="ops",
    content="The user mentioned they enjoy the new dashboard color scheme",
    importance=0.2  # Low importance
)

Inferred Importance

Dakera's ML query classifier can infer importance based on the content's characteristics. Memories containing:

Recency Weighting at Query Time

Beyond passive decay, Dakera supports active recency weighting at query time. This lets the same namespace serve different query patterns:

# High recency weight: "what happened recently?"
recent_events = client.memory.search(
    namespace="project-log",
    query="recent progress updates",
    limit=5,
    recency_weight=0.7  # Heavily favor recent memories
)

# Low recency weight: "what's the overall architecture?"
architecture = client.memory.search(
    namespace="project-log",
    query="system architecture decisions",
    limit=5,
    recency_weight=0.1  # Relevance dominates, age barely matters
)

Temporal Queries

Sometimes the agent needs to answer explicitly temporal questions: "what did the user say last week about their budget?" or "has this error happened before?" Dakera supports temporal filtering alongside semantic search:

# Search within a time range
last_week = client.memory.search(
    namespace="conversations",
    query="budget discussion",
    limit=5,
    created_after="2026-05-09T00:00:00Z",
    created_before="2026-05-16T00:00:00Z"
)

# Find the earliest memory matching a pattern
first_occurrence = client.memory.search(
    namespace="incidents",
    query="database connection timeout",
    limit=1,
    sort_by="created_at",
    sort_order="asc"
)

How Temporal Memory Affects Benchmark Scores

The LoCoMo benchmark specifically tests temporal reasoning — one of three question categories (alongside single-hop and multi-hop). Systems without temporal awareness systematically fail questions like:

Dakera's temporal memory system is a significant contributor to its 87.6% overall LoCoMo score, particularly on Category 3 (temporal inference) questions where it achieves 70.7% — compared to systems without temporal awareness that typically score below 40% on the same category.

For more details on how we benchmark temporal reasoning, see our benchmark methodology post.

Choosing the Right Decay Strategy

Use CaseStrategyHalf-Life
Scratch/working memoryExponential1-24 hours
Conversation contextExponential24-72 hours
Project notesLogarithmic1-4 weeks
Customer interactionsStep7/30/90 day tiers
User preferencesAdaptive30+ days
Identity/factsNoneInfinite

Implementation Pattern: Multi-Layer Memory

A production agent typically uses multiple namespaces with different decay strategies, forming a memory hierarchy:

from dakera import Dakera

client = Dakera(base_url="http://localhost:3300")

class AgentMemory:
    """Multi-layer memory with different temporal behaviors."""

    def __init__(self, agent_id: str):
        self.agent_id = agent_id
        # Configure layers
        self.layers = {
            "working": f"{agent_id}-working",     # Exponential, 4h
            "session": f"{agent_id}-session",     # Exponential, 48h
            "learned": f"{agent_id}-learned",     # Adaptive, 30d
            "identity": f"{agent_id}-identity",   # None (permanent)
        }

    def remember(self, content: str, layer: str = "session", importance: float = 0.5):
        """Store a memory in the appropriate layer."""
        client.memory.add(
            namespace=self.layers[layer],
            content=content,
            importance=importance
        )

    def recall(self, query: str, recency_weight: float = 0.3):
        """Search across all layers, merging results."""
        all_results = []
        for layer_name, namespace in self.layers.items():
            results = client.memory.search(
                namespace=namespace,
                query=query,
                limit=3,
                recency_weight=recency_weight
            )
            for r in results:
                r.layer = layer_name
            all_results.extend(results)

        # Sort by composite score across layers
        all_results.sort(key=lambda r: r.score, reverse=True)
        return all_results[:5]

# Usage
memory = AgentMemory("support-agent-1")
memory.remember("User's name is Alice", layer="identity", importance=1.0)
memory.remember("Alice asked about API rate limits", layer="session")
memory.remember("Working on response draft...", layer="working", importance=0.1)

Conclusion

Temporal memory transforms agents from stateless responders into systems that understand the passage of time. The key insight is that memory strength should not be binary (present or absent) — it exists on a spectrum that naturally evolves. Important memories persist. Ephemeral context fades. And the agent's recall patterns start to resemble the intelligent, adaptive behavior we expect from systems that truly "remember."

Try Dakera Today

Single binary, zero dependencies, 87.6% LoCoMo benchmark.

Get Started

Ready to get started?

Add long-term memory to your AI agents in minutes.

Get Started Free