Contents
01What Contextual Retrieval is
To search long documents, you cut them into small pieces called chunks. The problem is that a chunk loses its context. A chunk that says “the fee was 8% of total development cost” no longer says which project or which document it came from.
Contextual Retrieval, published by Anthropic, fixes this before anything is searched:
- An AI model reads the whole document and one chunk.
- It writes a short sentence saying where the chunk comes from, such as “This is from the final cost report for the Oak Street project.”
- That sentence is added to the chunk, and the search index is built from the result.
Anthropic reported 35–67% fewer retrieval failures on its own data. We wanted to see how it does on documents that look like a real company archive, and how it compares with the other tools a team would reach for.
02The dataset, and why it is hard
287 synthetic housing-finance documents: mortgages, liens, payoff letters, grant agreements, budgets and policies. Because we generated them from a known set of facts, we know every right answer to every question. The archive was built to be hard in the same ways real ones are:
37%
of files are scans, with OCR errors in names and numbers
77%
of chunks never name the client or project they belong to
72
loan documents built from the same templates
69
right documents for the hardest question
The 77% matters most here: it is exactly the problem Contextual Retrieval is meant to solve, so the test gives it every chance. We asked 81 questions of two kinds:
- Find one thing (60 questions): “Pull up the payoff letter for this client.” One or two right documents each.
- Find everything (21 questions): “Show everything about this project” or “How many loans did we issue in 2021?” Often six or more right documents, up to 69.
03Keyword search
Keyword search finds chunks that contain the words in the question. We used Meilisearch, a popular open-source search engine, exactly as it comes out of the box. Its defaults are built for short queries typed into a search box, and two of them hurt on full questions:
- It doesn’t weigh rare words. It ranks chunks by how many question words they contain, so a client name counts no more than “what” or “the”.
- It drops words when matches run short. If too few chunks contain every word, it removes words from the end of the question. In “What developer fee was charged on [project]?”, the project name goes first.
15.9% recall@20. Its “frequency matching” setting, which drops common words first, reached 35.4%. This is a mismatch between search-box defaults and full questions, not a verdict on keyword search.
04BM25 search
BM25 is also keyword search, with a different way of ranking. It gives more weight to rare words, so a client name or a loan number counts far more than “what” or “the”.
84.3% recall@20, on the same documents and the same chunks. Only the ranking changed, and it was the biggest single improvement in the whole study.
05Similarity search
Similarity search finds chunks with a similar meaning, even when they use different words. It handles paraphrasing well, and is weaker on exact names and numbers. We used bge-m3 embeddings in Qdrant.
83.1% recall@20, close to BM25 but strong on different questions.
06BM25 + similarity search
A hybrid runs both searches and merges the two result lists, so each covers the other’s blind spot: BM25 catches exact names, similarity catches paraphrase.
87.7% recall@20, the best of the four. This is our starting point for everything that follows.
No reranker, no filters, plain chunks. Every bar starts at zero, so lengths compare directly.
| Kind of question | Keyword | BM25 | Similarity | BM25 + similarity |
|---|---|---|---|---|
| Find one thing60 questions · 1–2 right documents | 18.3% | 93.6% | 96.4% | 97.5% |
| Find everything21 questions · often 6 or more | 9.1% | 57.7% | 45.0% | 59.8% |
Values: recall@20 · ↑ higher is betterworsebetterBold: best in row
A good search finds single documents easily. Every method struggles when a question needs many documents at once. The next three techniques are attempts to fix that, and each is added on its own to the same starting point.
07Reranker
After the search returns its top 30 results, a second AI model reads the question and each result together and re-orders them, keeping the best 20. It can lift a good document from 25th place to 5th. It cannot rescue one the search never returned. We used Voyage rerank-2.5-lite, at about $0.0001 per search.
87.7% → 92.1% recall@20. That is +4.4 points.
08Filtering
Filtering puts context back as structured tags instead of text. Most archives hold a limited set of document types with predictable fields: a mortgage has a borrower, a lender, an amount and a date.
- When a document is added, an AI model reads it once and tags it with facts such as client, project, year and document type.
- When a question comes in, one short AI call turns it into filters. “Every loan issued in 2021” becomes type = loan and year = 2021, and the search only looks at documents that match.
- If a filter is wrong, it loosens step by step, and an ordinary search fills any empty places, so a mistake costs ranking, not the answer.
87.7% → 94.4% recall@20. That is +6.7 points, the largest gain of any single technique.
09Contextual chunking
This is Contextual Retrieval itself: every chunk gets its AI-written sentence of context before indexing. For this archive that took 5,171 AI calls, with 59% of the prompt text served from cache.
87.7% → 88.1% recall@20. That is +0.4 points.
One likely reason the gain is smaller than Anthropic’s: our documents already carry strong clues such as names, loan numbers and addresses, so a sentence of context has less to add. It should do better on documents where a chunk means nothing on its own, like manuals, contracts with numbered clauses, and code.
10What if we use all of them together?
Contextual chunking, a reranker and filters, all on top of BM25 + similarity search:
Each “+” row adds one technique to BM25 + similarity search; they are not stacked. Filters are the first, untuned version.
All together reached 94.6%, the best result. Each technique fixes a different gap: filters narrow the search to the right documents, the reranker puts the best of them first, and contextual chunking gives each chunk back the context it lost when the document was split. On an archive whose documents already name their clients and loans, filters and the reranker carried most of the gain.
| Kind of question | Starting point | + Contextual | + Reranker | + Filters | All together |
|---|---|---|---|---|---|
| Find one thing60 questions · 1–2 right documents | 97.5% | 97.5% | 98.0% | 97.2% | 95.8% |
| Find everything21 questions · often 6 or more | 59.8% | 61.3% | 75.0% | 86.3% | 91.1% |
Values: recall@20 · ↑ higher is betterworsebetterBold: best in row
- “Find everything” questions are where the gains are: from 59.8% to 91.1% with everything on. Filtering did most of that work.
- “Find one thing” questions were already above 97%. Filters added a few small mistakes there, from 97.5% to 95.8% with everything on.
11What each one costs
| For 287 documents | Contextual chunking | Filtering |
|---|---|---|
| AI calls to build the indexone per chunk · two per document | 5,171 | 508 |
| Cost to build59% of contextual prompts served from cache | $2.61 | $0.65 |
| AI calls per searchcontextual chunking adds nothing at search time | 0 | 1 |
| Scales withhow each cost grows as the archive grows | chunks | documents |
The two make different trade-offs. Contextual chunking does all its work once, when the index is built, and costs nothing per search. Filtering is lighter to build but adds one short AI call to every question. The reranker needs no work at indexing and costs about $0.0001 per search.
12What we would recommend
- Check how your keyword search ranks. If it is a search-box engine at default settings, switch to BM25 ranking.
- Combine BM25 with similarity search, and add a reranker. That reached 92.1% here with no AI work at indexing.
- Add filtering if people ask “everything about…” or “how many…” questions. It is the technique that moved them most.
- Test Contextual Retrieval on your own documents, especially if your chunks mean little without their source. Measure recall before and after.
13Limitations and method
- The documents are synthetic. They were generated from the same facts the filters look for, which favours filtering. Real archives contain documents that fit no template, and fields that are missing or wrong.
- 81 questions is a small set. Differences under about 1.5 points are noise.
- Tuned filters are left out of the comparison. A tuned version reached 96.5% recall@20 with BM25 + similarity, but its fixes were found by reading failures on these same questions, so the number is optimistic.
- Only search was scored, not the quality of the final answers.
- One of each. One similarity model, one AI model for context sentences and filters, Meilisearch at default and frequency settings only, and a simple BM25 with no stemming.
Show method details
- Documents
- 287 files (247 PDF, 37 Word, 3 text) generated from a seeded set of facts: 32 fictional clients, 58 loans, 19 home sales and 8 payoffs, plus projects, grants and policies. 106 files were turned into scan-like PDFs with typos; 33 are duplicates filed in a second folder.
- Chunks
- 1,000 characters with a 200-character overlap: 5,856 chunks.
- Questions
- 27 information needs, each phrased three ways: 81 questions and 329 right documents.
- Keyword search
- Meilisearch 1.54 at default settings, and with frequency matching.
- BM25
- Okapi BM25 (k1 1.2, b 0.75), no stemming.
- Similarity search
- bge-m3 embeddings (1,024 dimensions) in Qdrant.
- Hybrids
- The two result lists merged with reciprocal rank fusion (k = 60).
- Reranker
- Voyage rerank-2.5-lite, re-ordering the top 30 files and keeping 20. NVIDIA’s open Nemotron 1B reranker, run on 8 setups, scored within 0.6 points of it.
- Contextual chunking
- One call per chunk to Qwen3.8-27B; 59% of prompt tokens served from cache.
- Filtering
- Each file sorted into one of 11 document families, then 9 fields extracted. Filters loosen step by step if they leave fewer than 3 files, and unfiltered search fills any empty places. 286 of 287 files were classified correctly.
- Scoring
- Recall@20: the share of right documents in the top 20 files. Duplicate copies count as one document. Questions with more than 20 right documents are capped at 20.
- Setups
- 7 search methods × 2 chunk types × 3 filter settings × reranker on or off = 84, plus 4 single-idea runs.
14All 88 setups
Want a different combination? Filter by search method, filtering, chunking or reranker. Sorted best first; the outlined row is the starting point.
Showing 88 of 88 setups
| # | Search | Recall@20 (higher is better) | Recall@10 (higher is better) | Chunks | Filtering | Reranker |
|---|---|---|---|---|---|---|
| 48 | Similarity | 97.0% | 94.7% | Contextual | tuned | On |
| 41 | Similarity | 96.9% | 95.2% | Plain | tuned | — |
| 60 | Meilisearch + similarity | 96.6% | 95.6% | Contextual | tuned | On |
| 77 | BM25 + similarity | 96.5% | 94.9% | Plain | tuned | — |
| 47 | Similarity | 96.2% | 92.7% | Contextual | tuned | — |
| 66 | Meilisearch (freq.) + similarity | 96.1% | 95.7% | Plain | tuned | On |
| 42 | Similarity | 95.9% | 95.1% | Plain | tuned | On |
| 87 | Meilisearch + similarity | 95.8% | 94.7% | Contextual | routed | On |
| 36 | BM25 | 95.7% | 95.4% | Contextual | tuned | On |
| 34 | BM25 | 95.6% | 86.7% | Contextual | untuned | On |
| 86 | Meilisearch + similarity | 95.4% | 93.5% | Plain | routed | On |
| 53 | Meilisearch + similarity | 95.3% | 93.1% | Plain | tuned | — |
| 58 | Meilisearch + similarity | 95.3% | 86.9% | Contextual | untuned | On |
| 78 | BM25 + similarity | 95.3% | 94.6% | Plain | tuned | On |
| 70 | Meilisearch (freq.) + similarity | 95.2% | 86.8% | Contextual | untuned | On |
| 72 | Meilisearch (freq.) + similarity | 95.2% | 94.2% | Contextual | tuned | On |
| 84 | BM25 + similarity | 95.2% | 94.5% | Contextual | tuned | On |
| 40 | Similarity | 95.1% | 86.5% | Plain | untuned | On |
| 46 | Similarity | 95.1% | 87.3% | Contextual | untuned | On |
| 54 | Meilisearch + similarity | 95.0% | 94.3% | Plain | tuned | On |
| 64 | Meilisearch (freq.) + similarity | 94.8% | 87.1% | Plain | untuned | On |
| 65 | Meilisearch (freq.) + similarity | 94.8% | 94.3% | Plain | tuned | — |
| 83 | BM25 + similarity | 94.8% | 94.2% | Contextual | tuned | — |
| 30 | BM25 | 94.7% | 94.5% | Plain | tuned | On |
| 76 | BM25 + similarity | 94.6% | 86.0% | Plain | untuned | On |
| 82 | BM25 + similarity | 94.6% | 85.9% | Contextual | untuned | On |
| 75 | BM25 + similarity | 94.4% | 87.5% | Plain | untuned | — |
| 28 | BM25 | 94.0% | 85.9% | Plain | untuned | On |
| 39 | Similarity | 94.0% | 85.8% | Plain | untuned | — |
| 59 | Meilisearch + similarity | 94.0% | 91.9% | Contextual | tuned | — |
| 52 | Meilisearch + similarity | 93.7% | 86.3% | Plain | untuned | On |
| 71 | Meilisearch (freq.) + similarity | 93.3% | 91.9% | Contextual | tuned | — |
| 29 | BM25 | 93.2% | 93.0% | Plain | tuned | — |
| 45 | Similarity | 93.2% | 85.7% | Contextual | untuned | — |
| 63 | Meilisearch (freq.) + similarity | 92.9% | 86.6% | Plain | untuned | — |
| 44 | Similarity | 92.7% | 89.1% | Contextual | — | On |
| 35 | BM25 | 92.6% | 93.6% | Contextual | tuned | — |
| 27 | BM25 | 92.4% | 85.5% | Plain | untuned | — |
| 81 | BM25 + similarity | 92.3% | 87.9% | Contextual | untuned | — |
| 74 | BM25 + similarity | 92.1% | 87.8% | Plain | — | On |
| 32 | BM25 | 91.8% | 89.0% | Contextual | — | On |
| 51 | Meilisearch + similarity | 91.8% | 83.7% | Plain | untuned | — |
| 80 | BM25 + similarity | 91.7% | 87.7% | Contextual | — | On |
| 69 | Meilisearch (freq.) + similarity | 90.7% | 85.5% | Contextual | untuned | — |
| 38 | Similarity | 89.9% | 85.5% | Plain | — | On |
| 33 | BM25 | 89.7% | 85.6% | Contextual | untuned | — |
| 68 | Meilisearch (freq.) + similarity | 89.5% | 88.5% | Contextual | — | On |
| 57 | Meilisearch + similarity | 89.4% | 81.9% | Contextual | untuned | — |
| 26 | BM25 | 89.2% | 86.9% | Plain | — | On |
| 62 | Meilisearch (freq.) + similarity | 88.5% | 87.1% | Plain | — | On |
| 56 | Meilisearch + similarity | 88.3% | 87.4% | Contextual | — | On |
| 79 | BM25 + similarity | 88.1% | 88.6% | Contextual | — | — |
| 73 | BM25 + similaritystarting point | 87.7% | 86.1% | Plain | — | — |
| 88 | Meilisearch + similarity | 86.1% | 84.7% | Plain | — | On, duplicates merged |
| 50 | Meilisearch + similarity | 85.9% | 84.7% | Plain | — | On |
| 43 | Similarity | 84.5% | 82.3% | Contextual | — | — |
| 25 | BM25 | 84.3% | 83.4% | Plain | — | — |
| 31 | BM25 | 84.0% | 83.6% | Contextual | — | — |
| 37 | Similarity | 83.1% | 81.8% | Plain | — | — |
| 61 | Meilisearch (freq.) + similarity | 82.5% | 80.0% | Plain | — | — |
| 67 | Meilisearch (freq.) + similarity | 80.9% | 80.0% | Contextual | — | — |
| 55 | Meilisearch + similarity | 78.6% | 76.7% | Contextual | — | — |
| 49 | Meilisearch + similarity | 78.3% | 74.8% | Plain | — | — |
| 85 | Meilisearch + similarity | 78.3% | 75.9% | Ctx. embeddings only | — | — |
| 18 | Keyword (Meilisearch, frequency) | 39.6% | 40.0% | Plain | tuned | On |
| 22 | Keyword (Meilisearch, frequency) | 39.6% | 38.8% | Contextual | untuned | On |
| 23 | Keyword (Meilisearch, frequency) | 39.6% | 40.0% | Contextual | tuned | — |
| 24 | Keyword (Meilisearch, frequency) | 39.6% | 40.0% | Contextual | tuned | On |
| 17 | Keyword (Meilisearch, frequency) | 39.4% | 39.4% | Plain | tuned | — |
| 16 | Keyword (Meilisearch, frequency) | 38.3% | 38.8% | Plain | untuned | On |
| 15 | Keyword (Meilisearch, frequency) | 38.0% | 37.2% | Plain | untuned | — |
| 20 | Keyword (Meilisearch, frequency) | 37.7% | 37.6% | Contextual | — | On |
| 14 | Keyword (Meilisearch, frequency) | 37.6% | 37.4% | Plain | — | On |
| 21 | Keyword (Meilisearch, frequency) | 36.6% | 36.4% | Contextual | untuned | — |
| 13 | Keyword (Meilisearch, frequency) | 35.4% | 35.5% | Plain | — | — |
| 19 | Keyword (Meilisearch, frequency) | 33.7% | 34.0% | Contextual | — | — |
| 12 | Keyword (Meilisearch) | 29.5% | 29.4% | Contextual | tuned | On |
| 10 | Keyword (Meilisearch) | 29.4% | 27.0% | Contextual | untuned | On |
| 11 | Keyword (Meilisearch) | 28.8% | 25.0% | Contextual | tuned | — |
| 6 | Keyword (Meilisearch) | 27.5% | 27.5% | Plain | tuned | On |
| 4 | Keyword (Meilisearch) | 26.9% | 25.0% | Plain | untuned | On |
| 9 | Keyword (Meilisearch) | 25.9% | 24.1% | Contextual | untuned | — |
| 8 | Keyword (Meilisearch) | 24.1% | 23.7% | Contextual | — | On |
| 5 | Keyword (Meilisearch) | 23.7% | 23.7% | Plain | tuned | — |
| 2 | Keyword (Meilisearch) | 20.7% | 20.9% | Plain | — | On |
| 3 | Keyword (Meilisearch) | 20.7% | 19.5% | Plain | untuned | — |
| 7 | Keyword (Meilisearch) | 18.9% | 16.9% | Contextual | — | — |
| 1 | Keyword (Meilisearch) | 15.9% | 16.0% | Plain | — | — |
Score cellsworsebetterRecall: share of the right documents found in the top 20 (or top 10) results · ↑ higher is better
Synthetic documents, questions and answer key. Search: Meilisearch 1.54, BM25, and Qdrant with bge-m3 for similarity. AI model for context sentences and filters: Qwen3.8-27B. Reranker: Voyage rerank-2.5-lite.
