RAG : Connecter un LLM à Vos Documents Internes en Pratique

RAG : Connecter un LLM à Vos Documents Internes en Pratique

Votre équipe pose chaque semaine les mêmes questions au service RH, à la comptabilité ou au support technique. Et si un assistant IA pouvait répondre instantanément en s’appuyant sur vos propres procédures internes, vos contrats, vos fiches produit ? Selon une étude McKinsey de 2024, les entreprises qui déploient des assistants IA connectés à leur base documentaire réduisent de 40 % le temps consacré à la recherche d’information. La technologie qui rend cela possible s’appelle le RAG — Retrieval-Augmented Generation.

Qu’est-ce que le RAG et pourquoi dépasse-t-il le LLM seul ?

Un LLM comme GPT-4 ou Claude possède une connaissance générale impressionnante, mais il ignore tout de votre entreprise. Il ne connaît pas votre catalogue produit mis à jour le mois dernier, vos procédures RH spécifiques, ni les clauses de vos contrats fournisseurs.

Face à ce problème, deux approches naïves existent : envoyer tous vos documents dans le prompt (limité par la fenêtre de contexte et coûteux), ou fine-tuner le modèle sur vos données (coûteux, lent, et le modèle “oublie” les mises à jour). Le RAG offre une troisième voie, bien plus élégante.

Le principe : avant de générer une réponse, le système récupère les passages les plus pertinents de votre base documentaire, puis les injecte dans le contexte du LLM. Le modèle répond en s’appuyant sur des faits réels tirés de vos documents — et non sur des suppositions.

Comparaison des approches :

CritèreLLM seulFine-tuningRAG
Coût initialFaibleÉlevéMoyen
Mise à jour docsImpossibleLongImmédiat
Précision métierFaibleBonneTrès bonne
Traçabilité sourcesNonNonOui

Le pipeline RAG étape par étape

1. Ingestion et découpage (Chunking)

La première étape consiste à transformer vos documents en fragments exploitables. Un PDF de 50 pages doit être découpé en morceaux cohérents — ni trop petits (perte de contexte), ni trop grands (dilution de la pertinence).

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # ~500 caractères par chunk
    chunk_overlap=50,      # chevauchement pour ne pas couper les idées
    separators=["\n\n", "\n", ". ", " "]
)

chunks = splitter.split_documents(documents)

Stratégies de découpage selon le type de document :

  • Documents structurés (PDF avec titres) : découpage par section
  • Emails ou messages courts : par conversation
  • Textes longs et denses : chevauchement de 10-15 % recommandé
  • Tableaux ou données tabulaires : conserver la ligne d’en-tête dans chaque chunk

2. Embedding : transformer le texte en vecteurs

Chaque chunk est ensuite converti en un vecteur numérique (embedding) qui capture sa signification sémantique. Deux phrases qui veulent dire la même chose, même avec des mots différents, auront des vecteurs proches.

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
# Modèle multilingue — idéal pour du français

embeddings = model.encode(chunks, batch_size=32)

Pour le français, le modèle camembert-base ou paraphrase-multilingual-mpnet-base-v2 donne d’excellents résultats. Les services cloud (OpenAI Embeddings, Cohere) offrent des embeddings de haute qualité si vous n’avez pas de contrainte de souveraineté des données.

3. Stockage dans une base vectorielle

Les vecteurs sont stockés dans une base de données spécialisée qui permet une recherche par similarité ultra-rapide parmi des millions de documents.

import chromadb

client = chromadb.PersistentClient(path="./ma_base_docs")
collection = client.get_or_create_collection("documents_internes")

collection.add(
    embeddings=embeddings.tolist(),
    documents=chunks,
    ids=[f"chunk_{i}" for i in range(len(chunks))],
    metadatas=[{"source": doc.metadata["source"]} for doc in chunks]
)

Choix de la base vectorielle selon vos besoins :

SolutionTypeIdéal pour
ChromaDBLocal/Open-sourcePrototypage, PME < 100k docs
QdrantSelf-hosted/CloudProduction, filtres complexes
PineconeSaaSScalabilité sans ops
pgvectorExtension PostgreSQLSi vous avez déjà PostgreSQL
WeaviateSelf-hosted/CloudMultimodal (texte + images)

4. Retrieval : trouver les passages pertinents

Quand un utilisateur pose une question, celle-ci est aussi convertie en vecteur, puis on cherche les chunks les plus proches dans la base.

def retrieve(question: str, n_results: int = 5):
    query_embedding = model.encode([question])
    
    results = collection.query(
        query_embeddings=query_embedding.tolist(),
        n_results=n_results,
        include=["documents", "metadatas", "distances"]
    )
    return results

Astuce : combinez la recherche vectorielle (sémantique) avec une recherche par mots-clés (BM25) pour les termes techniques ou les références exactes. LangChain propose un EnsembleRetriever à cet effet.

5. Génération : le LLM répond avec contexte

Les chunks récupérés sont injectés dans le prompt avant la question de l’utilisateur :

from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

prompt_template = ChatPromptTemplate.from_template("""
Tu es un assistant interne de l'entreprise. Réponds uniquement en te basant 
sur les documents fournis. Si l'information n'est pas dans les documents, 
dis-le clairement.

Documents pertinents :
{context}

Question : {question}

Réponse :
""")

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

def generate_answer(question: str):
    results = retrieve(question)
    context = "\n\n---\n\n".join(results["documents"][0])
    
    chain = prompt_template | llm
    response = chain.invoke({"context": context, "question": question})
    return response.content

Cas pratique : la base de connaissances RH d’une PME

Prenons une PME de 80 employés avec des procédures RH dispersées dans 200 documents Word et PDF. Voici comment le RAG transforme l’expérience :

Avant le RAG : un employé qui demande “Combien de jours de congé puis-je poser en une seule fois ?” doit appeler les RH ou chercher dans une arborescence de dossiers.

Avec le RAG : il interroge l’assistant qui retrouve instantanément la clause correspondante dans la convention collective et dans le règlement intérieur, en citant les sources.

L’implémentation avec LlamaIndex, alternative à LangChain plus orientée recherche documentaire :

from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.node_parser import SentenceSplitter

# Charger tous les documents RH
documents = SimpleDirectoryReader("./docs_rh").load_data()

# Créer l'index
index = VectorStoreIndex.from_documents(
    documents,
    transformations=[SentenceSplitter(chunk_size=512)]
)

# Interroger
query_engine = index.as_query_engine(similarity_top_k=4)
response = query_engine.query("Quelle est la politique de télétravail ?")
print(response)
# Sources citées automatiquement

Optimiser la qualité de votre RAG

Le problème des mauvaises réponses vient souvent du retrieval, pas du LLM. Voici les leviers d’optimisation :

Enrichir les métadonnées : ajoutez au moment de l’indexation la date du document, le département, le type (procédure/contrat/FAQ). Cela permet des filtres précis (“cherche uniquement dans les documents RH mis à jour après janvier 2024”).

HyDE (Hypothetical Document Embeddings) : au lieu d’embedder directement la question, demandez au LLM de générer d’abord un document hypothétique qui répondrait à cette question, puis embedder ce document. Améliore la précision de 15-25 % sur les questions courtes.

Reranking : après le retrieval initial, utilisez un modèle de reranking (Cohere Rerank, cross-encoder) pour reclasser les résultats par pertinence réelle.

from sentence_transformers import CrossEncoder

reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

def rerank(question, chunks, top_k=3):
    pairs = [(question, chunk) for chunk in chunks]
    scores = reranker.predict(pairs)
    ranked = sorted(zip(scores, chunks), reverse=True)
    return [chunk for _, chunk in ranked[:top_k]]

Considérations pour une PME française

Souveraineté des données : si vos documents contiennent des données sensibles (RH, financières, clients), privilégiez une solution on-premise. ChromaDB + Ollama (LLM local) permet un RAG entièrement hébergé chez vous, sans envoyer de données vers des serveurs américains.

Coût : pour 10 000 pages de documents, l’indexation initiale avec OpenAI Embeddings coûte environ 0,50 €. Les requêtes quotidiennes restent négligeables (< 10 € / mois pour 100 utilisateurs).

Maintenance : prévoyez une stratégie de mise à jour. Quand un document est modifié, les anciens chunks doivent être supprimés et remplacés. LangChain propose des DocumentLoader avec détection de changements pour automatiser cela.

Ce que le RAG ne peut pas faire

Il faut être honnête sur les limites : le RAG ne raisonne pas sur plusieurs documents simultanément (ex : “compare ces trois contrats”), ne gère pas bien les questions nécessitant des calculs complexes, et peut citer des sources contradictoires sans les réconcilier. Pour ces cas, des architectures plus avancées (agents multi-outils, GraphRAG) sont nécessaires.

Conclusion

Le RAG est aujourd’hui la technique la plus pragmatique pour connecter l’intelligence des LLM à la connaissance de votre entreprise. Il ne nécessite pas de GPU, se met en place en quelques jours, et donne des résultats immédiatement utilisables. La clé du succès réside dans la qualité du découpage documentaire et d’une stratégie de retrieval adaptée à vos types de documents.

Votre entreprise possède une richesse documentaire inexploitée — procédures, historiques, expertises métier. Le RAG est le pont entre cette connaissance et l’efficacité opérationnelle que l’IA peut apporter.

Prêt à connecter vos documents à un LLM ? L’équipe Brio Novia accompagne les PME françaises dans la conception et le déploiement de solutions RAG sur mesure. Contactez-nous à contact@brio-novia.eu pour un audit gratuit de votre base documentaire.