Published Build3 min read
Your Embedding Model Cannot See ndots:5
A single practitioner's ten-query eval moved top-3 hits from 5 to 8 by adding BM25 and a reranker. The more useful part is the three fixes that did not work, one of which failed silently.
Written for builders.See today for builders

What happened
- A query for the literal string ndots:5 against the author's wiki index returned the article that exists specifically to explain ndots:5 at position seven.
- Ahead of the correct article sat three general DNS articles, two Kubernetes networking posts, and something about service discovery.
- Across a fixed 10-query eval set of things the author actually searches for (config keys, error strings, CLI flags), dense-only retrieval put the correct article in the top 3 for 5 of them.
- Hybrid retrieval with a reranker on the same 10 queries put the correct article in the top 3 for 8 of them.
- 5 of 10 is 50 percent top-3 accuracy and 8 of 10 is 80 percent, an absolute improvement of 3 queries or 30 percentage points.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
A developer writing on dev.to fixed a wiki index whose search for the literal string `ndots:5` returned the one article written specifically to explain `ndots:5` at position seven, behind three general DNS articles, two Kubernetes networking posts, and something about service discovery [1][2]. On a fixed ten-query eval set built from the config keys, error strings and CLI flags he actually searches for, dense-only retrieval put the correct document in the top three for five queries; dense plus BM25 sparse vectors, reciprocal rank fusion and a cross-encoder reranker got eight [3][4][24].
That is 50 percent to 80 percent, a move of three queries [5]. It is also n=10, one corpus, one author, self-reported, so treat it as a direction rather than a benchmark. The mechanism it illustrates is the durable part. Technical corpora are dense with tokens that carry near-zero semantic weight and near-total discriminative weight: `max_cstate`, `ErrImagePull`, a CVE number, a Helm value path [6]. An embedding model compresses those into a vector where they barely register against the surrounding prose [7]. Semantic similarity is a fuzzy match by design, and fuzzy is the wrong operation when the user typed an exact identifier.
The three attempted fixes are worth more than the result. A larger dense model moved the eval by roughly one query out of ten and cost more VRAM plus more latency per ingest batch [8]; the author's read is that adding dimensions does not create a keyword index, because no dense model is trained to treat `ndots:5` as an atomic symbol [9]. LLM query expansion into three paraphrases, embedded and unioned, helped on vague questions and hurt on precise ones, because the paraphrases diluted the exact term being searched [10]. It also turned a 40ms retrieval into a 900ms one and made results non-deterministic between runs [11]. He calls that acceptable for a chat UI and bad for an agent that retrieves twenty times inside one task [12], which pencils out to about 18 seconds of retrieval per task instead of under a second [13].
The expensive failure was the reranker. The plan was ordinary: over-retrieve 20 dense candidates, then rerank with a cross-encoder that sees query and document together [14]. Ollama was already in the cluster and GGUF conversions of popular rerankers exist on Hugging Face [15]. Scores came back as numbers with no errors and no NaNs, weakly correlated with relevance, and sometimes ordered worse than the pre-rerank list [16]. According to the author, the reason is structural: a cross-encoder reranker is a sequence-classification model, an encoder backbone plus a trained head emitting one relevance logit [17]. Convert it to GGUF, serve it through a runtime built for causal generation and embedding extraction, and the classification head usually is not in the picture; what returns is a pooled hidden state or an untrained logit, in a response shape identical to a real score, with no warning [18]. His rule generalizes: when a model's output is a scalar, inspection cannot tell you it is the right scalar, so validate rerankers against a fixed query set with known answers before wiring them in [19].
The shipped stack is a Qdrant collection with two named vector spaces, dense embeddings from `qwen3-embedding:0.6b`, sparse BM25 vectors, and the reranker on ONNX through FastEmbed with no GPU in that stage [20][21]. Dense and sparse live on the same point: one document, one ID, two representations, one payload [22]. Split them into two collections and you get two ingest paths that drift, and you learn about the drift during a retrieval failure [23].
Watch whether the eval set survives contact with a real corpus at scale, and whether the same 5-to-8 shape holds when someone runs it on thousands of queries instead of ten. Watch, too, for the class of silent scalar failures this post names, which applies to any classification head served through a generation runtime.
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
A query for the literal string ndots:5 against the author's wiki index returned the article that exists specifically to explain ndots:5 at position seven.
- [2]
Ahead of the correct article sat three general DNS articles, two Kubernetes networking posts, and something about service discovery.
ReportedView cited source - [3]
Across a fixed 10-query eval set of things the author actually searches for (config keys, error strings, CLI flags), dense-only retrieval put the correct article in the top 3 for 5 of them.
- [4]
Hybrid retrieval with a reranker on the same 10 queries put the correct article in the top 3 for 8 of them.
- [6]
Technical corpora are full of tokens that carry near-zero semantic weight and near-total discriminative weight, such as max_cstate, Modifier.IDF, ErrImagePull, a CVE number, or a Helm value path.
ReportedView cited source - [7]
An embedding model compresses such identifier tokens into a vector where they barely register against the surrounding prose.
Sources & coverage · 1 publisher
The reporting this story was synthesized from, earliest first. Every link goes to the original.
Cited in this coverage: dev.to post by author 'futhgar'
Additional citations
- author's own measurement
- author's explanation
- author's argument
- author's own testing
- author's diagnosis

