Créer des applications RAG sans OpenAI - Deuxième partie : Mixtral, Milvus et OctoAI
Ce blog est écrit par Yujian Tang et Thierry Moreau.
La génération augmentée par récupération (RAG) est le cas d’utilisation le plus populaire des LLMs à émerger en 2023. Alors que la plupart des exemples montrent comment construire un RAG avec les LLMs GPT d’OpenAI, cette série aborde la manière de construire un RAG sans OpenAI. Consultez Construire un RAG sans OpenAI, première partie ici.
Ce tutoriel est le deuxième volet de cette série, couvrant la construction d’un RAG avec Milvus, Mixtral hébergé via OctoAI, et LangChain. En particulier, nous ne partageons dans ce tutoriel que la partie émergée de l’iceberg des nombreux avantages de Mixtral : ses capacités multilingues.
Dans ce blog, nous couvrirons :
- Pile technologique RAG open source MMO(L)
- Mixtral
- Milvus
- OctoAI
- LangChain
- L’architecture de l’application RAG
- Configurez vos outils RAG
- Sélectionnez et chargez vos données
- Interrogez vos données avec OctoAI et Mixtral
- Résumé
Trouvez le notebook sur GitHub.
Pile technologique RAG open source MMO(L)
Il existe de nombreuses façons de construire des applications RAG, et lors de ma dernière étude de marché, j’ai trouvé plus de 50 outils différents dans la pile LLM. Dans cet exemple, nous nous concentrons sur quatre : Mixtral comme LLM, Milvus comme base de données vectorielle, OctoAI pour servir le LLM et le modèle d’embedding, et LangChain comme orchestrateur. Avant de plonger dans l’architecture, découvrons un peu les outils impliqués.
Mixtral
Mixtral 8x7B, ou simplement « Mixtral » en abrégé, est le dernier modèle publié par la startup française pionnière de l’IA Mistral. Lancé en décembre 2023 (avec l’article publié en janvier 2024), il représente une extension significative du précédent grand modèle de langage fondationnel, Mistral 7B. Mixtral est un modèle de langage Sparse Mixture of Experts (SMoE) 8x7B doté de capacités plus importantes que le Mistral 7B original. Il est plus grand, utilisant 13B paramètres actifs lors de l’inférence sur 47B paramètres, et prend en charge plusieurs langues, le code et une fenêtre de contexte de 32k. Début 2024, Mixtral est le modèle open source le mieux noté dans les classements LLM.
Milvus
Le cœur de la mémoire de notre application RAG est la base de données vectorielle. Milvus est une base de données vectorielle hautement évolutive destinée aux applications d’entreprise. Sa structure de système distribué inhérente permet une mise à l’échelle fluide lorsque vous approchez les véritables niveaux de production de vecteurs. Milvus fournit également d’autres fonctionnalités d’entreprise comme la multi-tenance, le contrôle d’accès basé sur les rôles et la haute disponibilité.
OctoAI
OctoAI fournit l’infrastructure permettant d’exécuter des modèles LLM à l’échelle de la production. OctoAI facilite l’intégration, par les développeurs IA, des modèles hébergés d’OctoAI, qui offrent une sélection de puissants modèles open source, notamment Mixtral et des ajustements fins communautaires comme Nous Hermes. Environ 9 nouvelles inscriptions OctoAI sur 10 commencent avec Mixtral comme modèle LLM de choix, et aujourd’hui Mixtral sur OctoAI génère quotidiennement des milliards de tokens pour les clients. Dans ce tutoriel, nous substituerons GPT au modèle très populaire Mixtral.
LangChain
LangChain est sans doute le framework d’application LLM le plus populaire du marché. LangChain inclut des intégrations avec presque tous les outils que vous pouvez imaginer. Bien que vous puissiez l’utiliser de nombreuses façons, dans cet exemple, nous l’utilisons pour tout connecter. Nous chargeons Milvus et le point de terminaison OctoAI via LangChain, puis nous l’utilisons pour tout « chaîner » ensemble.
L’architecture de l’application RAG
Chaque application RAG comporte quatre composants essentiels : le LLM, la base de données vectorielle, le modèle d’embedding et l’orchestrateur. Sous tout cela se trouve la couche d’infrastructure. Dans cette configuration, nous utilisons Mixtral comme LLM, Milvus comme base de données vectorielle, GTE Large comme modèle d’embedding, LangChain comme orchestrateur, et OctoAI comme couche d’infrastructure qui sert GTE Large et Mixtral.
Configurer vos outils RAG
Commençons par configurer nos outils RAG. Dans cette section, nous configurons nos endpoints d’inférence pour le LLM et les embeddings, et nous lançons notre base de données vectorielle. Commençons par installer les prérequis. Nous avons besoin de pymilvus et milvus pour travailler avec Milvus. Nous avons besoin de langchain, sentence-transformers et tiktoken pour utiliser les fonctionnalités de LangChain dans cet exemple. Enfin, nous avons également besoin de octoai-sdk pour interfacer avec les API d’embedding et de complétion de texte d’OctoAI.
Nous utilisons ce premier bloc de code pour charger la plupart de nos imports nécessaires depuis LangChain, y compris les imports Milvus et OctoAI. Nous chargeons également le module LLMChain, qui nous permet d’enchaîner des fonctions, et le module PromptTemplate, que nous utilisons pour transmettre des prompts à nos LLMs. Nous devons également charger nos variables d’environnement en mémoire. Il existe de nombreuses façons de le faire, mais pour cet exemple, nous utilisons load_dotenv de la bibliothèque python-dotenv.
# ! pip install pymilvus milvus langchain sentence-transformers tiktoken octoai-sdk python-dotenv
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain_community.llms.octoai_endpoint import OctoAIEndpoint
from langchain_community.embeddings import OctoAIEmbeddings
from langchain_community.vectorstores import Milvus
from dotenv import load_dotenv
import os
load_dotenv()
# the line below is just to show that you need to have your OCTOAI_API_TOKEN
os.environ["OCTOAI_API_TOKEN"] = os.getenv("OCTOAI_API_TOKEN")
L’étape suivante consiste à initialiser notre accès au LLM. OctoAI nous permet d’accéder facilement à des modèles hébergés et de les personnaliser selon notre cas d’utilisation. Nous utilisons OctoAIEndpoint de LangChain et transmettons l’endpoint (indiqué dans le code) pour accéder à un modèle. Nous transmettons également les paramètres LLM requis. Pour cet exemple, il s’agit du nom du modèle, du nombre maximal de tokens (la longueur du prompt de sortie), ainsi que d’autres paramètres pour indiquer au modèle ce que vous voulez qu’il fasse (par exemple, le prompt système) et à quel point il doit être créatif dans la sortie (“presence_penalty”, “temperature”, “top_p”). Pour les embeddings, nous transmettons simplement l’URL de l’endpoint OctoAI. Par défaut, il utilisera GTE Large. Au fil du temps, davantage de modèles d’embedding seront ajoutés à OctoAI.
llm = OctoAIEndpoint(
endpoint_url="https://text.octoai.run/v1/chat/completions",
model_kwargs={
"model": "mixtral-8x7b-instruct-fp16",
"max_tokens": 128,
"presence_penalty": 0,
"temperature": 0.01,
"top_p": 0.9,
"messages": [
{
"role": "system",
"content": "You are a helpful assistant. Keep your responses limited to one short paragraph if possible.",
},
],
},
)
embeddings = OctoAIEmbeddings(endpoint_url="https://text.octoai.run/v1/embeddings")
Le dernier élément de tout cela est la base de données vectorielle. Nous utilisons Milvus Lite comme base de données vectorielle. Importez default_server depuis Milvus, puis appelez la fonction start() pour démarrer le serveur.
from milvus import default_server
default_server.start()
Sélectionner et charger vos données
Une fois tout configuré, il est temps de charger nos données. Pour cet exemple, vous pouvez trouver les données dans le dépôt GitHub. Si vous souhaitez obtenir les données, elles sont simplement extraites de Wikipédia. Une fois nos données disponibles, il est temps de les charger dans une base de données vectorielle. Nous utilisons LangChain et Milvus pour cette tâche.
Les deux imports dont nous avons besoin depuis LangChain pour charger ces documents sont un séparateur de texte — CharacterTextSplitter dans ce cas — et Document. Nous pouvons maintenant charger l’intégralité du répertoire de données sous forme de liste de « Documents », une abstraction de LangChain. Pour cet exemple, nous chargeons un répertoire intitulé « data ». Nous créons également une liste vide pour contenir notre liste de Documents.
Ensuite, nous parcourons chacun des fichiers du répertoire. Nous lisons le fichier et utilisons un séparateur de texte pour découper le texte en morceaux. Pour cet exemple, nous utilisons une taille de morceau de 512 et un chevauchement de morceaux de 64. Ces valeurs ont été choisies simplement parce qu’elles ont généralement du sens. N’hésitez pas à les ajuster comme vous le souhaitez afin de voir en quoi les résultats peuvent différer.
Une fois le texte découpé en morceaux, nous le stockons sous forme de document avec quelques métadonnées. Les métadonnées que nous conservons avec le texte sont le titre du document et le numéro du morceau, afin de savoir où se trouve le morceau dans le document.
from langchain.text_splitter import CharacterTextSplitter
from langchain.schema import Document
files = os.listdir("./data")
file_texts = []
for file in files:
with open(f"./data/{file}") as f:
file_text = f.read()
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
chunk_size=512, chunk_overlap=64,
)
texts = text_splitter.split_text(file_text)
for i, chunked_text in enumerate(texts):
file_texts.append(Document(page_content=chunked_text,
metadata={"doc_title": file.split(".")[0], "chunk_num": i}))
Une fois les Documents prêts, il est temps de les insérer dans la base de données vectorielle. Nous utilisons l’intégration Milvus de LangChain et appelons la fonction from_documents. Transmettez les documents, le modèle d’embeddings, les arguments de connexion pour l’instance Milvus Lite et un nom pour les collections. Nous appelons simplement la méthode as_retriever() pour ajouter un LLM par-dessus et commencer à travailler avec Milvus comme base de données vectorielle pour cet exemple RAG de base.
vector_store = Milvus.from_documents(
file_texts,
embedding=embeddings,
connection_args={"host": "localhost", "port": default_server.listen_port},
collection_name="cities"
)
retriever = vector_store.as_retriever()
Interroger vos données avec OctoAI et Mixtral
Tout est prêt. Nous pouvons maintenant utiliser LangChain pour orchestrer la partie récupération de notre puzzle. Nous commençons par fournir un modèle à LangChain. Nous devons transmettre deux variables au modèle de prompt — le contexte que nous récupérons et la question que nous voulons poser. Nous pouvons traiter la chaîne du modèle comme une f-string, puis la transmettre à la fonction from_template de PromptTemplate.
Après le modèle, nous configurons la partie « chaîne ». Nous avons besoin du module RunnablePassthrough pour transmettre le contexte et du StrOutputParser pour analyser la sortie. Ensuite, nous configurons la chaîne. D’abord, nous indiquons à LangChain où obtenir le contexte et la question, puis nous les transmettons au prompt, qui est ensuite transmis au LLM et enfin à l’analyseur de sortie de chaîne pour l’analyse. Pour poser une question, nous faisons simplement un invoke de la chaîne.
template = """Answer the question based only on the following context:
{context}
Question: {question}
"""
prompt = PromptTemplate.from_template(template)
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
chain.invoke("How big is the city of Seattle?")
Pour l’exemple de « Quelle est la taille de la ville de Seattle », nous pouvons voir ci-dessous une sortie attendue. La réponse est exacte et étayée par les informations de Wikipédia que nous avons stockées dans la base de données vectorielle.
Exploiter les capacités multilingues de Mixtral grâce à OctoAI
Puisque nous utilisons Mixtral, exploitons certaines de ses capacités uniques, comme son expertise multilingue. L’avantage d’utiliser OctoAI est que nous pouvons exploiter le même endpoint avec une instruction différente pour faire différentes choses. Dans ce cas, nous dirons au modèle de répondre en français et non en anglais.
# Let's make this a bit more fun and showcase the multilingual capabilities of Mixtal which really outshine other open source models
# Our Vector DB is populated with entries from english text - even the embedding model we're using here, GTE-Large
# works best on english text. However Mixtral has good mutlilingual capabilities in French, German, Spanish and Italian.
# So what we'll do is ask the assistant to only answer in french in the system and user prompt. RAG here is performed based on
# english text, but upon producing the user response, the Mixtral LLM will generate tokens in a different language here (french)
llm = OctoAIEndpoint(
endpoint_url="https://text.octoai.run/v1/chat/completions",
model_kwargs={
"model": "mixtral-8x7b-instruct-fp16",
"max_tokens": 128,
"presence_penalty": 0,
"temperature": 0.1,
"top_p": 0.9,
"messages": [
{
"role": "system",
"content": "You are a helpful assistant who responds in french and not in english.",
},
],
},
)
Nous utilisons également un modèle légèrement modifié pour demander à Mixtral de répondre en français. Le prompt doit être recréé à partir du nouveau modèle, et la chaîne doit être recréée à partir du nouveau prompt, du modèle et du LLM. La chaîne peut être invoquée de la même manière. Nous posons la même question en anglais et attendons une réponse en français.
template = """Answer the question in french based only on the following context:
{context}
Question: {question}
"""
prompt = PromptTemplate.from_template(template)
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
chain.invoke("How big is the city of Seattle?")
La réponse devrait ressembler à l’image ci-dessous.
Résumé
Dans ce tutoriel, nous avons exploré une autre façon de créer du RAG sans OpenAI. Dans la partie 1, nous avons utilisé Symbl.AI comme LLM ; dans celle-ci, nous avons utilisé Mixtral de Mistral hébergé par OctoAI. Les autres éléments du framework RAG que nous avons utilisés sont Milvus comme base de données vectorielle, LangChain comme orchestrateur, et GTE-Large, également hébergé par OctoAI, comme modèle d’embedding.
Nous avons configuré nos outils RAG et chargé quelques données de Wikipédia comme données d’exemple. Ensuite, nous utilisons LangChain pour lire les données dans Milvus et ajouter « le big model » par-dessus. À la fin, nous avons également pris le temps d’explorer l’une des capacités uniques de Mixtral : la possibilité de travailler dans plusieurs langues. Pour cet exemple, nous avons utilisé le français. La prochaine fois, nous explorerons également d’autres langues !
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.

Why and How to Migrate from Self-Hosted Milvus to Zilliz Cloud
A simple, step-by-step guide to migrating from Milvus to Zilliz Cloud. Learn both endpoint and backup methods for a smooth, scalable vector database migration.

Zilliz Cloud Launches in AWS Australia, Expanding Global Reach to Australia and Neighboring Markets
We're thrilled to announce that Zilliz Cloud is now available in the AWS Sydney, Australia region (ap-southeast-2).



