Retrieve-and-Rerank diagramQuestion, Documents, Contextualizer, BM25 index, Vector index, Hybrid search, Reranker, Generator, Answer. Step captions follow the diagram.QuestionDocumentsContextualizerindex timeBM25 indexexact termsVector indexmeaningHybrid searchquery both + fuseReranker150 → 20GeneratorAnswerRetrieve-and-Rerank diagramQuestion, Documents, Contextualizer, BM25 index, Vector index, Hybrid search, Reranker, Generator, Answer. Step captions follow the diagram.QuestionDocumentsContextualizerindex timeBM25 indexexact termsVectorindexmeaningHybridsearchquery both+ fuseReranker150 → 20GeneratorAnswer

All steps

Wide net, then narrow

Index with context

Parts of this pattern
Question Input / output
The user's question at query time, such as 'What was ACME Corp's revenue growth in Q2 2023?'.
Documents Input / output
The corpus, split into chunks at index time, such as ACME Corp's quarterly filings.
Contextualizer LLM call
Reads each chunk with its whole document and writes a one-line context to prefix it, such as which company and quarter it is about.
BM25 index Memory
A keyword index over the prefixed chunks. It catches exact terms, names and codes that embeddings blur.
Vector index Memory
An embedding index over the same prefixed chunks. It catches paraphrases that share no keywords.
Hybrid search Aggregator
Runs the question against both indexes in parallel, then fuses the two candidate lists by chunk ID into one list of about 150 unique chunks.
Reranker Evaluator
Scores every candidate against the question, a slower but sharper relevance judgement, and keeps the top 20.
Generator LLM call
Answers from the 20 reranked chunks only, citing each.
Answer Input / output
The answer, citing the chunks it came from.
Documents → Contextualizer
Each chunk goes to the contextualizer together with its whole document.Chunk: 'The company's revenue grew by 3% over the previous quarter.' Document: ACME Corp 10-Q, Q2 2023
Contextualizer → BM25 index
The prefixed chunk is written to the keyword index.This chunk is from ACME Corp's Q2 2023 10-Q; the previous quarter's revenue was $314M. The company's revenue grew by 3%...
Contextualizer → Vector index
The same prefixed chunk, embedded, is written to the vector index.chunk-0412 -> [0.044, -0.102, 0.317, ...] with the same context prefix
Question → Hybrid searchquery
At query time the question goes to hybrid search, which runs it against both indexes at once.BM25: ACME Corp revenue growth Q2 2023 | vector: embed('What was ACME Corp's revenue growth in Q2 2023?')
BM25 index → Hybrid search150
Up to 150 keyword candidates, ranked by BM25 score.1. chunk-0412 (18.2) 2. chunk-0977 (15.1) 3. chunk-0413 (14.8) ...
Vector index → Hybrid search150
Up to 150 vector candidates, ranked by similarity.1. chunk-0977 (0.81) 2. chunk-0412 (0.79) 3. chunk-1530 (0.77) ...
Hybrid search → Reranker150
One fused list of unique candidates, still in cheap-retrieval order.150 chunks: chunk-0412, chunk-0977, chunk-0413, chunk-1530, ...
Reranker → Generatortop 20
Only the 20 best-scored chunks reach the generator, best first.1. chunk-0412 (9/10) ACME Q2 2023: revenue grew 3% 2. chunk-0413 (8/10) ACME Q2 2023 revenue table ...
Generator → Answer
The answer, with citations to the reranked chunks.ACME Corp's revenue grew 3% in Q2 2023 over the previous quarter [chunk-0412].

← → neighboursesc atlas

Knowledge & Memory

Retrieve-and-Rerank

Also known as Contextual retrieval · Two-stage retrieval · Hybrid search · Cross-encoder reranking · LLM reranking

Cast a wide net with cheap keyword and vector search over context-prefixed chunks, then let a reranker keep only the best few.

No single inventor · see source

Retrieving broadly and then reranking is long-standing information-retrieval practice with no single originator. Anthropic's 2024 post popularized the full pipeline with contextual chunk prefixes; using an LLM as the reranker was introduced by Weiwei Sun et al. (Shandong University, Baidu, Leiden University) in 2023.

Measured over one run

Nodes
9. Boxes in the diagram: models, tools, stores, people and plain code.
LLM calls
2. Model invocations in the first scenario: each time a model or agent node starts thinking, plus each branch it decides.
Tool calls
0. Messages sent to a tool node in the first scenario.
Steps
4. Steps in the first scenario, with repeated steps counted every time they run.
Scenarios
2. Distinct example runs you can switch between on the diagram.
Loop
no. Whether the flow cycles back on itself until a condition is met.
Parallel
yes. Whether two or more messages are in flight at the same time.
Branch
no. Whether the flow takes one path out of several, chosen at run time.
Human in the loop
no. Whether a person appears as a node, to approve, correct or answer.
Autonomy
low. How much the model decides the control flow itself, from fixed code paths (low) to open-ended (high).
Cost
medium. Relative token spend per request.
Latency
medium. Relative wall-clock time per request.

Counted from the first scenario's timeline when the site is built.

Problem

Cheap retrieval is either precise or broad, never both. A vector search for “What was ACME Corp’s revenue growth in Q2 2023?” finds chunks about revenue growth in general, from any company and any quarter. A keyword search finds “ACME” and “Q2 2023” but misses a chunk that never names them. Worse, a chunk cut from a filing may say only “revenue grew by 3% over the previous quarter”, which matches neither search well because it has lost its context. And if you raise the top-k to catch more, the generator drowns in near-misses.

How it works

The pattern has two halves.

At index time, a contextualizer reads each chunk of the documents together with its whole document and writes a one-line prefix: “This chunk is from ACME Corp’s Q2 2023 10-Q; the previous quarter’s revenue was $314M.” The prefixed chunk goes into a BM25 keyword index and a vector index. Now the orphaned “grew by 3%” chunk carries the company and the quarter, so both searches can find it. Anthropic calls this contextual retrieval.

At query time, hybrid search runs the question against both indexes at once, casting a wide net: about 150 candidates from each, which it fuses into one list. Then the reranker does the expensive part. It reads every candidate alongside the question, with a cross-encoder or an LLM, and keeps only the best 20. Fast retrieval maximizes recall, and the slow reranker restores precision. The generator answers from those 20, and the answer cites them.

In Anthropic’s tests, contextual embeddings cut top-20 retrieval failures by 35%, adding contextual BM25 brought that to 49%, and adding reranking brought it to 67%.

When to use

  • The corpus is too large to put in the prompt. Anthropic suggests about 200,000 tokens as the threshold below which you can skip retrieval entirely.
  • Plain vector search misses exact terms such as product names, error codes or ticker symbols.
  • Precision in the top few results matters, because the generator trusts whatever it is given.

When to avoid

  • The corpus fits in the context window. Put it all in the prompt and use prompt caching instead.
  • The latency budget cannot absorb a reranker scoring 150 candidates.
  • The corpus changes constantly, and re-running the contextualizer over every new chunk is too expensive.

Trade-offs

The contextualizer costs one model call per chunk, but only once, at index time, and prompt caching on the shared document makes it cheap. Keeping two indexes in sync costs operational effort. The reranker adds latency to every query, though a cross-encoder scoring 150 short chunks is usually far cheaper than a generation call. In return, you get the recall of broad search with the precision of a careful judge.

Failure modes

  • Bad prefixes: the contextualizer invents context the document does not support, and chunks get indexed under the wrong company or date. Keep prefixes short, and check a sample.
  • Narrow net: the first stage returns too few candidates, so the right chunk never reaches the reranker. Recall is set by the first stage; the reranker can only reorder what it is given.
  • Index drift: a chunk is updated in one index but not the other. Write to both under one chunk ID, in one job.
  • Reranker as single point of failure: when it times out, the request fails. Fall back to the fused order.

Build it with your AI

Paste this into Claude Code, Cursor, ChatGPT or any AI coding assistant.

Implement the Retrieve-and-Rerank pattern in this codebase, using the LLM client and language we already use.

## Goal

Answer questions over a large corpus by indexing context-prefixed chunks in both a keyword index and a vector index, retrieving a wide candidate set from both, reranking it, and answering from the top few.

## How it works

1. At index time, split each document into chunks. For each chunk, call the contextualizer model with the Chunk context prompt, passing the whole document and the chunk, and prepend the short context it returns.
2. Write every context-prefixed chunk to a BM25 keyword index and, embedded, to a vector index, under the same chunk ID.
3. At query time, search both indexes with the question in parallel, taking the top 150 candidates from each.
4. Fuse the two lists by chunk ID with Reciprocal Rank Fusion and keep the top 150 unique chunks.
5. Score each candidate against the question with a reranker (a cross-encoder model, or an LLM with the LLM reranker prompt) and keep the 20 highest-scoring chunks.
6. Call the generator model with the Grounded answer prompt, passing the question and the 20 chunks, and return its answer.

## Prompts

### Chunk context prompt

```markdown
<document>
{{whole_document}}
</document>

Here is the chunk we want to situate within the whole document:
<chunk>
{{chunk}}
</chunk>

Give a short, succinct context to situate this chunk within the overall
document, for the purpose of improving search retrieval of the chunk.
Answer only with the succinct context and nothing else.
```

### LLM reranker prompt

```markdown
Rate how well the passage answers the question.

<question>{{question}}</question>
<passage>{{passage}}</passage>

Reply with a single integer from 0 to 10, where 10 means the passage
directly contains the answer and 0 means it is unrelated. Judge only
relevance to this question, not the passage's general quality.
```

### Grounded answer prompt

```markdown
Answer the question using only the passages below, which are ranked
from most to least relevant.

<question>{{question}}</question>
<passages>{{passages}}</passages>

Cite the passage ID after each claim. If the passages do not answer the
question, say so.
```

## Stop rule

Stop after one retrieval, one rerank and one generation. If the best reranker score is below the relevance floor, say the corpus has no answer instead of generating.

## Requirements

- Make the candidate count (default 150), the kept count (default 20) and the relevance floor configurable.
- Cache the contextualizer calls, for example with prompt caching on the whole document, since every chunk of a document repeats it.
- Index the prefix and chunk text together in both indexes, and pass the prefixed chunk to the generator so it knows the source.
- If the reranker fails or times out, fall back to the fused order instead of failing the request, and log that it did. If the LLM reranker's reply for a candidate is not an integer from 0 to 10, score that candidate 0 and log it.
- Add a test with fake BM25 and vector indexes and a stub reranker that scores one chunk highest although it ranks 140th after fusion; assert that chunk is first in the 20 passed to the generator, and exactly 20 are passed.

Prompt templates

Chunk context prompt
<document>
{{whole_document}}
</document>
Here is the chunk we want to situate within the whole document:
<chunk>
{{chunk}}
</chunk>
Give a short, succinct context to situate this chunk within the overall
document, for the purpose of improving search retrieval of the chunk.
Answer only with the succinct context and nothing else.
LLM reranker prompt
Rate how well the passage answers the question.
<question>{{question}}</question>
<passage>{{passage}}</passage>
Reply with a single integer from 0 to 10, where 10 means the passage
directly contains the answer and 0 means it is unrelated. Judge only
relevance to this question, not the passage's general quality.
Grounded answer prompt
Answer the question using only the passages below, which are ranked
from most to least relevant.
<question>{{question}}</question>
<passages>{{passages}}</passages>
Cite the passage ID after each claim. If the passages do not answer the
question, say so.

Related patterns

Composes well with:

Sources

  1. Introducing Contextual Retrieval (Anthropic, 2024)
  2. Is ChatGPT Good at Search? Investigating Large Language Models as Re-Ranking Agents (Sun et al., 2023)

From the Agentic Atlas by Joran Kikke · Edit on GitHub · Suggest a correction