Accueil / Articles / Notes pratiques : La série AI : La partie la plus difficile de RAG, partie 2 — Pourquoi parfait

Notes pratiques : La série AI : La partie la plus difficile de RAG, partie 2 — Pourquoi parfait

Guide pratique détaillé : La série AI : La partie la plus difficile de RAG, partie 2 — Pourquoi c’est parfait : contrats, vérifications et emplacements de code prêts à l’emploi pour les équipes qui utilisent ce modèle.

1548 mots

Ce guide reconstitue le parcours allant des matières premières à un système fonctionnel pour : La série AI : La partie la plus difficile de RAG, partie 2 — Pourquoi des fragments parfaits entraînent encore de mauvaises récupérations. L’accent est mis sur des étapes exécutables, des vérifications explicites et du code que vous pouvez intégrer directement dans un dépôt sans devoir deviner l’intention. Pour l’étape d’aperçu, définissez les entrées, le responsable de l’étape et 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é. Enregistrez 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 les factures inattendues lorsque le parcours passe d’une démonstration à des environnements partagés.

D’abord, qu’est-ce qu’un embedding, au juste ?

Lorsque vous travaillez sur l’étape « Qu’est-ce que c’est ? », 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.

"How do I reset my password?"     → [0.81, 0.12, -0.44, 0.09]
"Steps to change your password"   → [0.79, 0.15, -0.41, 0.11]
"How do I cancel my subscription?"→ [0.22, 0.68, 0.05, -0.39]
from numpy import dot
from numpy.linalg import norm

def cosine_similarity(a, b):
    return dot(a, b) / (norm(a) * norm(b))

query = [0.80, 0.13, -0.42, 0.10]  # "reset my password"

print(cosine_similarity(query, [0.81, 0.12, -0.44, 0.09]))  # 0.998 - very close
print(cosine_similarity(query, [0.22, 0.68, 0.05, -0.39]))  # 0.310 - far apart

Pourquoi la récupération est véritablement difficile

Lorsque vous travaillez sur l’étape « Pourquoi la récupération est-elle vraiment efficace », notez d’abord les exigences : 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 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’améliorations apportées ultérieurement. Mesurez le taux de rappel sur un ensemble fixe de questions avant d’ajuster les prompts. Le simple changement de prompts ne résout que rarement un système de récupération insuffisant.

À quoi ressemble un véritable pipeline de récupération

Lors de la phase « Qu’est-ce qu’une récupération réelle ? », notez d’abord les conditions requises : les entrées nécessaires, 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 plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité et non un processus embrouillé. Mesurez le taux de rappel sur un ensemble de questions fixe avant d’ajuster les prompts. Changer fréquemment les prompts ne résout généralement pas un système de récupération insuffisant. Lors de la phase « Qu’est-ce qu’une récupération réelle ? », notez d’abord les conditions requises : les entrées nécessaires, 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. Enregistrez 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 les surprises financières lorsque le système passe de l’environnement de démonstration à des environnements partagés.

Recherche hybride : pourquoi la recherche vectorielle seule ne suffit pas

La phase de recherche hybride fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple réussi, un cas d’échec ainsi que la note de réversion avant d’élargir le périmètre. Conservez les paramètres de 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. 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.

from collections import defaultdict

def reciprocal_rank_fusion(result_lists, k=60):
    scores = defaultdict(float)
    for result_list in result_lists:
        for rank, doc_id in enumerate(result_list):
            scores[doc_id] += 1 / (k + rank + 1)
    return sorted(scores, key=scores.get, reverse=True)

vector_ids = [d.metadata["chunk_id"] for d in vector_store.similarity_search(query, k=20)]
bm25_ids = [d.metadata["chunk_id"] for d in bm25_index.search(query, k=20)]

final_ranking = reciprocal_rank_fusion([vector_ids, bm25_ids])

Le réclassement est l’étape que personne n’oublie jamais

Le processus de réclassification fonctionne le mieux lorsqu’il est considéré comme une surface mesurable. Capturez un exemple réussi, un cas d’échec ainsi que la note de réversion 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 la gestion 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 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.

from sentence_transformers import CrossEncoder

reranker = CrossEncoder("BAAI/bge-reranker-base")

def rerank(query, candidates, top_n=5):
    pairs = [(query, c["text"]) for c in candidates]
    scores = reranker.predict(pairs)
    for c, s in zip(candidates, scores):
        c["rerank_score"] = float(s)
    return sorted(candidates, key=lambda c: c["rerank_score"], reverse=True)[:top_n]

Résoudre le problème, pas seulement l’index

La phase « Fixing the Question Not » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de réversion 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é évoluent. La phase « Fixing the Question Not » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Enregistrez 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 les factures inattendues lorsque le processus passe de l’environnement de démonstration aux environnements partagés.

def hyde_retrieve(query, llm, vector_store, k=5):
    hypothetical = llm.invoke(f"Write a short passage answering: {query}")
    return vector_store.similarity_search(hypothetical, k=k)

Le mesurer plutôt que de l’estimer à l’œil

Pour l’étape « Mesurer plutôt que », définissez les entrées, le responsable de l’étape et les critères de fin 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é. 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 avoir à lire l’ensemble du système. 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 recall_at_k(retrieved_ids, relevant_ids, k):
    top_k = set(retrieved_ids[:k])
    return len(top_k & relevant_ids) / len(relevant_ids) if relevant_ids else 0.0

Ce que vous diriez à quelqu’un qui commence aujourd’hui

Pour l’étape « What you’d Tell », définissez les entrées, le responsable de l’étape et les critères de fin 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é. Documentez ensemble le parcours idéal 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 du produit, et non d’une mise en forme ultérieure. 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.

La vérité dérangeante sur RAG

Pour l’étape « The Uncomfortable Truth About », définissez les entrées, le responsable de l’étape et 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 deviner l’état caché. Préférez des unités petites et testables aux 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. Pour l’étape « The Uncomfortable Truth About », définissez les entrées, le responsable de l’étape et 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 deviner l’état caché. Enregistrez les temps d’exécution ainsi que le coût en tokens ou requêtes à côté des résultats fonctionnels. Une visibilité précoce du coût évite les factures inattendues lorsque le processus passe de la démonstration à la mise en partage.

d’environnements.

Liste de contrôle opérationnelle

L’étape de la liste de contrôle opérationnelle fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Recueillez un transcript idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre.

Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définez des critères de succès et refusez toute complétion partielle silencieuse.

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é changent.

Évaluez séparément les réponses en une seule interaction et les trajectoires en plusieurs interactions. L’agrégation des scores de conversation masque les échecs liés aux boucles infinies avec l’outil.

Rédigez un petit manuel d’utilisation : comment rotationner les clés, comment vider la file d’attente, comment réversionner la dernière ingestion.

Dokumentez 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 livrés font partie intégrante du produit, et non d’améliorations apportées ultérieurement.

Au préalable de promouvoir l’ensemble technique, figez les versions, conservez une transcription exemplaire pour le parcours critique, et vérifiez les étapes de rollback. Les environnements partagés nécessitent des limites de fréquence, des contrôles d’attribution et un responsable clair pour la rotation des secrets. Préférez une fiabilité simple à des démonstrations originales mais peu fiables.

Note de batch pour da3fc7392f6d : gardez les clés du fournisseur hors du répertoire de code, fixez un plafond pour les tokens par session, et stockez les transcriptions à côté des fichiers d’évaluation afin que les remplacements de modèles ultérieurs restent comparables.