Aller au contenu principal

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