Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

BM25
A word-matching score that ranks a document higher when it contains rare query words, with a cap on repetition.
chunk
A short passage cut from a longer document, small enough to retrieve and cite on its own.
citation
A pointer from an answer back to the exact passage it came from, so a reader can check it.
context window
The fixed amount of text a model can read in one request, counting the question and everything retrieved.
corpus
The whole collection of documents a system searches.
cosine similarity
A score from -1 to 1 for how closely two arrows point in the same direction, ignoring their lengths.
dot product
Multiply matching numbers in two lists and add the results; a larger total means closer agreement.
embedding
A list of numbers that places a piece of text in a space where similar meanings sit close together.
grounded answer
A reply built only from the passages that were retrieved, so its claims can be traced to them.
index
A structure built ahead of time so the system can find relevant passages without reading everything.
lexical search
Finding passages by the words they contain, matching the query's terms rather than its meaning.
magnitude
The length of a vector, found by squaring its numbers, adding them and taking the square root.
reranking
Scoring a short list of results again with a slower, more careful method and putting the best first.
retrieval
The step that selects passages from the corpus to put in front of the model.
top-k
The shortlist a search returns: its best few results, cut off at a number chosen in advance.
vector
An ordered list of numbers, where each position is one measured property.

RAG from the ground up

Better retrieval

Vectors miss exact terms; keywords miss synonyms. Combine them, then reorder the shortlist with a model that reads the query and the passage together.

Part 5 of 10

Advanced17 min readBack to Courses

Two ways to fail

A vector search for “ERR_CONNECTION_RESET” is a guess: the token is rare, and its embedding sits near other network errors rather than at the exact string. A keyword search for “how do I get my money back” finds nothing, because the document says “refund”.

Each method is strong where the other is weak. Vectors capture meaning and tolerate paraphrase; lexical search captures exact tokens, codes, names and numbers. A system that uses only one has a predictable blind spot.

The blind spot is predictable enough to test for. Take twenty real questions and split them into two piles: ones that hinge on an exact string and ones that hinge on a paraphrase. A vector-only system will fail the first pile; a keyword-only system will fail the second. The failures are not random, which means the fix is not a better prompt — it is the other retriever.

BM25 is still the baseline

BM25 scores a document by the query terms it contains, weighted by how rare each term is across the corpus, and normalised so a long document does not win by length alone. It is decades old, needs no training, and is hard to beat on exact-match queries.

Its weakness is vocabulary: it only matches words that appear. That is why it is paired with vectors rather than replaced by them.

The two weights inside BM25 are worth knowing by name. Term frequency saturates: the tenth mention of “refund” adds much less than the second, because a document that says it ten times is not ten times more relevant. Length normalisation divides by document length, so a long page does not win just by containing more words. Both are why BM25 beats a raw word count.

Hybrid retrieval with a reranker

from langchain.retrievers import EnsembleRetriever, ContextualCompressionRetriever
from langchain_cohere import CohereRerank

hybrid = EnsembleRetriever(
    retrievers=[bm25_retriever, vector_retriever],
    weights=[0.4, 0.6],
)

reranked = ContextualCompressionRetriever(
    base_compressor=CohereRerank(model="rerank-english-v3.0", top_n=4),
    base_retriever=hybrid,
)

docs = reranked.invoke("ERR_CONNECTION_RESET on upload")

BM25 in twenty lines

Score the documents, then add a query term that appears in every document and watch its contribution fall — that is the inverse document frequency at work.

Output
Click "Run" to execute your code

Fusion without a shared score

A vector score and a BM25 score are not comparable — one is a cosine, the other an unbounded sum. Adding them, even with weights, is arbitrary. Reciprocal rank fusion avoids the problem by using only positions: each document scores 1 divided by a constant plus its rank in each list.

It sounds too simple to work, and it works well. What matters is that a document found by both methods rises, and a document found by one still appears.

A concrete comparison shows the effect. With the usual constant of 60, a document ranked first by one retriever and fourth by the other scores about 0.0320; a document ranked second by both scores about 0.0323 and wins. Consensus beats a single strong vote, which is the behaviour you want when the two methods fail in different ways.

Query rewriting, the cheapest lever

Before adding another retriever, try rewriting the query. A short follow-up such as “and the team plan?” is missing the subject, and no retriever can find what the query never said. A rewriting step that expands it to “how much does the team plan cost” costs one cheap model call and often moves recall more than a reranker.

The same step handles vocabulary mismatch. Ask the model for three paraphrases, search all of them, and fuse the results. It is the same reciprocal rank fusion as above, applied to one retriever instead of two.

Retrieve wide, rerank narrow. Ask each retriever for twenty candidates, fuse them, then let the reranker choose the four that reach the prompt. Reranking is the expensive step, so it should see a short list.

Reranking reads both sides

A bi-encoder embeds the query and the document separately, which is what makes searching millions of vectors fast. A cross-encoder reads the pair together and scores relevance directly. It is far more accurate and far too slow to run over a corpus.

That is the whole architecture: cheap recall over everything, expensive precision over the shortlist. Skipping the second stage is how a system returns four passages that are all about the right topic and none of which answers the question.

The cost is per pair, not per query, so the shortlist length is the budget. Reranking twenty candidates is twenty forward passes; reranking a thousand is a thousand, and the latency shows. That is why the retriever asks for twenty and the reranker returns four, rather than the reranker scanning the corpus.

Fuse two rankings by position

The two retrievers disagree. Fuse them with reciprocal rank fusion, then add a third list and confirm the documents found by several lists rise.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Which query is a vector search most likely to miss?