Skip to main content
You have thousands of documents, and you need to find the one that answers a specific question. So how do you find it? 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. The rest of this cookbook walks through both steps, building a fast search shortlist first, then improving it with TypeSafe’s re-ranking. Along the way, you’re going to learn:
  • What fast search does, and why it isn’t the whole answer
  • What re-ranking is, and how it fits after a fast search step
  • How TypeSafe scores one candidate against a query, and how much that improves the result

Try it yourself

Open a query, candidate, and re-ranking question in the TypeSafe Playground

How do we find one document in thousands?

You have a pile of documents, and a query, a piece of text describing what you’re looking for. Somewhere in the pile is the one document that answers it. Checking every document against the query one at a time works, but doesn’t scale. Millions of documents means millions of comparisons per query. You can improve performance with a two-step approach:
  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 This cookbook tests that setup on a dataset of court opinions, in Re-ranking on a real example below. 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. Systems often combine both methods. This cookbook uses BM25 alone to keep the first step simple and focus on re-ranking. BM25 ranks passages by their shared words. The important point here is not which fast search method creates the shortlist, but that re-ranking examines only the passages on that shortlist.

What is re-ranking?

Re-ranking takes the shortlist fast search already produced and puts it in a better order. Instead of comparing the query against the whole corpus at once, it compares the query against each candidate on the shortlist individually, and sorts the shortlist 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 itself could come from asking a language model to look at the query and one candidate together, and judge how well that candidate answers the query. Re-ranking can find the best match on the shortlist even when it does not use exactly the same words as the query.

Re-ranking with TypeSafe

A re-ranker needs a comparable score for every query-candidate pair. A general-purpose language model can produce these scores, or rank the whole shortlist directly. For independent pair scoring, however, you need to define a scoring scale and prompt the model to apply the same standard to every candidate. Repeated calls can still produce different scores for the same pair, while general-purpose generation adds time and cost to a task that only needs one number.

What TypeSafe returns

With TypeSafe, the scoring request can remain 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 those criteria to every query-candidate pair and returns the noul directly. This gives the application the score it needs for sorting, without inventing a scoring scale for a general-purpose model. TypeSafe is built to perform this repeated scoring faster, cheaper, and more consistently. In simplified pseudocode, one TypeSafe scoring call looks like this:
TypeSafe reads the query and one candidate together against that question, and returns a noul. You can use this to re-rank a shortlist by running the same question against every candidate on it, then sorting the shortlist by the noul each call comes back with, highest first.
The diagram below shows how one request per candidate produces the scores used to reorder the shortlist.

Re-ranking on a real example

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

Setup

The first step installs the packages this walkthrough depends on.
  • bm25s and datasets build the fast search shortlist.
  • typesafe-sdk and cooksafe handle re-ranking and API caching.
  • matplotlib draws the result charts.
The next block sets up the TypeSafe client and the constants the rest of the walkthrough uses, such as which TypeSafe model to call and how large a shortlist fast search hands to the re-ranker. Calling TypeSafe needs a TYPESAFE_API_KEY.
The dataset used here is a real 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. The next cell builds the shortlist, using the technique described above:
  1. Load the corpus.
  2. Rank it against every query with BM25.
There’s no TypeSafe here yet, this is only the fast search step.
output

Fast search rarely ranks the right passage first

The chart shows where fast search puts the correct passage, out of 3,565 candidates. Fast search reliably narrows the corpus down to a shortlist that contains the right answer. It contains the right answer for 100% of the 40 queries. But that passage is rarely the top-ranked one on the shortlist, only 5% of the time. Re-ranking below only reorders the top 30 candidates already on the shortlist. It cannot add a passage that fast search did not select. Here, the shortlist contains the correct passage for all 40 queries, so re-ranking can focus on putting each one in a better position.

Re-ranking it with TypeSafe

Re-ranking scores every candidate on the shortlist against its query, then sorts by that score. The question TypeSafe asks about each pair is whether the candidate could be the passage the query’s removed citation points to. The next cell does the following:
  1. Define that question.
  2. Ask it once per candidate on every shortlist, 40 queries times 30 candidates, 1,200 calls in total, run concurrently instead of one after another.
  3. Sort each shortlist by the score TypeSafe returns, producing the re-ranked result.
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%
The reported token count and cost cover all 1,200 TypeSafe calls used to re-rank the 40 shortlists. Each CLERC row contains one correct passage and 20 negative passages. This walkthrough pools the passages from 170 rows into one shared corpus. For each of the 40 evaluation queries, BM25 selects 30 candidates from that full corpus, not only the 20 negatives supplied with that row. TypeSafe then reads the query against each selected candidate and re-ranks those 30 passages. This walkthrough asked one question per pair for clarity. A real application would often 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

The same building blocks show up elsewhere in TypeSafe’s docs:
  • 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.