📚 Cet article s’intègre dans le parcours IA d’ITSkillsCenter. Pour la vue d’ensemble — Claude, ChatGPT, RAG, agents, Ollama, plan 90 jours — voir le panorama IA pour PME francophones 2026.
Ce que vous saurez faire à la fin
- Monter un mini RAG en Python en 30 minutes
- Indexer vos documents dans une base vectorielle
- Reranker pour améliorer la précision
- Évaluer avec RAGAS
- Déployer via Streamlit
Vue d’ensemble 1 — Installation
pip install anthropic voyageai chromadb pypdf2 tiktoken
Vue d’ensemble 2 — Lire et chunker
from PyPDF2 import PdfReader
def lire_pdf(chemin):
reader = PdfReader(chemin)
return "\n".join(p.extract_text() or "" for p in reader.pages)
def chunk_text(texte, taille=800, overlap=100):
chunks, start = [], 0
while start < len(texte):
chunks.append(texte[start:start + taille])
start += taille - overlap
return chunks
Vue d’ensemble 3 — Indexer avec Chroma + Voyage
import chromadb, voyageai
vo = voyageai.Client()
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection("docs_itsc")
def indexer(chemin):
chunks = chunk_text(lire_pdf(chemin))
embs = vo.embed(chunks, model="voyage-3", input_type="document").embeddings
collection.add(
documents=chunks,
embeddings=embs,
metadatas=[{"source": chemin, "chunk": i} for i in range(len(chunks))],
ids=[f"{chemin}-{i}" for i in range(len(chunks))],
)
from pathlib import Path
for pdf in Path("./docs").glob("*.pdf"):
indexer(str(pdf))
Vue d’ensemble 4 — Interroger
import anthropic
claude = anthropic.Anthropic()
def ask(question, k=5):
q_emb = vo.embed([question], model="voyage-3", input_type="query").embeddings[0]
res = collection.query(query_embeddings=[q_emb], n_results=k)
contexte = "\n\n".join(
f"[{md['source']}] {doc}"
for doc, md in zip(res["documents"][0], res["metadatas"][0])
)
r = claude.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system="Réponds avec les infos du contexte uniquement. Cite les sources.",
messages=[{"role":"user","content":
f"<contexte>{contexte}</contexte>\n\nQuestion: {question}"}],
)
return r.content[0].text
print(ask("Politique de remboursement ?"))
Vue d’ensemble 5 — Chunking sémantique
import re, tiktoken
enc = tiktoken.get_encoding("cl100k_base")
def chunk_semantic(texte, target=800):
phrases = re.split(r'(?<=[.!?])\s+', texte)
chunks, courant, tok = [], "", 0
for p in phrases:
t = len(enc.encode(p))
if tok + t > target and courant:
chunks.append(courant.strip())
courant, tok = p, t
else:
courant += " " + p
tok += t
if courant: chunks.append(courant.strip())
return chunks
Vue d’ensemble 6 — Reranking
pip install cohere
import cohere
co = cohere.Client()
def ask_v2(question, k_retrieve=20, k_final=5):
q_emb = vo.embed([question], model="voyage-3", input_type="query").embeddings[0]
res = collection.query(query_embeddings=[q_emb], n_results=k_retrieve)
docs = res["documents"][0]
rerank = co.rerank(
model="rerank-multilingual-v3.0",
query=question, documents=docs, top_n=k_final,
)
top_docs = [docs[r.index] for r in rerank.results]
# Suite identique avec top_docs
Vue d’ensemble 7 — Métadonnées et filtres
results = collection.query(
query_embeddings=[q_emb],
n_results=5,
where={
"departement": {"$eq": "juridique"},
"niveau_acces": {"$in": ["public", "interne"]},
},
)
Vue d’ensemble 8 — Prompt caching
r = claude.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=[{
"type": "text",
"text": SYSTEM_PROMPT, # 3000+ tokens stables
"cache_control": {"type": "ephemeral"}
}],
messages=[...],
)
# -90% coût sur le system caché dès le 2e appel
Vue d’ensemble 9 — Évaluation RAGAS
pip install ragas
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision
dataset = {"question": [...], "answer": [...], "contexts": [...], "ground_truth": [...]}
result = evaluate(dataset, metrics=[faithfulness, answer_relevancy, context_precision])
# Seuil: faithfulness >= 0.9, context_precision >= 0.75
Vue d’ensemble 10 — Streamlit
import streamlit as st
st.title("Assistant ITSC")
if "historique" not in st.session_state:
st.session_state.historique = []
question = st.chat_input("Question ?")
if question:
with st.spinner("Recherche..."):
reponse = ask_v2(question)
st.session_state.historique.append({"role":"user","content":question})
st.session_state.historique.append({"role":"assistant","content":reponse})
for m in st.session_state.historique:
with st.chat_message(m["role"]):
st.write(m["content"])
Checklist
✓ Chunking sémantique par phrases
✓ Reranking pour précision top-5
✓ Métadonnées pour filtrer (permissions)
✓ Prompt caching sur system prompt
✓ RAGAS > 0.85 faithfulness
✓ Pipeline d'ingestion automatisé
Pourquoi connecter une IA a vos propres donnees
Un modele de langage entraine par Anthropic ou OpenAI ne connait ni votre catalogue produit, ni vos proces-verbaux internes, ni les notes de service de votre direction commerciale a Niamey. Le Retrieval Augmented Generation, ou RAG, comble exactement ce trou. L idee tient en une phrase: au moment ou l utilisateur pose une question, on cherche dans une base vectorielle les passages internes les plus pertinents, on les injecte dans le prompt, puis on demande au modele de repondre a partir de ce contexte. La reponse devient ancree dans vos documents, tracable et beaucoup moins sujette aux hallucinations.
Concretement, une PME ouest-africaine peut deployer un assistant interne qui repond aux RH sur la convention collective, ou un chatbot client qui maitrise reellement le tarif detaille de chaque service. Ce tutoriel construit un RAG complet en Python, avec Voyage AI pour les embeddings, Pinecone comme base vectorielle, et l API Anthropic Claude pour la generation. Le tout en moins de 200 lignes de code, sans dependance lourde, deployable sur un VPS a 5 dollars par mois ou sur Render gratuit.
Etape 1 — Comprendre la chaine RAG en cinq maillons
Maillon un: l ingestion. Vous parcourez vos PDF, fichiers Word, pages internes, et vous decoupez chaque document en chunks de 500 a 1000 caracteres avec un chevauchement de 100 caracteres. Maillon deux: l embedding. Chaque chunk est transforme en un vecteur numerique de plusieurs centaines de dimensions par un modele d embedding. Maillon trois: l indexation. Ces vecteurs sont stockes dans une base specialisee capable de retrouver les plus proches voisins en quelques millisecondes.
Maillon quatre: la requete. Quand l utilisateur pose une question, vous embedez sa question avec le meme modele, puis vous cherchez les cinq vecteurs les plus proches. Maillon cinq: la generation. Vous construisez un prompt qui contient la question et les cinq passages retrouves, et vous l envoyez a Claude. Le modele repond en s appuyant explicitement sur le contexte fourni. Cette architecture est la meme chez Notion AI, Mendable, ou les copilotes d entreprise Microsoft.
Etape 2 — Preparer l environnement Python
Sur Ubuntu 22.04 ou Debian 12, verifiez Python 3.11 minimum avec python3 –version. Creez un dossier de projet, par exemple ~/projets/rag-pme, et un environnement virtuel propre.
mkdir -p ~/projets/rag-pme && cd ~/projets/rag-pme
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install anthropic voyageai pinecone-client pypdf python-docx tiktoken
L isolation par venv evite les conflits avec d autres projets. Le resultat attendu est un prompt prefixe par (.venv) et un pip list qui montre les six paquets installes sans erreur. Si pip echoue sur tiktoken, installez d abord build-essential et python3-dev: sudo apt install build-essential python3-dev. Sur Windows, utilisez py -3.11 -m venv .venv puis .venv\Scripts\activate.
Etape 3 — Obtenir les trois cles API necessaires
Creez un compte sur console.anthropic.com et generez une cle sk-ant-api03… avec un credit minimal de 5 dollars. Creez un compte sur dash.voyageai.com et recuperez votre cle voyageai. Voyage AI offre 50 millions de tokens gratuits, suffisants pour indexer un corpus PME de plusieurs milliers de pages. Creez un compte sur app.pinecone.io en plan Starter gratuit et notez votre cle API ainsi que la region, par exemple us-east-1-aws.
Stockez ces trois cles dans un fichier .env a la racine du projet, jamais en dur dans le code source. Le fichier ressemble a ANTHROPIC_API_KEY=sk-ant-api03-xxx, VOYAGE_API_KEY=pa-xxx, PINECONE_API_KEY=xxx-xxx. Ajoutez .env a votre .gitignore. Cette discipline evite les fuites de cles sur GitHub, probleme quotidien chez les freelances africains qui apprennent encore les bonnes pratiques.
Etape 4 — Decouper et embarquer les documents
Creez ingest.py. Ce script parcourt un dossier docs/, lit chaque PDF avec pypdf, chaque .docx avec python-docx, decoupe le texte en chunks et appelle Voyage AI pour generer les embeddings. Le modele voyage-3-large produit des vecteurs de 1024 dimensions, excellents pour le francais.
import os, voyageai
from dotenv import load_dotenv
load_dotenv()
vo = voyageai.Client(api_key=os.environ["VOYAGE_API_KEY"])
result = vo.embed(["texte exemple"], model="voyage-3-large", input_type="document")
print(len(result.embeddings[0]))
L output attendu est 1024, qui confirme la dimension des vecteurs. Pour le decoupage, une boucle simple sur le texte avec une fenetre glissante de 800 caracteres et un pas de 700 caracteres donne un chevauchement naturel de 100 caracteres. Evitez les chunks trop grands, le modele d embedding plafonne en pertinence au-dela de 1024 tokens, et trop petits car la recherche perd de la semantique.
Etape 5 — Indexer dans Pinecone
Connectez-vous a Pinecone, creez un index nomme rag-pme avec dimension 1024 et metric cosine. En code, le snippet ci-dessous cree l index s il n existe pas et upsert les vecteurs par lots de 100, limite imposee par l API Pinecone Starter.
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
if "rag-pme" not in [i.name for i in pc.list_indexes()]:
pc.create_index(name="rag-pme", dimension=1024, metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1"))
index = pc.Index("rag-pme")
index.upsert(vectors=batch)
Chaque vecteur est un tuple id, embedding, metadata ou metadata contient le texte original et la source du document. C est crucial pour citer la source dans la reponse finale. Le resultat attendu apres ingestion d un corpus de 50 PDF est visible dans le dashboard Pinecone: nombre de vecteurs, taille en megaoctets, et latence moyenne de requete sous 50 millisecondes.
Etape 6 — Construire la fonction de recherche
Creez search.py qui prend une question utilisateur, l embede en mode query, et retourne les top 5 passages avec leur score.
def search(question, k=5):
qv = vo.embed([question], model="voyage-3-large",
input_type="query").embeddings[0]
res = index.query(vector=qv, top_k=k, include_metadata=True)
return [(m["metadata"]["text"], m["metadata"]["source"], m["score"])
for m in res["matches"]]
Notez le parametre input_type qui distingue document et query: Voyage AI optimise differemment les deux, c est documente et ameliore la precision de cinq a dix pour cent. Testez avec une question dont vous connaissez la reponse dans vos documents. Si le score top 1 est inferieur a 0,5, c est que votre question est mal formulee ou que le passage n existe pas dans le corpus.
Etape 7 — Generer la reponse avec Claude
Creez ask.py. La fonction assemble un prompt systeme qui cadre Claude, puis injecte la question et les passages retrouves.
from anthropic import Anthropic
client = Anthropic()
def ask(question):
passages = search(question)
context = "\n\n".join([f"[Source: {s}]\n{t}" for t, s, _ in passages])
msg = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system="Tu reponds uniquement a partir du contexte fourni. Cite la source entre crochets.",
messages=[{"role": "user", "content": f"Contexte:\n{context}\n\nQuestion: {question}"}]
)
return msg.content[0].text
Le modele claude-sonnet-4-6 est rapide et economique pour ce type de tache, autour de 3 dollars par million de tokens d entree. Pour des questions complexes en analyse juridique ou audit financier, basculez sur claude-opus-4-7 plus puissant. Le resultat attendu est une reponse en francais qui cite les sources entre crochets, par exemple Selon le reglement interieur [Source: rh/reglement.pdf], les conges annuels sont de 30 jours.
Etape 8 — Evaluer et ameliorer la qualite
Construisez un fichier eval.json contenant 20 paires question, reponse attendue. Faites tourner le RAG sur ces 20 questions et comparez manuellement. Les axes d amelioration sont nombreux: agrandir les chunks, augmenter k de 5 a 10, ajouter un re-ranker comme voyage-rerank-2 qui reordonne les passages selon leur pertinence reelle, ou enrichir la metadata avec la date du document pour ponderer la fraicheur.
Pour les questions ambigues, une technique eprouvee est la decomposition: demander a Claude de reformuler la question en trois sous-questions, faire une recherche pour chacune, fusionner les passages, puis generer une seule reponse. Cette methode coute trois fois plus en API mais double souvent la qualite sur les sujets transverses comme les politiques RH ou les processus financiers internes.
Etape 9 — Mettre en production sur un VPS Afrique
Pour servir cette pile en production, un VPS Hetzner CX22 a 4,90 euros mensuels avec Ubuntu 24.04 fait largement l affaire pour 100 utilisateurs simultanes. Installez nginx en reverse proxy, gunicorn pour servir une API Flask ou FastAPI sur le port 8000, et certbot pour le HTTPS Let s Encrypt. Activez ufw avec uniquement les ports 22, 80, 443 ouverts.
L enjeu principal est la latence: depuis Dakar, Pinecone us-east-1 repond en 120 a 180 millisecondes, Anthropic en 1 a 3 secondes selon la longueur. Pour reduire la latence, hebergez sur eu-west-1 Frankfurt qui descend a 80 millisecondes pour Pinecone. Ajoutez un cache Redis local sur les questions frequentes: 30 pour cent du trafic d un chatbot interne se resout sur 10 questions recurrentes, le cache divise alors la facture API par trois.
Etape 10 — Securiser et superviser
Ajoutez une authentification simple par jeton JWT, journalisez chaque question avec horodatage et identifiant utilisateur, et purgez les logs apres 90 jours pour respecter les bonnes pratiques de protection des donnees personnelles. Mettez en place un quota par utilisateur, par exemple 50 questions par jour, pour eviter les abus et plafonner la facture. Sur le même thème sur les couches d analyse de donnees, lisez notre tutoriel tableau de bord financier OHADA qui montre comment exposer les KPI d usage du RAG dans Excel, et le guide simulation Monte Carlo pour prevoir les pics d usage.
Surveillez aussi les reponses Claude qui retournent Je ne sais pas: c est le signal que le passage manque dans la base, donc qu un document doit etre ajoute. Tenez un tableau Excel des questions sans reponse a indexer chaque semaine. En six mois, votre RAG converge vers un assistant interne reellement utile, dont la valeur percue depasse largement les 5 dollars mensuels d API.