Skip to content

fix(search): over-fetch vector candidates so the sidechain filter can't starve results#115

Open
iceinvein wants to merge 1 commit into
obra:mainfrom
iceinvein:fix/vector-search-sidechain-recall
Open

fix(search): over-fetch vector candidates so the sidechain filter can't starve results#115
iceinvein wants to merge 1 commit into
obra:mainfrom
iceinvein:fix/vector-search-sidechain-recall

Conversation

@iceinvein

Copy link
Copy Markdown

Problem

Vector/semantic search returns far fewer results than limit, often zero at small limits, while text search works fine. On a ~31k-exchange index (~13% sidechain), mode: "vector" returned: limit 3 -> 0, limit 10 -> 2, limit 25 -> 11.

Cause

In searchConversations, the vec0 KNN query is:

WHERE vec.embedding MATCH ? AND k = ?
  AND e.is_sidechain = 0

vec0 applies the k cutoff before the WHERE, but k is only inflated (limit * 3) when metadata filters are present, never for the always-on is_sidechain = 0 filter. Subagent (sidechain) exchanges crowd the nearest neighbours and get removed after the cut, leaving fewer than limit results (often none at small limits). Text search is unaffected because it does not use k.

The is_sidechain = 0 filter was added in #42; this is the latent recall regression introduced alongside it.

Related: #111 (skip indexing sidechains) would hide this for fresh installs, but its stated assumption that search results are identical before and after does not hold for the vector path, and it does not fix the query for any index that still contains sidechain rows.

Fix

Over-fetch candidates with headroom for the always-on sidechain filter (limit + 100, or limit * 3 when metadata filters are also active) and trim to limit after filtering (the existing trim handles the upper bound). This matches the existing limit * 3 idiom; a fully guaranteed limit would require iterative k expansion, which could be a follow-up.

Test

Adds test/search-sidechain-recall.test.ts: indexes one real exchange among 20 sidechain neighbours on the query topic, then asserts vector search still returns it at limit: 3 and never returns sidechain rows. The test fails on the previous k = limit behaviour and passes with the fix.

Verification

  • Full suite: 208 passed, 1 pre-existing failure (show.test.ts date-format assertion) that also fails on clean main, unrelated to this change.
  • npm run build clean.

…'t starve results

vec0 applies the KNN `k` cutoff before the WHERE clause, so the always-on
`is_sidechain = 0` filter (and any metadata filters) can shrink the result set
below `limit`. With subagent (sidechain) exchanges crowding the nearest
neighbours, requesting exactly `limit` candidates returns far fewer than `limit`
real hits, often zero at small limits, while text search is unaffected.

Over-fetch candidates with headroom for the always-on sidechain filter
(`limit + 100`, or `limit * 3` when metadata filters are also active) and trim
to `limit` after filtering. The sidechain filter was introduced in obra#42.

Adds a regression test that indexes one real exchange among many sidechain
neighbours and asserts vector search still returns it at a small limit; the
test fails on the previous `k = limit` behaviour.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant