
Je suis Jules Ginhac, Co-Founder & ingénieur IA chez Apogée Consult à Lyon. Je conçois et déploie des architectures IA génératives (RAG, agents, LLMOps) pour des PME, startups et organisations publiques.
Reranking RAG : cross-encoder dédié ou LLM-as-judge, ce que coûte vraiment chaque approche
- rag
Cross-encoder ou LLM-as-judge pour reranker les résultats de votre RAG ? Comparaison honnête des coûts, latences et seuils de pertinence à partir de cas réels.
Reranking RAG : cross-encoder dédié ou LLM-as-judge, ce que coûte vraiment chaque approche
Le retrieval initial remonte les chunks les plus proches dans l'espace vectoriel, pas nécessairement les plus utiles pour répondre à la question posée. Le reranking est l'étape qui réordonne ce premier lot avant de l'envoyer au LLM.
Deux approches dominent : le cross-encoder dédié et le LLM-as-judge. Elles ne ciblent pas le même problème et ne coûtent pas pareil. Voici ce qu'on a mesuré.
Pourquoi le retrieval initial ne suffit pas
La similarité cosinus mesure la proximité sémantique entre la requête et le chunk. Elle ne mesure pas la capacité du chunk à répondre à la question. Ces deux choses divergent régulièrement.
Exemple concret : une requête "quel est le délai de garantie légale" remonte un chunk qui cite le délai dans un exemple négatif ("contrairement à la garantie légale de 2 ans, notre contrat prévoit..."). Similarité cosinus élevée, pertinence réelle nulle.
Un reranker examine la paire (question, chunk) ensemble et attribue un score de pertinence directe. C'est une tâche différente, et plus coûteuse.
Cross-encoder : le modèle dédié
Un cross-encoder prend la concaténation (question, chunk) et produit un score de pertinence. Il ne génère pas d'embedding séparé, il évalue directement la paire.
Modèles couramment utilisés :
BAAI/bge-reranker-v2-m3(Hugging Face) : multilingue, très bon rapport qualité/coût, tourne sur GPU A10G ou en CPU pour de petits volumescross-encoder/ms-marco-MiniLM-L-6-v2: rapide, léger, anglais seulement- Cohere Rerank API (docs) : service managé, facturé par appel
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")
def rerank_chunks(
query: str,
chunks: list[str],
top_n: int = 3,
) -> list[tuple[str, float]]:
"""
Reranke une liste de chunks par pertinence à la query.
On récupère top-10 au retrieval et on garde top-3 après reranking.
Le ratio 10→3 est empirique : en dessous de 5 candidats,
le reranker n'a pas assez de matière pour discriminer.
"""
pairs = [(query, chunk) for chunk in chunks]
scores = reranker.predict(pairs)
ranked = sorted(zip(chunks, scores), key=lambda x: x[1], reverse=True)
return ranked[:top_n]Latence : sur un GPU A10G, bge-reranker-v2-m3 traite 10 paires en 80-120 ms. En CPU, comptez 400-800 ms selon la longueur des chunks. Pour une application interactive avec une contrainte < 2 secondes end-to-end, le CPU est limite.
Coût Cohere : 0,002 USD pour 1 000 documents rerankés (grille tarifaire). Sur un volume de 50 000 requêtes/mois avec top-10, ça représente 1 USD/mois, négligeable.
LLM-as-judge : le modèle généraliste comme évaluateur
LLM-as-judge consiste à demander au LLM d'évaluer la pertinence de chaque chunk avant de le synthétiser. On passe les chunks un par un (ou en batch) avec un prompt d'évaluation.
JUDGE_PROMPT = """Tu es un évaluateur de pertinence documentaire.
Question : {query}
Extrait de document :
{chunk}
Cet extrait contient-il une information directement utile pour répondre à la question ?
Réponds uniquement par un JSON : {{"score": 0|1, "reason": "..."}}"""
async def llm_judge_chunk(
query: str,
chunk: str,
llm_client,
) -> dict:
response = await llm_client.chat.completions.create(
model="gpt-4o-mini", # modèle rapide et bon marché pour le jugement
messages=[{"role": "user", "content": JUDGE_PROMPT.format(
query=query,
chunk=chunk,
)}],
response_format={"type": "json_object"},
temperature=0,
)
return json.loads(response.choices[0].message.content)Latence : avec gpt-4o-mini, un jugement sur un chunk de 500 tokens prend 600-1200 ms. Sur 10 chunks en parallèle : 1,5-2 s selon la charge OpenAI. C'est significatif dans une boucle synchrone.
Coût : gpt-4o-mini coûte 0,15 USD / million de tokens input et 0,60 USD / million de tokens output (tarifs OpenAI). Un prompt de jugement avec un chunk de 500 tokens représente environ 700 tokens input. Sur 50 000 requêtes/mois avec top-10 chunks jugés : 50 000 × 10 × 700 = 350 millions de tokens → ~52 USD/mois. Non négligeable.
Comparatif synthétique
| Critère | Cross-encoder dédié | LLM-as-judge |
|---|---|---|
| Qualité de discrimination | Haute sur le domaine d'entraînement | Haute, flexible sur requêtes complexes |
| Latence (10 chunks) | 80-120 ms (GPU) / 400-800 ms (CPU) | 600-2000 ms (parallèle) |
| Coût opérationnel | Infra GPU ou API managée (~1 USD/mois à 50k req) | ~50 USD/mois à 50k req (gpt-4o-mini) |
| Multilingue | Selon le modèle (bge-v2-m3 : oui) | Oui, natif |
| Domaine spécialisé | Nécessite fine-tuning ou bon modèle générique | Prompt engineering suffisant |
| Explicabilité | Aucune (score brut) | Partielle (reason dans le JSON) |
Ce qu'on choisit et pourquoi
On utilise le cross-encoder dédié par défaut. Sur des corpus de documentation technique ou juridique francophone, bge-reranker-v2-m3 donne de bons résultats sans fine-tuning, et la latence GPU reste acceptable.
Le LLM-as-judge est pertinent dans deux cas précis : quand la requête nécessite un raisonnement multi-critères (pas seulement "est-ce pertinent" mais "est-ce pertinent ET récent ET applicable à ce contexte"), ou quand on a besoin d'une raison machine-readable pour logger la décision et la déboguer.
On n'utilise pas LLM-as-judge comme unique reranker en production sur un volume > 20 000 requêtes/mois, le coût devient inconfortable et la latence parallèle reste un risque.
Une limite réelle : les cross-encoders sont sensibles à la langue et au domaine. Un bge-reranker-v2-m3 sur un corpus de brevets techniques ou de textes médicaux peut sur-noter des chunks superficiellement proches mais incorrects sur le fond. Le LLM-as-judge avec un prompt métier spécifique dépasse le cross-encoder dans ces cas, mais à quel prix acceptable ?