0Pricing
AI Engineering Academy · Lesson

Cross-Encoder Re-ranking with Cohere and BGE

Integrate Cohere's rerank endpoint and the BGE-reranker model to score query-document pairs and reorder your top-k chunks by true relevance.

Cross-Encoder Re-ranking with Cohere and BGE is a free AI Engineering Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the AI Engineering Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is a Cross-Encoder?

A cross-encoder is a neural model that takes a query and a document as a single concatenated input and outputs a relevance score. Unlike a bi-encoder that embeds query and document separately, the cross-encoder's attention layers see both texts simultaneously, enabling it to model fine-grained relevance signals that a bi-encoder misses. This joint processing is what makes cross-encoders significantly more accurate at ranking.

# Bi-encoder: separate encoding
query_vec = encoder.encode(query)          # [768] vector
doc_vec = encoder.encode(document)         # [768] vector
score = cosine_similarity(query_vec, doc_vec)  # compare independently

# Cross-encoder: joint encoding
combined_input = '[CLS] ' + query + ' [SEP] ' + document + ' [SEP]'
logits = cross_encoder.forward(combined_input)  # score from joint attention
# The model attends from every query token to every document token

The Cohere Rerank API

Cohere Rerank is a cloud-hosted cross-encoder re-ranking API that accepts a query and a list of up to 1000 document texts and returns relevance scores. It uses large cross-encoder models trained on high-quality ranking datasets and consistently outperforms self-hosted small cross-encoders on most benchmarks. The API is priced per thousand documents re-ranked.

import cohere

co = cohere.Client('YOUR_COHERE_API_KEY')

candidates = [
    'How to configure pgvector in PostgreSQL for vector search',
    'BM25 scoring formula and hyperparameters explained',
    'Installing and using the pgvector extension for embeddings',
    'Hybrid search combining BM25 and dense retrieval',
]

result = co.rerank(
    model='rerank-english-v3.0',
    query='pgvector setup guide',
    documents=candidates,
    top_n=3,
    return_documents=True,
)

for item in result.results:
    print(f'Score {item.relevance_score:.4f}: {item.document.text[:60]}')

Cohere Rerank in a RAG Pipeline

Integrating Cohere Rerank into a RAG pipeline follows a simple pattern: retrieve 20-50 candidates from your vector store, extract their text, call the Cohere Rerank API, and take the top-K results to pass to the LLM. The latency overhead is typically 100-300ms, which is acceptable when the improved precision leads to better final answers.

import cohere
from openai import OpenAI

co = cohere.Client('COHERE_KEY')
client = OpenAI()

def rag_with_rerank(question: str, retriever, top_k: int = 5) -> str:
    # Stage 1: coarse retrieval
    candidates = retriever.invoke(question)  # returns 30 docs
    texts = [doc.page_content for doc in candidates]

    # Stage 2: Cohere re-ranking
    rerank_result = co.rerank(
        model='rerank-english-v3.0',
        query=question,
        documents=texts,
        top_n=top_k,
    )
    top_texts = [texts[r.index] for r in rerank_result.results]
    context = '\n\n'.join(top_texts)

    # Stage 3: generation
    response = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[
            {'role': 'system', 'content': 'Answer using only the provided context.'},
            {'role': 'user', 'content': f'Context:\n{context}\n\nQuestion: {question}'},
        ],
    )
    return response.choices[0].message.content

BGE Reranker: Open-Source Alternative

BGE Reranker (BAAI General Embedding) is an open-source family of cross-encoder models from the Beijing Academy of AI. The BAAI/bge-reranker-v2-m3 model supports multilingual re-ranking and achieves scores close to Cohere Rerank on English benchmarks while running locally. Use it with the sentence-transformers library for fully self-hosted re-ranking without API costs.

from sentence_transformers import CrossEncoder

# Load BGE reranker model
reranker = CrossEncoder(
    'BAAI/bge-reranker-v2-m3',
    max_length=512,
    device='cpu',  # use 'cuda' if GPU is available
)

def bge_rerank(query: str, documents: list[str], top_k: int = 5) -> list[dict]:
    pairs = [[query, doc] for doc in documents]
    scores = reranker.predict(pairs, show_progress_bar=False)
    ranked = sorted(
        zip(documents, scores.tolist()),
        key=lambda x: x[1],
        reverse=True,
    )
    return [{'text': doc, 'score': score} for doc, score in ranked[:top_k]]

BGE Reranker Model Variants

The BGE reranker family offers size-quality trade-offs. bge-reranker-base (278M parameters) is the fastest and smallest. bge-reranker-large (560M parameters) is more accurate. bge-reranker-v2-m3 supports multiple languages and is the recommended default for production. For maximum accuracy, bge-reranker-v2-gemma uses a Gemma backbone and leads most English benchmarks among open-source options.

# Model size vs accuracy trade-offs
models = [
    ('BAAI/bge-reranker-base',   '278M params', 'fast,   English-focused'),
    ('BAAI/bge-reranker-large',  '560M params', 'better, English-focused'),
    ('BAAI/bge-reranker-v2-m3',  '570M params', 'best for multilingual use'),
    ('BAAI/bge-reranker-v2-gemma', '2B params', 'SOTA open-source accuracy'),
]

# For most RAG applications, bge-reranker-v2-m3 offers the best
# balance of accuracy, multilingual support, and inference speed on CPU

Batching Cross-Encoder Predictions

Cross-encoders process (query, document) pairs one by one by default, but batch prediction dramatically improves throughput by running multiple pairs through the model simultaneously. The batch_size parameter controls how many pairs are processed together. On GPU, batch sizes of 32-128 are typical. On CPU, smaller batches of 4-16 reduce memory pressure while still improving throughput over sequential processing.

def batch_rerank(reranker, query: str, documents: list[str],
                 top_k: int = 5, batch_size: int = 32) -> list[dict]:
    pairs = [[query, doc] for doc in documents]

    # Batch prediction
    all_scores = reranker.predict(
        pairs,
        batch_size=batch_size,
        show_progress_bar=len(pairs) > 100,
    )

    ranked = sorted(
        zip(documents, all_scores.tolist()),
        key=lambda x: x[1],
        reverse=True,
    )
    return [{'text': doc, 'score': score} for doc, score in ranked[:top_k]]

LangChain CrossEncoderReranker Integration

LangChain provides CrossEncoderReranker as a document compressor that integrates cross-encoder re-ranking into the ContextualCompressionRetriever pattern. This lets you wrap any existing retriever with a re-ranking stage using just a few lines of code, and plug it into any LCEL chain that accepts a retriever interface.

from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
from langchain.retrievers import ContextualCompressionRetriever
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

# Base retriever (coarse stage)
base_retriever = FAISS.from_documents(
    documents, OpenAIEmbeddings()
).as_retriever(search_kwargs={'k': 30})

# Re-ranker (fine stage)
model = HuggingFaceCrossEncoder(model_name='BAAI/bge-reranker-v2-m3')
compressor = CrossEncoderReranker(model=model, top_n=5)

# Combined two-stage retriever
two_stage_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=base_retriever,
)

docs = two_stage_retriever.invoke('how does hybrid search work?')

Scoring Interpretation and Thresholding

Cross-encoder scores do not have a universal scale. BGE reranker outputs raw logits (often in the range -10 to +10) while Cohere returns a normalized relevance score between 0 and 1. You can apply a minimum score threshold to exclude very low-relevance documents from the context sent to the LLM, reducing noise even further. Start with a threshold at the 25th percentile of scores and tune from there.

def rerank_with_threshold(reranker, query, documents, top_k=5, min_score=-2.0):
    pairs = [[query, doc] for doc in documents]
    scores = reranker.predict(pairs)

    scored_docs = [
        {'text': doc, 'score': float(score)}
        for doc, score in zip(documents, scores)
        if float(score) >= min_score  # filter low-relevance docs
    ]

    scored_docs.sort(key=lambda x: x['score'], reverse=True)

    if not scored_docs:
        return []  # nothing passed the threshold
    return scored_docs[:top_k]

Cohere vs BGE: Which to Choose

Use Cohere Rerank when you want a managed API, need the highest accuracy on English enterprise data, and want to avoid GPU infrastructure. Use BGE Reranker when you need to self-host for data privacy, want zero per-query API cost, need multilingual support, or want to run offline. Both achieve similar NDCG scores on most benchmarks — the choice is primarily about infrastructure preferences and cost model.

Re-ranking Multilingual Content

If your corpus or users span multiple languages, choose a multilingual cross-encoder. bge-reranker-v2-m3 handles over 100 languages, and Cohere Rerank has a multilingual model variant. Cross-lingual re-ranking — scoring an English query against documents in French or Spanish — is also supported, which is useful in global enterprise deployments where knowledge bases exist in multiple languages.

from sentence_transformers import CrossEncoder

# Multilingual BGE reranker
ml_reranker = CrossEncoder('BAAI/bge-reranker-v2-m3')

# Cross-lingual example: English query, French document
query_en = 'How to configure vector search?'
doc_fr = 'La configuration de la recherche vectorielle dans PostgreSQL'

score = ml_reranker.predict([[query_en, doc_fr]])
print(f'Cross-lingual relevance score: {float(score):.3f}')
# Positive score indicates the document is relevant to the query
# despite being in a different language

Caching Re-ranking Results

Cross-encoder inference is the slowest part of a two-stage pipeline. For applications where the same (query, document) pairs recur frequently, caching re-ranking results can eliminate redundant inference. Hash the (query, document_id) pair as a cache key and store the score in Redis with a TTL. Cache hit rates of 30-50 percent are common in customer support applications where users frequently ask similar questions.

import redis
import hashlib
import json

r = redis.Redis(host='localhost', port=6379)

def cached_rerank_score(reranker, query: str, doc_text: str, doc_id: str) -> float:
    cache_key = 'rerank:' + hashlib.md5((query + doc_id).encode()).hexdigest()
    cached = r.get(cache_key)
    if cached:
        return float(cached)

    score = float(reranker.predict([[query, doc_text]]))
    r.setex(cache_key, 3600, str(score))  # cache 1 hour
    return score

Quick Check

Test your understanding of cross-encoder re-ranking from this lesson.

Lesson Recap

In this lesson you learned: Cohere Rerank provides a cloud-hosted cross-encoder API with state-of-the-art accuracy, BGE Reranker is an open-source alternative for self-hosted deployment including multilingual content, and batching dramatically improves throughput when scoring many (query, document) pairs. LangChain's CrossEncoderReranker integrates both into the ContextualCompressionRetriever pattern. Next up we implement contextual compression to reduce noise in retrieved chunks.

Frequently asked questions

Is the “Cross-Encoder Re-ranking with Cohere and BGE” lesson free?

Yes — the full text of “Cross-Encoder Re-ranking with Cohere and BGE” is free to read here on the web, and the AI Engineering Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the AI Engineering Academy course, upgrade to CoddyKit PRO.

What will I learn in “Cross-Encoder Re-ranking with Cohere and BGE”?

Integrate Cohere's rerank endpoint and the BGE-reranker model to score query-document pairs and reorder your top-k chunks by true relevance. You practise AI Engineering Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start AI Engineering Academy?

No prior experience is required. AI Engineering Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Cross-Encoder Re-ranking with Cohere and BGE” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this AI Engineering Academy lesson?

Yes. Every AI Engineering Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Why Two-Stage Retrieval Works
  2. Cross-Encoder Re-ranking with Cohere and BGE
  3. Contextual Compression and Relevance Filtering
  4. Measuring the Impact of Re-ranking
← Back to AI Engineering Academy