Aller au contenu principal

Frameworks RAG : LangChain, LlamaIndex, Haystack

Mis à jour le 29 juillet 2026

Au-delà du RAG from scratch

Vous avez construit un pipeline RAG complet en Python pur, et c’était le bon chemin : vous savez maintenant ce que fait chaque ligne, où se cachent les erreurs d’alignement et pourquoi un chunk mal découpé ruine une réponse. En production, vous pouvez aussi vous appuyer sur des frameworks spécialisés qui prennent en charge la plomberie et ajoutent des fonctionnalités avancées. Les trois principaux — LangChain, LlamaIndex et Haystack — sont tous compatibles avec Mistral AI, et le choix entre eux dépend moins de la qualité que du type d’application que vous visez.

LangChain avec Mistral

LangChain est le framework le plus populaire pour les applications LLM. Son atout principal est l’ampleur de son écosystème d’intégrations, complété par le module LangGraph pour les control flows avancés. L’installation regroupe le coeur, le connecteur Mistral, les intégrations communautaires et FAISS.

pip install langchain langchain-mistralai langchain-community faiss-cpu

Le RAG minimal tient alors en une trentaine de lignes, et l’intérêt est de voir ce qui a disparu par rapport à votre implémentation manuelle. Le batching, les retries, la conversion en float32, l’alignement entre vecteurs et métadonnées : tout cela est absorbé par FAISS.from_texts. Le RecursiveCharacterTextSplitter remplace vos fonctions de chunking avec les mêmes paramètres qu’auparavant, 1500 caractères et 200 de chevauchement. La chaîne finale s’écrit avec l’opérateur pipe, qui compose les étapes de gauche à droite : le retriever alimente le contexte, RunnablePassthrough fait circuler la question telle quelle, le prompt les assemble, le modèle génère et le parser extrait la chaîne de caractères.

from langchain_mistralai import ChatMistralAI, MistralAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

# Modèles Mistral
embeddings = MistralAIEmbeddings(model="mistral-embed")
llm = ChatMistralAI(model="mistral-large-latest", temperature=0.1)

# Découpage et indexation
splitter = RecursiveCharacterTextSplitter(
    chunk_size=1500,
    chunk_overlap=200,
)
chunks = splitter.split_text(document_text)

# Créer le vector store
vectorstore = FAISS.from_texts(chunks, embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# Chaîne RAG
prompt = ChatPromptTemplate.from_template(
    """Contexte : {context}

Question : {question}

Répondez en vous basant sur le contexte."""
)

rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# Utilisation
answer = rag_chain.invoke("Comment fonctionne FAISS ?")
print(answer)

LangGraph pour le Corrective, le Self et l’Adaptive RAG

La chaîne linéaire ci-dessus convient au RAG standard, mais elle ne sait pas boucler ni bifurquer — or c’est exactement ce que font les architectures des trois leçons précédentes. LangGraph répond à ce besoin en définissant les control flows sous forme de graphes. Le GraphState typé joue le rôle de mémoire partagée : chaque noeud le reçoit, en lit ce dont il a besoin et retourne les champs qu’il met à jour. Vous reconnaîtrez vos propres fonctions derrière retrieve_node, grade_node et generate_node, grade_document étant réutilisé tel quel.

Le mécanisme réellement nouveau est add_conditional_edges. Là où votre classe Python enchaînait les if, le graphe déclare une fonction de décision — ici route_after_grade — et la table des destinations possibles. Cette séparation entre la logique métier des noeuds et la topologie du flux rend le pipeline lisible d’un coup d’oeil et modifiable sans toucher au code des étapes. Une fois compilé, le graphe s’invoque comme une simple fonction.

from langgraph.graph import StateGraph, END
from typing import TypedDict

class GraphState(TypedDict):
    question: str
    documents: list
    generation: str
    web_search_needed: bool

def retrieve_node(state):
    """Noeud de retrieval."""
    question = state["question"]
    docs = retriever.get_relevant_documents(question)
    return {"documents": docs, "question": question}

def grade_node(state):
    """Noeud de grading des documents."""
    question = state["question"]
    docs = state["documents"]
    relevant = []
    web_needed = False

    for doc in docs:
        # Utiliser le LLM pour évaluer la pertinence
        grade = grade_document(question, doc.page_content)
        if grade:
            relevant.append(doc)
        else:
            web_needed = True

    return {"documents": relevant, "web_search_needed": web_needed}

def generate_node(state):
    """Noeud de génération."""
    question = state["question"]
    docs = state["documents"]
    context = "\n\n".join([d.page_content for d in docs])
    generation = llm.invoke(f"Contexte: {context}\n\nQuestion: {question}")
    return {"generation": generation.content}

def route_after_grade(state):
    """Décision conditionnelle après le grading."""
    if state["web_search_needed"]:
        return "web_search"
    return "generate"

# Construire le graphe
workflow = StateGraph(GraphState)

# Ajouter les noeuds
workflow.add_node("retrieve", retrieve_node)
workflow.add_node("grade", grade_node)
workflow.add_node("generate", generate_node)

# Définir le flux
workflow.set_entry_point("retrieve")
workflow.add_edge("retrieve", "grade")
workflow.add_conditional_edges("grade", route_after_grade, {
    "generate": "generate",
    "web_search": "web_search",
})
workflow.add_edge("generate", END)

# Compiler
app = workflow.compile()

# Exécuter
result = app.invoke({"question": "Comment fonctionne le RAG ?"})
print(result["generation"])

LlamaIndex avec Mistral

LlamaIndex pousse l’abstraction plus loin, parce qu’il est optimisé spécifiquement pour le RAG et la gestion de documents structurés.

pip install llama-index llama-index-llms-mistralai llama-index-embeddings-mistralai

La configuration passe par un objet Settings global qui fixe le modèle de génération et le modèle d’embeddings une fois pour toutes. SimpleDirectoryReader charge un répertoire entier en devinant les formats, et VectorStoreIndex.from_documents enchaîne chunking et encodage sans que vous ayez à les paramétrer. Le gain de concision est réel, mais mesurez ce qu’il implique : la stratégie de découpage, dont vous savez maintenant qu’elle détermine la qualité du retrieval, est ici choisie pour vous. C’est confortable pour démarrer, et c’est le premier réglage que vous devrez reprendre en main le jour où les réponses décevront. Notez que response.source_nodes conserve les métadonnées, donc la traçabilité que vous aviez implémentée à la main.

from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.llms.mistralai import MistralAI
from llama_index.embeddings.mistralai import MistralAIEmbedding
from llama_index.core import Settings

# Configuration globale
Settings.llm = MistralAI(model="mistral-large-latest", temperature=0.1)
Settings.embed_model = MistralAIEmbedding(model_name="mistral-embed")

# Charger les documents
documents = SimpleDirectoryReader("./data/").load_data()

# Créer l'index (chunking + embedding automatiques)
index = VectorStoreIndex.from_documents(documents)

# Créer le moteur de requêtes
query_engine = index.as_query_engine(similarity_top_k=3)

# Poser une question
response = query_engine.query("Quelles sont les étapes du RAG ?")
print(response)
print(f"\nSources: {[n.metadata for n in response.source_nodes]}")

Haystack avec Mistral

Haystack, développé par deepset, adopte une troisième philosophie : celle des pipelines modulaires, où chaque composant est nommé, ajouté puis explicitement connecté à ses voisins.

pip install haystack-ai mistral-haystack

Le pipeline d’indexation ci-dessous illustre bien cette approche. Trois composants sont déclarés — le découpage, l’encodage par MistralDocumentEmbedder, l’écriture dans le document store — puis reliés par des appels connect qui rendent le flux de données visible dans le code. Cette verbosité, qui peut paraître inutile sur un cas simple, devient un atout dès que le pipeline se ramifie ou qu’il faut remplacer un maillon sans toucher aux autres. Remarquez aussi la distinction entre MistralDocumentEmbedder et MistralTextEmbedder : le premier encode les documents à l’indexation, le second encodera les requêtes à l’interrogation, une séparation qui prévient l’erreur classique consistant à traiter les deux de la même façon.

from haystack import Pipeline
from haystack.components.writers import DocumentWriter
from haystack.components.preprocessors import DocumentSplitter
from haystack_integrations.components.embedders.mistral import (
    MistralDocumentEmbedder,
    MistralTextEmbedder,
)
from haystack_integrations.components.generators.mistral import MistralChatGenerator
from haystack.document_stores.in_memory import InMemoryDocumentStore

# Document store
doc_store = InMemoryDocumentStore()

# Pipeline d'indexation
indexing_pipeline = Pipeline()
indexing_pipeline.add_component("splitter", DocumentSplitter(
    split_by="passage", split_length=5
))
indexing_pipeline.add_component("embedder", MistralDocumentEmbedder(
    model="mistral-embed"
))
indexing_pipeline.add_component("writer", DocumentWriter(
    document_store=doc_store
))

indexing_pipeline.connect("splitter", "embedder")
indexing_pipeline.connect("embedder", "writer")

Lequel choisir

Chaque framework a ses forces, et le tableau ci-dessous résume l’arbitrage.

FrameworkForce principaleTerrain de prédilection
LangChainÉcosystème le plus riche, LangGraph pour les control flows complexesAgents et RAG avancé
LlamaIndexLe plus simple pour le RAG, gestion native des documents structurésDémarrage rapide, débutants
HaystackApproche pipeline modulaire, fort en évaluationMise en production

Prochaines étapes

Vous disposez désormais de toutes les bases pour construire des systèmes RAG avec Mistral AI. Vous savez encoder du texte avec mistral-embed et ses 1024 dimensions, encoder du code avec codestral-embed et ses dimensions configurables jusqu’à 3072, monter un RAG from scratch sur FAISS et l’API Mistral, puis le fiabiliser avec les architectures Corrective, Self et Adaptive. Cette dernière leçon y ajoute la voie des frameworks, LangChain, LlamaIndex et Haystack.

Pour aller plus loin, explorez les cookbooks officiels de Mistral AI ainsi que les notebooks de chaque framework, qui suivent l’évolution des API de plus près que n’importe quel cours. Le RAG est un domaine en mouvement rapide : les techniques de chunking, de retrieval et de vérification s’améliorent constamment, et c’est justement parce que vous avez écrit chaque étape à la main que vous saurez évaluer la prochaine qui apparaîtra.

Points clés à retenir

  • LangChain + LangGraph est le choix principal pour les control flows RAG avancés
  • LlamaIndex est le plus simple pour un RAG standard avec Mistral
  • Haystack excelle en modularité et évaluation de pipelines
  • Les trois frameworks supportent nativement mistral-embed et mistral-large
  • Commencez par le RAG from scratch pour comprendre, puis migrez vers un framework pour la production

Testez vos connaissances

Embeddings, RAG et variantes avancées : le grand contrôle final.

1. Quels sont les deux modèles d'embeddings de Mistral, et pour quoi faire ?

Réponse : mistral-embed pour le texte général, codestral-embed pour le code — chacun projette son domaine dans un espace où la similarité sémantique se mesure.

2. Quelles étapes composent le pipeline RAG du cours ?

Réponse : Préparer les données (chunking), créer les embeddings, les stocker dans un vector store (FAISS), puis retrieval + génération : la question retrouve ses passages, le modèle répond dessus.

3. Que corrige le Corrective RAG ?

Réponse : Les récupérations médiocres : il évalue la pertinence des passages retrouvés et déclenche une correction (nouvelle recherche, reformulation) avant de générer — au lieu de répondre sur du bruit.

4. Self-RAG et Adaptive RAG : quelle idée commune ?

Réponse : Rendre le pipeline réflexif : décider quand récupérer, critiquer ses propres sorties, adapter la stratégie à la question — le RAG cesse d’être un tuyau fixe.

5. Qu'apportent LangChain, LlamaIndex ou Haystack ?

Réponse : L’outillage : composants prêts pour l’ingestion, les index, les chaînes et l’évaluation — utiles pour aller vite, à condition de comprendre ce qu’ils assemblent (ce que ce cours vous a appris).

Vous savez désormais construire un RAG à la main et le confier à un framework en connaissance de cause — c’est le bon ordre.