Production RAG retrieves cheaply first (BM25 or vectors), then reranks the top few candidates with an expensive cross-encoder that reads the query and document together.
Implement rerank(query, docs, first_scores, cross_score, top_n, alpha=1.0):
top_n docs by first_scores (ties: lower index first).cross_score(query, doc). Only call it for those top_n docs, because it's expensive.alpha * cross_norm + (1 - alpha) * first_norm. Sort the top_n by final score, highest first, breaking ties by first-stage rank.