How Memory Decay Is Calculated in ai-memory: A Deep Dive into the Retention Score Algorithm

ai-memory calculates memory decay using a mathematically-driven retention score that combines exponential time decay, access frequency reinforcement, breadth-aware multipliers, and feedback-adjusted salience to determine when pages should be evicted from memory.

The akitaonrails/ai-memory project implements a sophisticated decay system for managing long-term memory in AI systems. Unlike simple LRU caches, it uses tunable mathematical formulas that balance recency, usage patterns, collaborative breadth, and explicit user feedback. This article breaks down exactly how the decay calculation works, referencing the actual Rust source code in crates/ai-memory-store/src/decay.rs.

Core Decay Parameters (DecayParams)

The foundation of memory decay calculation in ai-memory is the DecayParams struct, defined at lines 15-33 of decay.rs. This configuration object controls the shape of every decay curve across the system.

Field Purpose Default Value
lambda Per-day exponential decay rate for time-since-update 0.02 (~35-day half-life)
sigma Weight multiplier for access reinforcement 0.6
mu Per-day exponential decay rate for days-since-last-access 0.04
salience_default Baseline salience without feedback 1.0
cold_threshold Score cutoff for eviction candidacy 0.3
hard_delete_after_days Tombstone lifetime before permanent removal 90

These defaults are established in the Default implementation at lines 35-44. You can instantiate custom parameters to make memory decay faster or slower based on your use case.

use ai_memory_store::decay::DecayParams;

// Default conservative decay
let params = DecayParams::default();

// Aggressive decay: 10-day half-life, lower threshold
let aggressive = DecayParams {
    lambda: 0.07,        // ~10 day half-life
    cold_threshold: 0.5, // Evict sooner
    ..Default::default()
};

The Retention Score Formula

The primary function retention_score (lines 48-75) computes whether a memory page survives another day. It accepts four inputs and delegates to the breadth-aware variant with breadth disabled.

Input Parameters

  • age_days: Days since the page was last updated
  • access_count: Total historical searches that returned this page
  • days_since_access: Optional days since most recent access
  • salience: Optional explicit salience from user feedback

The function signature in crates/ai-memory-store/src/decay.rs:

pub fn retention_score(
    params: &DecayParams,
    age_days: f64,
    access_count: u32,
    days_since_access: Option<f64>,
    salience: Option<f64>,
) -> f64

At lines 66-73, this function simply calls retention_score_with_breadth with distinct_actors = 0 and breadth_weight = 0.0, preserving backward compatibility with the original formula.

Breadth-Aware Decay Extension

The full calculation lives in retention_score_with_breadth (lines 77-101). This extension adds a "distinct actors" factor so that pages read by many different operators decay more slowly—a key insight for collaborative knowledge systems.

The Complete Formula

let time_term = salience * (-params.lambda * age_days).exp();
let breadth = 1.0 + breadth_weight * (f64::from(distinct_actors.max(1)) - 1.0).ln_1p();
let access_term = days_since_access.map_or(0.0, |d| {
    params.sigma * (1.0 + f64::from(access_count)).ln()
        * (-params.mu * d).exp()
        * breadth
});
time_term + access_term

Component Breakdown

Time term: salience × e^(-lambda × age_days)

  • Exponentially decays based on page age
  • Scaled by salience (feedback-adjusted importance)
  • With default lambda = 0.02, halves every ~35 days at base salience

Access term: Complex reinforcement based on usage patterns

  • ln(access_count + 1): Diminishing returns for repeated access
  • e^(-mu × days_since_access): Recent access matters more than old access
  • Multiplied by sigma to control overall weight

Breadth factor: 1 + breadth_weight × ln(distinct_actors)

  • Default breadth_weight = 0.0 disables this feature
  • When enabled, collaborative pages (many distinct readers) resist decay
  • Uses ln_1p for numerical stability at low actor counts

The final score is the sum of time and access terms. Pages scoring below cold_threshold become eviction candidates.

Feedback-Driven Salience Adjustment

User feedback directly modifies how memory decay applies to specific pages. The salience_after_feedback function (lines 41-55) implements this closed-loop control.

Feedback Impact Mapping

Feedback Kind Salience Change Result
Helpful Increase by 0.25 Slower decay, longer retention
NotHelpful Decrease by 0.25 Faster decay, quicker eviction
Stale or Wrong Set to SALIENCE_MIN (0.25) Rapid decay, near-immediate candidate status

The function enforces bounds: SALIENCE_MIN = 0.25 and SALIENCE_MAX = 2.0. This means feedback can at most halve or double the time-term decay rate.

use ai_memory_core::FeedbackKind;
use ai_memory_store::decay::salience_after_feedback;

// Apply positive reinforcement
let boosted = salience_after_feedback(
    &params,
    Some(1.0),                  // Current salience
    FeedbackKind::Helpful
); // Returns 1.25

// Penalize outdated information
let penalized = salience_after_feedback(
    &params,
    Some(1.5),
    FeedbackKind::Stale
); // Returns 0.25 (minimum)

The updated salience feeds directly into subsequent retention_score calculations, creating a feedback loop between user judgments and automatic memory management.

The Forget-Sweep Eviction Process

Decay calculations alone don't remove pages—a periodic forget-sweep job orchestrates actual eviction. This process spans multiple files in the codebase.

Sweep Implementation Flow

  1. Query candidates: reader.decay_candidates filters pages potentially below threshold
  2. Recompute scores: Fresh retention scores using latest access statistics
  3. Soft delete: soft_delete_for_decay_if_latest creates tombstone for confirmed candidates (score < cold_threshold)
  4. Hard delete: hard_delete_decayed_page_chain permanently removes tombstones older than hard_delete_after_days

The sweep logic resides primarily in crates/ai-memory-store/src/ops.rs and is invoked by crates/ai-memory-wiki/src/wiki.rs as a background maintenance task.

Practical Calculation Examples

Example 1: Abandoned Page

use ai_memory_store::decay::{DecayParams, retention_score};

let params = DecayParams::default();

// Page: 60 days old, accessed 3 times, last access 45 days ago
let score = retention_score(&params, 60.0, 3, Some(45.0), None);
// Calculation: 1.0 * e^(-0.02*60) + 0.6 * ln(4) * e^(-0.04*45)
//             ≈ 0.301 + 0.6 * 1.386 * 0.165
//             ≈ 0.301 + 0.137 = 0.438

// Result: Above cold_threshold (0.3), survives this sweep

Example 2: Actively Used Page with Breadth

use ai_memory_store::decay::retention_score_with_breadth;

// Same page, but with 5 distinct readers and breadth enabled
let score = retention_score_with_breadth(
    &params, 60.0, 3, Some(5.0), None, 5, 0.3
);
// Breadth factor: 1 + 0.3 * ln(5) ≈ 1.48
// Access term now multiplied by 1.48, significantly boosting retention

Example 3: Negative Feedback Cascade

let params = DecayParams::default();

// Initial state: healthy page
let mut salience = Some(1.0);
let mut score = retention_score(&params, 30.0, 20, Some(2.0), salience);
// score ≈ 0.55 + 0.52 = 1.07 (well above threshold)

// Receive Wrong feedback
salience = Some(salience_after_feedback(&params, salience, FeedbackKind::Wrong));
// salience = 0.25

// Recalculate
score = retention_score(&params, 30.0, 20, Some(2.0), salience);
// score ≈ 0.138 + 0.13 = 0.268 (below threshold, eviction candidate)

Key Files and Their Roles

File Path Functionality
crates/ai-memory-store/src/decay.rs Core decay mathematics: DecayParams, retention_score, retention_score_with_breadth, salience_after_feedback
crates/ai-memory-store/src/ops.rs Database operations: soft_delete_for_decay_if_latest, hard_delete_decayed_page_chain, decay_candidates query
crates/ai-memory-wiki/src/wiki.rs Orchestrates periodic background sweep using decay logic
crates/ai-memory-core/src/feedback.rs Defines FeedbackKind enum used in salience adjustment

Summary

  • Memory decay in ai-memory is calculated through a tunable retention score combining exponential time decay, logarithmic access reinforcement, and optional breadth multipliers.
  • The DecayParams struct in decay.rs provides six coefficients to customize decay curves without code changes.
  • retention_score and retention_score_with_breadth implement the pure mathematical functions—no side effects, easy to test and reason about.
  • Feedback integration via salience_after_feedback creates a closed loop where user judgments directly modify mathematical decay rates.
  • Eviction is two-phase: soft-delete creates tombstones scored below cold_threshold, then hard-delete removes them after hard_delete_after_days.

Frequently Asked Questions

How does the lambda parameter affect memory decay?

The lambda parameter controls the exponential decay rate for the time-since-update term in the retention score formula. With the default value of 0.02, a page loses half its time-term contribution approximately every 35 days. Increasing lambda to 0.07 shortens this half-life to roughly 10 days, making the system forget older information faster. This parameter directly scales the exponent in e^(-lambda × age_days) as implemented in crates/ai-memory-store/src/decay.rs lines 89-90.

Can memory decay be disabled entirely?

Complete disabling is not supported, but you can approximate it by setting extreme parameter values. Set lambda and mu to 0.0 to eliminate exponential decay, sigma to a large value to maximize access reinforcement, and cold_threshold to near zero. However, this configuration is untested and may cause unbounded memory growth. The system is designed for managed decay—consider adjusting hard_delete_after_days to a large value instead if you need extended retention.

What happens when multiple users access the same page?

When breadth_weight is non-zero in retention_score_with_breadth, pages accessed by more distinct actors receive a multiplicative boost to their access term. The breadth factor 1 + breadth_weight × ln(distinct_actors) means that purely collaborative pages resist decay proportionally to how widely they've been read. This implements the insight that broadly useful institutional knowledge should persist longer than personally relevant but niche information.

How does feedback interact with the decay calculation?

Feedback modifies the salience variable that scales the time term in every retention score calculation. Helpful feedback increases salience by 0.25 (capped at 2.0), causing the page to decay more slowly. NotHelpful decreases it by 0.25 (floored at 0.25). Stale or Wrong feedback immediately drops salience to the minimum of 0.25, often pushing the page below cold_threshold and triggering eviction. This salience adjustment persists across all future decay calculations until new feedback arrives.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →