Semantic Ranking
Recall's ranker fuses seven factors into a single hybrid score, then applies a small set of additive boosts. Every weight, half-life, and tiebreak in this page comes from the recall engine source.
The default formula
hybrid_score = 0.40 * semantic_norm
+ 0.20 * cluster_norm
+ 0.15 * recency_norm
+ 0.15 * importance_norm
+ 0.10 * usage_normWeights are normalised to sum to 1 even if a caller overrides them. Quality, decay, and graph factors are also computed, but their default weights are 0 — they are kept available for explainability and for callers who want to opt them in.
Semantic factor (weight 0.40)
Cosine similarity from the vector search, mapped to 1 - clamp(distance, 0, 1). This is the only factor that takes the query string into account directly.
Recency factor (weight 0.15) — half-life by intent
| Detected query intent | Half-life |
|---|---|
HIGH (recency-sensitive) | 24 hours |
MEDIUM (default) | 72 hours |
LOW (background knowledge) | 168 hours (7 days) |
Score formula: recency_score = exp(-hours_since_created * ln(2) / half_life). Older memories decay logarithmically rather than dropping off a cliff.
Importance factor (weight 0.15)
The importance column is in [0, 1] and feeds in directly. It defaults to 0.5 when the caller did not provide one. The ranker does not use the platform-computed importance_score here — that field drives the cost gate, not recall.
Usage factor (weight 0.10) — log-scale
usage_score = min(1.0, log(1 + usage_count) / 5.0). The log keeps a memory recalled a thousand times from drowning out a memory recalled five times.
Cluster factor (weight 0.20)
When the cluster weight is non-zero, each memory's score includes the cosine similarity between the query vector and the centroid of the memory's cluster. Mapped from [-1, 1] to [0, 1] for normalisation parity. Centroids are bulk-loaded in a single round trip per query batch.
Per-batch normalisation
Every factor is min-max normalised using only the memories in the current result batch — not global stats. Two consequences worth remembering:
- Adding a high-scoring outlier to the batch suppresses everyone else proportionally.
- The same memory can score differently between batches because the normalisation reference set has changed. This is intentional.
The variance floor tiebreak (σ < 0.02)
If the per-batch standard deviation of hybrid_score falls below 0.02, the ranking is essentially flat. The engine then applies a deterministic tiebreak chain: (semantic_score desc, importance desc, recency desc, id desc). The response carries tiebreak_applied=true so callers can tell when this fired.
Additive boosts on top of the hybrid score
| Boost | Magnitude | When it fires |
|---|---|---|
supports edge | +0.08 | Multi-hop traversal lands on this memory through a supports edge. |
extends edge | +0.07 | Reached via an extends edge. |
continuation | +0.06 | Reached via a continuation edge. |
detail_of | +0.05 | Detail-of edge. |
summary_of | +0.04 | Summary-of edge. |
similar / references | +0.03 each | Similar / references edges. |
derived_from / caused_by | +0.02 each | Derivation or causal edges. |
contradiction | +0.01 | A contradicting memory is surfaced for explainability. |
| Personalisation | up to +0.10 (capped) | Memory matches an entity or topic the user has shown affinity for. |
| Session continuity | +0.03 | The memory was returned earlier in the same session_id. |
Adaptive weights when the caller skips them
If the request omits weights, the engine inspects the query features and picks an intent-aware weight profile — factual queries get more semantic, recent-news queries get more recency, analytical queries get more graph. This is a soft tweak; the floor is always the default profile above.