Changer le thème

Ollama Embedding en pratique : recherche vectorielle locale et RAG

Easton editorial illustration: MCP trust and permission gateway

Plus de 200 PDF sur le disque, et vous cherchez le détail d’un plan technique vu il y a six mois. Recherche par mots-clés ? Inutile — vous vous souvenez du sens, pas des formulations exactes. Près d’une heure perdue. À ce moment-là, on rêve d’un outil qui « comprend le sens ».

Pire encore : ces documents touchent à l’architecture interne. Les envoyer dans le cloud pour une recherche vectorielle ? Hors de question. La ligne rouge de la confidentialité est nette.

Les embeddings Ollama règlent ce cas de figure : tout tourne en local, les données ne sortent pas, et la recherche devient sémantique. Après quelques jours d’essais entre mxbai, nomic et Qwen3, et un tour des bases vectorielles, les pièges apparaissent — mauvais modèle, mauvaise base, et la suite devient pénible.

Cet article rassemble ces retours d’expérience. À la fin, vous pourrez monter un RAG local de A à Z, du traitement documentaire à la recherche sémantique, avec le code complet.

La gamme de modèles Embedding Ollama

Ollama propose plusieurs modèles d’embedding ; au début, difficile de trancher. La doc officielle affirme que mxbai dépasse text-embedding-3-large d’OpenAI — ça sonne bien, mais en pratique ? Après les tests, voici une réponse directe.

Un tableau comparatif pour cadrer le choix :

ModèleDimensionContexteTaillePoints forts
mxbai-embed-large1024512 tokens670MChoix généraliste, tête du classement MTEB
nomic-embed-text7688192 tokens274MLongs textes, extension de contexte
Qwen3 Embedding10248192 tokens~ 600MSortie 2026, adapté au chinois

mxbai-embed-large est celui que j’utilise le plus. Pourquoi ? Simple, stable. Ses vecteurs 1024 dimensions suffisent dans la majorité des cas ; sur le benchmark MTEB (Massive Text Embedding Benchmark), il se place très haut, un peu au-dessus de text-embedding-3-large d’OpenAI. Recherche documentaire, recherche dans du code : c’est le choix sûr.

nomic-embed-text mise sur 8192 tokens de contexte. Pour un article entier ou un long fil de conversation, c’est utile. Plus léger (274M), donc souvent plus rapide que mxbai. Dimension 768 : en théorie un peu moins expressif — en test, peu de différence sur textes courts ; sur longs textes, nomic gagne.

Qwen3 Embedding est sorti par Alibaba en avril 2026. Le chinois est vraiment bon : sur des articles techniques, « mécanismes de tolérance aux pannes des systèmes distribués » et « conception tolante aux pannes » se rapprochent ; mxbai accroche moins. Contenu surtout en chinois : à essayer.

En résumé : débuter avec mxbai sans hésiter ; longs textes → nomic ; contenu chinois → Qwen3 en priorité. Tester les trois prend une demi-heure — les résultats comptent plus que le tableau.

Choisir une base de données vectorielle

Modèle choisi, où stocker ? Le choix de la base vectorielle piège autant que le modèle. Trop petit : la montée en charge fait mal ; trop gros : gaspillage. Trois options courantes :

BaseCas d’usageVolumeCaractéristiques
ChromaDBDébut, projets persomoins de 100 000 entréesPrêt à l’emploi, zéro config
FAISSPerformance mono-machine, recherche100 000 – 1 millionOpen source Meta, très rapide
MilvusProduction, entreprisemillions+Distribué, extensible, riche

ChromaDB est mon conseil pour débuter. Une commande : pip install chromadb. API claire : quelques lignes pour indexer et interroger. Index HNSW (Hierarchical Navigable Small World), largement suffisant à petite échelle. Limite : déploiement mono-machine ; au-delà de ~100 000 entrées, les perfs baissent.

FAISS, chez Meta, est un classique. C++ pur, vraiment rapide : sur 500 000 entrées, latence stable au milliseconde. C’est surtout une librairie de recherche, pas une base complète — stockage et fichiers d’index à gérer vous-même. Pour les bidouilleurs ou les besoins extrêmes de perf.

Milvus vise la production : distribué, persistance, plusieurs types d’index, offre cloud (Zilliz Cloud). Configuration et coût plus lourds. Millions d’entrées, haute dispo, équipe : là, ça vaut l’investissement.

Ma règle : perso / prototypage → ChromaDB, vite opérationnel ; recherche perf → FAISS ; production → Milvus ou cloud. Ne comptez pas migrer ChromaDB → Milvus sans douleur : formats et API diffèrent.

RAG complet : mise en œuvre

Moins de théorie, plus de code. Voici un RAG local de bout en bout : PDF → recherche sémantique, avec Ollama + ChromaDB.

Préparation de l’environnement

Dépendances :

pip install ollama chromadb langchain langchain-community pypdf

Ollama doit tourner, modèles téléchargés :

ollama pull mxbai-embed-large
ollama pull qwen2.5:7b  # pour générer les réponses

Code

import ollama
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import chromadb

# 1. Charger le PDF
loader = PyPDFLoader("./your_document.pdf")
docs = loader.load()

# 2. Découpage — chunk trop grand : recherche imprécise ; trop petit : perte d'info
splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,      # 800 caractères par bloc
    chunk_overlap=100,   # 100 caractères de chevauchement
)
chunks = splitter.split_documents(docs)

# 3. Embeddings Ollama + stockage ChromaDB
client = chromadb.Client()
collection = client.create_collection("my_docs")

for i, chunk in enumerate(chunks):
    response = ollama.embed(
        model="mxbai-embed-large",
        input=chunk.page_content,
    )
    embedding = response["embeddings"][0]

    collection.add(
        ids=[str(i)],
        embeddings=[embedding],
        documents=[chunk.page_content],
        metadatas=[{"source": chunk.metadata.get("source", "unknown")}],
    )

print(f"Indexé : {len(chunks)} fragments")

# 4. Recherche sémantique
query = "Quels sont les mécanismes de tolérance aux pannes des systèmes distribués ?"
query_embedding = ollama.embed(
    model="mxbai-embed-large",
    input=query,
)["embeddings"][0]

results = collection.query(
    query_embeddings=[query_embedding],
    n_results=3,
)

# 5. Réponse à partir du contexte récupéré
context = "\n\n".join(results["documents"][0])
response = ollama.chat(
    model="qwen2.5:7b",
    messages=[
        {
            "role": "system",
            "content": "Répondez à partir du contenu documentaire ci-dessous. Si l'information manque, dites-le clairement.",
        },
        {"role": "user", "content": f"Contenu : {context}\n\nQuestion : {query}"},
    ],
)

print(f"Réponse : {response['message']['content']}")

Lancez le script. Le flux reste linéaire : découpage → vecteurs → base → requête → réponse.

Pièges fréquents :

chunk_size : ne pas le fixer au hasard. À 200 caractères, les extraits sont des miettes, impossibles à assembler. 500-1000 caractères est une zone stable.

Traitement par lots : document volumineux, appels un par un = lent. Regroupez :

# Embeddings par lot — gain net
batch_texts = [chunk.page_content for chunk in chunks[:50]]
batch_embeddings = ollama.embed(
    model="mxbai-embed-large",
    input=batch_texts,
)["embeddings"]

Seuil de similarité : ChromaDB renvoie n_results entrées, pertinentes ou non. Parfois il faut filtrer :

# Filtrer par distance
results = collection.query(
    query_embeddings=[query_embedding],
    n_results=10,
)
filtered = [
    doc for doc, dist in zip(results["documents"][0], results["distances"][0])
    if dist < 0.3
]

À ce stade, vous avez un RAG local. Remplacez le PDF et la requête — le reste tient.

Réglages et bonnes pratiques

Système en place, place à l’optimisation. Quelques paramètres changent tout.

chunk_size :

500-1000 caractères en pratique. Trop petit : sens tronqué, mauvais rapprochement avec la question. Trop grand : bruit, plusieurs sujets dans le même bloc.

Selon le type : docs techniques structurés → découpage par paragraphe ; conversations fragmentées → blocs ~500 caractères. Testez sur vos données — pas de valeur universelle.

Lots pour accélérer :

Un appel par fragment = latence réseau à chaque fois. Lots de 50-100 : plusieurs fois plus rapide. Ne dépassez pas la limite d’entrée du modèle d’embedding.

Seuil de similarité :

Entre 0,7 et 0,85 selon l’exigence. Haut (0,85) : peu de faux positifs, moins de rappel ; bas (0,7) : plus de rappel, plus de bruit. Corpus propre et questions nettes → seuil haut ; questions floues → seuil plus bas.

Conseil : commencez avec 50-100 documents, validez la recherche, puis scalez. Tout indexer avant d’ajuster coûte cher en temps.

Synthèse

L’essentiel :

Modèle selon le cas — généraliste mxbai, long texte nomic, chinois Qwen3. Base : ChromaDB pour démarrer, FAISS si la perf prime, Milvus en production. Le code ci-dessus suffit pour un premier run.

Points forts : local, données maîtrisées ; modèles Ollama gratuits ; ChromaDB simple. Limites : plafond mono-machine, au-delà du million il faut une autre architecture.

Lancez le script avec une cinquantaine de documents, observez qualité de recherche et de réponse, puis étendez. Paramètres au vert, puis volume.

Pour aller plus loin avec LangChain et Ollama (chaînes de dialogue, appels d’outils), voir l’article « Intégration LangChain + Ollama en pratique » — celui-ci couvre la recherche vectorielle ; les deux ensemble dessinent une feuille de route LLM locale complète.

Construire un système RAG local

Mettre en place une recherche vectorielle locale avec Ollama + ChromaDB

⏱️ Estimated time: 30 min

  1. 1

    Step 1: Installer les dépendances et préparer les modèles

    Installez les dépendances avec les commandes suivantes :

    ```bash
    pip install ollama chromadb langchain langchain-community pypdf
    ollama pull mxbai-embed-large
    ollama pull qwen2.5:7b
    ```

    Vérifiez que le service Ollama est démarré.
  2. 2

    Step 2: Charger et découper les documents

    Chargez le PDF avec PyPDFLoader, découpez avec RecursiveCharacterTextSplitter :

    ```python
    loader = PyPDFLoader("./your_document.pdf")
    docs = loader.load()
    splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,
    chunk_overlap=100,
    )
    chunks = splitter.split_documents(docs)
    ```

    chunk_size conseillé : 500-1000 caractères.
  3. 3

    Step 3: Générer les vecteurs et les stocker

    Appelez l'API Ollama pour les embeddings, stockez dans ChromaDB :

    ```python
    client = chromadb.Client()
    collection = client.create_collection("my_docs")

    for i, chunk in enumerate(chunks):
    response = ollama.embed(
    model="mxbai-embed-large",
    input=chunk.page_content,
    )
    embedding = response["embeddings"][0]
    collection.add(
    ids=[str(i)],
    embeddings=[embedding],
    documents=[chunk.page_content],
    )
    ```

    Pour de gros volumes, privilégiez le traitement par lots.
  4. 4

    Step 4: Recherche sémantique et génération de réponse

    Vectorisez la requête, récupérez les documents pertinents, puis générez la réponse avec un LLM :

    ```python
    query_embedding = ollama.embed(
    model="mxbai-embed-large",
    input=query,
    )["embeddings"][0]

    results = collection.query(
    query_embeddings=[query_embedding],
    n_results=3,
    )

    context = "\n\n".join(results["documents"][0])
    response = ollama.chat(
    model="qwen2.5:7b",
    messages=[
    {"role": "system", "content": "Répondez aux questions à partir du contenu du document"},
    {"role": "user", "content": f"Document : {context}\nQuestion : {query}"},
    ],
    )
    ```

    Ajustez le seuil de similarité pour filtrer les résultats si besoin.

FAQ

Quel modèle Embedding Ollama choisir ?
Il n'y a pas de meilleur absolu — tout dépend du cas :

• mxbai-embed-large : choix généraliste, résultats stables pour la plupart des scénarios
• nomic-embed-text : longs textes, jusqu'à 8192 tokens
• Qwen3 Embedding : adapté au chinois, sorti en 2026

Testez les trois et retenez ce qui marche le mieux en pratique.
ChromaDB, FAISS ou Milvus — lequel prendre ?
Selon la taille des données et le contexte :

• ChromaDB : idéal pour débuter, zéro config, adapté à moins de 100 000 entrées
• FAISS : performance maximale en mono-machine, jusqu'à un million, stockage à gérer soi-même
• Milvus : production, déploiement distribué, millions d'entrées et plus

Projets perso : ChromaDB ; production : Milvus.
Quelle taille pour chunk_size ?
Conseil : 500-1000 caractères. Trop petit : sémantique incomplète ; trop grand : bruit à la recherche. Docs techniques : découper par paragraphe ; historiques de conversation : blocs d'environ 500 caractères. Ajustez en testant sur votre cas.
Comment accélérer la génération d'embeddings ?
Traitement par lots : regroupez 50-100 textes par appel API Ollama, bien plus rapide qu'appel par appel. Surveillez la taille des lots pour ne pas dépasser la limite d'entrée du modèle.
Quel seuil de similarité utiliser ?
Ajustez entre 0,7 et 0,85. Seuil haut (0,85) : précision élevée, moins de rappel ; seuil bas (0,7) : plus de rappel, risque de bruit. Corpus propre et questions précises : seuil haut ; questions floues : seuil plus bas.
Quels avantages et inconvénients d'un RAG local ?
Avantages : confidentialité des données, modèles gratuits, déploiement simple.

Inconvénients : performance limitée sur une seule machine, au-delà du million d'entrées il faut monter en gamme, maintenance des services à votre charge.

7 min de lecture · Publié le: 8 avr. 2026 · Mis à jour le: 30 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog