My semantic search worked at 1,000 documents and failed at 57,000
I built a hybrid retrieval engine over years of my own working notes, measured it honestly, and the measurement said half of it does not work. This is that result.
The result that argued against my own design
On a 1,271-chunk development corpus the semantic arm clearly beat keyword search on paraphrased questions — the ones where you describe a memory in different words than you originally used. That is what justified building a hybrid at all.
Measured at full scale for the first time, against ten real questions judged by hand, it returned something useful five times. Every success was a keyword-shaped question. Four of the five paraphrased questions — the exact case the semantic arm existed to solve — returned nothing useful.
| Arm | Rank of the correct answer · 5 paraphrase questions |
|---|---|
| Keyword (BM25) | 3 · 1 · 1 · 5 · 1 |
| Semantic | >200 · 7 · 2 · 107 · 1 |
The answers were being retrieved. The semantic arm was burying them. Keyword search alone beat every hybrid configuration — the exact inverse of the small-corpus result. Similarity stops discriminating as the candidate pool grows; rare words stay rare.
How it got there
- Small corpus, clear win. 1,271 chunks. Semantic beat keyword on paraphrase, decisively.
- Real scale, five of ten. Judged by hand, no partial credit.
- The obvious explanation was wrong. "The bigger corpus is diluted" — tested by restricting the search to the single highest-quality source. No improvement. Rejected in one run rather than argued about.
- Recall or rank? A diagnostic separated answers that were never retrieved from answers that were retrieved and then buried. It was rank.
- The tuning already shipped was a regression. A query-adaptive router — use the expensive re-ranker only when the query looks keyword-shaped — had been tuned on the small corpus and shipped as the default. At full scale it measured worst of six configurations, scoring 0.000 on paraphrased questions where always re-ranking scored 0.200. Removed as default the same day.
- Then the evaluation itself had a bug. A code review found the scorer retrieving deeper than the judging pool went, so the re-ranked configuration was returning documents nobody had graded and being marked wrong for it. Fixing it improved that configuration, 0.352 to 0.447 — it did not rescue the conclusion.
The parts that are actually rare
Questions about the method? Email is best — [email protected] · Back to portfolio