How ai-memory's Decay/Salience Model Works and What Memory Feedback Controls
ai-memory uses an exponential decay formula with tunable parameters to score page retention, where memory feedback directly adjusts salience to speed up or slow down memory loss.
The akitaonrails/ai-memory Rust crate implements a deterministic forgetting system for AI conversation memory. Every stored page receives a computed retention score that determines whether it survives periodic cleanup sweeps. The only way operators override automatic decay is through memory feedback — explicit signals like Helpful, NotHelpful, Stale, or Wrong that nudge a page's salience multiplier.
Decay/Salience Model: How Retention Scores Are Calculated
The core algorithm lives in crates/ai-memory-store/src/decay.rs. The retention_score function combines time decay, access patterns, and explicit salience into a single scalar:
pub fn retention_score(
params: &DecayParams,
age_days: f64,
access_count: u32,
days_since_access: Option<f64>,
salience: Option<f64>,
) -> f64
The DecayParams Tunables
The model behavior is controlled through DecayParams, defined in the same file and exposed to users via TOML configuration:
| Parameter | Controls |
|---|---|
lambda |
Per-day exponential decay on age_days (time since last content update) |
sigma |
Magnitude of the access-reinforcement boost |
mu |
Decay rate for access recency — recent hits matter more |
salience_default |
Fallback salience when no explicit feedback exists |
cold_threshold |
Score floor below which pages become eviction candidates |
hard_delete_after_days |
Tombstone lifetime before permanent removal |
The Retention Score Formula
The implementation breaks the score into two multiplicative terms:
- Time term:
salience * exp(-lambda * age_days)— base decay modified by salience - Access term:
sigma * ln(1 + access_count) * exp(-mu * days_since_access)— logarithmic growth with count, penalized by staleness
A retention_score_with_breadth variant also accepts distinct_actors and breadth_weight to reward collaboratively-accessed pages.
Breadth-Aware Scoring
For team or multi-agent deployments, breadth weighting prevents single-user spam from artificially inflating scores. The distinct_actors count captures how many unique operators touched the page.
What Memory Feedback Tunes
Memory feedback is the operator override mechanism for automatic decay. When a retrieved page is marked with feedback, salience_after_feedback in decay.rs recomputes the salience multiplier:
pub fn salience_after_feedback(
params: &DecayParams,
current: Option<f64>,
kind: ai_memory_core::FeedbackKind,
) -> f64
FeedbackKind Variants and Effects
Source: crates/ai-memory-core/src/page.rs
| Feedback | Salience Change | Behavioral Effect |
|---|---|---|
Helpful |
+ SALIENCE_STEP |
Slows decay, extends retention |
NotHelpful |
- SALIENCE_STEP |
Accelerates decay |
Stale |
Reset to SALIENCE_MIN (0.25) |
Near-immediate eviction candidacy |
Wrong |
Reset to SALIENCE_MIN (0.25) |
Same as Stale, routes to memory_lint report |
Salience clamps between SALIENCE_MIN (0.25) and SALIENCE_MAX (2.0). Since the retention score multiplies the time term by salience, feedback creates multiplicative leverage on decay curves.
Feedback Does Not Delete Directly
Per crates/ai-memory-store/src/ops.rs, Stal and Wrong feedback only:
- Reset salience to minimum
- Generate lint entries for operator review
Actual deletion requires the periodic sweep to compute a sub-threshold retention score and invoke soft_delete_for_decay_if_latest.
Practical Code Examples
Computing Retention Manually
use ai_memory_store::decay::{self, DecayParams};
let params = DecayParams::default(); // λ=0.02, σ=0.6, μ=0.04, etc.
let age_days = 120.0; // 4 months since update
let access_cnt = 30;
let days_since_access = Some(3.0); // actively used 3 days ago
let salience = Some(1.5); // previously rated Helpful
let score = decay::retention_score(
¶ms, age_days, access_cnt, days_since_access, salience
);
// Score determines eviction candidacy vs. cold_threshold
Applying Feedback to Adjust Salience
use ai_memory_core::FeedbackKind;
use ai_memory_store::decay::{self, DecayParams};
let params = DecayParams::default();
let current = Some(1.0);
let new_salience = decay::salience_after_feedback(
¶ms, current, FeedbackKind::Helpful
);
// new_salience = 1.0 + 0.25 = 1.25 (clamped if exceeding SALIENCE_MAX)
Store-Level Workflow
// Simplified from crates/ai-memory-store/src/ops.rs patterns
let feedback = FeedbackKind::NotHelpful;
let updated = decay::salience_after_feedback(
&store.decay_params(),
page.salience,
feedback
);
store.update_page_salience(page.id, updated);
// Next sweep recomputes retention with new salience
Key Source Files Reference
| File | Responsibility |
|---|---|
crates/ai-memory-store/src/decay.rs |
DecayParams, retention_score, salience_after_feedback, constants |
crates/ai-memory-core/src/page.rs |
FeedbackKind enum, routing to lint reports |
crates/ai-memory-store/src/ops.rs |
Store operations: feedback application, soft deletion logic |
crates/ai-memory-mcp/src/server.rs |
TOML configuration exposure for decay parameters |
Summary
- Decay/salience model combines exponential time decay with logarithmic access reinforcement, tunable via six
DecayParams. - Retention score is deterministic and recomputed per sweep; pages below
cold_thresholdbecome eviction candidates. - Memory feedback is the sole operator control mechanism, adjusting salience multipliers to accelerate (
NotHelpful), slow (Helpful), or reset (Stale/Wrong) decay. - Feedback never deletes immediately — it nudges the curve and optionally routes to
memory_lintfor human review. - Salience bounds (0.25–2.0) create bounded but meaningful leverage over retention duration.
Frequently Asked Questions
What happens if I never provide memory feedback?
Pages use salience_default (typically 1.0) and rely purely on access patterns and age decay. Highly-accessed content survives longer; neglected content expires automatically.
How quickly does NotHelpful feedback erase a page?
It depends on current salience and other parameters. Each NotHelpful reduces salience by SALIENCE_STEP (0.25). At default settings, two consecutive negative ratings drop salience from 1.0 to 0.5, halving the time term's contribution and likely pushing the page below cold_threshold within the next sweep cycle.
Why don't Stale and Wrong delete immediately?
The design preserves auditability. These signals reset salience and flag pages in memory_lint, allowing operators to review what the system considered worth forgetting. Permanent deletion only occurs after hard_delete_after_days of tombstone status.
Can I disable decay entirely?
Not directly. You could set lambda = 0.0, mu = 0.0, and cold_threshold = -inf, but this is not the intended use case. The model assumes bounded memory requires bounded retention; feedback exists precisely to let operators influence what stays relevant.
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 →