# How ai-memory's Decay/Salience Model Works and What Memory Feedback Controls

> Discover how ai-memory's decay salience model uses exponential decay and tunable parameters to manage memory retention. Learn how memory feedback controls salience and memory loss speed.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: deep-dive
- Published: 2026-08-20

---

**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`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs). The `retention_score` function combines **time decay**, **access patterns**, and **explicit salience** into a single scalar:

```rust
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:

1. **Time term**: `salience * exp(-lambda * age_days)` — base decay modified by salience
2. **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`](https://github.com/akitaonrails/ai-memory/blob/main/decay.rs) recomputes the salience multiplier:

```rust
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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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

```rust
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(
    &params, age_days, access_cnt, days_since_access, salience
);
// Score determines eviction candidacy vs. cold_threshold

```

### Applying Feedback to Adjust Salience

```rust
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(
    &params, current, FeedbackKind::Helpful
);
// new_salience = 1.0 + 0.25 = 1.25 (clamped if exceeding SALIENCE_MAX)

```

### Store-Level Workflow

```rust
// 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`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs) | `DecayParams`, `retention_score`, `salience_after_feedback`, constants |
| [`crates/ai-memory-core/src/page.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/page.rs) | `FeedbackKind` enum, routing to lint reports |
| [`crates/ai-memory-store/src/ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs) | Store operations: feedback application, soft deletion logic |
| [`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/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_threshold` become 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_lint` for 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.