Accueil / Articles / Notes pratiques : Dartboard RAG : Lorsque Top-K renvoie trois versions identiques

Notes pratiques : Dartboard RAG : Lorsque Top-K renvoie trois versions identiques

Guide pas à pas fonctionnel des notes pratiques : Dartboard RAG : Lorsque Top-K renvoie trois versions identiques : contrats, vérifications et emplacements de code à insérer pour les équipes utilisant ce modèle.

2352 mots

Les notes suivantes reconstituent une approche pratique pour aborder le sujet « Dartboard RAG : Lorsque Top-K renvoie trois versions du même chunk ». L’accent est mis sur les contrats, les vérifications et les placeholders de code à insérer, plutôt que sur une présentation motivante.

Le problème des contextes dupliqués

Lors de l’étape consacrée au problème des contextes dupliqués, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de maintenir l’honnêteté des modifications ultérieures du code. Enregistrez également les temps d’exécution ainsi que le coût en tokens ou en requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite des factures inattendues lorsque le projet passe de l’environnement de démonstration à des environnements partagés. Mesurez le taux de rappel sur un ensemble de questions fixe avant d’ajuster les prompts. Changer fréquemment les prompts ne résout que rarement un système de récupération insuffisant.

Top 3:
1. "Greenhouse gases trap heat in the atmosphere, causing warming..."  (sim 0.91)
2. "Atmospheric greenhouse gases are the primary driver of climate change..."  (sim 0.89)
3. "The trapping of heat by greenhouse gases leads to rising temperatures..."  (sim 0.88)

L’analogie du tableau de fléchettes

Lors de la phase d’analogie du carreau de fléchettes, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les administrateurs peuvent auditer sans devoir lire l’ensemble du système. Mesurez le taux de rappel sur un ensemble fixe de questions avant d’ajuster les prompts. Un changement fréquent des prompts ne résout que rarement un système de récupération insuffisant.

Chunks plotted by relevance to query
                    (closer to center = higher cosine sim)

                       ●●●         ← cluster of near-duplicate chunks
                      ●●●          (all about "greenhouse gases")
                      ● bull's-eye = QUERY

                                ●     ← chunk about deforestation
                                       (relevant but different topic)

                    ●           ●     ← chunks about agriculture, ocean carbon

                       ●  ●          ← chunks about historical climate


   STANDARD TOP-3 picks:           DARTBOARD TOP-3 picks:
   3 closest darts                 1 closest, then darts that are also
   = 3 darts in the same           good but spread across the board
     spot near bull's-eye          = better coverage of relevant content

Le pipeline

Lorsque vous travaillez sur l’étape du pipeline, notez d’abord le contrat : les entrées requises, le signal de succès, ainsi que ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non traités font partie intégrante du produit, et non d’améliorations apportées ultérieurement. Mesurez le taux de rappel sur un ensemble de questions fixe avant d’ajuster les prompts. Un changement fréquent des prompts ne résout que rarement un système de récupération insuffisant.

Étape 1 — Récupérer plus avec FAISS

Lors de l’exécution de l’Étape 1 « Over-fetch with stage », notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Préférez des unités petites et testables à des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Mesurez le taux de rappel sur un ensemble de questions fixe avant d’ajuster les prompts. Un changement fréquent des prompts ne résout que rarement un système de récupération insuffisant.

fetch_k = self.k * self.oversampling     # default: 5 × 3 = 15
candidates = vector_store.search(query_embedding, k=fetch_k)

Étape 2 — Calcul des matrices de distance

Lors de l’exécution de l’étape 2 « Calculer la distance », écrivez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez les terminations partielles silencieuses. Mesurez le taux de rappel sur un ensemble de questions fixe avant d’ajuster les prompts. Le changement fréquent des prompts ne résout que rarement un système de récupération insuffisant. Lors de l’exécution de l’étape 2 « Calculer la distance », écrivez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les flags fonctionnels doivent être regroupés en un seul endroit que les administrateurs peuvent auditer sans avoir à lire l’ensemble du système.

# Normalize all vectors so dot product = cosine similarity
query_norm = query_vec / np.linalg.norm(query_vec)
cand_norm = candidate_matrix / np.linalg.norm(candidate_matrix, axis=1, keepdims=True)

# Distance = 1 - cosine_similarity
query_distances = 1.0 - np.dot(query_norm, cand_norm.T)        # (1, N)
document_distances = 1.0 - np.dot(cand_norm, cand_norm.T)      # (N, N)

Étape 3 — Conversion en probabilités log-normales

L’étape de conversion en phase 3 fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript parfait, un cas d’échec et la note de rollback avant d’élargir le périmètre. Documentez ensemble le parcours réussi et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie du produit, et non d’une mise en forme ultérieure. Séparez la politique de segmentation de la politique de récupération. Modifier l’une ne doit pas obliger à réécrire l’autre lorsque les métriques de qualité évoluent.

def lognorm(dist, sigma):
    return -np.log(sigma) - 0.5 * np.log(2 * np.pi) - dist**2 / (2 * sigma**2)

Étape 4 — La boucle de sélection gourmande

La étape 4, la phase de cupidité, fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript parfait, un cas d’échec et la note de rollback avant d’élargir le périmètre. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit pointer vers une seule responsabilité plutôt que vers un processus embrouillé. Séparez la politique de segmentation de la politique de récupération. Modifier l’une ne doit pas obliger à réécrire l’autre lorsque les métriques de qualité changent.

# Step 1: pick most relevant first
most_relevant_idx = np.argmax(query_probs)
selected_indices = [most_relevant_idx]
max_distances = doc_probs[most_relevant_idx].copy()    # diversity tracker

# Step 2-6: iteratively add diverse + relevant chunks
while len(selected_indices) < num_results:
    # For each candidate, compute "diversity from any selected"
    updated_distances = np.maximum(max_distances, doc_probs)

    # Combine relevance + diversity
    combined = (diversity_weight * updated_distances
                + relevance_weight * query_probs[np.newaxis, :])

    # Aggregate per candidate (logsumexp for numerical stability)
    normalized = logsumexp(combined, axis=1)

    # Mask already-selected
    for idx in selected_indices:
        normalized[idx] = -np.inf

    # Pick the best
    best_idx = np.argmax(normalized)
    max_distances = updated_distances[best_idx]
    selected_indices.append(best_idx)

Que fait réellement les mathématiques (de manière intuitive)

La phase « What the math is » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Traitez cette phase comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez toute mise en œuvre partielle silencieuse. Séparez la politique de segmentation de la politique de récupération. Modifier l’une ne doit pas obliger à réécrire l’autre lorsque les métriques de qualité évoluent. La phase « What the math is » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Conservez les configurations en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système.

Relevance →
            Low              ●◯

                   ●         ●           ←  Standard top-k picks these 3
                                              (highest relevance, regardless of diversity)
                    ●        ●
                       ●●●●●●  ●  ●  ●     ← Many similar high-relevance chunks
            High         (cluster)


            Diversity ↓
            from
            selected
                        ↓
                     ↓     ↓   ←  Dartboard picks 1 from cluster,
                                 then far-away ones with high relevance still

Les poids — à quoi sert chacun

Pour déterminer les poids de chaque étape, il faut définir les entrées, le responsable de l’étape ainsi que les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Documentez conjointement le parcours normal et les scénarios de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non traités font partie intégrante du produit, et non d’une mise en forme ultérieure. Citez les passages qui justifient réellement la réponse. Sans citations, les opérateurs ne peuvent pas distinguer une hallucination d’un manque d’indexation.

relevance_weight = 1.0     # how much we care about chunks being close to query
diversity_weight = 1.0     # how much we care about chunks being different from each other

Un exemple concret : le test sur le corpus dupliqué

Pour l’exemple fonctionnel A, définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables à des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Citez les passages qui fondent réellement la réponse. Sans citations, les opérateurs ne peuvent pas distinguer une hallucination d’un manque d’indexation.

1. "Greenhouse gases cause warming..."  (sim 0.91)
2. "Greenhouse gases cause warming..."  (sim 0.91)  ← DUPLICATE
3. "Greenhouse gases cause warming..."  (sim 0.91)  ← DUPLICATE
4. "Greenhouse gases cause warming..."  (sim 0.91)  ← DUPLICATE
5. "Greenhouse gases cause warming..."  (sim 0.91)  ← DUPLICATE

Unique results: 1/5
1. "Greenhouse gases cause warming..."  (highest relevance — wins first pick)
2. "Deforestation reduces the carbon sink..."  (different chunk, still relevant)
3. "Industrial agriculture emits methane..."  (third unique cause)
4. "Land-use changes alter surface albedo..."  (fourth unique cause)
5. "Fossil fuel combustion is the largest CO₂ source..."  (related to #1 but different angle)

Unique results: 5/5

L’essentiel en quelques lignes

Pour l’essence d’une étape, définissez les entrées, le responsable de l’étape et les critères de fin avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définissez des vérifications de succès et refusez toute complétion partielle silencieuse. Citez les passages qui ont réellement servi de base à la réponse. Sans citations, les opérateurs ne peuvent pas distinguer une hallucination d’un manque d’indexation.

def dartboard_select(query_emb, candidate_embs, k=5, sigma=0.1):
    # 1. Compute distance matrices
    query_dist = 1 - cosine(query_emb, candidate_embs)        # query→each
    doc_dist   = 1 - cosine(candidate_embs, candidate_embs)   # each→each

    # 2. Convert distances to log-probabilities
    query_probs = lognorm(query_dist, sigma)
    doc_probs   = lognorm(doc_dist, sigma)

    # 3. Pick most relevant first
    selected = [np.argmax(query_probs)]
    max_distances = doc_probs[selected[0]].copy()

    # 4. Iteratively add diverse + relevant
    while len(selected) < k:
        updated = np.maximum(max_distances, doc_probs)
        combined = updated + query_probs[np.newaxis, :]   # equal weights = sum
        scores = logsumexp(combined, axis=1)
        for idx in selected:
            scores[idx] = -np.inf       # don't re-select

        best = np.argmax(scores)
        max_distances = updated[best]
        selected.append(best)

    return selected

Pour l’essence d’une étape, définissez les entrées, le responsable de cette étape ainsi que les critères de fin avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système.

Réglages que vous pourriez modifier

Lorsque vous travaillez sur les paramètres de contrôle, écrivez d’abord le contrat : entrées requises, signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non traités font partie intégrante du produit, et non d’améliorations apportées ultérieurement. Mesurez le taux de rappel sur un ensemble fixe de questions avant d’ajuster les prompts. Un changement fréquent des prompts ne résout que rarement un système de récupération insuffisant.

Où cela fonctionne, et où non

Lorsque vous travaillez sur l’élément « Où cela obtient son état », notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Préférez des unités petites et testables à des scripts volumineux. Lorsqu’une étape échoue, l’échec doit pointer vers une seule responsabilité plutôt que vers un processus complexe et embrouillé. Évaluez le taux de rappel sur un ensemble fixe de questions avant d’ajuster les prompts. Un changement fréquent des prompts ne résout que rarement un système de récupération insuffisant.

L’idée principale à retenir

Lors de la phase « The bigger idea worth », notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’honnêteté des modifications ultérieures du code. Considérez cette phase comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez les terminations partielles silencieuses. Mesurez le taux de rappel sur un ensemble fixe de questions avant d’ajuster les prompts. Un changement fréquent des prompts ne résout que rarement un système de récupération insuffisant. Lors de la phase « The bigger idea worth », notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’honnêteté des modifications ultérieures du code. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les flags fonctionnels doivent être regroupés en un seul endroit que les administrateurs peuvent auditer sans devoir lire l’ensemble du système.

Une dernière pensée

La phase « Une dernière pensée » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript parfait, un cas d’échec et la note de rollback avant d’élargir le périmètre. Documentez en même temps le parcours optimal et celui de récupération. Les tentatives répétées, les contrôles humains et le traitement des messages non livrés font partie intégrante du produit, et non d’une mise en forme ultérieure. Séparez la politique de segmentation des données de la politique de récupération ; modifier l’une ne doit pas obliger à réécrire l’autre lorsque les métriques de qualité évoluent.

Liste de contrôle opérationnelle

Pour la phase de liste de contrôle opérationnelle, définissez les entrées, le responsable de chaque étape et les critères d’achèvement avant de modifier du code. Les opérateurs doivent pouvoir exécuter à nouveau l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Enregistrez les temps d’exécution ainsi que le coût des tokens ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le parcours passe de l’environnement de démonstration aux environnements partagés.

Citez les passages qui servent réellement de base à la réponse. Sans citations, les opérateurs ne peuvent pas distinguer une hallucination d’un manque d’indexation.

Rédigez un petit guide opérationnel : comment rotationner les clés, comment vider la file d’attente, comment annuler la dernière ingestion.

Gardez les configurations en dehors du code de l’application. Les fichiers d’environnement, les bases de stockage des secrets et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système.

Citez les passages qui servent réellement de base à la réponse. Sans citations, les opérateurs ne peuvent pas distinguer une hallucination d’un manque d’indexation.

Au préalable de promouvoir la pile logicielle, figez les versions, conservez une transcription exemplaire pour le parcours critique et confirmez les étapes de rollback. Les environnements partagés nécessitent des limites de débit, des vérifications de location et un responsable clair pour la rotation des secrets. Préférez une fiabilité solide à de brillantes démonstrations ponctuelles.

Note de lot pour fd4991fea9d9 : éviter d’inclure les clés du fournisseur dans le répertoire, fixer une limite pour les tokens par session, et stocker les transcriptions à côté des fichiers d’évaluation afin que les remplacements ultérieurs de modèles restent comparables.