Imagine: you ask a chatbot "Which standard describes the JWT format?" and it gives a lengthy answer about cookies and sessions, without ever mentioning RFC 7519. Sound familiar? This is a classic failure of pure vector search. Embeddings capture meaning well, but they falter on exact terms, abbreviations, and numbers. In production, such mistakes are costly: users lose trust, and businesses lose money. There is a solution — hybrid search combining BM25 and dense vectors, followed by a cross-encoder reranker. This is exactly what the course RAG Systems from Scratch on the asibiont.com platform is dedicated to.
Why pure vector search fails
Vector embeddings (e.g., from sentence-transformers) convert text into high-dimensional vectors, where proximity reflects semantic similarity. This is powerful for general queries, but there's a catch: the model can "blur" an exact term. For instance, the query "error 0x80070005" after embedding becomes just "access error," and the search returns articles about administrator rights instead of the specific code. BM25 (Okapi BM25) — a probabilistic model based on term frequency and inverse document frequency — on the contrary, precisely finds rare words. But it doesn't understand synonyms and context.
The question: how to combine the best of both worlds? The answer is hybrid search with Reciprocal Rank Fusion (RRF). RRF combines ranked lists from BM25 and vector search, assigning each document a score using the formula 1/(k + rank), where k is a constant (usually 60). This is a robust and simple method that requires no training.
How to build a hybrid pipeline in Python
The course "RAG Systems from Scratch" provides a step-by-step recipe. Here are the key steps:
- Chunking — split documents into fragments (e.g., 512 tokens with overlap).
- BM25 indexing — use the
rank_bm25library to build a sparse index. - Vector index — generate embeddings via
sentence-transformers(modelall-MiniLM-L6-v2orintfloat/multilingual-e5-largefor Russian) and store in Qdrant. - Search — for a query, get two lists: top-k from BM25 and top-k from vector search.
- Reciprocal Rank Fusion — combine the results.
- Reranking — pass the top-100 through a cross-encoder (e.g.,
cross-encoder/ms-marco-MiniLM-L-6-v2), which evaluates the relevance of the "query-document" pair and reorders the output.
Example code for RRF:
from rank_bm25 import BM25Okapi
from qdrant_client import QdrantClient
from sentence_transformers import SentenceTransformer, CrossEncoder
# Initialization
bm25 = BM25Okapi(tokenized_corpus)
encoder = SentenceTransformer('intfloat/multilingual-e5-large')
cross_encoder = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
def hybrid_search(query, top_k=10):
# BM25
bm25_scores = bm25.get_scores(query.split())
bm25_ranks = sorted(range(len(bm25_scores)), key=lambda i: bm25_scores[i], reverse=True)[:top_k]
# Vector search
query_vec = encoder.encode(query)
vector_results = qdrant.search(collection_name='docs', query_vector=query_vec, limit=top_k)
vector_ranks = [hit.id for hit in vector_results]
# RRF
rrf_scores = {}
for rank, doc_id in enumerate(bm25_ranks):
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + 1/(60 + rank + 1)
for rank, doc_id in enumerate(vector_ranks):
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + 1/(60 + rank + 1)
fused = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)[:top_k]
# Reranking
pairs = [(query, docs[doc_id]) for doc_id, _ in fused]
rerank_scores = cross_encoder.predict(pairs)
reranked = [doc_id for (doc_id, _), _ in sorted(zip(fused, rerank_scores), key=lambda x: x[1], reverse=True)]
return reranked
Quality metrics: recall@k and nDCG
To ensure the pipeline works, metrics are needed. Recall@k shows what proportion of relevant documents we found in the top-k. nDCG (normalized Discounted Cumulative Gain) takes position into account: the higher the relevant document, the better. In the course, you will learn to build your own labeled dataset and evaluate these metrics. Without them, it's impossible to know if reranking helps or if you're just wasting resources.
How learning on asibiont.com works
The asibiont.com platform uses AI generation of personalized lessons. The neural network analyzes your level, goals, and pace, then creates text lessons that explain complex topics in simple language. No videos — only text and practice. Access 24/7: learn when convenient. The AI adapts the program: if you're already familiar with embeddings, it skips the basics and goes straight to hybrid search. If something is unclear, you can ask a question — the AI will answer and provide additional examples. This is not just a course, but an adaptive trainer.
Who the course "RAG Systems from Scratch" is for
- Developers who want to build production RAG systems, not just demos.
- Data Scientists looking to deepen their knowledge in information retrieval and NLP.
- Technical leads who need to make architecture decisions.
- Students and researchers interested in modern search approaches.
The course does not require deep mathematical knowledge, but basic Python is necessary. You will learn not only to build a pipeline but also to evaluate it, monitor it, and deploy it to production with caching.
Why AI learning is effective
Traditional courses follow a fixed program. AI learning on asibiont.com is dynamic: the neural network sees where you stumble and offers additional exercises. It explains BM25 through an analogy with keyword search in a library, and RRF through a voting metaphor. This accelerates understanding. Plus, you get instant feedback on your code.
Hybrid search with RRF and reranking is not academic theory but a working tool. Many companies already use it in their products, from legal assistants to tech support. By mastering these skills, you will become a sought-after specialist.
Ready to level up your RAG systems? Start learning at RAG Systems from Scratch — and within a few days, you'll be able to build hybrid search that doesn't fail on exact queries.
Comments