Ranking & Scoring

How Mnemosyne ranks and scores retrieval results across different search modes.

Scoring Pipeline


flowchart LR
  Q[Query] -->|embed| V[Vector Score]
  Q -->|tokenize| T[FTS5 Score]
  M[Memory] -->|field| I[Importance]
  V -->|0.5| F[Weighted Sum]
  T -->|0.3| F
  I -->|0.2| F
  F -->|recency| R[Recency Multiplier]
  R -->|sort| S[Ranked Results]

Individual Scorers

Vector Score

Cosine similarity between query embedding and memory embedding:

vector_score = dot(query_vec, mem_vec) / (norm(query_vec) * norm(mem_vec))

Range: 0.0 (unrelated) to 1.0 (identical)

FTS5 Score

BM25 relevance score normalized to 0–1:

fts_score = bm25_score / max_bm25_score_in_results

Importance Score

Each memory has an importance field (set at ingestion time, range 0.0–1.0). This is used directly as the importance component in scoring.

Rank Fusion

The three components are combined with fixed weights (not configurable per-query):

# Fixed-weight fusion for episodic memory
base_score = (vector_score * 0.5) + (fts_score * 0.3) + (importance * 0.2)

This weighting balances semantic similarity (50%), exact term matching (30%), and user-specified importance (20%).

Recency Decay

After fusion, a recency multiplier is applied to boost recently created memories:

recency_decay = exp(-age_hours / half_life_hours)
final_score = base_score * (0.7 + 0.3 * recency_decay)

The formula ensures:

  • Fresh memories (age ≈ 0): recency_decay ≈ 1.0 → score multiplied by ~1.0
  • Old memories (age >> half_life): recency_decay ≈ 0.0 → score multiplied by ~0.7

This gives a maximum 30% boost for recent content while preserving 70% of the score for older but relevant memories.

Result Deduplication

When the same memory appears in multiple result sets, Mnemosyne deduplicates and keeps the highest score:

# Deduplication strategy
seen_ids = set()
for result in all_results:
  if result.id not in seen_ids:
      seen_ids.add(result.id)
      yield result
Fixed Weights

The scoring weights (0.5 / 0.3 / 0.2) are fixed and not configurable through the recall() API. recall() accepts only query and top_k parameters.