Génération RAG : Choisir et optimiser son LLM
Guide complet pour sélectionner et configurer votre LLM dans un système RAG : prompting, température, tokens et optimisation des réponses.
- Auteur
- Équipe Ailog
- Date de publication
- Temps de lecture
- 20 min de lecture
- Niveau
- intermediate
Génération RAG : Choisir et optimiser son LLM
La phase de génération est le moment où votre système RAG transforme les documents récupérés en une réponse cohérente et utile. Le choix du LLM et son paramétrage déterminent la qualité finale de l'expérience utilisateur. Ce guide vous accompagne dans la sélection, la configuration et l'optimisation de votre modèle de génération.
Le rôle du LLM dans un système RAG
Contrairement à un LLM utilisé seul, le LLM dans un système RAG ne génère pas à partir de ses connaissances internes. Il synthétise, reformule et structure les informations issues du retrieval.
Ce que fait le LLM RAG Synthèse : Condenser plusieurs documents en une réponse concise Reformulation : Adapter le langage technique au niveau de l'utilisateur Structuration : Organiser l'information de manière logique Contextualisation : Relier la réponse à la question posée Détection d'insuffisance : Identifier quand les documents ne permettent pas de répondre
Ce que le LLM RAG ne devrait PAS faire • Inventer des informations absentes des documents (hallucination) • Contredire les sources fournies • Répondre à des questions hors scope sans le signaler
Comparatif des LLMs pour le RAG
Modèles propriétaires
| Modèle | Forces | Faiblesses | Coût (1M tokens) | Usage RAG | |--------|--------|------------|------------------|-----------| | GPT-4o | Polyvalent, raisonnement | Prix élevé | ~$5 input / $15 output | Production premium | | GPT-4o-mini | Bon rapport qualité/prix | Moins performant sur tâches complexes | ~$0.15 / $0.60 | Production standard | | Claude 3.5 Sonnet | Excellent suivi instructions | Contexte 200k parfois sous-utilisé | ~$3 / $15 | Production premium | | Claude 3 Haiku | Ultra rapide, économique | Moins nuancé | ~$0.25 / $1.25 | Volume élevé | | Gemini 1.5 Pro | Contexte 1M tokens | API parfois instable | ~$1.25 / $5 | Documents très longs |
Modèles open source
| Modèle | Paramètres | VRAM requise | Performance RAG | Auto-hébergeable | |--------|------------|--------------|-----------------|------------------| | Llama 3.1 70B | 70B | 48GB+ | Excellente | Oui (serveur dédié) | | Llama 3.1 8B | 8B | 8GB | Bonne | Oui (GPU grand public) | | Mistral 7B | 7B | 6GB | Bonne | Oui | | Mixtral 8x7B | 46.7B (MoE) | 32GB | Très bonne | Oui | | Qwen2 72B | 72B | 48GB+ | Excellente | Oui |
Critères de choix
``python def choisir_llm( budget_mensuel: float, volume_requetes: int, exigence_qualite: str, "standard", "premium" contrainte_hebergement: str, "cloud", "souverain", "on-premise" langues: list[str] ) -> str:
tokens_par_requete = 2000 estimation moyenne RAG tokens_mensuels = volume_requetes tokens_par_requete
if contrainte_hebergement == "on-premise": if exigence_qualite == "premium": return "Llama 3.1 70B ou Qwen2 72B" return "Llama 3.1 8B ou Mistral 7B"
if contrainte_hebergement == "souverain": return "Mistral Large via API Mistral (hébergé UE)"
cout_gpt4o_mini = tokens_mensuels 0.15 / 1_000_000 cout_claude_haiku = tokens_mensuels 0.25 / 1_000_000
if budget_mensuel < cout_gpt4o_mini: return "Modèle open source auto-hébergé"
if exigence_qualite == "premium": if "fr" in langues: return "Claude 3.5 Sonnet (excellent en français)" return "GPT-4o"
return "GPT-4o-mini ou Claude 3 Haiku" `
Configurer le prompt système
Le prompt système définit le comportement du LLM dans votre application RAG. C'est l'élément le plus critique de la configuration.
Structure d'un prompt RAG efficace
`python SYSTEM_PROMPT = """Tu es un assistant IA pour {nom_entreprise}, spécialisé dans {domaine}.
RÈGLES STRICTES : Réponds UNIQUEMENT à partir des documents fournis dans le contexte Si l'information n'est pas dans les documents, dis "Je n'ai pas cette information dans ma base de connaissances" Ne jamais inventer d'informations Cite tes sources quand c'est pertinent
STYLE DE RÉPONSE : • Ton : {ton} (professionnel/amical/technique) • Longueur : {longueur} (concis/détaillé) • Format : Utilise des listes à puces pour plus de 3 éléments • Langue : Réponds dans la langue de la question
CONTEXTE MÉTIER : {contexte_specifique}
DOCUMENTS DISPONIBLES : {documents_retrieves}
QUESTION DE L'UTILISATEUR : {question} """ `
Exemples de prompts par cas d'usage
Support client e-commerce
`python ECOMMERCE_PROMPT = """Tu es l'assistant virtuel de la boutique {nom_boutique}.
OBJECTIFS : • Aider les clients avec leurs commandes, retours et questions produits • Maximiser la satisfaction client • Orienter vers le support humain si nécessaire
RÈGLES : Base tes réponses UNIQUEMENT sur les documents fournis Pour les questions sur une commande spécifique, demande le numéro de commande Ne communique jamais d'informations personnelles d'autres clients Si une demande dépasse tes capacités, propose de contacter le service client
TON : Amical et serviable, comme un vendeur en boutique
DOCUMENTS : {context}
QUESTION : {question} """ `
Base de connaissances technique
`python TECH_KB_PROMPT = """Tu es un expert technique pour {produit}.
OBJECTIFS : • Fournir des réponses techniques précises • Inclure des exemples de code quand pertinent • Guider vers la documentation officielle
RÈGLES : Réponds uniquement à partir de la documentation fournie Précise la version du produit concernée si mentionnée Signale les limitations connues ou bugs documentés Propose des alternatives si la solution demandée n'existe pas
FORMAT : • Commence par une réponse directe • Puis détaille si nécessaire • Termine par un exemple de code si applicable
DOCUMENTATION : {context}
QUESTION : {question} """ `
Paramètres de génération
Température
La température contrôle la créativité vs la cohérence des réponses.
`python Basse température (0.0 - 0.3) : Réponses déterministes, factuelles Idéal pour : Support client, FAQ, documentation technique temperature = 0.1
Température moyenne (0.4 - 0.7) : Équilibre créativité/cohérence Idéal pour : Chat général, reformulation temperature = 0.5
Haute température (0.8 - 1.0) : Réponses variées, créatives Idéal pour : Brainstorming, génération de contenu À ÉVITER en RAG (risque d'hallucination) temperature = 0.9 `
Top-p (Nucleus Sampling)
Limite les tokens considérés à ceux représentant une probabilité cumulée.
`python top_p = 0.9 : Considère les tokens jusqu'à 90% de probabilité cumulée Plus restrictif = plus cohérent Plus large = plus varié
from openai import OpenAI client = OpenAI()
response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}], temperature=0.2, top_p=0.9, Combiné avec température basse pour RAG ) `
Max tokens
Limite la longueur de la réponse générée.
`python Calcul du budget tokens def calculer_max_tokens( context_length: int, model_limit: int = 128000, GPT-4o reserve_output: int = 2000 ) -> int: """ Assure qu'on laisse assez de place pour la réponse """ prompt_tokens = context_length + 500 + instructions available = model_limit - prompt_tokens return min(available, reserve_output) `
Frequency et Presence Penalty
Contrôlent la répétition dans les réponses.
`python response = client.chat.completions.create( model="gpt-4o-mini", messages=messages, frequency_penalty=0.3, Pénalise les tokens déjà utilisés presence_penalty=0.1, Encourage les nouveaux sujets ) `
Optimiser le contexte fourni au LLM
Formatage des documents récupérés
`python def format_context(documents: list[dict], max_tokens: int = 4000) -> str: """ Formate les documents pour le contexte LLM """ formatted_docs = [] total_tokens = 0
for i, doc in enumerate(documents): Estimation tokens (1 token ≈ 4 caractères en français) doc_tokens = len(doc["text"]) // 4
if total_tokens + doc_tokens > max_tokens: break
formatted = f""" --- SOURCE {i+1}: {doc.get("source", "Document")} SCORE DE PERTINENCE: {doc.get("score", "N/A")}
{doc["text"]} --- """ formatted_docs.append(formatted) total_tokens += doc_tokens
return "\n".join(formatted_docs) `
Ordre des documents
L'ordre impacte l'attention du LLM. Deux stratégies principales :
`python def order_documents(documents: list[dict], strategy: str = "best_first") -> list: """ Ordonne les documents selon la stratégie choisie """ if strategy == "best_first": Le plus pertinent en premier return sorted(documents, key=lambda x: x["score"], reverse=True)
elif strategy == "lost_in_middle": Les meilleurs au début et à la fin (évite le "lost in the middle") sorted_docs = sorted(documents, key=lambda x: x["score"], reverse=True) n = len(sorted_docs) reordered = [] for i in range(n): if i % 2 == 0: reordered.insert(0, sorted_docs[i]) else: reordered.append(sorted_docs[i]) return reordered
return documents `
Compression du contexte
Pour les documents longs, résumer avant d'envoyer au LLM final :
`python async def compress_context( documents: list[str], query: str, llm_compressor ) -> str: """ Compresse les documents en gardant l'info pertinente """ compression_prompt = f""" Extrais les informations pertinentes pour répondre à cette question : Question : {query}
Documents : {chr(10).join(documents)}
Résumé des informations pertinentes : """
compressed = await llm_compressor.generate(compression_prompt) return compressed `
Gestion des hallucinations
Les hallucinations sont le risque principal en RAG. Voici comment les minimiser.
Détection d'hallucination
`python def detect_hallucination( response: str, context: str, query: str, verifier_llm ) -> dict: """ Vérifie si la réponse contient des hallucinations """ verification_prompt = f""" Analyse cette réponse et vérifie si elle est fidèle au contexte fourni.
CONTEXTE FOURNI : {context}
QUESTION : {query}
RÉPONSE À VÉRIFIER : {response}
ANALYSE : La réponse contient-elle des affirmations non présentes dans le contexte ? La réponse contredit-elle le contexte ? Score de fidélité (0-100) :
Réponds en JSON : {{"hallucinations": [...], "contradictions": [...], "score": X}} """
result = verifier_llm.generate(verification_prompt) return json.loads(result) `
Stratégies anti-hallucination
`python class RAGGenerator: def __init__(self, llm, verifier_llm=None): self.llm = llm self.verifier = verifier_llm
async def generate_with_verification( self, query: str, context: str, max_retries: int = 2 ) -> dict: """ Génère une réponse avec vérification anti-hallucination """ for attempt in range(max_retries + 1): Génération response = await self.llm.generate( self._build_prompt(query, context), temperature=0.1 Basse pour réduire hallucinations )
Vérification si verifier disponible if self.verifier: check = detect_hallucination(response, context, query, self.verifier)
if check["score"] >= 80: return { "response": response, "confidence": check["score"], "verified": True }
Retry avec instruction plus stricte context = self._add_anti_hallucination_instruction(context, check) else: return {"response": response, "verified": False}
Fallback : réponse prudente return { "response": "Je ne suis pas certain de pouvoir répondre précisément à cette question avec les informations disponibles.", "confidence": 0, "verified": True } `
Streaming et latence
Pour une expérience utilisateur fluide, le streaming est essentiel.
Implémentation du streaming
`python from openai import OpenAI
client = OpenAI()
async def stream_response(prompt: str): """ Stream la réponse token par token """ stream = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}], stream=True )
for chunk in stream: if chunk.choices[0].delta.content: yield chunk.choices[0].delta.content
Utilisation avec FastAPI from fastapi import FastAPI from fastapi.responses import StreamingResponse
app = FastAPI()
@app.post("/chat") async def chat(request: ChatRequest): prompt = build_rag_prompt(request.query, request.context)
return StreamingResponse( stream_response(prompt), media_type="text/event-stream" ) `
Optimisation de la latence
`python import asyncio from functools import lru_cache
class OptimizedRAG: def __init__(self): self.retriever = Retriever() self.llm = LLM()
async def query(self, question: str) -> str: Paralléliser retrieval et préparation prompt retrieval_task = asyncio.create_task( self.retriever.search(question) )
Pré-calculer les parties statiques du prompt base_prompt = self._get_cached_prompt()
Attendre le retrieval documents = await retrieval_task
Construire et envoyer au LLM full_prompt = base_prompt.format( context=format_context(documents), question=question )
return await self.llm.generate(full_prompt)
@lru_cache(maxsize=1) def _get_cached_prompt(self) -> str: return self._load_system_prompt() `
Métriques de qualité de génération
Faithfulness (Fidélité)
Mesure si la réponse est fidèle au contexte fourni.
`python def calculate_faithfulness( response: str, context: str, evaluator_llm ) -> float: """ Score de fidélité entre 0 et 1 """ prompt = f""" Évalue la fidélité de cette réponse par rapport au contexte.
Contexte : {context} Réponse : {response}
Pour chaque affirmation dans la réponse, vérifie si elle est : • Supportée par le contexte (1 point) • Non mentionnée dans le contexte (0 point) • Contredite par le contexte (-1 point)
Score final (0-1) : """
result = evaluator_llm.generate(prompt) return float(result) `
Answer Relevancy (Pertinence)
Mesure si la réponse répond bien à la question.
`python def calculate_relevancy( question: str, response: str, evaluator_llm ) -> float: """ Score de pertinence entre 0 et 1 """ prompt = f""" Évalue si cette réponse répond bien à la question posée.
Question : {question} Réponse : {response}
Critères : • La réponse adresse-t-elle directement la question ? (0-0.4) • La réponse est-elle complète ? (0-0.3) • La réponse est-elle concise et claire ? (0-0.3)
Score final (0-1) : """
result = evaluator_llm.generate(prompt) return float(result) `
Intégration avec différents fournisseurs
OpenAI
`python from openai import OpenAI
class OpenAIGenerator: def __init__(self, model: str = "gpt-4o-mini"): self.client = OpenAI() self.model = model
async def generate(self, prompt: str, kwargs) -> str: response = self.client.chat.completions.create( model=self.model, messages=[{"role": "user", "content": prompt}], temperature=kwargs.get("temperature", 0.2), max_tokens=kwargs.get("max_tokens", 2000) ) return response.choices[0].message.content `
Anthropic Claude
`python from anthropic import Anthropic
class ClaudeGenerator: def __init__(self, model: str = "claude-3-5-sonnet-20241022"): self.client = Anthropic() self.model = model
async def generate(self, prompt: str, system: str = None, kwargs) -> str: messages = [{"role": "user", "content": prompt}]
response = self.client.messages.create( model=self.model, max_tokens=kwargs.get("max_tokens", 2000), system=system or "Tu es un assistant RAG précis et factuel.", messages=messages ) return response.content[0].text `
Modèle local avec Ollama
`python import httpx
class OllamaGenerator: def __init__(self, model: str = "llama3.1:8b", base_url: str = "http://localhost:11434"): self.model = model self.base_url = base_url
async def generate(self, prompt: str, kwargs) -> str: async with httpx.AsyncClient() as client: response = await client.post( f"{self.base_url}/api/generate", json={ "model": self.model, "prompt": prompt, "stream": False, "options": { "temperature": kwargs.get("temperature", 0.2) } } ) return response.json()["response"] ``
Prochaines étapes
Maintenant que vous maîtrisez la génération RAG, explorez ces sujets avancés : • RAG Conversationnel : Mémoire et contexte - Gérer les conversations multi-tours • Agents RAG : Orchestration multi-agents - Aller au-delà du RAG simple • Évaluer un système RAG - Métriques et méthodologies complètes
Pour une vue d'ensemble complète, consultez notre Introduction au RAG.
---
Simplifiez-vous la vie avec Ailog
Configurer et optimiser un LLM pour le RAG demande beaucoup d'itérations. Avec Ailog, bénéficiez d'une configuration optimisée clé en main : • Prompts pré-optimisés pour chaque cas d'usage (support, e-commerce, KB interne) • Anti-hallucination intégré avec vérification automatique • Streaming natif pour une expérience utilisateur fluide • Multi-LLM* : Basculez entre GPT-4, Claude, ou modèles souverains sans changer votre code
Démarrez gratuitement avec Ailog et déployez un assistant RAG optimisé en quelques minutes.