Kemory · Retrieval
Semantic + lexical · rank fusion

How retrieval
actually works.

Kemory exposes several ways to read memory, and they are not interchangeable. This page explains what happens when you search, which tool to reach for, and how to read a result honestly — including the places where a tool's name suggests more than it does.

One engine underneath

Every read path funnels into the same search. It runs up to three legs in parallel and fuses them by rank.

LegWhat it matchesWhat it is good at
SemanticYour query embedded and compared by cosine similarity against every memory's vectorFinding a memory you can only describe loosely
LexicalYour query's terms as full-text search, ordered by term frequency and rarity. Conservative spelling variants are expanded on the query side — transliteration pairs like vaastu/vastu find each other in both directions, originals ranking firstFinding a memory by an exact identifier — ticket number, error code, config key
Blind indexIdentifier-shaped tokens indexed as keyed hashes, matched by equalityExact identifiers on encrypted accounts, where text matching cannot read the content

The legs are combined with reciprocal rank fusion, then re-ranked on five signals: semantic similarity (the largest weight), recency, how often the memory has been accessed, its proximity in the memory graph, and its measured usefulness.

The blind index deliberately covers identifiers only — prose is never indexed that way.

How you phrase a query changes what you get

Retrieval compares your query against each memory's own wording. A query phrased the way the note was written beats one phrased the way you would describe it to a colleague.

Prefer domain vocabulary, service and tool names, config keys, identifiers and error strings:

CORS_ALLOWED_ORIGINS admin frontend 403

rather than:

why did the admin site stop working

Both will return something. The first is far more likely to return the right thing.

Reading a result honestly

Kemory tells you how a search actually ran, and those fields are worth reading.

FieldWhat it means
search_mode_effectiveWhat really executed. hybrid_dense_only means the lexical half was skipped because your content is encrypted — recall is genuinely weaker for rare tokens in that mode, so an empty result is not evidence the memory does not exist.
total + total_modeFor a hybrid search total_mode is page and total counts the results on this page — not the memories in your account. Only exact mode totals are corpus counts.
flat_pagetrue means the results are a saturated band of near-identical scores: the signature of nearest-neighbour background noise for a topic your memory does not actually cover.
top_relevance, relevance_spreadThe shape of the result set. A page with a genuine match spreads much wider than a background one.

Read shape rather than an absolute similarity number. Similarity scores drift upward as a store grows, so no fixed threshold stays meaningful over time.

Which tool to use

ToolUse it for
kemory_askThe one federated tool: searches memories, chat turns and files together (the cross-surface engine below) and, when the question calls for it, synthesises an answer with citations instead of returning a bare list. Reach for it when you don't know which surface holds the answer.
kemory_recall_memoryThe main search — text query, optional namespace and content-type filters, pagination. kemory_recall is a friendlier alias with identical ranking that can also return your profile in the same call.
kemory_find_similarSearching by relevance to a reference string. Without arguments it shares recall_memory's ranking exactly. It additionally accepts min_similarity — a caller-supplied floor applied to semantic matches only (the same mechanism as recall's min_relevance). The floor is a floor, not a verdict: results stay relevance-ranked, and exact-token hits are never floored. Absent min_similarity, no threshold applies.
kemory_get_contextRelevant background for a conversation rather than a list of hits, optionally synthesised. When the results are flat background noise it deliberately returns nothing rather than filling your prompt with near-misses.
kemory_get_user_contextA cross-namespace overview of everything you have stored. The right call at the start of a session.
kemory_get_session_contextA prompt-ready block for one namespace session: recent exchanges verbatim, older ones folded into a rolling digest.
kemory_list_namespaces
kemory_list_projects
Discovery. Worth calling before a scoped search so you filter on a namespace that exists.
kemory_get_raw
kemory_get_compressed
Reading a whole namespace, uncompressed or at a chosen compression tier.
kemory_get_historyThe full provenance of one memory: every state change, who made it, and why.

Tuning retrieval (self-hosted)

These are server-side settings. On hosted Kemory they are managed for you and cannot be set per account; they matter if you run Community Edition or your own deployment.

VariableDefaultWhat it changes
KMV_RRF_K60The rank-fusion constant. Lower values let a leg's top hits dominate the merge; higher values flatten the contribution across ranks.
KMV_DENSE_CANDIDATES50How many candidates the semantic leg retrieves before fusion.
KMV_SPARSE_CANDIDATES50How many candidates the lexical leg retrieves before fusion.
KMV_RANK_W_VECTOR_SIM0.35Weight of semantic similarity in the final ranking.
KMV_RANK_W_RECENCY0.20Weight of recency.
KMV_RANK_W_ACCESS_FREQ0.15Weight of how often a memory has been read.
KMV_RANK_W_GRAPH_PROXIMITY0.15Weight of proximity in the memory graph.
KMV_RANK_W_UTILITY_SALIENCE0.15Weight of measured usefulness.
KMV_UNIFIED_COSINE_FLOOR0.80Calibrates the chat and file legs onto the memory leg's scale in cross-surface search. Not the same thing as the row below — see Cross-surface search.
MCP_MIN_RELEVANCE0.0 (off)An absolute similarity floor applied to results. See below for why this is off.
GET_CONTEXT_FLAT_GATEtrueWhether kemory_get_context suppresses a flat, background-noise page instead of returning it.
KMV_UNIFIED_RRF_RELEVANCE_WEIGHTINGtrueCross-surface search only: weight each result's rank contribution by its within-leg relevance. false reverts to pure rank interleaving.
KMV_UNIFIED_V2_PLAN_FOR_CHAT_FILEtrueCross-surface search only: drive the chat and file lexical legs from the same parsed query plan as the memory leg (phrases, negations, spelling variants). false reverts each leg to its own raw-token parse.

Raising a weight does not raise quality on its own — the five are normalised against each other, so increasing one necessarily reduces the influence of the rest. Change one at a time and measure against a labelled set you control.

What we tried that did not work

Retrieval quality attracts plausible fixes. These were each implemented, measured against a labelled evaluation set, and rejected. They are documented here so the same ground is not covered twice — including by us.

Splitting memories into chunks before embedding — actively harmful. The intuition is sound: one vector averaging a long, multi-topic note matches none of its topics sharply. Measured, the median rank improved while recall@1, recall@5 and MRR all degraded. Chunking helps some mid-ranked results and hurts the ones that matter most. Splitting notes on their own structure rather than arbitrary boundaries scored worse, not better.

Cross-encoder re-rankers — no measurable effect. Two were tried. Neither moved the numbers enough to justify roughly doubling search latency on CPU inference.

An asymmetric query prefix for the embedding model — measured negative. The model family documents a prefix for query-side encoding. Applying it made results slightly worse on our corpus, so it ships off.

Truncation as the explanation — ruled out. Long notes exceeding the embedding model's input window were an obvious suspect for poor paraphrase recall. Removing truncation from the equation did not close the gap; the cause is vocabulary mismatch, not lost text.

A fixed similarity floor — inert at scale. The most-requested fix for "irrelevant results" is a minimum similarity score. It does not work: across a store of ~18,500 memories, a topic provably absent from the store still returned raw similarities of 0.55–0.8, because the nearest-of-N score rises as N grows. Genuine matches score in the same band. Any threshold high enough to exclude the noise also excludes real answers. This is why the floor ships off, and why the page-shape signals above exist instead — shape is scale-invariant, absolute scores are not.

What did work: phrasing a query in the vocabulary the memory was written in. That is the single intervention that has consistently moved retrieval quality, and it is why the phrasing guidance above is the most useful thing on this page.

Cross-surface search

POST /api/v1/search/unified searches memories, chat turns and files together — it is the engine behind kemory_ask and the extension's Enhance. Each surface is searched by its own leg, and three rules govern the merge.

Scores never compare across surfaces. The surfaces' raw scores are on different scales, so each result reports its own leg-native score — compare within a type, never across types.

The chat and file legs are calibrated onto the memory leg's scale. The memory leg reports a blended rank score in which textual relevance is only about a third of the total; chat and file hits arrive ranked by cosine alone, where anything genuinely on-topic sits at 0.80 or above. Two numbers, two meanings, and the higher one belongs to the weaker signal — left uncorrected, loosely-related chats outranked the memory that actually answered the question. So a floor of 0.80 applies to the chat and file legs only, set by KMV_UNIFIED_COSINE_FLOOR.

This is a scale correction, not a relevance threshold.

It reads as a contradiction of the rejected fixed floor and is not. That experiment applied an absolute threshold to the memory leg, where scores rise with corpus size and genuine matches share a band with noise. This one converts between two legs' native scales, and it can only ever tighten: a caller asking for a min_score above 0.80 keeps their own value, and one who passes 0 — exhaustive retrieval, debugging — has opted out and gets everything.

The floor is a blunt instrument and is no longer the only one: ordering within the cosine legs is now also reranked on a second signal, which promotes the genuine answer rather than merely removing the tail. The floor stays because it is cheap and only ever raises the bar. The durable fix is to give chat and file the same hybrid treatment memories get, so they carry a lexical signal rather than being calibrated for the lack of one — that needs blind-index staging and a backfill.

The merge is rank-based, weighted by within-leg relevance. Pure rank interleaving treated a leg's weak second hit and another leg's near-top second as equals, producing a strict round-robin of types. Each contribution is now weighted by how close the hit is to its own leg's best score — a ratio taken inside one leg, invariant to that leg's scale, so the cross-surface rule above still holds. A surface with genuinely weak results contributes fewer of them.

All three surfaces share one query plan. The query is parsed once — quoted phrases as adjacency groups, negated terms dropped, spelling variants expanded — and the same plan drives the memory, chat and file lexical legs. A vaastu query lexically matches a chat titled Vastu Mirror Placement, not just memories.

Asking instead of searching

POST /api/v1/ask runs exactly this search and then composes an answer from what comes back, returning the evidence alongside it. It searches identically — same legs, same merge, same floor — so the two surfaces cannot disagree about what is relevant. The evidence is returned whether or not an answer could be composed, so a question it cannot answer degrades to ordinary search rather than to nothing. Also available as kemory ask and as the kemory_ask MCP tool.

Capture versus consolidate

Two write tools are easy to confuse:

  • kemory_capture_session extracts many discrete durable facts from a conversation and stores each one.
  • kemory_consolidate_session produces one semantic summary of a session.

Use capture when the conversation contained several things worth remembering separately; use consolidate when you want a single durable record of what a session was about.

Scoping and isolation

Every read is filtered by user and organisation in the database, not in a prompt. That applies identically across REST, MCP and every tool listed above. Agents additionally pass a permission check before any read or write.

See the Kemory overview for connection and scoping detail, and the Privacy Policy for data handling.