How ai-memory Implements Decay Math and Salience-Scaled Retention for Forget-Sweep

ai-memory calculates retention scores using exponential decay formulas that combine page age, access frequency, operator feedback, and breadth of readership to determine which episodic pages survive the periodic forget-sweep.

The akitaonrails/ai-memory repository implements a deterministic, mathematically-grounded approach to knowledge eviction in its long-term memory store. The algorithm lives primarily in crates/ai-memory-store/src/decay.rs, where pure functions compute retention scores without hidden state—making the sweep fully testable and predictable.

Core Decay Parameters in DecayParams

The DecayParams struct defines six coefficients that shape the retention curve. These defaults are tuned for roughly 35-day half-life on aging content:

Field Default Purpose
lambda 0.02 Exponential decay per day for page age
sigma 0.6 Magnitude of access-reinforcement term
mu 0.04 Decay per day for days-since-last-access
salience_default 1.0 Fallback when no operator feedback exists
cold_threshold 0.5 Score floor for eviction candidacy
hard_delete_after_days 30 Tombstone lifetime before permanent removal

The Default implementation for DecayParams lives at lines 15-45 of crates/ai-memory-store/src/decay.rs. All parameters are pub and can be overridden at store initialization or via the MCP server admin API.

The Retention Score Formula

Basic Score: retention_score()

The entry point retention_score() (lines 48-75) accepts age, access count, days since access, and optional salience. It delegates to retention_score_with_breadth() with breadth_weight: 0.0, preserving backward compatibility with the historical formula.

Extended Score: retention_score_with_breadth()

Lines 94-119 contain the full computation. The final score sums two terms:

Time term: salience · exp(-λ · age_days)

Access term: σ · ln(1 + access_count) · exp(-μ · days_since_access) · breadth

The breadth factor rewards pages read by distinct operators: 1 + breadth_weight · ln(1 + distinct_actors - 1). This prevents "siloed" pages—those accessed many times by one operator—from gaming the access term.

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

let params = DecayParams::default();
let score = retention_score(
    &params,
    120.0,           // age_days: 4 months old
    35,              // access_count
    Some(3.0),       // days_since_access: recent hit
    None,            // no explicit salience
);
// Score combines: (1.0 · e^(-2.4)) + (0.6 · ln(36) · e^(-0.12))

Salience Scaling via Operator Feedback

The salience_after_feedback() function (lines 40-55) adjusts a page's retention multiplier based on operator judgments. The implementation uses hard bounds and discrete steps:

  • SALIENCE_MIN: 0.25 (floor for Stale/Wrong feedback)
  • SALIENCE_MAX: 2.0 (ceiling)
  • SALIENCE_STEP: 0.25 (increment/decrement for Helpful/NotHelpful)

Stale or Wrong feedback immediately drops salience to 0.25. Helpful increments by 0.25; NotHelpful decrements by 0.25.

use ai_memory_store::decay::{DecayParams, salience_after_feedback};
use ai_memory_core::FeedbackKind;

let params = DecayParams::default();
let boosted = salience_after_feedback(&params, None, FeedbackKind::Helpful);
// Returns 1.25: default 1.0 + SALIENCE_STEP

let penalized = salience_after_feedback(&params, Some(1.5), FeedbackKind::Wrong);
// Returns 0.25: immediate floor

The feedback system creates a closed-loop decay model: operator engagement directly modulates the mathematical survival curve.

The Four-Stage Forget-Sweep Flow

Stage 1: Candidate Selection

Reader::decay_candidates() (lines 3183-3218 in reader.rs) queries the pages table and filters:

  • Computed retention score below cold_threshold
  • pinned = false (pinned pages are decay-immune)
  • Not already superseded

The query applies the retention_score formula in SQL or loads rows for Rust-side scoring depending on index availability.

Stage 2: Soft Delete

For each candidate, Ops::soft_delete_for_decay_if_latest (lines 1655-1720 in ops.rs) marks:

  • superseded_at = NOW() on the page record
  • Creates a decay tombstone entry tracking the deletion reason

Soft deletion preserves version history and allows potential resurrection if the page is re-imported.

Stage 3: Hard Delete

After hard_delete_after_days elapse, Reader::decay_tombstones_before() lists expired tombstones. Ops::hard_delete_decayed_page_chain (lines 1800-1885) recursively removes:

  • The tombstone record
  • All version ancestry from the database
  • Associated wiki files on disk

Stage 4: Pinning Protection

Pages can be manually pinned via CLI or API. The pinned boolean is checked in every sweep query, providing a simple override for critical knowledge.

use ai_memory_store::Store;

let store = Store::open("data/")?;
store.run_forget_sweep(&DecayParams::default())?;
// Executes full pipeline: candidates → soft delete → hard delete cleanup

Orchestration by the Consolidator

The sweep is not triggered automatically by the store. Instead, crates/ai-memory-consolidate/src/sweep.rs implements a periodic job that:

  1. Fetches decay_candidates() from the store
  2. Optionally applies a configured decay_breadth_weight (via ServerBuilder::with_decay_breadth_weight)
  3. Invokes the soft/hard delete operations
  4. Logs metrics on pages evicted vs. retained

This separation of concerns allows the store to remain stateless while the consolidator handles scheduling and observability.

Pure-Functional Design Benefits

The decay implementation avoids hidden state:

  • retention_score_with_breadth takes only parameters—it does not query the database
  • salience_after_feedback computes deterministically from inputs
  • Test suites exercise edge cases: ancient pages (large age_days), recent access spikes, boundary salience values, and breadth-weight variations

This design ensures that identical inputs always produce identical scores, making the forget-sweep behavior reproducible across environments.

Summary

  • DecayParams configures six coefficients (λ, σ, μ, thresholds) governing the retention curve
  • retention_score_with_breadth computes survival probability from age, access history, recency, salience, and operator breadth
  • salience_after_feedback implements bounded, stepped salience adjustments based on operator judgments
  • Four-stage sweep: candidate selection → soft delete → tombstone aging → hard delete
  • Pinning provides manual override for decay immunity
  • Pure functions make the algorithm testable and deterministic

Frequently Asked Questions

How does ai-memory balance age decay against access frequency?

The retention formula uses a sum of two exponential terms. Age-driven decay (exp(-λ · age_days)) continuously erodes the salience-weighted base score, while access history (ln(1 + access_count)) provides multiplicative reinforcement that itself undergoes recency decay (exp(-μ · days_since_access)). A page with massive historical access but no recent hits still decays—the access term's half-life is roughly 17 days with default μ=0.04.

What happens when multiple operators access the same page?

The breadth factor in retention_score_with_breadth rewards distribution across distinct actors. With positive breadth_weight, a page read once each by five operators scores higher than a page read five times by one operator—even with identical total access counts. This prevents "echo chamber" retention where single-user obsessive access preserves niche content.

Can the decay parameters be changed at runtime?

Yes. The MCP server in crates/ai-memory-mcp/src/server.rs exposes DecayParams through its admin API. ServerBuilder::with_decay_breadth_weight specifically allows tuning the breadth multiplier without code changes. However, parameter changes only affect future sweep calculations—already-computed scores in running systems are not retroactively adjusted.

Why does ai-memory use soft deletion before hard deletion?

The two-phase eviction supports operational safety and potential recovery. Soft deletion (setting superseded_at) immediately removes the page from search results while preserving data. If the page is re-ingested from an external source, the system can detect version ancestry. The hard_delete_after_days delay (default 30 days) provides a window for administrative intervention before permanent removal.

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 →