kb-search Enhancements Proposal

Bobby · 2026-07-07 · Prompted by a review of tobi/qmd (Tobi Lütke's local hybrid search engine)

Summary

qmd and kb-search v2 solve overlapping problems, but qmd's retrieval pipeline is measurably ahead: its own benchmarks show BM25-only at ~0.50, vector-only at ~0.70, and the full hybrid + reranked pipeline at ~1.00. Our kb does hybrid FTS + vector but stops there — no rank fusion, no reranking, no query expansion, and no way to measure whether a change helps or hurts. This proposal lists five enhancements, ordered by value-for-effort, plus the already-tracked JSON ingestion item.

#EnhancementImpactEffort
1LLM reranking stageHigh — biggest single search-quality leverMedium
2RRF fusion for FTS + vector mergingMedium-highLow
3Bench harness + --explain tracesHigh (enables everything else)Low-medium
4Context descriptions on tags/sourcesMediumLow
5Query expansionMediumMedium-high
6.json file ingestion (already tracked)MediumLow

Current state

kb-search v2 (engine v3.2.2) runs on the RTX 4070 box with BAAI/bge-base-en-v1.5 (768-dim). It holds ~2,310 documents (1,944 PDFs, 237 notes, 129 markdown) in ~123k chunks. Search is hybrid FTS + vector with a blended relative score. Strengths over qmd: binary ingestion (PDF/docx/HTML), tags, ingestion job queue, dedup, original export, and multi-client API access. The proposals below close the retrieval-quality gap without giving any of that up.

Proposals

1. LLM reranking stage HIGH IMPACT

Add a cross-encoder reranking pass over the top-K hybrid candidates. qmd uses qwen3-reranker-0.6b (~640MB GGUF) — small enough to sit alongside bge on the 4070 permanently. Flow: hybrid retrieval pulls ~40 candidates → reranker scores each (query, chunk) pair → final order blends retrieval and reranker scores.

qmd's position-aware blend is worth copying wholesale: rank 1–3 keep 75% retrieval weight, 4–10 get 60%, 11+ get 40%. This stops the reranker destroying exact-match hits while letting it rescue mid-ranked semantic matches.

API: add rerank: bool (default true) to the search endpoint, with --no-rerank in the CLI for latency-sensitive callers.

2. RRF fusion MEDIUM-HIGH

Replace the current score blend with Reciprocal Rank Fusion when merging FTS and vector lists: score = Σ 1/(k + rank + 1), k=60. Rank-based fusion sidesteps the incomparability of BM25 scores (unbounded) and cosine similarity (0–1). qmd adds a top-rank bonus (+0.05 for #1, +0.02 for #2–3 in any list) to preserve exact matches — cheap and effective.

Pure engine-side change, no API impact. Scores become comparable across queries too, which fixes the "score is relative, not absolute" caveat in the current skill docs.

3. Bench harness + explain traces DO FIRST

We currently have no way to know if any of the above helps. Add:

A fixture of 20–30 real queries against the existing corpus (lab infra questions, manual lookups, note recall) gives a regression baseline before touching ranking. This should land before #1 and #2 so their benefit is provable.

4. Context descriptions MEDIUM

qmd's standout idea: attach a one-line description to a collection or path (e.g. "Meeting transcripts", "Lab infrastructure runbooks") and return it with every matching result. For kb, the natural unit is the tag: kb tag-describe ops "Lab operations runbooks and procedures", returned as tag_contexts in search results. Helps an LLM consumer (me) judge which of several similar-scoring chunks actually answers the question — descriptions cost nothing at query time.

5. Query expansion LATER

qmd fine-tuned a 1.7B model to generate 2 query variants, searching all three and fusing via RRF. Real quality gains, but the heaviest lift: another model resident in VRAM, ~1–2s latency, and much of the benefit is available cheaper — I already do multi-query decomposition client-side per the kb skill. Park until #1–#3 have landed and the bench shows remaining headroom.

6. JSON ingestion TRACKED

The original scope of this task: kb rejects .json uploads, forcing renames to .txt. Add .json (and sensibly .yaml/.yml/.toml) to the accepted extensions, ingesting as text. Optional nicety: pretty-print minified JSON before chunking so chunks break on structure.

Suggested order

  1. #3 bench harness — establish the baseline (a weekend-sized job).
  2. #6 JSON support — small, independent, already promised.
  3. #2 RRF fusion — low-risk engine change, measure against baseline.
  4. #1 reranker — the big win, measured.
  5. #4 tag contexts — anytime, independent.
  6. #5 query expansion — only if the bench still shows a gap.

References