0Pricing
AI Prompt Engineering · Leçon

Réordonner les fragments récupérés

Réordonnancement par encodeur croisé.

Réordonner les fragments récupérés est une leçon AI Prompt Engineering gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Prompt Engineering, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Prompt Engineering comprend 4 leçons au total.

Pourquoi reclasser ?

La récupération de première étape (dense ou creuse) optimise le rappel à grande échelle : faire apparaître le segment de référence parmi les 50 premiers. Elle est rapide mais grossière. Un reclasseur de deuxième étape réordonne ensuite cette présélection pour améliorer la précision et placer les segments réellement pertinents en tête.

Cette approche consistant à récupérer largement puis à reclasser précisément constitue la colonne vertébrale du RAG avancé.

def two_stage(query, k_retrieve=50, k_final=5):
    candidates = first_stage_retrieve(query, k_retrieve)  # high recall
    reranked = rerank(query, candidates)                  # high precision
    return reranked[:k_final]

Bi-encodeur ou cross-encodeur

Un bi-encodeur encode séparément la requête et le document en vecteurs, puis les compare par cosinus ; il est rapide et indexable, mais perd l’interaction entre la requête et le document. Un cross-encodeur transmet ensemble la requête et un candidat au modèle et produit un score de pertinence, ce qui permet de saisir les interactions fines.

Les cross-encodeurs sont bien plus exacts, mais ne peuvent pas être précalculés ; ils ne sont donc exécutés que sur la présélection.

# Bi-encoder: score = cos(enc(q), enc(d))     -> precomputable
# Cross-encoder: score = model(q, d) -> 0..1   -> per-pair, no index
def cross_encode(query, doc):
    return cross_encoder.predict([(query, doc)])[0]  # joint attention

Une passe de reclassement par cross-encodeur

Le reclasseur attribue un score à chaque candidat par rapport à la requête, puis les trie par ordre décroissant. Comme le cross-encodeur porte conjointement son attention sur la requête et le document, il résout des nuances de pertinence que le bi-encodeur a manquées : négation, besoins de correspondance exacte et distinctions entre réponse et sujet.

Cette étape améliore généralement l’exactitude des réponses davantage que toute autre amélioration individuelle du RAG.

def rerank(query, candidates):
    pairs = [(query, c.text) for c in candidates]
    scores = cross_encoder.predict(pairs)        # batched
    for c, s in zip(candidates, scores):
        c.rerank_score = s
    return sorted(candidates, key=lambda c: c.rerank_score, reverse=True)

LLM comme reclasseur

Lorsqu’aucun cross-encodeur entraîné ne convient à votre domaine, un LLM peut effectuer le reclassement. Le classement par liste demande au modèle d’ordonner une liste de passages selon leur pertinence en un seul appel ; l’évaluation élément par élément attribue un score à chaque passage indépendamment.

Le classement par liste saisit les comparaisons relatives et économise des jetons, mais surveillez le biais lié à la position et veillez à ce que l’analyse de la sortie reste robuste lorsque le modèle omet ou duplique des identifiants.

def llm_listwise(query, candidates):
    passages = '\n'.join(
        '[' + str(i) + '] ' + c.text for i, c in enumerate(candidates)
    )
    prompt = (
        'Rank the passages by relevance to the query. '
        'Return only IDs, most relevant first.\nQuery: ' + query +
        '\n' + passages
    )
    order = parse_ids(llm(prompt, temperature=0))
    return [candidates[i] for i in order]

Latence et taille de la présélection

Le coût du reclassement augmente avec la taille de la présélection. Un cross-encodeur appliqué à 50 candidats coûte bien moins cher que s’il est appliqué à 500. Choisissez une valeur de k suffisamment grande à la première étape pour récupérer le segment de référence (validez le rappel@k), mais suffisamment petite pour permettre le reclassement dans votre budget de latence.

Regroupez par lots le calcul des scores des paires et exécutez-le sur du matériel accéléré ; les cross-encodeurs se parallélisent bien entre les paires.

def tune_shortlist(eval_set, ks=(20, 50, 100, 200)):
    # find smallest k where recall@k saturates -> rerank fewer pairs
    return {k: (recall_at_k(eval_set, k), rerank_latency(k)) for k in ks}

Première étape hybride et réordonnancement

Le meilleur rappel est obtenu grâce à une première étape hybride (récupération dense et BM25 fusionnées), qui alimente une seule liste restreinte dédupliquée pour le réordonnanceur. La recherche dense récupère les paraphrases, tandis que la recherche creuse récupère les identifiants exacts ; l’encodeur croisé trie ensuite l’union selon la pertinence réelle.

Cette combinaison est robuste pour différents types de requêtes, des questions en langage naturel aux recherches littérales.

def hybrid_then_rerank(query, k_final=6):
    dense = dense_retrieve(query, 50)
    sparse = bm25_retrieve(query, 50)
    fused = dedup(rrf(dense, sparse))     # reciprocal rank fusion
    return rerank(query, fused)[:k_final]

Seuils de score et limites

Les scores du réordonnanceur peuvent être calibrés. Au lieu de toujours conserver les premiers n résultats, appliquez un seuil de pertinence : gardez les segments dont le score dépasse une valeur donnée et, si aucun ne le fait, renvoyez honnêtement l’absence de réponse. Vous éviterez ainsi de surcharger l’invite avec du contenu faiblement pertinent.

Ajustez le seuil sur un jeu de validation afin d’équilibrer la couverture des réponses possibles et l’inclusion de distracteurs.

def threshold_select(reranked, tau=0.3, max_n=8):
    kept = [c for c in reranked if c.rerank_score >= tau][:max_n]
    if not kept:
        return None        # signal: no sufficiently relevant context
    return kept

Diversité après le réordonnancement

Un tri fondé uniquement sur la pertinence peut renvoyer plusieurs segments presque identiques provenant du même document, ce qui gaspille le budget de contexte. Appliquez MMR ou des plafonds par document après le réordonnancement afin que l’ensemble final couvre des facettes et des sources distinctes.

C’est important pour les questions à plusieurs étapes dont la réponse s’étend sur plusieurs documents.

def diversify(reranked, max_per_doc=2, k=6):
    out, per_doc = [], {}
    for c in reranked:
        d = c.meta['doc_id']
        if per_doc.get(d, 0) < max_per_doc:
            out.append(c)
            per_doc[d] = per_doc.get(d, 0) + 1
        if len(out) == k:
            break
    return out

Ordre destiné au générateur

Après avoir sélectionné les meilleurs segments, placez-les de manière à exploiter l’attention. Compte tenu de l’effet de perte au milieu, placez le segment ayant le score le plus élevé au début ou à la fin du contexte, plutôt que de l’enfouir parmi les autres.

Certaines chaînes de traitement classent les segments par pertinence croissante afin que le meilleur soit placé au plus près de la question, suivant une stratégie de récence des exemples limités.

def order_for_llm(chunks):
    chunks = sorted(chunks, key=lambda c: c.rerank_score)  # ascending
    return chunks                 # most relevant chunk ends up last,
                                  # nearest the trailing question

Évaluer le réordonnanceur

Évaluez le réordonnanceur à l’aide de mesures de classement, principalement NDCG et MRR, sur la pertinence annotée entre les requêtes et les segments, puis mesurez la justesse des réponses en aval. Un réordonnanceur qui améliore NDCG mais pas les réponses réorganise peut-être des segments que le générateur traitait déjà correctement.

Évaluez toujours la qualité de la tâche finale, et pas seulement les mesures de classement.

import math

def ndcg_at_k(relevances, k):
    dcg = sum(r / math.log2(i + 2) for i, r in enumerate(relevances[:k]))
    ideal = sorted(relevances, reverse=True)
    idcg = sum(r / math.log2(i + 2) for i, r in enumerate(ideal[:k]))
    return dcg / idcg if idcg else 0.0

Une chaîne de réordonnancement en production

De bout en bout : récupérez une liste restreinte hybride de 50 candidats, dédupliquez-la, réordonnez-la avec un encodeur croisé, appliquez un seuil de score, diversifiez-la par document, ordonnez les segments pour exploiter l’attention, puis générez avec des citations. Utilisez le seuil comme condition pour renvoyer honnêtement l’absence de réponse.

Mettez en cache les représentations vectorielles et les scores du réordonnanceur pour chaque paire (requête, segment) lorsque le trafic se répète, afin de réduire les coûts.

def pipeline(query):
    shortlist = hybrid_then_rerank(query, k_final=20)
    kept = threshold_select(shortlist, tau=0.3, max_n=8)
    if kept is None:
        return 'No relevant information found.'
    ctx = order_for_llm(diversify(kept))
    return generate_with_citations(query, ctx)

Vérification rapide

Choisissez la bonne architecture de réordonnancement.

Récapitulatif

Points clés à retenir :

  • Récupérez largement pour maximiser le rappel, puis réordonnez la liste restreinte pour améliorer la précision.
  • Les encodeurs croisés évaluent conjointement les paires requête-document (précis, mais non indexables) ; les encodeurs doubles sont rapides, mais sommaires.
  • Le réordonnancement par liste ou par point avec un LLM est une solution de repli ; surveillez le biais de position et l’analyse des résultats.
  • Ajustez la taille de la liste restreinte pour saturer le rappel dans les limites de latence ; combinez-la avec une récupération hybride à la première étape.
  • Appliquez des seuils de score, diversifiez par document, ordonnez les segments pour l’attention et évaluez avec NDCG ainsi qu’avec la justesse des réponses en aval.

Questions Fréquemment Posées

La leçon « Réordonner les fragments récupérés » est-elle gratuite ?

Oui — le texte complet de « Réordonner les fragments récupérés » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Prompt Engineering, passe à CoddyKit PRO. Le cours AI Prompt Engineering comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Réordonner les fragments récupérés » ?

Réordonnancement par encodeur croisé. Tu pratiques AI Prompt Engineering avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer AI Prompt Engineering ?

Aucune expérience préalable n'est requise. AI Prompt Engineering sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.

Combien de temps prend la leçon « Réordonner les fragments récupérés » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon AI Prompt Engineering ?

Oui. Chaque leçon AI Prompt Engineering inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Au-delà du RAG naïf
  2. Réordonner les fragments récupérés
  3. Compression du contexte
  4. Réécriture des requêtes et HyDE
← Retour à AI Prompt Engineering