#630·memU

Feature: Temporal decay for retrieval scoring

Author: skylabbyCreated Aug 7, 2026Updated Aug 11, 2026

title: "Feature: Temporal decay for retrieval scoring" labels: ["enhancement", "memory"]

Problem

memU's retrieval treats all segments equally regardless of age. A fact from 6 months ago with a 0.82 similarity score ranks above a fact from yesterday with 0.81 — even when the older fact has been superseded.

Real example from production use (Hermes Agent, 235 segments, 7 weeks of daily use):

When asking "SSH password for homelab servers," memU returns segments about SSH credentials. All are from the same import date (2026-07-31) so they have equal temporal weight. But in practice, passwords change, hosts get decommissioned, and preferences evolve. Without temporal signal, stale information competes on equal footing with current information.

After 6 months of use: The retrieval store will contain facts about hosts that no longer exist, passwords that have been rotated, and preferences the user has since changed. Every one of those stale facts will still get high similarity scores for relevant queries, crowding out newer, correct information.

Proposed solution

Add an optional recency_bias parameter to the retrieval query that applies exponential decay to similarity scores based on segment age:

adjusted_score = similarity_score × 0.5^(age_days / half_life_days)
  • Default half_life_days = 0 (disabled — no behavior change for existing users)
  • Configurable per-retrieval or globally via MEMU_RECENCY_HALF_LIFE env var
  • Applies at query time in the vector store layer (not as post-processing), so it affects which segments make it into the top-N, not just their final ranking

A prototype wrapper (post-processing only, with the limitations that implies) confirmed the concept works: with a 60-day half-life, 7-day-old segments receive a 0.922× multiplier while the ranking stays stable. At 180 days, the same segments would drop to 0.77×.

Implementation notes

  • Where: MemoryService.progressive_retrieve() or the underlying vector store query
  • Cost: One multiplication per candidate segment — negligible
  • Backward compatibility: Disabled by default (half-life = 0)
  • Config surface: MEMU_RECENCY_HALF_LIFE in config.env, or --half-life N on memu retrieve
  • Why query-time, not index-time: The decay curve should be tunable per query context. A query about "what happened in February" wants less decay than "what's the current setup."

Success criteria

After enabling with a 90-day half-life:

  • Segments older than 90 days are weighted at ≤50% of their raw similarity score
  • Segments from the last week are weighted at ≥95%
  • Queries about current state ("what's my SSH setup") surface recent facts first
  • Queries about historical state ("what was the old firewall config") still work (the query semantics naturally match older segments)
  • No measurable latency increase