Construire un RAG multilingue avec Milvus, LangChain et OpenAI LLM
Au cours des deux dernières années, la Retrieval Augmented Generation (RAG) est rapidement devenue l'une des techniques les plus populaires pour construire des applications GenAI alimentées par des grands modèles de langage (LLM). À la base, le RAG améliore les résultats d'un LLM en fournissant des informations contextuelles sur lesquelles le modèle n'a pas été pré-entraîné. Le RAG multilingue est un RAG étendu qui traite les données textuelles dans plusieurs langues.
Yujian Tang, PDG d'OSS4AI, a récemment pris la parole lors d'une réunion sur les données non structurées organisée par Zilliz. Il a parlé du RAG et de ses composants fondamentaux et a montré comment construire un RAG multilingue pour répondre aux divers défis linguistiques du monde réel.
Dans ce billet, nous récapitulerons les points clés de la présentation de Yujian et nous vous guiderons dans la mise en œuvre d'un RAG multilingue. Si vous souhaitez en savoir plus sur l'intervention de Yujian, nous vous recommandons de regarder sa présentation sur YouTube.
Qu'est-ce que le RAG et comment fonctionne-t-il ?
L'une des principales limites des applications basées sur le LLM est leur dépendance vis-à-vis des données sur lesquelles elles ont été formées. Si le LLM n'a pas été exposé à certaines informations ou à un domaine de connaissances entier pendant la préformation, il ne comprendra pas les relations linguistiques nécessaires pour générer des réponses précises. Ce manque de données peut conduire le LLM à admettre qu'il ne connaît pas la réponse ou, pire, à "halluciner" et à fournir des informations incorrectes.
Le RAG est une technique populaire qui répond aux problèmes d'hallucination des LLM en leur fournissant des informations contextuelles supplémentaires. Elle permet également aux développeurs et aux entreprises d'accéder à leurs données privées ou propriétaires sans se soucier des problèmes de sécurité.
Figure 1 - Fonctionnement de RAG] (https://assets.zilliz.com/Figure_1_How_RAG_works_246044aacf.png)
RAG commence par un modèle d'intégration qui transforme les données textuelles en intégrations vectorielles, des représentations numériques qui capturent le sens sémantique du texte. Le système RAG stocke ensuite ces vecteurs dans une base de données vectorielle telle que Milvus ou Zilliz Cloud, qui les indexe pour la recherche de similaritéh.
Lorsqu'un utilisateur soumet une requête, le modèle d'intégration convertit également l'entrée en un vecteur. Ensuite, le système RAG compare la similarité de ce vecteur de requête avec les vecteurs de la base de données vectorielles en calculant leur distance dans l'espace vectoriel à haute dimension. Si des données pertinentes sont trouvées, le système RAG récupère ces informations et les ajoute à la requête originale pour former une nouvelle invite pour le LLM. Le LLM utilise ces informations supplémentaires pour générer une réponse plus précise et contextuelle, dépassant ce qu'il pourrait produire en se basant uniquement sur ses données d'apprentissage.
Qu'est-ce que le RAG multilingue ?
La RAG multilingue étend les capacités de la RAG traditionnelle pour prendre en charge plusieurs langues. Il intègre un modèle d'intégration formé dans différentes langues, ce qui permet au système de traiter et de générer des réponses dans différentes langues. Grâce à cette approche multilingue, le système RAG peut traiter des requêtes dans n'importe quelle langue, extraire des informations pertinentes quelle que soit la langue d'origine et fournir des réponses précises et adaptées au contexte dans la langue préférée de l'utilisateur.
Comment créer une application RAG multilingue : un guide étape par étape
Maintenant que nous avons appris les concepts et les composants fondamentaux de RAG, nous allons mettre en œuvre une application RAG multilingue étape par étape.
Cet exemple d'application contient deux parties : un scraper web et l'application principale.
Le web scraper** récupère l'ensemble des données requises sur Internet.
L'application principale crée des encastrements vectoriels, effectue une recherche de similarité vectorielle et génère les réponses.
Le scraper web
Tout d'abord, nous allons récupérer des données de [Wikipedia] (https://www.wikipedia.org/) et les utiliser comme informations contextuelles pour cet exemple de RAG.
Définir les titres:** Nous commençons par définir une liste appelée
wiki_titles, qui contient une liste de villes. Chaque ville représente un fichier texte que le web scraper va remplir avec le contenu de l'entrée Wikipedia correspondante. Par exemple, "Atlanta.txt" contiendra du texte provenant de la page Atlanta de Wikipedia.Nous itérons sur chaque ville dans
wiki_titles, faisons une requête GET à l'API de Wikipedia, et extrayons le contenu de la page à partir de la réponse JSON. Le texte est ensuite enregistré dans un fichier texte correspondant à chaque ville.
from pathlib import Path
import requêtes
wiki_titles = [
"Atlanta",
"Berlin",
"Boston",
"Le Caire",
"Chicago",
"Copenhague",
"Houston",
"Karachi",
"Lisbonne",
"Londres",
"Moscou",
"Munich",
"Paris",
"Pékin", # Français pour Pékin
"San Francisco",
"Seattle",
"Shanghai",
"Tokyo",
"Toronto",
]
data_path = Path("./french_city_data")
data_path.mkdir(exist_ok=True) # S'assurer que le répertoire existe
for title in wiki_titles :
response = requests.get(
"https://fr.wikipedia.org/w/api.php",
params={
"action" : "query",
"format" : "json",
"titles" : titre,
"prop" : "extraits",
"explaintext" : True,
},
).json()
page = next(iter(response["query"]["pages"].values()))
wiki_text = page.get("extract", "") # Utiliser .get() pour éviter KeyError
if wiki_text : # Vérifie que l'extrait n'est pas vide
with open(data_path / f"{title}.txt", "w") as fp :
fp.write(wiki_text)
else :
print(f "Aucun extrait trouvé pour {titre}")
Préparation de l'environnement
Commencez par préparer votre environnement de développement en installant les bibliothèques nécessaires : la base de données vectorielle Milvus, LangChain, OpenAI et les transformateurs de phrases.
En outre, vous devrez inclure votre clé API si vous vous connectez à un LLM par le biais d'une API, comme OpenAI. Cette clé peut être stockée dans un fichier .env séparé et accessible en utilisant load_dotenv() et os .
Voici le code pour installer les bibliothèques et charger votre clé API :
!pip install -qU pymilvus langchain sentence-transformers tiktoken openai
from dotenv import load_dotenv
import os
load_dotenv() # Charge les variables d'environnement à partir du fichier .env
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY") # Définir la clé API
Installation des bibliothèques: Utilisez
pippour installerpymilvus,langchain,sentence-transformers,tiktoken, etopenai.Chargement des variables d'environnement:** Utilisez
dotenvpour charger les variables d'environnement à partir d'un fichier.env.Set API Key: Récupère la clé API OpenAI à partir des variables d'environnement et la définit.
Assurez-vous que votre fichier .env contient votre clé API OpenAI dans le format suivant :
OPENAI_API_KEY=votre_clé_api_ici
Initialisation du LLM
Une fois votre environnement mis en place, l'étape suivante consiste à définir le LLM que vous utiliserez dans votre application. Dans l'extrait de code ci-dessous, nous y parvenons en important la bibliothèque OpenAI et en définissant le LLM à l'aide du constructeur OpenAI.
from langchain.llms import OpenAI
llm = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
Vous pouvez opter pour un LLM open-source afin d'éviter les coûts associés aux appels à l'API OpenAI. Hugging Face offre des centaines de milliers de modèles d'apprentissage profond avec lesquels vous pouvez expérimenter. Pour utiliser un LLM open-source de HuggingFace, vous devez importer la [bibliothèque Transformers] (https://huggingface.co/docs/transformers/index).
from transformers import pipeline
llm = pipeline('text-generation', model='gpt2') # Remplacez 'gpt2' par le modèle de votre choix
Vous pouvez passer d'OpenAI à un modèle open-source en modifiant le code d'importation et d'initialisation correspondant.
Choisir un modèle d'intégration approprié
Lors de la construction d'un système RAG multilingue, [notre choix de modèle d'intégration] (https://zilliz.com/blog/choosing-the-right-embedding-model-for-your-data) est tout aussi important que notre choix de LLM, car le modèle d'intégration doit être compatible avec la langue avec laquelle vous travaillez. Pour cet exemple, où la langue est le français, les encastrements HuggingFace par défaut sont suffisants. Cependant, vous devez identifier et utiliser le modèle d'intégration le plus approprié pour d'autres langues.
Le [MTEB Leaderboard] (https://huggingface.co/spaces/mteb/leaderboard) sur HuggingFace est une ressource précieuse pour trouver des modèles d'intégration. Ce classement répertorie les modèles d'intégration les plus performants pour différentes langues, comme les intégrations chinoises et polonaises identifiées par Yujian. Lors de la sélection d'un modèle d'intégration, vous devez spécifier le nom du modèle en tant que paramètre.
Voici comment configurer le modèle d'intégration :
from langchain_community.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings()
# Alternativement, pour les embeddings chinois, le modèle est # passé en paramètre, par ex,
# HuggingFaceEmbeddings(nom_du_modèle="TownsWu/PEG")
Chargement et découpage des données en morceaux
Ensuite, nous chargeons les fichiers de données sur les villes que nous avons récupérés sur Wikipédia et nous les divisons en segments, ou morceaux. En divisant le texte en morceaux, nous évitons de comparer une requête à l'ensemble du document, ce qui améliore l'efficacité de la recherche d'informations. Plus le segment est petit, comme déterminé par le paramètre chunk_size, plus la précision est grande, mais plus les opérations de recherche sont nombreuses. Plus il y a de chevauchement entre les morceaux, tel que défini par le paramètre chunk_overlap, moins il y a de risque de perte de contexte - au prix d'une redondance accrue.
from langchain.text_splitter import CharacterTextSplitter
from langchain.schema import Document
files = os.listdir("./french_city_data")
textes_fichiers = []
pour fichier dans fichiers :
avec open(f"./french_city_data/{file}") as f :
texte_fichier = f.read()
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
chunk_size=512, chunk_overlap=64,
)
texts = text_splitter.split_text(file_text)
pour i, chunked_text dans enumerate(texts) :
file_texts.append(Document(page_content=chunked_text,
metadata={"doc_title" : file.split(".")[0], "chunk_num":i}))
Chargement des documents dans Milvus
Après avoir découpé les fichiers de données urbaines et les avoir stockés sous forme de liste de documents, nous devons les charger dans une base de données vectorielles, en l'occurrence la base de données vectorielles Milvus. Le code ci-dessous gère le chargement initial et les mises à jour lorsque les données de la ville sont stockées dans Milvus.
from langchain_community.vectorstores import Milvus
# Pour la première exécution
vector_store = Milvus.from_documents(
file_texts,
embedding=embeddings,
connection_args={"host" : "localhost", "port" : 19530},
collection_name="french_cities"
)
# si vos données sont déjà stockées dans Milvus
vector_store = Milvus(
embedding_function=embeddings,
connection_args={"host" : "localhost", "port" : 19530},
collection_name="villes françaises"
)
Créer un Retriever
Ensuite, nous allons initialiser notre récupérateur, une interface qui renvoie les documents d'une source particulière en fonction d'une requête donnée. Le code ci-dessous utilise le magasin vectoriel que nous avons créé à la dernière étape comme récupérateur et l'affecte à une variable.
retriever = vector_store.as_retriever()
Créer un modèle d'invite en utilisant LangChain
Les modèles d'invite vous permettent de formater précisément votre entrée dans un LLM au sein de votre application. Ils sont particulièrement utiles dans les cas où vous souhaitez réutiliser le même modèle d'invite mais avec des ajustements mineurs - comme dans notre application RAG multilingue où nous pouvons utiliser le même modèle d'invite pour plusieurs langues.
Les modèles d'invite vous permettent également de construire une invite à partir d'une entrée dynamique, par exemple une entrée utilisateur ou des données extraites d'un magasin de vecteurs. Dans notre application, nous inclurons dynamiquement la question, qui sera transmise directement à la chaîne, et le contexte que le récupérateur a obtenu du magasin de vecteurs.
from langchain.prompts import ChatPromptTemplate
template="""
Vous êtes un assistant pour les tâches de réponse aux questions. Utilisez les éléments de contexte suivants pour répondre à la question. Si vous ne connaissez pas la réponse, dites simplement que vous ne savez pas.
Utilisez trois phrases au maximum et soyez concis.
Répondez en français.
Question : {question}
Contexte : {contexte}
Réponse :"""
prompt = ChatPromptTemplate.from_template(template)
Enchaîner les composants pour créer l'application RAG
Le chaînage est un processus qui relie les composants pour créer des applications d'IA de bout en bout, ce qui est l'une des principales capacités de LangChain.
Le code ci-dessous montre comment construire une chaîne qui inclut les éléments suivants : le contexte du récupérateur, l'invite d'entrée traitée par la fonction runnablepassthrough(), le modèle d'invite, le LLM, et le stroutparser(), qui produit la réponse de l'invocation de la chaîne.
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
chain = (
{"context" : retriever, "question" : RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
Faire des requêtes avec la chaîne
Une fois la chaîne construite, vous pouvez l'invoquer à l'aide de diverses requêtes. Par exemple :
response = chain.invoke("Dites-moi un fait historique sur Karachi.")
Cette requête donne la réponse suivante en français !
"Karachi a été mentionnée pour la première fois dans l'ouvrage Histoire des plantes de Théophraste au IIIe siècle av. J.-C. Elle a été occupée par les Britanniques au début du XIXe siècle et est devenue la capitale du Sind en 1839. En 1876, le futur fondateur du Pakistan, Muhammad Ali Jinnah, est né et enterré à Karachi."
Pour démontrer les capacités multilingues, voici une autre question, en français cette fois :
response_2 = chain.invoke("Racontez-moi un fait historique sur Karachi.")
Bien que la question sous-jacente soit la même, les différentes langues (anglais et français) conduisent à des enchâssements différents, ce qui donne un résultat distinct :
"Karachi est une ville qui a été fondée par les Britanniques au début du XIXe siècle et qui est devenue la capitale du Sind. Elle a été un important centre économique et a connu une croissance rapide, notamment grâce à son port. Depuis les années 1980, la ville a été le théâtre de conflits ethniques et religieux, et en 2012, elle a été le site de l'incendie industriel le plus meurtrier de l'histoire".
Félicitations ! Vous avez réussi à créer une application RAG multilingue. Gardez à l'esprit que les embeddings sont essentiels à l'interprétation des langues par le LLM. Sélectionnez l'intégration la plus appropriée pour prendre en charge plusieurs langues et intégrez-la dans votre application.
Résumé
Ouah ! Ce billet est assez long. Récapitulons certains de ses points clés.
La [Retrieval augmented generation] (https://zilliz.com/learn/Retrieval-Augmented-Generation) (RAG) est un cadre qui augmente la sortie LLM en insérant des données supplémentaires dans les invites d'entrée. RAG peut résoudre les problèmes ennuyeux d'hallucination LLM.
Le RAG multilingue est un RAG étendu qui gère les documents multilingues.
Les modèles d'intégration, les bases de données vectorielles et les LLM sont trois éléments essentiels des applications RAG.
Le choix du modèle d'intégration est l'élément clé du développement d'applications RAG multilingues. Le classement MTEB de HuggingFace est une excellente ressource pour trouver le bon modèle pour votre application.
Autres ressources
Présentation complète de Yuijan Tang sur YouTube](https://www.youtube.com/watch?v=UhRD_ALzAnU&list=PLPg7_faNDlT5F_6J8jjFSMw-Ars_ogtZk&index=28)
Modèles d'intégration les plus performants pour vos applications GenAI | Zilliz
Techniques d'amélioration du RAG :
[Amélioration des RAG avec les graphes de connaissances à l'aide de WhyHow] (https://zilliz.com/blog/enhance-rag-with-knowledge-graphs)
Optimisation des RAG avec les Rerankers : le rôle et les compromis
[Conseils pratiques et astuces pour les développeurs d'applications RAG] (https://zilliz.com/blog/praticial-tips-and-tricks-for-developers-building-rag-applications)
Défis infrastructurels liés à la mise à l'échelle de RAG avec des modèles d'IA personnalisés
Continuer à lire

How to Improve Retrieval Quality for Japanese Text with Sudachi, Milvus/Zilliz, and AWS Bedrock
Learn how Sudachi normalization and Milvus/Zilliz hybrid search improve Japanese RAG accuracy with BM25 + vector fusion, AWS Bedrock embeddings, and practical code examples.

Zilliz Named "Highest Performer" and "Easiest to Use" in G2's Summer 2025 Grid® Report for Vector Databases
Zilliz shines in G2's Summer 2025 Grid® Report as both "Highest Performer" and "Easiest to Use," solving the performance-usability dilemma.

Vector Databases vs. Key-Value Databases
Use a vector database for AI-powered similarity search; use a key-value database for high-throughput, low-latency simple data lookups.
