Comment configurer LangChain avec la base de données VectorAI pour un RAG sur site
Résumé
- Ce tutoriel explique comment créer un pipeline RAG local à l'aide de LangChain, en utilisant VectorAI DB comme base de données vectorielle.
- L'abstraction VectorStore de LangChain permet aux équipes de changer de backend vectoriel sans avoir à réécrire la chaîne de récupération.
- Les développeurs peuvent utiliser les embeddings d'OpenAI ou travailler entièrement en local avec les embeddings de HuggingFace et Ollama.
- L'workflow ie aborde la configuration de Docker, le découpage des documents, le stockage vectoriel, la recherche par similarité et une chaîne RAG complète.
- Ce modèle convient aux cas d'utilisation sur site, en environnement isolé, en périphérie et liés à la localisation des données, pour lesquels les bases de données vectorielles dans le cloud ne constituent pas la solution idéale.
LangChain’s VectorStore L'abstraction sépare la chaîne de recherche de la base de données qui stocke vos représentations vectorielles. Cela signifie que vous pouvez remplacer un magasin de vecteurs hébergé par VectorAI DB tout en conservant, dans l'ensemble, inchangées la structure du moteur de recherche, la prompt et la chaîne LLM.
Ce tutoriel vous guide dans la mise en place de ce pipeline à partir de zéro. Vous exécuterez VectorAI DB en local avec Docker, chargerez et segmenterez des documents, générerez des représentations vectorielles, les stockerez dans une base de données vectorielle locale, puis connecterez cette base à une chaîne RAG LangChain. Le tutoriel couvre à la fois les représentations vectorielles OpenAI et celles de HuggingFace en local, et explique comment remplacer le LLM OpenAI par Ollama pour obtenir un pipeline entièrement local.
À la fin de ce guide, vous disposerez d’une application RAG (Retrieval-Augmented Generation) fonctionnelle, capable de fonctionner sans base de données vectorielle dans le cloud. Si vous effectuez une migration depuis Pinecone, une instance hébergée de Qdrant déploiement ou un autre magasin vectoriel géré, le changement est moins important qu’il n’y paraît. Pour comprendre pourquoi les développeurs délaissent les magasins vectoriels hébergés au profit d’alternatives locales, consultez l’article comparatif sur les bases de données vectorielles publié par l’ Embarqué .

Architecture du pipeline LangChain + VectorAI DB. Parcours d’ingestion (en haut) : les documents transitent par le chargeur, le séparateur de texte, le modèle d’embedding, puis sont stockés dans VectorAI DB. Parcours d’ requête ation (en bas) : la question d’ utilisateur ation est Embarqué à l’aide du même modèle ; VectorAI DB récupère les segments les plus proches, et les résultats transitent par le modèle de prompt et le LLM pour produire une réponse citée.
Conditions préalables.
Vérifiez les points suivants avant de commencer.
- Docker est installé et fonctionne.
- Python .10 ou version ultérieure.
- VectorAI DB Community Edition s'exécute en local. Si vous ne l'avez pas encore configurée, suivez le guide d'installation de VectorAI DB avant de continuer.
- Ollama installé avec
llama3.2tiré. Courirollama pull llama3.2sur un ordinateur disposant d'un accès à Internet.
Fonctionnement de l'interface VectorStore de LangChain
LangChain’s VectorStore La classe de base définit une interface standard que tout backend compatible implémente. Cette interface couvre quatre opérations principales : from_documents() pour créer un magasin et importer des documents en un seul appel, add_texts() pour ajouter du contenu à une boutique existante, similarity_search() pour récupérer les documents les plus proches d'une requête, et as_retriever() pour convertir le magasin en un « retriever » destiné à être utilisé dans une chaîne LangChain Expression Language (LCEL).
Tout backend compatible peut être intégré au chemin de recherche commun de LangChain, même si les fonctionnalités spécifiques à chaque backend, telles que la syntaxe de filtrage, les métriques de distance et la recherche hybride, varient toujours. Ce qui ne change pas, c’est la chaîne de recherche elle-même. Le moteur de recherche, le modèle de prompt, le modèle linguistique de grande envergure (LLM) et l’analyseur de sortie restent les mêmes, quel que soit le backend dans lequel sont stockés les vecteurs.
Le schéma ci-dessous illustre cela concrètement. Le panneau de gauche présente une instanciation d’un magasin de vecteurs dans le cloud. Le panneau de droite montre l’équivalent dans VectorAI DB. L’importation à la ligne 1, ainsi que le nom de la classe et les paramètres de connexion à la ligne 7, changent. Les lignes 8 à 17 (les documents, l’embedding, l’appel au moteur de recherche et la chaîne LCEL) sont identiques dans les deux cas.

Comparaison côte à côte entre l'instanciation d'un magasin vectoriel dans le cloud et celle d'une base de données VectorAI. L'importation et le changement de nom de classe. La logique de la chaîne aux lignes 15 à 17 est identique.
Étape 1 : Démarrer VectorAI DB
Exécutez VectorAI DB sous forme de conteneur Docker. Récupérez l'image et lancez-la avec un volume persistant :
docker pull actian/vectorai:latest
docker run -d --name vectorai \
-v ./local_data:/var/lib/actian-vectorai \
-p 6573-6575:6573-6575 \
-e ACTIAN_VECTORAI_ACCEPT_EULA=YES \
actian/vectorai:latest
Le conteneur expose l'API REST sur le port 6573, gRPC sur le port 6574 et une interface utilisateur Web locale sur le port 6575. L'intégration LangChain s'effectue via gRPC à l'adresse localhost:6574. L'édition Community suffit pour ce tutoriel et prend en charge jusqu'à 5 000 vecteurs stockés.
Vérifiez que le conteneur a bien démarré :
docker logs vectorai
Tu devrais voir Ready to accept connections... vers la fin de la sortie avant de continuer.

Sortie du terminal issue du contrôle d'intégrité de la connexion, confirmant la version et l'état du serveur de base de données VectorAI.
Étape 2 : Installer les dépendances d'Python
Installez tous les paquets nécessaires en une seule commande :
pip install langchain langchain-core langchain-text-splitters \
langchain-actian-vectorai langchain-openai \
langchain-huggingface langchain-ollama \
actian-vectorai-client sentence-transformers
LangChain a commencé à répartir ses intégrations dans des paquets autonomes. langchain-huggingface remplace les classes HuggingFace qui se trouvaient auparavant dans langchain-community, et langchain-ollama remplace les classes Ollama. Ce tutoriel utilise exclusivement les paquets autonomes actuels. L'utilisation de la version obsolète langchain-community Ces chemins d'accès généreront des avertissements de dépréciation dans les versions plus récentes de LangChain.
Si vous prévoyez d'utiliser la méthode d'intégration OpenAI à l'étape 5, configurez votre clé d'interface de programmation d'application (API) avant d'exécuter le code d'intégration :
export OPENAI_API_KEY="your-api-key-here"
Étape 3 : Se connecter à la base de données VectorAI à partir de Python
Vérifiez la connexion avant de charger des documents. Créez une collection de test, assurez-vous qu'elle apparaît dans la liste des collections, puis supprimez-la. Cette étape permet de vérifier que le serveur accepte à la fois les opérations de lecture et d'écriture avant de poursuivre.
from actian_vectorai import VectorAIClient, VectorParams, Distance
# Connect to VectorAI DB over gRPC.
client = VectorAIClient("localhost:6574")
client.connect()
# Create a test collection to confirm the server accepts writes.
client.collections.create(
"connection_test",
vectors_config=VectorParams(size=128, distance=Distance.Cosine),
)
# Confirm the collection was created.
collections = client.collections.list()
print(f"Collections: {collections}")
# Expected: ['connection_test']
# Remove the test collection before proceeding.
client.collections.delete("connection_test")
print("Connection verified. Ready to proceed.")
client.close()
Si client.connect() soulève une ConnectionError, vérifiez que le conteneur Docker est en cours d'exécution avec docker ps et que le port 6574 n'est pas bloqué par un autre processus.
Étape 4 : Chargement et découpage des documents
Utilisez un petit ensemble de documents intégrés, afin de ne pas avoir de dépendances externes pour cette étape. Le contenu aborde les concepts de base de données vectorielle et de RAG, qui permettent d'obtenir des résultats de recherche pertinents aux étapes 6 et 7.
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
# Inline documents keep this tutorial self-contained.
# In a production pipeline, replace this with a document loader
# such as PyPDFLoader, DirectoryLoader, or a custom ingestion process.
raw_documents = [
Document(page_content="""A vector database stores high-dimensional numerical
representations of data called embeddings. Each embedding captures the semantic
meaning of the original content, allowing the database to find similar items by
comparing their positions in vector space rather than matching exact keywords.""",
metadata={"source": "intro", "topic": "vector-databases"}),
Document(page_content="""Retrieval-Augmented Generation combines a retrieval
system with a language model. The retrieval component finds relevant documents from
a vector store based on the user question, and the language model generates an answer
grounded in those retrieved documents rather than relying on its training data alone.""",
metadata={"source": "intro", "topic": "rag"}),
Document(page_content="""Embedding models convert text into fixed-length numerical
vectors. The choice of embedding model determines the vector dimension and the quality
of semantic similarity. OpenAI text-embedding-ada-002 produces 1536-dimensional vectors.
Sentence transformers such as all-MiniLM-L6-v2 produce 384-dimensional vectors and
run locally without an external API key.""",
metadata={"source": "intro", "topic": "embeddings"}),
Document(page_content="""Metadata filtering narrows the candidate set before
running similarity search. Attaching fields such as document type, date, or source
to each stored vector enables queries scoped to a specific subset of your collection
without changing the embedding or search logic.""",
metadata={"source": "intro", "topic": "filtering"}),
]
# RecursiveCharacterTextSplitter preserves sentence boundaries before splitting.
splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)
docs = splitter.split_documents(raw_documents)
print(f"Produced {len(docs)} chunks from {len(raw_documents)} documents.")
Les metadata champ sur chacun Document est stockée sous forme de données utiles dans la base de données VectorAI et peut être filtrée lors de la recherche.

Sortie du terminal indiquant le nombre de blocs générés à partir des quatre documents intégrés.
Étape 5 : Embarquer et enregistrement des documents
C'est le cœur de ce tutoriel. Deux méthodes s'offrent à vous selon que vous disposiez d'une clé API OpenAI ou que vous deviez travailler entièrement hors ligne. Choisissez-en une et utilisez-la systématiquement tout au long des étapes 6 et 7.
Une contrainte s'applique aux deux méthodes. Une collection créée avec un modèle d'embedding ne peut pas accepter de vecteurs provenant d'un autre modèle, car leurs dimensions diffèrent. Les embeddings d'OpenAI sont à 1 536 dimensions. Le modèle HuggingFace utilisé ci-dessous produit des vecteurs à 384 dimensions. Si vous changez de modèle d'embedding entre deux exécutions, transmettez force_recreate=True pour supprimer et recréer automatiquement la collection.
Piste n° 1 : Embeddings OpenAI
from langchain_actian_vectorai import ActianVectorAIVectorStore
from langchain_openai import OpenAIEmbeddings
from actian_vectorai import VectorAIClient
store = ActianVectorAIVectorStore.from_documents(
documents=docs,
embedding=OpenAIEmbeddings(), # produces 1536-dim vectors
collection_name="rag_documents",
url="localhost:6574",
force_recreate=True,
)
print("Done.")
# Confirm vectors are stored and queryable.
client = VectorAIClient("localhost:6574")
client.connect()
print(f"Active collections: {client.collections.list()}")
test = store.similarity_search("vector database", k=1)
print(f"Test search returned {len(test)} result. Vectors are queryable.")
client.close()
from_documents() gère la connexion à la base de données VectorAI, crée la collection et insère les vecteurs en un seul appel. C'est la seule partie du pipeline qui change lorsque vous modifiez le backend.
Piste 2 : Embeddings HuggingFace locaux. Utilisez cette piste si le pipeline doit s'exécuter sans appels d'API externes.
from langchain_actian_vectorai import ActianVectorAIVectorStore
from langchain_huggingface import HuggingFaceEmbeddings
from actian_vectorai import VectorAIClient
# all-MiniLM-L6-v2 produces 384-dim vectors and runs on CPU.
# Downloads ~90 MB on first use. Subsequent runs load from cache.
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
store = ActianVectorAIVectorStore.from_documents(
documents=docs,
embedding=embeddings, # produces 384-dim vectors
collection_name="rag_documents",
url="localhost:6574",
force_recreate=True,
)
print("Done.")
# Confirm vectors are stored and queryable.
client = VectorAIClient("localhost:6574")
client.connect()
print(f"Active collections: {client.collections.list()}")
test = store.similarity_search("vector database", k=1)
print(f"Test search returned {len(test)} result. Vectors are queryable.")
client.close()
Le téléchargement initial du modèle nécessite une connexion Internet. Une fois mis en cache, ce parcours s'exécute entièrement hors ligne.

Sortie du terminal pour le chemin 2 (embeddings HuggingFace locaux) confirmant que la collection a été créée et que les vecteurs peuvent faire l'objet de requêtes.
Lorsque vous avez besoin d'une configuration explicite de la collecte. Les from_documents() Le constructeur déduit la dimension du vecteur à partir du modèle d'intégration et crée automatiquement la collection. Si vous avez besoin d'un contrôle direct sur des paramètres tels que la métrique de distance, utilisez VectorAIClient directement et transmettre le client au magasin de vecteurs :
from actian_vectorai import VectorAIClient, VectorParams, Distance
from langchain_actian_vectorai import ActianVectorAIVectorStore
from langchain_openai import OpenAIEmbeddings
client = VectorAIClient("localhost:6574")
client.connect()
client.collections.create(
"rag_documents",
vectors_config=VectorParams(size=1536, distance=Distance.Cosine),
)
store = ActianVectorAIVectorStore(
client=client,
collection_name="rag_documents",
embedding=OpenAIEmbeddings(),
)
Étape 6 : requête le Vector Store
Effectuez une recherche par similarité sur les documents stockés. Le code ci-dessous se reconnecte à la collection existante afin de s'exécuter correctement à partir d'une nouvelle session d'Python .
/,code>from langchain_actian_vectorai import ActianVectorAIVectorStore
from langchain_huggingface import HuggingFaceEmbeddings
from actian_vectorai import VectorAIClient
# Utiliser le même modèle d’embedding que celui utilisé lors de l’ingestion.
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
client = VectorAIClient("localhost:6574")
client.connect()
store = ActianVectorAIVectorStore(
client=client,
collection_name="rag_documents",
embedding=embeddings,
)
# Recherche de similarité de base : renvoie les k documents les plus similaires.
results = store.similarity_search("Comment fonctionne le RAG ?", k=3)
for doc in results:
print(doc.page_content[:120])
print()
Pour récupérer les documents avec leurs notes brutes, utilisez similarity_search_with_score():
scored = store.similarity_search_with_score("How does RAG work?", k=3)
for doc, score in scored:
print(f"score={score:.4f} {doc.page_content[:100]}")
Lorsque l'intégration renvoie une distance cosinus, les valeurs les plus faibles indiquent des correspondances plus proches. Utilisez la distribution des scores issue de vos propres données pour choisir un seuil plutôt que de supposer un seuil universel :
# Example threshold. Tune this against your own evaluation data.
THRESHOLD = 0.3
confident_results = [
(doc, score) for doc, score in scored if score < THRESHOLD
]
Pour normaliser les scores sur une échelle de 0 à 1, où une valeur plus élevée indique une plus grande pertinence, utilisez similarity_search_with_relevance_scores():
relevance = store.similarity_search_with_relevance_scores("How does RAG work?", k=3)
for doc, score in relevance:
print(f"relevance={score:.3f} {doc.page_content[:100]}")

Sortie du terminal affichant les résultats d'une recherche par similarité, avec les scores cosinus bruts et les scores de pertinence normalisés pour les trois méthodes de recherche.
Étape 7 : Créer la chaîne RAG
Connectez le « retriever » à un LLM et créez une chaîne complète de questions-réponses à l'aide de LCEL. Les deux blocs de code se reconnectent à la base de données VectorAI au début afin de s'exécuter correctement à partir d'une nouvelle session.
Avec OpenAI
from langchain_actian_vectorai import ActianVectorAIVectorStore
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from actian_vectorai import VectorAIClient
embeddings = OpenAIEmbeddings()
client = VectorAIClient("localhost:6574")
client.connect()
store = ActianVectorAIVectorStore(
client=client,
collection_name="rag_documents",
embedding=embeddings,
)
retriever = store.as_retriever(search_type="similarity", search_kwargs={"k": 3})
# For diverse results that cover different aspects of the query,
# use Max Marginal Relevance search instead:
# retriever = store.as_retriever(
# search_type="mmr",
# search_kwargs={"k": 4, "fetch_k": 20, "lambda_mult": 0.5},
# )
prompt = ChatPromptTemplate.from_template("""Answer the question using only the
context below. If the context does not contain enough information to answer,
say so.
Context:
{context}
Question: {question}
Answer:""")
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
query = "What is Retrieval-Augmented Generation and how does it work?"
print(f"Query: {query}")
print()
answer = chain.invoke(query)
print(f"Answer: {answer}")
Remplacez le LLM par Ollama pour obtenir un pipeline entièrement local :
from langchain_actian_vectorai import ActianVectorAIVectorStore
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_ollama import OllamaLLM
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from actian_vectorai import VectorAIClient
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
client = VectorAIClient("localhost:6574")
client.connect()
store = ActianVectorAIVectorStore(
client=client,
collection_name="rag_documents",
embedding=embeddings,
)
retriever = store.as_retriever(search_type="similarity", search_kwargs={"k": 3})
prompt = ChatPromptTemplate.from_template("""Answer the question using only the
context below. If the context does not contain enough information to answer,
say so.
Context:
{context}
Question: {question}
Answer:""")
# Run `ollama pull llama3.2` before using this path.
# llama3.2 requires approximately 2 GB of available RAM.
# If you see an out-of-memory error, use llama3.2:1b (~700 MB) instead.
llm = OllamaLLM(model="llama3.2")
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
query = "What is Retrieval-Augmented Generation and how does it work?"
print(f"Query: {query}")
print()
answer = chain.invoke(query)
print(f"Answer: {answer}")
La réponse provient directement des documents stockés, et non des données d’ apprentissage s du modèle. Le moteur de recherche a extrait le fragment pertinent, la requête l’a transmis en tant que contexte, et le LLM a généré une réponse fondée sur ce contenu. L’approche OpenAI produit la même structure avec la même requête.
La structure de la chaîne LCEL est identique sur les deux chemins. Seule l'instanciation du LLM change. C'est le même principe que l'échange de mémoire vectorielle à l'étape 5. L'abstraction permet de séparer la logique de la chaîne du composant qui effectue le travail.

Sortie du terminal issue du chemin Ollama, affichant l'requête e et la réponse « grounded » extraite des documents stockés. Le chemin OpenAI produit une sortie équivalente en utilisant ChatOpenAI à la place d'OllamaLLM.
Quand utiliser ce modèle et quand envisager d'autres solutions
Ce modèle convient aux serveurs « sur site », aux déploiements en cloud privé, aux réseaux « air-gapped » où les documents ne peuvent pas quitter le réseau, aux environnements de conformité soumis à des exigences en matière de localisation des données, aux périphériques de périphérie où un magasin de vecteurs local léger est préférable à une dépendance au cloud, ainsi qu’aux déploiements où le coût est un facteur déterminant et où les frais de sortie liés aux bases de données vectorielles dans le cloud constituent un sujet de préoccupation.
Dans ces situations, envisagez une approche différente. Pour les charges de travail vectorielles inférieures à 1 million, au sein d’une équipe utilisant déjà Postgres, l’utilisation de pgvector sur la base de données existante est plus simple à mettre en œuvre qu’un conteneur supplémentaire. Pour les fonctions « serverless » soumises à des exigences strictes en matière de démarrage à froid, le temps de démarrage du conteneur ajoute une latence qui peut s’avérer inacceptable. Pour les équipes qui ne contrôlent pas leur propre infrastructure, un magasin vectoriel géré élimine la charge opérationnelle liée à ce modèle.
Pour conclure
Vous avez mis en place un pipeline RAG s'appuyant sur un magasin de vecteurs local. Le modèle de langage de grande échelle (LLM), le modèle de prompt, le moteur de recherche et l'analyseur de sortie sont les mêmes que ceux qu'utiliserait un backend hébergé. C'est là que réside l'intérêt pratique de LangChain : VectorStore Résumé : le remplacement des backends affecte l'appel d'instanciation, et non la chaîne.
La règle à suivre désormais est simple. Si votre base de données « charge de travail » ne dépasse pas la limite de 5 000 vecteurs imposée par la Community Edition, la solution locale présentée ici est suffisante. Si elle dépasse ce seuil ou nécessite des fonctionnalités telles que la recherche hybride ou la multi-location, l’offre payante de VectorAI DB et l’écosystème d’intégration plus étendu de LangChain s’adaptent tous deux de manière indépendante l’un de l’autre.
Pour un backend de mémoire persistante pour les agents, consultez la section « Utilisation de VectorAI DB comme backend de mémoire persistante pour les agents CrewAI ». Pour un pipeline RAG s'exécutant sur du matériel aux ressources limitées, consultez la section « Exécution de Gemma 4 sur du matériel en périphérie avec VectorAI DB ».