Scoring
iLAB Memory rankea observations con dos esquemas de scoring distintos: SearchScore para mem_search, ContextScore para los memories[] que devuelve mem_session_start. Comparten helpers pero combinan signals con pesos distintos — y sus valores no son comparables.
Nunca compares un valor de SearchScore con uno de ContextScore. Diferentes fórmulas, diferentes escalas, diferente significado semántico. El único invariante que comparten es el rango [0.0, 1.0]. Esto es Braess #5 — ver Architecture.
Los dos esquemas
SearchScore
Usado por mem_search. Combina la relevancia BM25 a tu query, recency y revision count. Más alto = más relevante a la query que hiciste.
ContextScore
Usado por mem_session_start.memories. Combina recency, revision count y una prioridad por type. Más alto = más saliente como contexto default a cargar.
SearchScore — relevancia a una query
SearchScore = 0.60 * fts5_rank
+ 0.25 * recency_score(updated_at)
+ 0.15 * revision_score(revision_count)
| Signal | Peso | Qué mide |
|---|---|---|
fts5_rank | 0.60 | BM25 de SQLite FTS5, pre-normalizado a [0, 1] |
recency_score | 0.25 | decay exponencial, half-life de 30 días |
revision_score | 0.15 | lineal en revision_count, capeado en 10 |
El signal dominante es el match de la query. Recency es desempate secundario — una observation de 6 meses que clava la query igual le gana a una flamante que no matchea.
ContextScore — saliencia como contexto default
ContextScore = 0.50 * recency_score(updated_at)
+ 0.30 * revision_score(revision_count)
+ 0.20 * type_priority(type)
| Signal | Peso | Qué mide |
|---|---|---|
recency_score | 0.50 | mismo decay exponencial, half-life de 30 días |
revision_score | 0.30 | lineal en revision_count, capeado en 10 |
type_priority | 0.20 | peso por type (ver abajo) |
Acá no hay query — el objetivo es "si tengo que cargar N memorias al arrancar la sesión, ¿cuáles N importan más para este usuario ahora?" Recency manda; type priority es un dedo en la balanza para los hechos estables del usuario.