Core concepts
Retrieval & ranking
Four signals decide what an agent recalls. This page covers what each one measures, how they are combined, and how to change the balance when the results are not what you want.
The four signals
| Signal | Measures | Source |
|---|---|---|
| Keyword | Term overlap, length-normalised | BM25 over an inverted index |
| Vector | Semantic closeness | The index metric: cosine, L2, or inner product |
| Recency | How fresh the record is | Exponential decay on a configurable half-life |
| Importance | How much the record matters | The value you set per record, 0.0 to 1.0 |
Fusion
Keyword and vector scores live on different scales: BM25 is unbounded, cosine is not. Klyro rescales both onto a common range before combining them, then adds the recency and importance terms.
FUSION RRF switches to reciprocal rank fusion, which ranks by position rather than by score. It is steadier when one of the two rankings scored nearly everything the same, which happens with short queries or a narrow corpus.
Recency decay
Recency halves every HALFLIFE seconds, defaulting to seven days. A record written moments ago scores 1.0 on this signal; one written a half-life ago scores 0.5; one from four half-lives back scores about 0.06.
When an agent keeps recalling something stale, raise the recency weight before rewriting the prompt. When it keeps missing exact names or error codes, raise the keyword weight. Both are one command, and WITHSCORES tells you which one to reach for.
Reading a score breakdown
WITHSCORES returns the fused score together with the components that produced it, so a surprising result is a data question rather than a guess.
The three query commands
| Command | Uses | When to use it |
|---|---|---|
| MEM.SEARCH | Keyword only | Exact terms matter and you have no query vector |
| MEM.VSEARCH | Vector only | Pure similarity, e.g. deduplicating near-identical memories |
| MEM.QUERY | Either or both, fused | The default: pass what you have and let the index decide |
MEM.QUERY given only TEXT runs a keyword search, given only a vector runs a semantic one, and given both fuses them. That is why application code can call one command whether or not an embedding was available for a given turn.
Return flags
| Flag | Effect |
|---|---|
| NOTEXT | Omit the record text, when you only need ids and scores |
| WITHMETA | Include the metadata pairs with each hit |
| WITHVEC | Include the stored vector |
| WITHSCORES | Include the fused score and its components |
Metrics
The metric is fixed at creation. Cosine is the default and the right choice for most embedding models; vectors are normalised at insert so the comparison is a dot product at query time.
| METRIC | Comparison | Notes |
|---|---|---|
| COSINE | Angle between vectors | Default; magnitudes are normalised away |
| L2 | Euclidean distance | Smaller is closer; converted so higher scores rank first |
| IP | Inner product | For models trained with an unnormalised objective |
Scoring visits every candidate that survives the filters, bounded by mem-max-scan. That is exact rather than approximate, and it suits indexes of thousands to low tens of thousands of records. Filter hard on large indexes, and see limitations for the ceiling.
