It worked on five hundred documents and broke at fifty thousand
In short
Retrieval degrades at scale because top-k is a fixed budget in a contest whose entrant count keeps rising. At 4,000 chunks a k of 5 keeps the best 0.13 percent of the corpus; at 400,000 chunks the same k keeps the best 0.00125 percent. Measure recall at the candidate stage before you change an embedding model, a chunker or a prompt.
Key takeaways
- Nothing broke. A k that was 0.13 percent of the corpus became 0.00125 percent of it, and nobody moved k.
- One measurement splits the problem: is the answer chunk absent from the candidates, or present at rank 12?
- Absent means widening k, adding a lexical arm or fixing the chunk. Present-but-low means ordering, which reranking fixes.
- Four scale effects produce this: candidate crowding, near-duplicate flooding, missing scope filters, and neighbourhood density.
- Score spread is the tell for density. When the top 20 sit inside 0.02 of each other, ordering is noise.
- Widen k and measure before touching the embedding model. It is the cheapest experiment and it settles the argument.
If retrieval was good at five hundred documents and is unreliable at fifty thousand, the most likely answer is that nothing broke. Top-k is a fixed budget in a contest whose entrant count grew a hundredfold. The chunk that used to win still scores exactly what it always scored; it now has vastly more company above it, and the size of the shortlist never changed to compensate.
Put numbers on it. Five hundred documents chunked at roughly eight chunks each is about 4,000 candidates, so a k of 5 is keeping the top 0.13 percent of the corpus. Fifty thousand documents is about 400,000 candidates, and the same k of 5 is now keeping the top 0.00125 percent. That is a hundredfold tightening of the selection criterion, applied silently, by a config value nobody edited.
Prove it with a recall probe before you change anything
The single most expensive mistake here is swapping an embedding model on a hunch. A recall probe costs an afternoon and tells you which half of the pipeline is at fault, so run it first. It measures one thing: for a question whose answer you already know, does the answer-bearing chunk appear in the candidate set at all?
- Assemble twenty questions whose answer document you can name. Take them from real logs, not from imagination — invented questions use the document's own vocabulary and the probe passes for the wrong reason.
- Record the expected chunk id for each, by hand. This is the tedious step and there is no substitute for it.
- Run each question against the full index with k raised to 50, and record the rank at which the expected chunk appears, or 'absent' if it never does.
- Re-run the same twenty against a restricted index that holds only the original document set — a metadata filter on source or ingest date is usually enough to reconstruct it without a second index.
- Compute recall at 5 and recall at 50 for both runs. Four numbers. That table is the whole diagnosis.
- Where the rank moved but stayed inside 50, note the score of the expected chunk and the score of the chunk that beat it. The gap between those two scores is the second most useful number on the page.
Absent from the candidates, or present and ranked twelfth
Everything downstream depends on this split, and teams routinely spend weeks on the wrong side of it. The two branches share a symptom and share no remedy.
| Probe result | What it means | What fixes it | What will not help |
|---|---|---|---|
| Absent from the top 50 | The chunk is unreachable by this query. Scoring never got the chance to rank it. | A lexical arm, a metadata field the query can target, or re-chunking the source | Reranking, prompt changes, a bigger context window — none of them can see a passage that was never fetched |
| Present at rank 12 of 50, absent from the top 5 | Retrieval found it. Ordering demoted it below a dozen plausible competitors. | Widen k, then rerank the wider list; or add a filter that removes the competitors | A different embedding model, usually. The chunk is already in the neighbourhood |
| Present at rank 1, answer still wrong | This is not a retrieval problem at all. | Synthesis work — see the sibling page on the passages being right | Any amount of k tuning |
| Present at small scale, absent at large scale, same query | Confirms the scale hypothesis outright. | Continue to the four effects below | Blaming the ingest of the new documents before you know which effect is operating |
If the third row is where you land, retrieval succeeded and the fault is in what happened afterwards; the right passages and a wrong answer is the page that owns that case. If only one named document misbehaves while the corpus as a whole is fine, this page is the wrong one and the single-document forensic sequence is the right one.
The four things a bigger corpus actually changes
Growth does not degrade retrieval in one undifferentiated way. It does four distinct things, each with its own signature in the probe data, and the fix for one is inert against the others.
| Effect | Mechanism | Signature in the probe | First move |
|---|---|---|---|
| Candidate crowding | More chunks now score above the answer chunk, on count alone. Its absolute score is unchanged. | Expected chunk sits at rank 8 to 30; its score is within 0.03 of its score on the small index | Raise k, then rerank the longer list |
| Near-duplicate flooding | Boilerplate — headers, disclaimers, a standard preamble — repeats across thousands of documents and fills the shortlist with copies of one paragraph | Three or more of the top 5 are near-identical text from different documents | Collapse duplicates at index time; the data cluster owns that work |
| Missing scope filters | At 500 documents everything was in scope for everyone. At 50,000 the corpus spans years, regions and product lines, and nothing narrows the pool | Winners are right-topic-wrong-scope: correct policy, wrong year or wrong market | Extract the scoping fields and filter on them inside the search |
| Neighbourhood density | As chunk count rises the score gap between rank 1 and rank 20 collapses, so ordering is decided by rounding rather than meaning | Score spread across the top 20 under 0.02, and reruns reorder the same list | Reranking, or a second retrieval arm that scores on different evidence |
Candidate crowding has a close relative worth knowing about: because k is zero-sum, a single new source that ranks moderately well on many queries will evict previously winning passages across the board. That is a new source pushing good passages out, and it is the same arithmetic arriving in one step instead of gradually. Neighbourhood density has one too — when the spread is that tight, the same question returning different passages every run stops being a bug report and starts being a measurement of how little signal separates the top of your list.
A shortlist of five was a generous filter at four thousand chunks and is a starvation ration at four hundred thousand. The number did not become wrong; the corpus moved out from under it.
What to change, and in what order
The order matters because each step is cheaper than the next and because a later step is uninterpretable until the earlier ones are settled.
- Raise k to 50 and re-measure. If recall at 50 is high and recall at 5 is poor, you have an ordering problem and you now know it rather than believe it.
- Add a reranking pass over the widened list, not over the original five. What a reranker actually does is reorder an existing candidate list, so it pays only when the first stage is already finding the passage — the trade-off is laid out in more candidates versus reranking.
- If recall at 50 is also poor, stop tuning ranking. The passage is unreachable, and the usual cause is vocabulary: a query built from words the document does not contain. Add a lexical arm as set out in keyword matching versus vector similarity.
- Extract the two or three fields that scope your corpus — effective date, market, product line — and apply them as metadata filters inside the retrieval query rather than as a post-filter over results.
- Collapse near-duplicate boilerplate at index time, so one canonical copy competes instead of forty.
- Only now consider the index itself. Whether the vectors live in the database you already run or in a dedicated store is an operational decision covered in database extension versus dedicated vector store, and it is almost never the cause of a quality regression.
What widening k costs, and where the cost shows up
Going from 5 candidates to 50 barely moves search latency — an approximate index is doing nearly the same graph traversal either way. It multiplies what you send to the model. If all 50 passages reach the prompt, you have multiplied input tokens per answer by roughly ten while traffic stayed flat, which is exactly the shape described in a bill that jumped without more traffic.
The standard resolution is to widen the candidate stage and keep the prompt narrow: fetch 50, rerank, pass 6. Recall is set by the wide stage and prompt size is set by the narrow one. It also avoids stuffing a long context with marginal passages, which brings its own well-documented positional problems.
Where the fix is not
Three things get blamed for this and rarely deserve it. The embedding model, ruled out in ten minutes by comparing the answer chunk's score across the two probe runs. The chunker, unless the probe shows the answer split across a boundary. And the prompt, which cannot influence anything that never reached it.
The durable version of this work is not a tuning session. It is a fifty-line harness that runs the probe set on every index rebuild and fails loudly when recall at 5 drops, which is the kind of narrowly scoped internal tooling we describe under MVP and product builds. The rest of this silo — how passages get chosen, filtered, ordered and cited — sits in retrieval and grounding, inside the wider engineering library.
Frequently asked questions
Short answers to the follow-ups this page tends to raise.
Why does adding more documents hurt RAG accuracy?
Because top-k is a fixed shortlist and every new document adds competitors to it. The answer-bearing chunk keeps its score and loses its rank, so a k of 5 that comfortably contained the answer at 4,000 chunks contains it far less often at 400,000. Adding documents also adds boilerplate that clusters tightly in the same region of the embedding space, which crowds the shortlist further.
How do I tell whether retrieval or generation is at fault?
Log the retrieved chunk ids and check whether the answer-bearing chunk was in the set. If it was absent, the fault is in retrieval and no prompt change will help. If it was present and the answer is still wrong, retrieval succeeded and the fault is in synthesis. That one observable resolves the question in seconds and is the reason to instrument the candidate set before you instrument anything else.
Should I just raise top-k and leave it there?
Raise it in the candidate stage and keep the prompt narrow. Fetching 50 and passing 6 after reranking gets you the recall benefit without the token cost or the positional problems of a very long context. Leaving k at 50 all the way into the prompt buys recall by paying in precision, latency and spend, and past a point the extra passages actively dilute the answer.
Does a bigger corpus need a different embedding model?
Almost never, and it is the most expensive thing to change on a guess. Compare the answer chunk's similarity score on the small index against the large one: if the score is unchanged and only the rank moved, the model is behaving identically and the shortlist size is the problem. Change the model only when the answer chunk scores poorly in absolute terms, which usually points at vocabulary.
- retrieval
- recall
- top-k
- scaling
The work behind this page
Builds from our portfolio that this page draws on.
AskVault
An AI internal knowledge-search platform that answers employee questions from your own docs — grounded in citations, with knowledge gaps surfaced and deflection tracked.
Productivity AINotewell
An AI meeting assistant that records and transcribes every meeting, extracts the decisions and action items, assigns owners and due dates, and tracks follow-through until it's done.
Productivity AIRead next
- You know the document is indexed and it still never comes backFour layers can quietly drop one document, and they look identical from the outside. Probe them in cost order — existence, filter, exact text, rank — and each probe eliminates exactly one.diagnostic
- The right passages were retrieved and the answer is still wrongIf the answer-bearing passage was in the context and the answer is still wrong, retrieval tuning cannot help. Four synthesis defects produce this, and each leaves its own mark in the output.diagnostic
- The assistant quoted a policy that was replaced last quarterThe old version is still indexed, or it is gone and a cache is still serving it, or both versions are live and the old one ranks higher. One probe separates them.diagnostic
- The citation points at the wrong paragraphA misaligned citation is a pipeline bug with a deterministic cause; an invented one is a grounding failure. One exact-string search against the original file separates them in under 5 minutes.diagnostic
- "Dedicated team" defined by what it does not promiseThe phrase commits a supplier to almost nothing on its own. What it usually means in practice, and the three artefacts that turn it into something you can verify.definition
- Everything is now a change requestArguing about whether a change request is fair goes nowhere. Sorting the last 10 into 3 buckets says whether the bid was underscoped, the scope grew, or nobody decided.diagnostic
Working on something in this space?
Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.
Start the conversation