How ai-memory Calculates Memory Decay: Retention Scores, Decay Parameters & Eviction Logic
ai-memory calculates memory decay using a tunable retention score that combines exponential time decay, access reinforcement, user feedback salience, and optional breadth multipliers to determine when pages should be evicted.
The akitaonrails/ai-memory repository implements a mathematically-driven decay system in Rust. Rather than simple least-recently-used (LRU) eviction, it uses a sophisticated scoring function that weighs page age, access patterns, explicit user feedback, and even how many distinct actors have interacted with content. This article breaks down exactly how the formulas work and where to find them in the source.
Decay Parameters: The Tunable Constants
All decay behavior is controlled by the DecayParams struct in crates/ai-memory-store/src/decay.rs (lines 15-33). These six fields shape every retention decision:
| Field | Default | Purpose |
|---|---|---|
lambda |
0.02 |
Per-day exponential decay rate for the time-since-update term (~35 day half-life) |
sigma |
0.6 |
Weight multiplier for the access-reinforcement term |
mu |
0.04 |
Per-day exponential decay rate for days-since-last-access |
salience_default |
1.0 |
Baseline salience when no explicit feedback exists |
cold_threshold |
0.1 |
Score boundary below which pages become eviction candidates |
hard_delete_after_days |
90 |
Tombstone lifetime before permanent removal |
The default implementation (lines 35-44) provides these values, but operators can override any parameter for different decay curves.
Retention Score Formula: The Core Decay Calculation
The retention_score function (lines 48-75) is the entry point for calculating page value. It delegates to retention_score_with_breadth with breadth disabled, preserving backward compatibility with the original formula.
Base Inputs
age_days— days elapsed sinceupdated_ataccess_count— cumulative search hits against the pagedays_since_access— optional days since most recent readsalience— optional explicit salience from user feedback
Breadth-Aware Extension
When enabled, retention_score_with_breadth (lines 77-101) adds a "distinct actors" multiplier:
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
Three components drive the final score:
- Time term (
salience * e^(-lambda * age_days)) — unconditional exponential decay scaled by salience; pages lose value over time regardless of access - Access term — logarithmic reward for total accesses (
ln(access_count + 1)), multiplied by exponential decay of recency (e^(-mu * days_since_access)) and breadth - Breadth factor (
1 + breadth_weight * ln(distinct_actors)) — pages touched by many different operators decay more slowly; setbreadth_weight = 0.0to disable
If the summed score drops below cold_threshold, the page qualifies for soft deletion.
Feedback-Driven Salience Adjustment
User feedback directly modifies how quickly pages decay. The salience_after_feedback function (lines 41-55) maps FeedbackKind to salience shifts:
| Feedback | Effect | Salience Range |
|---|---|---|
Helpful |
Increase by 0.25 | Clamped to SALIENCE_MAX = 2.0 |
NotHelpful |
Decrease by 0.25 | Clamped to SALIENCE_MIN = 0.25 |
Stale / Wrong |
Drop to minimum | Set to 0.25 immediately |
The adjusted salience feeds directly into the time_term (and indirectly access_term via breadth), creating a feedback loop where valued content persists longer and rejected content accelerates toward eviction.
The Forget-Sweep Eviction Process
Decay calculation triggers actual eviction through a coordinated three-phase sweep implemented across ops.rs and wiki.rs:
- Candidate identification —
reader.decay_candidatesqueries pages likely below threshold based on stored metadata - Score recomputation — fresh access statistics re-evaluate each candidate's current retention score
- Soft deletion — pages scoring below
cold_thresholdbecome tombstones viasoft_delete_for_decay_if_latest - Hard deletion — tombstones exceeding
hard_delete_after_daysare permanently purged viahard_delete_decayed_page_chain
This two-stage deletion (soft then hard) allows for potential recovery mechanisms or audit trails before permanent data loss.
Complete Working Example
use ai_memory_store::decay::{DecayParams, retention_score, salience_after_feedback};
use ai_memory_core::FeedbackKind;
// Default decay configuration
let params = DecayParams::default();
// Page: 30 days old, 10 total accesses, last read 2 days ago
let score = retention_score(¶ms, 30.0, 10, Some(2.0), None);
println!("Retention score: {:.4}", score); // Example: ~0.42
// User marks as helpful: salience increases from 1.0 → 1.25
let boosted_salience = salience_after_feedback(¶ms, None, FeedbackKind::Helpful);
let boosted_score = retention_score(¶ms, 30.0, 10, Some(2.0), Some(boosted_salience));
println!("After helpful feedback: {:.4}", boosted_score); // Higher persistence
Summary
- ai-memory decay uses exponential formulas, not simple timestamps, to model memory degradation
DecayParamsindecay.rsprovides six tunable coefficients controlling curve shape- Retention score combines time decay, access reinforcement logarithms, and optional breadth multipliers
- Feedback signals directly modulate salience, creating user-influenced memory persistence
- Two-phase eviction soft-deletes candidates below threshold, then hard-deletes aged tombstones
Frequently Asked Questions
How does ai-memory memory decay differ from standard LRU cache eviction?
ai-memory replaces fixed-size LRU with a scored decay model where retention depends on mathematical functions of age, access patterns, and explicit feedback rather than recency alone. The retention_score_with_breadth formula allows configuration of half-lives (lambda, mu) and user-driven salience shifts that no LRU can express.
What happens when breadth_weight is set to zero?
Setting breadth_weight = 0.0 disables the distinct-actors multiplier, collapsing retention_score_with_breadth to the original retention_score behavior. The breadth factor becomes exactly 1.0, and decay depends solely on time, access count, recency, and salience.
Can retention scores increase over time, or only decrease?
Scores can increase. A surge in access_count, a recent access resetting days_since_access, positive FeedbackKind::Helpful signals raising salience, or new distinct actors (with breadth enabled) all boost the computed score. The formulas are dynamic evaluations, not monotonic counters.
Where is the cold_threshold actually enforced during eviction?
The threshold comparison happens during the forget-sweep in crates/ai-memory-store/src/ops.rs, where recomputed scores are checked before soft_delete_for_decay_if_latest tombstones the page. The sweep runner in crates/ai-memory-wiki/src/wiki.rs orchestrates this periodic job.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →