Génération augmentée par récupération
Avez-vous déjà rencontré un scénario comme celui-ci :
Vous avez un manuel de produit de plusieurs centaines de pages, et un utilisateur pose une question très spécifique. Vous cherchez longtemps mais vous ne trouvez pas où se trouve la réponse.
-
Votre entreprise a accumulé plusieurs années de documents internes, de comptes rendus de réunion et de spécifications techniques. Les nouveaux employés qui veulent comprendre une règle métier particulière n'ont aucune idée d'où commencer à chercher.
-
Vous avez une pile d'ebooks, de rapports de recherche et d'articles, et vous voulez que l'IA réponde à des questions en se basant sur ce contenu, mais si vous collez le livre entier directement à l'IA, la fenêtre de contexte ne pourra pas le contenir.
C'est le problème que RAG vise à résoudre.
RAG (Génération augmentée par récupération) est une technologie qui permet aux grands modèles de langage de répondre à des questions à partir de bases de connaissances spécifiques.。
L'idée centrale de RAG est simple : d'abord récupérer le contenu le plus pertinent de votre base de connaissances, puis envoyer ce contenu avec la question de l'utilisateur au LLM, et laisser le LLM répondre en fonction de ce contexte.
Sans RAG, le LLM ne connaît que ce qui se trouve dans ses données d'entraînement ; avec RAG, le LLM peut utiliser vos données privées et à jour.
Pourquoi RAG est-il nécessaire ?
RAG n'est pas la seule solution à ces problèmes, mais c'est actuellement l'une des options les plus pratiques et les moins coûteuses.
Problème de date de coupure des connaissances des LLM.
-
Tous les LLM ont une date de coupure des connaissances—les données d'entraînement ne vont que jusqu'à un certain point dans le temps, et ils ne connaissent pas les événements postérieurs.
-
Par exemple, la date de coupure des connaissances de GPT-4 est octobre 2023. Si vous l'interrogez sur des nouvelles de 2024, il ne peut que répondre « Je ne sais pas. »
-
Plus important encore, vos données privées (documents internes, manuels produits, politiques d'entreprise) n'ont jamais figuré dans les données d'entraînement du LLM—comment pourrait-il bien les connaître ?
Les données privées ne peuvent pas être directement saisies dans un LLM.
Vous pourriez penser : ne puis-je pas simplement coller les documents directement dans le LLM ?
La réponse est : les documents courts fonctionnent, mais pas les documents longs.
-
Premièrement, la fenêtre de contexte du LLM est limitée. Par exemple, GPT-3.5 ne dispose que de 16K tokens, soit environ 10,000 caractères. Un manuel de plusieurs centaines de pages ne rentrera tout simplement pas.
-
Deuxièmement, même si la fenêtre de contexte est suffisamment grande, insérer tout le document ne fonctionne pas bien. Les LLM ont tendance à se perdre dans les textes longs et à ne pas trouver les informations réellement pertinentes.
-
Enfin, il y a le coût. GPT-4 avec un contexte de 128K coûte 10 $ par million de tokens d’entrée. Si vous fourrez un livre entier à chaque fois, votre portefeuille va pleurer.
RAG vs Fine-tuning : comment choisir
Une autre façon de fournir de nouvelles données à un LLM est le fine-tuning — continuer à entraîner le modèle avec de nouvelles données.
Mais le fine-tuning et le RAG sont fondamentalement différents, avec des cas d’utilisation entièrement différents :
| Élément de comparaison | RAG | Fine-tuning |
|---|---|---|
| Fraîcheur des données | Peut être mis à jour à tout moment, immédiatement utilisable | Nécessite un réentraînement, cycle long |
| Volume de données applicable | Très important (millions de documents) | Moyen (de milliers à dizaines de milliers d’échantillons) |
| Source de citation | Peut indiquer de quel document provient la réponse | Impossible de retracer la source |
| Problème d'hallucination | Réduit les hallucinations en fournissant du contexte | Peut encore produire des hallucinations |
| Modification des connaissances | Il suffit de supprimer ou de mettre à jour des documents | Nécessite un réentraînement, difficile à « oublier » |
| Barrière technique | Relativement faible, peut utiliser des frameworks existants | Relativement élevée, nécessite un GPU et de l'expérience en entraînement |
| Coût | Principalement une base de données vectorielle et Embedding | Coût d'entraînement élevé, et coût d'inférence élevé également |
Une règle empirique simple : si vous voulez que le LLM « connaisse » certaines connaissances factuelles (comme les descriptions de produits, les politiques d'entreprise), utilisez RAG ; si vous voulez que le LLM « apprenne » un certain style ou une certaine capacité (comme le style d'écriture, les conventions de code), utilisez le fine-tuning.
Aperçu de l'architecture RAG
RAG se divise en deux phases : la phase d'indexation hors ligne et la phase de récupération en ligne.
Tout d'abord, regardons un schéma d'architecture complet :
Étape d'indexation hors ligne (préparation des données)
Cette phase s'exécute en arrière-plan et les utilisateurs ne la voient pas directement.
Sa tâche consiste à traiter vos documents dans un format qui peut être récupéré rapidement.
Les étapes sont les suivantes :
-
1. Chargement des documents: Lire des documents dans différents formats tels que PDF, TXT, DOCX et pages web.
-
2. Découpage des documents: Diviser les documents longs en segments de texte plus petits, généralement de quelques centaines à un millier de mots.
-
3. Vectorisation: Utiliser un modèle d'Embedding pour convertir chaque segment de texte en un vecteur (une suite de nombres).
-
4. Stockage: Stocker les vecteurs et le texte original ensemble dans une base de données vectorielle.
Cette phase ne doit être effectuée qu'une seule fois, ou réexécutée lorsque les documents sont mis à jour.
Étape de récupération en ligne (requête utilisateur)
Cette phase se déroule en temps réel lorsque l'utilisateur pose une question.
Les étapes sont les suivantes :
1. Vectorisation de la question: Utilisez le même modèle d'Embedding pour convertir également la question de l'utilisateur en vecteur.
-
2. Recherche par similarité: Trouvez les segments de texte les plus proches du vecteur de la question dans la base de données vectorielle.
-
3. Construction du Prompt: Utilisez les segments de texte récupérés comme contexte et assemblez-les avec la question de l'utilisateur pour former un Prompt.
-
4. Génération par LLM: Envoyez le Prompt au LLM et laissez-le répondre en fonction du contexte.
-
5. Renvoyer la réponse: Renvoyez la réponse du LLM à l'utilisateur, généralement accompagnée des sources documentaires citées.
Introduction aux bases de données vectorielles
La base de données vectorielle est l'un des composants essentiels de RAG. Pour la comprendre, vous devez d'abord comprendre ce qu'est un vecteur.
Qu'est-ce qu'un vecteur (Embedding) ?
Un vecteur (Embedding) est une chaîne de nombres convertie à partir de texte.
Par exemple, le mot "chat" pourrait être converti en un vecteur comportant plusieurs centaines de dimensions comme [0.23, -0.45, 0.12, 0.89, ...].
Le point clé est :Les textes ayant une sémantique similaire auront des vecteurs proches dans l'espace.。
Par exemple :
- La distance vectorielle entre "chat" et "minou" est très faible.
- La distance vectorielle entre "chat" et "chien" est plus faible que celle entre "chat" et "voiture".
- La distance vectorielle entre "J'aime les chats" et "J'adore les chats" est très faible.
C'est le fondement de la "recherche sémantique" — une correspondance par le sens, et non par mot-clé.
Le principe de calcul de la similarité sémantique.
La similarité entre deux vecteurs est généralement calculée à l'aide de la "similarité cosinus".
L'intervalle de la similarité cosinus est de -1 à 1 :
- 1 signifie exactement la même chose
- 0 signifie sans rapport
- -1 signifie complètement opposé
En pratique, on ne s'intéresse généralement qu'à la similarité positive ; plus elle est proche de 1, plus c'est pertinent.
Lorsque vous posez une question, la base de données vectorielle calcule rapidement la similarité entre le vecteur de la question et tous les vecteurs de documents, puis renvoie les quelques résultats les plus pertinents.
Base de données vectorielle courante
Il existe de nombreuses bases de données vectorielles sur le marché, chacune avec ses propres caractéristiques :
| Base de données | Type | Fonctionnalités | Cas d'utilisation |
|---|---|---|---|
| Chroma | Local/Open source | Léger, facile à utiliser, adapté à Python | Prototypage, applications à petite échelle |
| Pinecone | Service cloud/SaaS | Service managé, aucune opération requise, mise à l'échelle élastique | Environnements de production, applications à grande échelle |
| Weaviate | Open source/Managé | Riche en fonctionnalités, prend en charge GraphQL | Scénarios nécessitant des fonctionnalités avancées |
| Qdrant | Open source/Managé | Hautes performances, écrit en Rust | Scénarios avec des exigences de hautes performances |
| Milvus | Open source/Managé | Fonctionnalités complètes, de niveau entreprise | Applications d'entreprise à grande échelle |
| FAISS | Bibliothèque locale | De Facebook, extrêmement rapide | Scénarios où la persistance n'est pas nécessaire |
Pour les débutants, nous recommandons de commencer avec Chroma — il est facile à installer, ne nécessite aucune configuration supplémentaire et est très adapté à l'apprentissage et au prototypage.
Flux de traitement des documents
Le traitement des documents est une étape de RAG souvent négligée, mais en réalité très importante.
La qualité de votre récupération dépend en grande partie de la qualité du traitement des documents.
Formats de documents pris en charge.
LangChain prend en charge de nombreux formats de documents :
- Texte brut : .txt, .md
- PDF:.pdf
- Office : .docx, .pptx, .xlsx
- Pages web : HTML, URL
- Code : .py, .js, .java, etc.
- JSON:.json, .jsonl
- CSV:.csv
Chaque format possède un chargeur correspondant, ce qui rend son utilisation très pratique.
Stratégie de segmentation des documents
Le découpage de documents est le processus qui consiste à diviser de longs documents en morceaux plus petits.
Cela semble simple, mais il y a de nombreuses nuances :
- Trop petit : peut perdre le contexte, un sens complet est tronqué
- Trop grand : peut contenir des informations non pertinentes, gaspillant la fenêtre de contexte
Stratégies de découpage courantes :
| Stratégie | Principe | Scénarios applicables |
|---|---|---|
| Par nombre de caractères | Diviser simplement par nombre de caractères, par ex., tous les 500 caractères par segment | Scénarios simples avec de faibles exigences de contexte |
| Par paragraphe | Diviser par caractères de nouvelle ligne, en gardant les paragraphes aussi intacts que possible | Documents bien formatés |
| Découpage récursif | Privilégiez d'abord les paragraphes, puis les phrases si c'est trop long, et enfin les mots | Le premier choix pour la plupart des scénarios |
| Découpage sémantique | Découper en fonction de la similarité sémantique afin de garantir la complétude sémantique | Scénarios avec des exigences sémantiques élevées |
Dans la plupart des cas, utiliser RecursiveCharacterTextSplitter de LangChain suffit.
Conseils pour choisir la taille de segmentation
Chunk Size est un paramètre qui doit être ajusté selon le scénario.
Quelques directives de référence :
- Scénarios de questions-réponses : 500-1000 caractères, suffisamment pour contenir une paire question-réponse complète
- Scénarios de résumé : 1000-2000 caractères, davantage de contexte est nécessaire
- Scénarios de code : possibilité de découper par fonction ou classe, en gardant les blocs de code intacts
Il existe également un paramètre important : Overlap — le nombre de caractères qui se chevauchent entre des blocs de texte adjacents.
Le chevauchement peut empêcher des informations importantes d'être scindées entre deux segments ; il est généralement défini entre 10%-20% de la taille du segment.
Ne poursuivez pas aveuglément des paramètres de découpage « optimaux ». Commencez par 500 caractères et un chevauchement de 50, testez les résultats réels, puis ajustez.
Modèle d'embedding
Un modèle d'embedding est un modèle qui convertit le texte en vecteurs.
Sa qualité affecte directement les performances de récupération — un bon modèle d'embedding peut comprendre précisément la sémantique et classer les documents pertinents plus haut.
Qu'est-ce qu'un modèle d'embedding ?
Un modèle d'embedding est un réseau de neurones pré-entraîné qui prend du texte en entrée et produit des vecteurs.
Son objectif d'entraînement est : pour des textes ayant une sémantique similaire, les vecteurs de sortie doivent aussi être similaires.
Par exemple :
- « Comment apprendre Python » et « méthodes d'apprentissage de Python » → les vecteurs sont proches
- « Les chats sont mignons » et « Les chiots sont mignons » → les vecteurs sont un peu éloignés mais restent relativement proches
- « Comment apprendre Python » et « Les chats sont mignons » → la distance vectorielle est très grande
Série OpenAI text-embedding-3
OpenAI propose actuellement les modèles d'embedding les plus populaires :
| Modèle | Dimensions | Prix (par million de tokens) | Fonctionnalités |
|---|---|---|---|
| text-embedding-3-small | 1536 | $0.02 | Très bon rapport coût-efficacité, suffisant pour la plupart des scénarios |
| text-embedding-3-large | 3072 | $0.13 | De meilleures performances, adapté aux besoins de haute précision |
text-embedding-3-small est actuellement le meilleur choix—il est plus performant que la génération précédente text-embedding-ada-002 et moins cher.
Modèle d'embedding open source
Si vous ne souhaitez pas utiliser OpenAI, ou si vous avez des exigences de confidentialité des données, il existe également de nombreuses options open source :
| Modèle | Dimensions | Fonctionnalités |
|---|---|---|
| sentence-transformers/all-MiniLM-L6-v2 | 384 | Léger, rapide, performances correctes |
| sentence-transformers/all-mpnet-base-v2 | 768 | De meilleures performances, mais plus lent et plus volumineux |
| BAAI/bge-large-zh-v1.5 | 1024 | Optimisé spécifiquement pour le chinois |
| Alibaba-NLP/gte-large-zh | 1024 | Créé par Alibaba, bonnes performances en chinois |
Pour les scénarios en chinois, nous recommandons la série BAAI/bge ou la série Alibaba-NLP/gte—elles sont spécifiquement optimisées pour le chinois et offrent de meilleures performances que les modèles généralistes.
Stratégie de récupération
La récupération n'est pas aussi simple que « trouver les documents les plus pertinents »—différentes stratégies conviennent à différents scénarios.
Recherche par similarité vectorielle
C'est la méthode de récupération la plus courante : calculez la similarité entre le vecteur de la question et tous les vecteurs de documents, puis renvoyez les K documents les plus pertinents.
Avantages : comprend la sémantique, les synonymes et les quasi-synonymes peuvent tous être mis en correspondance.
Inconvénients : peut être moins précis que la recherche par mots-clés pour les termes précis et les noms propres.
Par exemple, si un utilisateur demande « tutoriel Python d'exemple », la recherche vectorielle peut comprendre que « tutoriel » et « cours » sont similaires, mais si l'utilisateur demande « documentation Python3.10 d'exemple », la recherche par mots-clés peut être plus précise.
Recherche par mots-clés (BM25)
C'est la méthode utilisée par les moteurs de recherche traditionnels : faire correspondre les mots-clés et calculer un score en fonction de la fréquence des termes, de la fréquence inverse des documents, etc.
-
Avantages : efficace pour la correspondance exacte de termes, de noms de produits, de numéros de modèle, etc.
-
Inconvénients : ne peut pas comprendre les synonymes ni les relations sémantiques.
Par exemple, si un utilisateur demande comment apprendre la programmation, la recherche par mots-clés ne peut trouver que les documents contenant « programmation » et ne peut pas trouver les documents qui disent « écrire du code » mais qui signifient la même chose.
Recherche hybride
Puisque les deux méthodes ont leurs avantages et leurs inconvénients, pourquoi ne pas les utiliser ensemble ?
La recherche hybride effectue simultanément une recherche vectorielle et une recherche par mots-clés, puis fusionne et reclasse les résultats.
Ainsi, elle peut tirer parti de la compréhension sémantique tout en assurant une correspondance exacte.
De nombreuses bases de données vectorielles prennent en charge la recherche hybride, comme Weaviate, Pinecone, Qdrant, etc.
Si vous ne savez pas laquelle utiliser, choisissez la recherche hybride — dans la plupart des cas, c'est celle qui fonctionne le mieux.
Optimisation de la phase de génération
La récupération des documents pertinents n'est que la première étape. Il existe également de nombreuses techniques pour amener le LLM à générer de bonnes réponses à partir de ces documents.
Conception de modèle de prompt
Le modèle de prompt est un facteur clé affectant la qualité des résultats dans le RAG.
Un bon prompt RAG inclut généralement :
- Définition du rôle : indiquer au LLM quel rôle il joue
- Contexte : le contenu du document récupéré
- Description de la tâche : ce qu'il faut demander au LLM de faire
- Contraintes : par exemple, "répondez uniquement en vous basant sur le contexte" et "si vous ne savez pas, dites que vous ne savez pas"
- Format de sortie : comment vous voulez que le LLM réponde
Un modèle classique :
Exemple
RAG_PROMPT_TEMPLATE = """Vous êtes un {role} professionnel.
Veuillez répondre à la question de l'utilisateur en vous basant sur le contexte suivant. Si le contexte ne contient pas d'informations pertinentes, dites "Désolé, je n'ai pas trouvé d'informations pertinentes."
Contexte :
{context}
Question de l'utilisateur : {question}
Veuillez répondre dans un langage concis et précis : """
Comment réduire les hallucinations ?
L'hallucination est le phénomène où un LLM fabrique des faits—elle peut sembler très convaincante, mais tout est inventé.
Le RAG peut réduire les hallucinations, mais ne peut pas complètement les éliminer.
Quelques techniques pour réduire les hallucinations :
- Mettez l'accent sur « répondre uniquement en fonction du contexte » dans le prompt
- Demandez au LLM de citer des extraits du texte original dans ses réponses
- Si les résultats de récupération ne sont pas pertinents, demandez au LLM de dire « Je ne sais pas »
- Utilisez plusieurs LLM pour valider les réponses par recoupement
- Demandez au LLM de d'abord trouver le contenu pertinent dans le contexte, puis de répondre
Annotation des citations de sources
Il est important d'indiquer aux utilisateurs d'où vient la réponse—cela augmente non seulement la crédibilité, mais facilite aussi la vérification par les utilisateurs.
Pratiques courantes :
- Indiquez la source dans la réponse, par ex. : « selon le document A, page 2 »
- Listez les titres et les liens des documents de référence à la fin de la réponse
- Permettez aux utilisateurs de cliquer et d'aller directement à l'emplacement du texte original
Certains frameworks avancés de LangChain (comme LlamaIndex) peuvent le faire automatiquement pour vous.
Mise en pratique : construire un système de questions-réponses sur documents basé sur LangChain
Assez parlé—passons à la pratique et construisons un système RAG complet.
Nous allons utiliser LangChain + Chroma + OpenAI pour construire un système de questions-réponses documentaire.
Configuration de l'environnement
Installez d'abord les bibliothèques requises :
pip install langchain langchain-openai langchain-chroma python-dotenv
Ensuite, créez un fichier .env dans le répertoire du projet et renseignez votre clé API OpenAI :
OPENAI_API_KEY=你的-api-key-在这里
Implémentation complète du code
Il s'agit d'un système RAG complet, incluant la création d'index et la fonctionnalité d'interrogation :
Exemple
# Implémentation complète du système de questions-réponses sur documents RAG
# ============================================
import os
from dotenv import load_dotenv
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from langchain.schema import Document
def load_documents_from_directory(directory: str) -> list[Document]:
"""Charge les documents depuis le répertoire
À des fins de démonstration, nous créons manuellement quelques documents
Dans des projets réels, vous pouvez utiliser DirectoryLoader pour charger de vrais fichiers
"""
documents = [
Document(
page_content="""Le site de tutoriels EXAMPLE a été fondé en 2013, créé par l'équipe Rookie Tutorial.
Le site Web vise à fournir des tutoriels de programmation faciles à comprendre pour les débutants en programmation.
Nous proposons des tutoriels pour de multiples langages de programmation, notamment HTML, CSS, JavaScript, Python, Java, C++, et plus encore.""",
metadata={"source": "example_intro.txt", "page": 1}
),
Document(
page_content="""Le tutoriel Python d'EXAMPLE couvre tous les points de connaissance importants de Python.
Y compris : la syntaxe de base de Python, les types de données, les instructions de contrôle, les fonctions, les modules, les opérations sur les fichiers, etc.
Le tutoriel fournit un grand nombre d'exemples de code, chacun soigneusement conçu et testé.
Nous fournissons également un éditeur Python en ligne, vous permettant d'exécuter du code directement dans votre navigateur.""",
metadata={"source": "python_tutorial.txt", "page": 1}
),
Document(
page_content="""Les tutoriels EXAMPLE se caractérisent par un contenu concis, des exemples riches et un accent sur la pratique.
Nous croyons que "la meilleure façon d'apprendre la programmation est d'écrire du code."
Par conséquent, chaque point de connaissance est accompagné d’exemples de code exécutables, ce qui vous permet d’apprendre et de pratiquer en même temps.
Nos tutoriels sont entièrement gratuits, et tout le monde peut y accéder et apprendre.""",
metadata={"source": "features.txt", "page": 1}
),
Document(
page_content="""En plus des tutoriels textuels, EXAMPLE propose également des tutoriels vidéo.
Les tutoriels vidéo sont plus intuitifs et adaptés aux apprenants visuels.
Nos tutoriels vidéo couvrent des sujets populaires tels que Python, le développement Web, l'analyse de données, et plus encore.
Vous pouvez trouver nos vidéos sur Bilibili et YouTube.""",
metadata={"source": "video_course.txt", "page": 1}
),
]
print(f"{len(documents)} documents chargés")
return documents
def split_documents(documents: list[Document], chunk_size: int = 500, chunk_overlap: int = 50) -> list[Document]:
"""Diviser les documents"""
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""],
)
splits = text_splitter.split_documents(documents)
print(f"Diviser {len(documents)} documents en {len(splits)} segments de texte")
return splits
def create_vector_store(splits: list[Document], persist_directory: str = "./chroma_db") -> Chroma:
"""Créer une base de données vectorielle"""
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = Chroma.from_documents(
documents=splits,
embedding=embeddings,
persist_directory=persist_directory,
)
print(f"Base de données vectorielle créée, avec un total de {vector_store._collection.count()} enregistrements")
return vector_store
def load_vector_store(persist_directory: str = "./chroma_db") -> Chroma:
"""Charger la base de données vectorielle existante"""
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = Chroma(
persist_directory=persist_directory,
embedding_function=embeddings,
)
print(fBase de données vectorielle chargée, total {vector_store._collection.count()} enregistrements)
return vector_store
def create_rag_chain(vector_store: Chroma):
"""Créer une chaîne de questions-réponses RAG"""
# Modèle de prompt personnalisé
template = """Vous êtes un assistant professionnel du service client EXAMPLE.
Veuillez répondre à la question de l'utilisateur en vous basant sur le contexte suivant. Si le contexte ne contient pas d'informations pertinentes, dites "Désolé, je n'ai pas trouvé d'informations pertinentes."
Gardez la réponse concise et claire, en utilisant le chinois.
Contexte :
{context}
Question de l'utilisateur : {question}
Réponse : """
prompt = PromptTemplate(
template=template,
input_variables=["context", "question"],
)
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# Créer une chaîne de récupération
rag_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vector_store.as_retriever(search_kwargs={"k": 3}),
chain_type_kwargs={"prompt": prompt},
return_source_documents=True,
)
return rag_chain
def main():
"""Fonction principale"""
# Charger les variables d'environnement
load_dotenv()
# Vérifier si la base de données existe déjà
db_exists = os.path.exists("./chroma_db") and os.path.isdir("./chroma_db")
if db_exists:
print("Base de données existante trouvée, chargement...")
vector_store = load_vector_store()
else:
print("Création d'une nouvelle base de données...")
# 1. Charger les documents
docs = load_documents_from_directory("./data")
# 2. Découper les documents
splits = split_documents(docs)
# 3. Créer une base de données vectorielle
vector_store = create_vector_store(splits)
# 4. Créer une chaîne RAG
rag_chain = create_rag_chain(vector_store)
# 5. Tester les requêtes
test_questions = [
"Quand EXAMPLE a-t-il été fondé ?",
"Quelles sont les fonctionnalités du tutoriel Python d'EXAMPLE ?",
"EXAMPLE a-t-il des tutoriels vidéo ?",
"Comment apprendre le C++ sur EXAMPLE ?",
"Les tutoriels d'EXAMPLE nécessitent-ils un paiement ?",
]
for question in test_questions:
print(f"\n{'='*60}")
print(f"Question : {question}")
print(f"{'='*60}")
result = rag_chain.invoke({"query": question})
answer = result["result"]
sources = result["source_documents"]
print(f"Réponse : {answer}")
print("\nSources de référence :")
for i, doc in enumerate(sources, 1):
print(f" [{i}] {doc.metadata['source']} (page {doc.metadata['page']})")
print(f" Extrait : {doc.page_content[:50]}...")
if __name__ == "__main__":
main()
En exécutant ce programme, vous verrez une sortie similaire à ceci :
创建新的数据库...
已加载 4 个文档
已将 4 个文档切分为 4 个文本块
向量数据库已创建,共 4 条记录
============================================================
问题:EXAMPLE 是什么时候创立的?
============================================================
回答:EXAMPLE 教程网站创立于 2013 年。
参考来源:
[1] example_intro.txt (第 1 页)
片段:EXAMPLE 教程网站创立于 2013 年,由Example团队创建。...
[2] features.txt (第 1 页)
片段:除了文字教程,EXAMPLE 还提供了视频教程。...
============================================================
问题:EXAMPLE 的 Python 教程有什么特点?
============================================================
回答:EXAMPLE 的 Python 教程涵盖了 Python 的所有重要知识点,包括基础语法、数据类型、控制语句、函数、模块、文件操作等,并且提供了大量经过精心设计和测试的代码示例,还提供了在线 Python 编辑器,可以直接在浏览器中运行代码。
参考来源:
[1] python_tutorial.txt (第 1 页)
片段:EXAMPLE 的 Python 教程涵盖了 Python 的所有重要知识点。...
[2] features.txt (第 1 页)
片段:EXAMPLE 教程的特点是内容简洁、示例丰富、注重实践。...
Félicitations ! Vous avez réussi à construire un système complet de questions-réponses sur documents RAG.
Optimisation avancée : Re-ranking, HyDE
Le RAG de base fonctionne, mais si vous voulez de meilleurs résultats, vous pouvez essayer ces méthodes d'optimisation avancées.
Réordonnancement
Les résultats renvoyés par la recherche vectorielle sont triés par similarité, mais une similarité élevée ne signifie pas nécessairement qu'il s'agit de la réponse la plus appropriée à la question.
Le réordonnancement consiste à : d'abord utiliser la recherche vectorielle pour renvoyer les K meilleurs résultats (par ex., 20), puis utiliser un modèle plus précis pour réordonner ces résultats et sélectionner les meilleurs pour les envoyer au LLM.
Modèles de réordonnancement courants :
- cross-encoder/ms-marco-MiniLM-L-6-v2
- BAAI/bge-reranker-large
Bien que cela ajoute une étape supplémentaire, l'amélioration des résultats est évidente et vaut la peine d'être faite.
HyDE(Plongement de document hypothétique)
HyDE est une idée astucieuse : d'abord utiliser le LLM pour générer une "réponse hypothétique" à la question de l'utilisateur, puis utiliser cette réponse hypothétique pour la recherche.
Pourquoi est-ce efficace ? Parce que les questions sont généralement courtes et ont une faible densité d'information ; tandis que les réponses hypothétiques sont plus longues, ont un style plus proche de celui d'un document et présentent une similarité plus élevée avec de vrais documents.
Par exemple :
- Question de l'utilisateur : "Quand EXAMPLE a-t-il été fondé ?"
- Réponse hypothétique : "Le site de tutoriels EXAMPLE a été fondé en 2013, créé par l'équipe Rookie Tutorial, dans le but de fournir des tutoriels de qualité aux débutants en programmation."
L'utilisation de la réponse hypothétique pour la recherche permet souvent de trouver des documents plus pertinents.
Autres extensions