Retrieval et Génération
Du vecteur à la réponse
Vous savez maintenant indexer des documents et rechercher les plus pertinents. L’étape finale du RAG consiste à injecter le contexte retrouvé dans un prompt pour que le LLM génère une réponse fondée sur vos données.
Le prompt RAG
La structure du prompt RAG est simple mais cruciale :
def build_rag_prompt(question, retrieved_chunks):
"""Construire le prompt RAG avec le contexte retrouvé."""
# Formater le contexte
context_parts = []
for i, chunk in enumerate(retrieved_chunks):
source = chunk.get("source", "source inconnue")
context_parts.append(f"[Document {i+1} - {source}]\n{chunk['text']}")
context = "\n\n---\n\n".join(context_parts)
prompt = f"""Vous etes un assistant expert. Repondez a la question en vous basant
UNIQUEMENT sur le contexte fourni ci-dessous. Si l'information n'est pas dans
le contexte, dites-le clairement.
## Contexte
{context}
## Question
{question}
## Reponse"""
return prompt
Générer la réponse
from mistralai import Mistral
client = Mistral(api_key=api_key)
def generate_answer(question, retrieved_chunks, model="mistral-large-latest"):
"""Générer une réponse RAG avec Mistral."""
prompt = build_rag_prompt(question, retrieved_chunks)
response = client.chat.complete(
model=model,
messages=[
{
"role": "system",
"content": "Vous etes un assistant precis qui repond en se basant sur le contexte fourni."
},
{
"role": "user",
"content": prompt,
}
],
temperature=0.1, # Basse température pour des réponses factuelles
)
return response.choices[0].message.content
# Exemple
question = "Quelles sont les étapes principales du RAG ?"
results = search_index(question, index, chunks, k=3)
answer = generate_answer(question, results)
print(answer)
Pipeline complet : question vers réponse
import numpy as np
import faiss
from mistralai import Mistral
client = Mistral(api_key=api_key)
class RAGPipeline:
"""Pipeline RAG complet avec Mistral et FAISS."""
def __init__(self, chunks, embeddings):
self.chunks = chunks
self.embeddings = embeddings.astype(np.float32)
# Créer l'index FAISS
dimension = embeddings.shape[1]
self.index = faiss.IndexFlatL2(dimension)
self.index.add(self.embeddings)
def retrieve(self, question, k=3):
"""Étape 1 : retrouver les chunks pertinents."""
response = client.embeddings.create(
model="mistral-embed",
inputs=[question],
)
query_vector = np.array(
[response.data[0].embedding], dtype=np.float32
)
distances, indices = self.index.search(query_vector, k)
results = []
for dist, idx in zip(distances[0], indices[0]):
if idx >= 0:
results.append({
"text": self.chunks[idx]["text"],
"source": self.chunks[idx].get("source", ""),
"distance": float(dist),
})
return results
def generate(self, question, context_chunks):
"""Étape 2 : générer la réponse avec le contexte."""
prompt = build_rag_prompt(question, context_chunks)
response = client.chat.complete(
model="mistral-large-latest",
messages=[{"role": "user", "content": prompt}],
temperature=0.1,
)
return response.choices[0].message.content
def ask(self, question, k=3):
"""Pipeline complet : question -> réponse sourcée."""
# Retrieval
context = self.retrieve(question, k=k)
# Generation
answer = self.generate(question, context)
return {
"question": question,
"answer": answer,
"sources": [c["source"] for c in context],
"n_chunks_used": len(context),
}
# Utilisation
rag = RAGPipeline(chunks, embeddings)
result = rag.ask("Comment fonctionne l'indexation dans FAISS ?")
print(f"Question: {result['question']}")
print(f"Reponse: {result['answer']}")
print(f"Sources: {result['sources']}")
Prompt engineering pour le RAG
Quelques techniques pour améliorer la qualité des réponses :
# 1. Prompt avec instruction de citation
prompt_cite = """Repondez en citant les documents sources entre crochets.
Exemple : "Le RAG améliore la précision [Document 1]."
Contexte: {context}
Question: {question}"""
# 2. Prompt avec format structuré
prompt_structured = """Repondez au format suivant :
**Reponse** : [votre réponse concise]
**Details** : [explication détaillée]
**Sources** : [liste des documents utilisés]
**Confiance** : [haute/moyenne/basse selon la couverture du contexte]
Contexte: {context}
Question: {question}"""
# 3. Prompt avec gestion de l'absence d'information
prompt_honest = """Si le contexte ne contient pas assez d'information pour
repondre completement, indiquez clairement :
- Ce que vous pouvez affirmer avec certitude
- Ce qui manque dans le contexte
- Des suggestions pour obtenir l'information manquante
Contexte: {context}
Question: {question}"""
Température et paramètres
# Pour le RAG, utilisez une température basse
response = client.chat.complete(
model="mistral-large-latest",
messages=[{"role": "user", "content": prompt}],
temperature=0.1, # Factuel, peu de créativité
max_tokens=1024, # Limiter la longueur de la réponse
top_p=0.95, # Nucleus sampling conservateur
)
Points clés à retenir
- Le prompt RAG injecte le contexte retrouvé avant la question
- Utilisez une température basse (0.1-0.3) pour des réponses factuelles
- Demandez au modèle de citer ses sources pour la traçabilité
- La classe RAGPipeline encapsule tout le flux : retrieve, generate, ask
- Gérez le cas ou le contexte ne contient pas assez d’information