Skip to main content
You have thousands of documents, and you need to find the one that answers a specific question. First, use a quick method such as keyword matching to cut those thousands of candidates down to a shortlist of plausible ones. We call this fast search. Fast search is good at that, but it can’t tell you which candidate on the shortlist is correct. That’s where re-ranking comes in. It scores every candidate on the shortlist against the query directly, and puts the best one first. Both steps run below on 3,565 court opinion passages from the CLERC dataset: BM25 builds a fast search shortlist of 30 candidates for each of 40 queries, then TypeSafe re-ranks each shortlist. With re-ranking, the correct passage lands in first place for 18% of queries, up from 5% with fast search alone.

Try it yourself

Open this example in the TypeSafe Playground

How do we find one document in thousands?

Checking every document against the query, one at a time, takes one comparison per document: millions of documents means millions of comparisons per query. Two steps are faster:
  1. Cut the pile down to a short list of likely candidates, using a method fast enough to run on the whole pile.
  2. Apply a more accurate step to that short list, to find the exact right answer.
Animated diagram: a pile of documents narrows to a fast search shortlist, then re-ranking
reorders that shortlist so the correct answer rises to the
top Fast search is any method that can compare a query against every document in a large corpus and quickly return a ranked shortlist. Common methods include keyword search, such as BM25, and dense embeddings, which compare passages by meaning. The first step here is BM25 and nothing else. BM25 ranks passages by shared words. The choice of fast search method is a side issue: re-ranking only ever sees the passages that make the shortlist.

What is re-ranking?

Re-ranking puts the shortlist in a better order. Instead of comparing the query against the whole corpus at once, it scores the query against each candidate on the shortlist and sorts by that score. Diagram: a ranked shortlist on the left, an arrow labeled "re-rank," and the re-ordered
version on the right with the true answer moving from the middle to the
top The score can come from a language model. Give it the query and one candidate together and ask how well the candidate answers the query. Re-ranking then finds the best match on the shortlist even when its wording differs from the query’s.

Re-ranking with TypeSafe

A re-ranker needs a comparable score for every query-candidate pair.

What TypeSafe returns

With TypeSafe, the scoring request is a yes/no question:
A plain yes or no would not be enough to rank 30 candidates. A Noul instead returns a number between 0 and 1, called a noul. The noul is TypeSafe’s estimate of how likely the answer is to be yes. The question’s criteria define what counts as true and false. TypeSafe applies them to every query-candidate pair and returns the noul directly. That noul is the score the application sorts on. In simplified pseudocode, one TypeSafe scoring call looks like this:
To re-rank a shortlist, ask the same question about every candidate and sort by noul, highest first:

A re-ranking example

Fast search and re-ranking now run on CLERC, a legal retrieval dataset. This example uses 3,565 court opinion passages and 40 queries.

Setup

  • bm25s and datasets build the fast search shortlist.
  • typesafe-sdk and cooksafe handle re-ranking and API caching.
  • matplotlib draws the result charts.
To replay the published numbers without an API key, download json_cache.json from typesafe-public-examples into the folder you run the script from. Calling TypeSafe needs a TYPESAFE_API_KEY.
The dataset used here is a corpus of US court opinions, 170 rows pooled together. Each row breaks down like this:
  • Query: an opinion excerpt with a citation removed.
  • Gold: the passage the removed citation pointed to, the one correct answer to the query.
  • Candidates: every other passage in the corpus, each one something the query could be matched against by mistake.
Of the 170 rows, 40 are picked to evaluate as queries. The other 130 only ever appear as candidates. Fast search loads the corpus and ranks it against every query with BM25. TypeSafe isn’t involved yet.
output

Fast search is unlikely to rank the right passage first

The chart shows where fast search puts the correct passage, out of 3,565 candidates. The shortlist contains the right answer for 100% of the 40 queries, but ranks it first only 5% of the time. Re-ranking only reorders the top 30 candidates already on the shortlist. It cannot add a passage that fast search did not select.

Re-ranking the shortlist with TypeSafe

The question TypeSafe asks about each pair is whether the candidate could be the passage the query’s removed citation points to. Re-ranking takes three steps:
  1. Define that question.
  2. Ask it once per candidate on every shortlist: 40 queries times 30 candidates, or 1,200 calls, run concurrently.
  3. Sort each shortlist by the noul TypeSafe returns.
output

Re-ranking moves the right answer toward the top

The chart compares fast search against fast search plus re-ranking, at three thresholds. Re-ranking moves the correct passage closer to the top at every one of them:
  • Top 1: 5% → 18%
  • Top 5: 15% → 35%
  • Top 10: 38% → 62%
This walkthrough asked one question per pair for clarity. A real application would ask several questions about the same pair in one call. See the parallel questions cookbook and the Speculative Fan-Out pattern for how.

What’s next

  • Noul, for how TypeSafe turns a yes/no question into a score.
  • Speculative Fan-Out, for asking several questions about one document in a single call.
  • Line-by-line Search, for another way to search a corpus by meaning rather than keywords.