Skip to main content
Glama
scoring_algorithm.mdβ€’15.5 kB
# Temporal Decay Scoring Algorithm **Version:** 0.2.0 **Last Updated:** 2025-01-07 ## Overview Mnemex uses a novel temporal decay algorithm that mimics human memory dynamics. Memories naturally fade over time unless reinforced through use. This document explains the mathematical model, parameter tuning, and design rationale. ## Model Selection Mnemex supports three decay models. Choose per use case via `MNEMEX_DECAY_MODEL`: 1. Power‑Law (default): $$ f(\Delta t) = \left(1 + \frac{\Delta t}{t_0}\right)^{-\alpha} $$ - Heavier tail; retains older-but-important memories better. - Parameters: $\alpha$ (shape), $t_0$ (characteristic time). We derive $t_0$ from a chosen half‑life $H$ via $t_0 = H / (2^{1/\alpha} - 1)$. 2. Exponential: $$ f(\Delta t) = e^{-\lambda\,\Delta t} $$ - Lighter tail; simpler and forgets sooner. - Parameter: $\lambda$ (from half‑life). 3. Two‑Component Exponential: $$ f(\Delta t) = w\,e^{-\lambda_f\,\Delta t} + (1-w)\,e^{-\lambda_s\,\Delta t} $$ - Forgets very recent items faster (fast component) but keeps a heavier tail (slow component). - Parameters: $\lambda_f, \lambda_s, w$. Combined score (all models): $$ \text{score} = (n_{\text{use}})^\beta \cdot f(\Delta t) \cdot s $$ ## Core Formula $$ \text{score} = (n_{\text{use}})^\beta \cdot e^{-\lambda \cdot \Delta t} \cdot s $$ Where: - $n_{\text{use}}$: Number of times the memory has been accessed (touches) - $\beta$ (beta): Use count weight exponent (default: 0.6) - $\lambda$ (lambda): Decay constant (default: $2.673 \times 10^{-6}$ for 3-day half-life) - $\Delta t$: Time delta in seconds since last access ($t_{\text{now}} - t_{\text{last used}}$) - $s$: Base multiplier (range: 0.0-2.0, default: 1.0) ## Parameter Reference (at a glance) - $\beta$ (beta): Sub-linear exponent for use count. - Default: 0.6; Range: 0.0–1.0 - Higher β†’ frequent memories gain more; Lower β†’ emphasize recency - $\lambda$ (lambda): Exponential decay constant. - Computed from half-life: $\lambda = \ln(2) / t_{1/2}$ - Example values: 1-day = $8.02\times 10^{-6}$, 3-day = $2.67\times 10^{-6}$, 7-day = $1.15\times 10^{-6}$ - $\Delta t$: Seconds since last use. - Larger $\Delta t$ β†’ lower score via $e^{-\lambda\Delta t}$ - $s$ (strength): Importance multiplier. - Default: 1.0; Range: 0.0–2.0; Can be nudged by touch with boost - $\tau_{\text{forget}}$: Forget threshold. - Default: 0.05; If score < $\tau_{\text{forget}}$ β†’ forget - $\tau_{\text{promote}}$: Promote threshold. - Default: 0.65; If score β‰₯ $\tau_{\text{promote}}$ β†’ promote - Usage promotion rule: $n_{\text{use}} \ge 5$ within 14 days (configurable) - Captures frequently referenced info even if not extremely recent ## Components Explained ### 1. Use Count Component: $(n_{\text{use}})^\beta$ **Purpose:** Reward frequently accessed memories. **Why Exponent?** - Linear growth ($\beta=1.0$) over-rewards high use counts - Sub-linear ($\beta<1.0$) provides diminishing returns - Default $\beta=0.6$ balances reward vs. diminishing returns **Examples:** | Use Count | $n^{0.6}$ | Boost Factor | |-----------|-----------|--------------| | 1 | 1.00 | 1.0x | | 5 | 2.63 | 2.6x | | 10 | 3.98 | 4.0x | | 50 | 11.45 | 11.4x | **Tuning Guidelines:** - **Higher $\beta$** (0.8-1.0): Strongly favor frequently used memories - **Lower $\beta$** (0.3-0.5): Reduce impact of use count, emphasize recency - **$\beta=0.0$**: Disable use count entirely (pure temporal decay) ### 2. Decay Component: $e^{-\lambda \cdot \Delta t}$ **Purpose:** Exponential decay over time ([Ebbinghaus forgetting curve](https://en.wikipedia.org/wiki/Forgetting_curve)). **Why Exponential?** - Models human memory better than linear decay - Creates natural "forgetting" behavior - Continuous and smooth (no sudden drops) **Half-Life Calculation:** $$ \lambda = \frac{\ln(2)}{t_{1/2}} $$ Where $t_{1/2}$ is the half-life in seconds. For a 3-day half-life: $$ \lambda = \frac{\ln(2)}{3 \times 86400} = 2.673 \times 10^{-6} $$ **Decay Curves:** ``` Time Since Last Use | Score Multiplier (Ξ»=3-day half-life) --------------------|------------------------------------- 0 hours | 1.000 (100%) 12 hours | 0.917 (92%) 1 day | 0.841 (84%) 3 days | 0.500 (50%) ← Half-life 7 days | 0.210 (21%) 14 days | 0.044 (4%) 30 days | 0.001 (0.1%) ``` **Tuning Guidelines:** - **1-day half-life** ($\lambda=8.02 \times 10^{-6}$): Aggressive decay, forget quickly - **3-day half-life** ($\lambda=2.67 \times 10^{-6}$): Default, balanced retention - **7-day half-life** ($\lambda=1.15 \times 10^{-6}$): Gentle decay, longer retention - **14-day half-life** ($\lambda=5.73 \times 10^{-7}$): Very gentle, archives slowly ### 3. Strength Multiplier **Purpose:** Boost or dampen specific memories based on importance. **Range:** 0.0 to 2.0 - **0.0-0.5**: Low priority, ephemeral - **1.0**: Normal (default) - **1.5-2.0**: High priority, critical **Use Cases:** ```python # Security credentials - critical strength = 2.0 # User preferences - important strength = 1.5 # Normal conversation context strength = 1.0 # Tentative ideas, exploratory thoughts strength = 0.5 ``` **Strength Over Time:** - Can be increased via `touch_memory(boost_strength=True)` - Caps at 2.0 to prevent runaway growth - Only way to "permanently" resist decay (besides re-touching) ## Decision Thresholds ### Forget Threshold: $\tau_{\text{forget}} = 0.05$ **Purpose:** Delete memories with very low scores. **Rationale:** - Below 5% of original score β†’ likely irrelevant - Prevents database bloat ### Threshold Summary - Forget if $\text{score} < \tau_{\text{forget}}$ (default 0.05) - Promote if $\text{score} \ge \tau_{\text{promote}}$ (default 0.65) - Or promote if $n_{\text{use}}\ge 5$ within 14 days (usage-based) - User can override by touching memory **Example:** For a memory with $n_{\text{use}}=1$, $s=1.0$, $\beta=0.6$, $\lambda=2.673 \times 10^{-6}$ (3-day half-life), and $\Delta t = 30$ days: $$ \begin{align*} \text{score} &= (1)^{0.6} \cdot e^{-2.673 \times 10^{-6} \cdot 2{,}592{,}000} \cdot 1.0 \\ &= 1.0 \cdot e^{-6.93} \cdot 1.0 \\ &= 0.001 \end{align*} $$ Since $0.001 < 0.05$ β†’ **FORGET** ### Promote Threshold: $\tau_{\text{promote}} = 0.65$ **Purpose:** Move high-value memories to long-term storage (LTM). **Dual Criteria (OR logic):** 1. **Score-based:** $\text{score} \geq 0.65$ 2. **Usage-based:** $n_{\text{use}} \geq 5$ within 14 days **Rationale:** - Score-based: Catches quickly-important info (e.g., high strength + recent) - Usage-based: Catches frequently referenced info (even if not recent) **Example Scenario 1:** High score (strong memory, recently used) $$ \begin{align*} n_{\text{use}} &= 3 \\ \Delta t &= 1 \text{ hour} = 3600 \text{ seconds} \\ s &= 2.0 \\ \\ \text{score} &= (3)^{0.6} \cdot e^{-2.673 \times 10^{-6} \cdot 3600} \cdot 2.0 \\ &= 1.93 \cdot 0.99 \cdot 2.0 \\ &= 3.82 \end{align*} $$ Since $3.82 > 0.65$ β†’ **PROMOTE** **Example Scenario 2:** High use count (frequently accessed) - $n_{\text{use}} = 5$ - $\Delta t = 7$ days - Memory created 10 days ago Within 14-day window **AND** $n_{\text{use}} \geq 5$ β†’ **PROMOTE** ## Complete Decision Logic ```python from enum import Enum class MemoryAction(Enum): KEEP = "keep" # Normal retention FORGET = "forget" # Delete from STM PROMOTE = "promote" # Move to LTM def decide_action(memory, now): """Determine action for a memory.""" # Calculate current score time_delta = now - memory.last_used score = ( math.pow(memory.use_count, Ξ²) * math.exp(-Ξ» * time_delta) * memory.strength ) # Check promotion criteria if score >= Ο„_promote: return MemoryAction.PROMOTE, "High score" age_days = (now - memory.created_at) / 86400 if memory.use_count >= 5 and age_days <= 14: return MemoryAction.PROMOTE, "Frequently used" # Check forget criteria if score < Ο„_forget: return MemoryAction.FORGET, "Low score" # Default: keep in STM return MemoryAction.KEEP, f"Score: {score:.3f}" ``` ## Parameter Interactions ### High $\beta$ + Short Half-Life $$ \beta = 0.8, \quad \lambda = 8.02 \times 10^{-6} \text{ (1-day half-life)} $$ **Effect:** Strongly favor frequently used, recent memories. Aggressive forgetting. **Use Case:** High-velocity environments, rapid context switching. ### Low $\beta$ + Long Half-Life $$ \beta = 0.3, \quad \lambda = 5.73 \times 10^{-7} \text{ (14-day half-life)} $$ **Effect:** Gentle decay, less emphasis on use count. Longer retention. **Use Case:** Slow-paced projects, archival needs. ### High Strength + Normal Decay $$ s = 2.0, \quad \beta = 0.6, \quad \lambda = 2.67 \times 10^{-6} \text{ (3-day half-life)} $$ **Effect:** Important memories resist decay longer. **Use Case:** Critical information (credentials, decisions) in normal workflow. ## Worked Examples ### Example A: Low-use, recent, normal strength Given: $n_{\text{use}}=1$, $\beta=0.6$, $\lambda=2.673\times10^{-6}$ (3-day), $\Delta t=6$ hours, $s=1.0$. 1) Use factor: $(1)^{0.6}=1.00$ 2) Decay: $e^{-2.673\times10^{-6}\cdot 21600} = e^{-0.0578}=0.9439$ 3) Strength: $1.0$ Score: $1.00\times 0.9439\times 1.0=0.944$ β†’ Keep (between thresholds) ### Example B: Frequent-use, mildly stale, normal strength Given: $n_{\text{use}}=6$, $\beta=0.6$, $\lambda=2.673\times10^{-6}$ (3-day), $\Delta t=2$ days, $s=1.0$. 1) Use factor: $(6)^{0.6}\approx 2.93$ 2) Decay: $e^{-2.673\times10^{-6}\cdot 172800} = e^{-0.462} = 0.629$ 3) Strength: $1.0$ Score: $2.93\times 0.629 \approx 1.84$ β†’ $\ge 0.65$ β‡’ Promote (score-based) ### Example C: High strength, older, modest use Given: $n_{\text{use}}=3$, $\beta=0.6$, $\lambda=2.673\times10^{-6}$ (3-day), $\Delta t=5$ days, $s=1.5$. 1) Use factor: $(3)^{0.6}\approx 1.93$ 2) Decay: $e^{-2.673\times10^{-6}\cdot 432000} = e^{-1.156} = 0.315$ 3) Strength: $1.5$ Score: $1.93\times 0.315 \times 1.5 \approx 0.91$ β†’ $\ge 0.65$ β‡’ Promote (score-based) ### Example D: Rarely used, very old Given: $n_{\text{use}}=1$, $\beta=0.6$, $\lambda=2.673\times10^{-6}$ (3-day), $\Delta t=21$ days, $s=1.0$. 1) Use factor: $1.00$ 2) Decay: $e^{-2.673\times10^{-6}\cdot 1,814,400} = e^{-4.85} = 0.0078$ 3) Strength: $1.0$ Score: $\approx 0.0078$ β†’ $< 0.05$ β‡’ Forget ## Visualizations ### Decay Curves ($n_{\text{use}}=1$, $s=1.0$) ``` Score 1.0 |● | ● 0.8 | ● | ● 0.6 | ● | ●● 0.4 | ●● | ●●● 0.2 | ●●●● | ●●●●●●● 0.0 |_________________________________●●●●●●●●●●●●●● 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 days Legend: ● = score at each day Horizontal line at 0.65 = promotion threshold Horizontal line at 0.05 = forget threshold ``` ### Use Count Impact ($\Delta t=1$ day, $s=1.0$, $\beta=0.6$) ``` Score 4.0 | ● | ● 3.0 | ● | ● 2.0 | ● | ● 1.0 |● ● ● ● ● |_______________________________________________ 0 5 10 15 20 use_count ``` ### Strength Modulation ($n_{\text{use}}=1$, $\Delta t=1$ day) ``` Score 2.0 | ● (s=2.0) | ● (s=1.5) 1.0 |● (s=1.0) | ● (s=0.5) 0.0 |_________________________________ ``` ## Tuning for Different Use Cases ### Personal Assistant (Balanced) ```env MNEMEX_DECAY_LAMBDA=2.673e-6 # 3-day half-life MNEMEX_DECAY_BETA=0.6 MNEMEX_FORGET_THRESHOLD=0.05 MNEMEX_PROMOTE_THRESHOLD=0.65 ``` ### Development Environment (Aggressive) ```env MNEMEX_DECAY_LAMBDA=8.02e-6 # 1-day half-life MNEMEX_DECAY_BETA=0.8 MNEMEX_FORGET_THRESHOLD=0.10 MNEMEX_PROMOTE_THRESHOLD=0.70 ``` ### Research / Archival (Conservative) ```env MNEMEX_DECAY_LAMBDA=5.73e-7 # 14-day half-life MNEMEX_DECAY_BETA=0.4 MNEMEX_FORGET_THRESHOLD=0.03 MNEMEX_PROMOTE_THRESHOLD=0.50 ``` ### Meeting Notes (High Velocity) ```env MNEMEX_DECAY_LAMBDA=1.60e-5 # 12-hour half-life MNEMEX_DECAY_BETA=0.9 MNEMEX_FORGET_THRESHOLD=0.15 MNEMEX_PROMOTE_THRESHOLD=0.75 ``` ## Implementation Notes ### Precision Use floating-point for all calculations: ```python # Good time_delta = float(now - last_used) score = math.pow(use_count, beta) * math.exp(-lambda_ * time_delta) * strength # Bad (integer overflow risk) time_delta = now - last_used # int score = use_count ** beta * math.exp(-lambda_ * time_delta) * strength ``` ### Caching Scores change over time. Either: 1. Calculate on-demand (accurate but slower) 2. Cache with TTL (faster but approximate) STM uses on-demand calculation for accuracy. ### Batch Operations For garbage collection, calculate scores in batch: ```python now = int(time.time()) memories_to_delete = [] for memory in all_memories: score = calculate_score(memory, now) if score < forget_threshold: memories_to_delete.append(memory.id) # Delete in batch delete_memories(memories_to_delete) ``` ## Comparison to Other Approaches ### vs. Fixed TTL (e.g., Redis) **Redis-style:** $$ \text{if } (t_{\text{now}} - t_{\text{created}}) > \text{TTL} \implies \text{delete} $$ **STM-style:** $$ \text{if } \text{score}(t) < \tau_{\text{forget}} \implies \text{delete} $$ **Advantage:** STM rewards frequent use. Redis deletes unconditionally. ### vs. LRU (Least Recently Used) **LRU:** $$ \text{if cache full} \implies \text{evict(least recently used)} $$ **STM:** $$ \text{if } \text{score}(t) < \tau_{\text{forget}} \implies \text{forget} $$ **Advantage:** STM uses exponential decay + use count. LRU only tracks recency. ### vs. Linear Decay **Linear decay:** $$ \text{score} = \max\left(0, 1.0 - \frac{t_{\text{age}}}{t_{\text{max}}}\right) $$ **STM exponential decay:** $$ \text{score} = e^{-\lambda \cdot \Delta t} $$ **Advantage:** Exponential matches Ebbinghaus curve (human forgetting). ## Future Enhancements ### Adaptive Decay Adjust $\lambda$ based on memory category: $$ \lambda_{\text{effective}} = \begin{cases} 0.5 \cdot \lambda_{\text{base}} & \text{if category = "credentials"} \\ 2.0 \cdot \lambda_{\text{base}} & \text{if category = "ephemeral"} \\ \lambda_{\text{base}} & \text{otherwise} \end{cases} $$ ### Spaced Repetition For stable memories with high use counts: $$ t_{\text{next review}} = t_{\text{now}} + \Delta t_{\text{interval}} $$ Where $\Delta t_{\text{interval}}$ increases with each successful review. ### Context-Aware Strength Boost strength based on conversation context: $$ s = \begin{cases} 2.0 & \text{if is security critical(content)} \\ 1.5 & \text{if is decision(content)} \\ 1.3 & \text{if is preference(content)} \\ 1.0 & \text{otherwise} \end{cases} $$ --- **Note:** The combination of exponential decay, sub-linear use count, and strength modulation creates memory dynamics that closely mimic human cognition. These parameters can be tuned for different use cases and workflows.

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mnemexai/mnemex'

If you have feedback or need assistance with the MCP directory API, please join our Discord server