Hierarchical Memory

Category: Architecture

Problem

Not all memories are equal. A user's name should persist forever, last week's conversation topics should fade gradually, and the current session's working context should be immediately available but ephemeral. Treating all memories with the same storage and decay strategy leads to either information overload or premature forgetting.

Architecture

This pattern uses three namespace tiers in Dakera — each with its own decay configuration. Short-term memory captures the active session and decays aggressively. Long-term memory holds durable facts and decays slowly. Permanent memory stores identity-level information with no decay at all.

Flow

  • Classify incoming memories by tier (ephemeral, durable, permanent)
  • Route each memory to the appropriate namespace
  • Configure per-namespace decay rates
  • At recall time, query all tiers and merge results by relevance

Implementation

from dakera import Dakera

client = Dakera(base_url="http://localhost:3300", api_key="dk-...")

# Define the three memory tiers
TIERS = {
    "short_term": {
        "namespace_suffix": "stm",
        "decay_rate": 0.15,       # Aggressive: decays within hours
        "default_importance": 0.5
    },
    "long_term": {
        "namespace_suffix": "ltm",
        "decay_rate": 0.005,      # Slow: decays over weeks
        "default_importance": 0.75
    },
    "permanent": {
        "namespace_suffix": "perm",
        "decay_rate": 0.0,        # Never decays
        "default_importance": 1.0
    }
}

def store_hierarchical(user_id: str, content: str, tier: str):
    """Store a memory in the appropriate tier namespace."""
    config = TIERS[tier]
    namespace = f"user-{user_id}-{config['namespace_suffix']}"

    client.memory.store(
        content=content,
        namespace=namespace,
        metadata={
            "tier": tier,
            "importance": config["default_importance"]
        }
    )

def recall_all_tiers(user_id: str, query: str) -> list:
    """Recall from all memory tiers and merge results."""
    all_results = []

    for tier_name, config in TIERS.items():
        namespace = f"user-{user_id}-{config['namespace_suffix']}"
        results = client.memory.recall(
            query=query,
            namespace=namespace,
            top_k=5
        )
        for r in results["results"]:
            r["tier"] = tier_name
            all_results.append(r)

    # Sort by score, with permanent tier getting a boost
    all_results.sort(key=lambda x: x["score"] * (1.2 if x["tier"] == "permanent" else 1.0), reverse=True)
    return all_results[:10]

# Usage
store_hierarchical("alice", "User's name is Alice Chen", "permanent")
store_hierarchical("alice", "User is working on a React migration project", "long_term")
store_hierarchical("alice", "Currently debugging a useEffect hook", "short_term")

context = recall_all_tiers("alice", "Help with React hooks")
# Returns all relevant memories, prioritizing permanent identity facts

When to Use This Pattern

  • Agents that maintain long relationships with users across many sessions
  • Applications that need both immediate context and historical knowledge
  • Systems where storage cost matters — aggressive decay on ephemeral data saves space
  • Any architecture that mirrors human memory (working, episodic, semantic)

Key Considerations

  • Promote memories between tiers — repeated short-term patterns should move to long-term
  • Configure decay per namespace using Dakera's decay configuration API
  • Keep permanent memory small and curated — it should only contain verified identity facts
  • Consider a promotion heuristic: if a fact is recalled more than N times, upgrade its tier