kb-search Enhancements Proposal
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.
| # | Enhancement | Impact | Effort |
|---|---|---|---|
| 1 | LLM reranking stage | High — biggest single search-quality lever | Medium |
| 2 | RRF fusion for FTS + vector merging | Medium-high | Low |
| 3 | Bench harness + --explain traces | High (enables everything else) | Low-medium |
| 4 | Context descriptions on tags/sources | Medium | Low |
| 5 | Query expansion | Medium | Medium-high |
| 6 | .json file ingestion (already tracked) | Medium | Low |
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:
kb bench fixture.json— run a fixture of queries with known-relevant docs, report precision@k / recall / MRR per backend (fts-only, vec-only, hybrid, hybrid+rerank). Directly mirrorsqmd bench.--explainon search — per-result score breakdown (FTS score, vector score, fusion contribution, rerank score).
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
- #3 bench harness — establish the baseline (a weekend-sized job).
- #6 JSON support — small, independent, already promised.
- #2 RRF fusion — low-risk engine change, measure against baseline.
- #1 reranker — the big win, measured.
- #4 tag contexts — anytime, independent.
- #5 query expansion — only if the bench still shows a gap.
References
- tobi/qmd — architecture, fusion weights, and bench design borrowed from here
- qmd score fusion detail: RRF k=60, top-rank bonus +0.05/+0.02, position-aware blend 75/60/40% retrieval weight
- Reranker model:
hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF(~640MB)