Les garde-fous des LLM en pratique : ce qui fonctionne réellement
Contrôlez le risque, pas seulement le modèle.
Les LLM sont imprévisibles. Ils hallucinent, fuient des données, génèrent du contenu nuisible ou refusent des demandes légitimes. Les garde-fous contraignent le comportement du modèle sans sacrifier ses capacités.
L’essentiel est de savoir quels garde-fous sont importants et lesquels sont simplement du bruit.
Les garde-fous ne visent pas à contrôler le modèle. Ils visent à contrôler le risque.

Validation des entrées
Le garde-fou le plus important. Une mauvaise entrée donne une mauvaise sortie, et une mauvaise entrée peut également injecter du prompt dans votre système.
Stratégie 1 : Assainissement des prompts
Assainissez les motifs dangereux dès le début :
import re
class PromptSanitizer:
def __init__(self):
self.dangerous_patterns = [
r"ignore\s+previous\s+instructions",
r"system\s+prompt",
r"you\s+are\s+now\s+free",
r"break\s+out\s+of",
]
def sanitize(self, prompt: str) -> str:
for pattern in self.dangerous_patterns:
prompt = re.sub(pattern, "[REDACTED]", prompt, flags=re.IGNORECASE)
return prompt
Ce n’est pas infaillible. Les entrées adverses sont créatives. Mais cela capture les cas évidents, et les cas évidents sont les plus courants.
Stratégie 2 : Limites de longueur des entrées
Les limites de longueur empêchent le gaspillage de jetons et les délais d’attente :
class InputValidator:
def __init__(self, max_length: int = 10000):
self.max_length = max_length
def validate(self, prompt: str) -> tuple[bool, str]:
if len(prompt) > self.max_length:
return False, f"Entrée trop longue : {len(prompt)} > {self.max_length}"
return True, "OK"
Stratégie 3 : Filtrage du contenu
Le filtrage du contenu bloque les violations de politique. Les motifs ici dépendent de votre domaine :
class ContentFilter:
def __init__(self):
self.blocked_topics = [
"violence", "hate speech", "self-harm",
"sexual content", "illegal activities",
]
def filter(self, prompt: str) -> tuple[bool, str]:
prompt_lower = prompt.lower()
for topic in self.blocked_topics:
if topic in prompt_lower:
return False, f"Bloqué : {topic}"
return True, "OK"
La correspondance de chaînes simple est rapide mais imprécise. Pour la production, utilisez un modèle classificateur — même un petit comme qwen3-1.7b — pour détecter les violations de politique. C’est plus précis et plus difficile à contourner.
Filtrage des sorties
La sortie du modèle doit également être vérifiée. Structure, contenu et faits.
Stratégie 1 : Validation des réponses
Validez d’abord la structure. Si vous attendez du JSON, vérifiez la présence de JSON :
class ResponseValidator:
def __init__(self):
self.required_fields = ["answer", "confidence"]
def validate(self, response: dict) -> tuple[bool, str]:
for field in self.required_fields:
if field not in response:
return False, f"Champ manquant : {field}"
return True, "OK"
Stratégie 2 : Filtrage du contenu
Filtrez le contenu nuisible :
class OutputFilter:
def __init__(self):
self.blocked_patterns = [
r"kill\s+someone",
r"bomb\s+recipe",
r"hate\s+speech",
r"self-harm",
]
def filter(self, response: str) -> tuple[bool, str]:
for pattern in self.blocked_patterns:
if re.search(pattern, response, re.IGNORECASE):
return False, f"Bloqué : {pattern}"
return True, "OK"
Stratégie 3 : Vérification des faits
La vérification des faits est plus difficile. Vous ne pouvez pas valider chaque affirmation, donc choisissez celles qui comptent :
class FactChecker:
def __init__(self):
self.known_facts = {
"capital of france": "Paris",
"population of usa": "330 million",
"speed of light": "299,792,458 m/s",
}
def check(self, claim: str) -> tuple[bool, str]:
claim_lower = claim.lower()
for fact, truth in self.known_facts.items():
if fact in claim_lower and truth not in claim_lower:
return False, f"Vérification des faits échouée : {fact}"
return True, "OK"
Pour une vérification des faits réelle, vous avez besoin d’un pipeline de récupération. Vérifiez les affirmations contre une base de connaissances, pas un dictionnaire codé en dur.
Mécanismes de sécurité
Stratégie 1 : Limitation de débit
La limitation de débit empêche l’abus :
import time
from collections import deque
class RateLimiter:
def __init__(self, max_requests: int = 10, window: int = 60):
self.max_requests = max_requests
self.window = window
self.requests = deque()
def allow(self) -> bool:
now = time.time()
while self.requests and self.requests[0] < now - self.window:
self.requests.popleft()
if len(self.requests) >= self.max_requests:
return False
self.requests.append(now)
return True
Stratégie 2 : Gestion du budget de jetons
La gestion du budget de jetons plafonne les coûts par demande :
class TokenBudget:
def __init__(self, max_tokens: int = 1000):
self.max_tokens = max_tokens
def validate(self, response: str) -> tuple[bool, str]:
token_count = len(response.split())
if token_count > self.max_tokens:
return False, f"Limite de jetons dépassée : {token_count} > {self.max_tokens}"
return True, "OK"
Stratégie 3 : Gestion de la fenêtre de contexte
La gestion de la fenêtre de contexte empêche le débordement :
class ContextManager:
def __init__(self, max_context: int = 4096):
self.max_context = max_context
self.context = []
def add(self, message: str):
self.context.append(message)
self.trim()
def trim(self):
while len(" ".join(self.context)) > self.max_context:
self.context.pop(0)
Le rognage par fenêtre glissante est simple mais perd le contexte initial. De meilleures approches utilisent la résumé ou la compression basée sur l’attention, mais celles-ci ajoutent de la latence.
Conformité
Les systèmes d’entreprise ont besoin de garde-fous de conformité. Deux qui comptent le plus :
Motif 1 : Résidence des données
Résidence des données — assurez-vous que les données restent dans les limites géographiques requises :
class DataResidency:
def __init__(self, allowed_regions: list[str]):
self.allowed_regions = allowed_regions
def validate(self, region: str) -> tuple[bool, str]:
if region not in self.allowed_regions:
return False, f"Région non autorisée : {region}"
return True, "OK"
Motif 2 : Journalisation d’audit
Journalisation d’audit — journalisez toutes les interactions avec le modèle :
import json
from datetime import datetime
class AuditLogger:
def __init__(self, log_file: str = "audit.log"):
self.log_file = log_file
def log(self, request: dict, response: dict):
entry = {
"timestamp": datetime.now().isoformat(),
"request": request,
"response": response,
}
with open(self.log_file, "a") as f:
f.write(json.dumps(entry) + "\n")
Les journaux d’audit sont critiques pour le débogage et la conformité. Rendez-les structurés, en ajout uniquement, et stockés de manière sécurisée.
Les assembler
Motif 1 : Garde-fous simples
Un pipeline de garde-fous simple :
class SimpleGuardrails:
def __init__(self):
self.input_validator = InputValidator(max_length=10000)
self.output_filter = OutputFilter()
def process(self, prompt: str) -> str:
valid, message = self.input_validator.validate(prompt)
if not valid:
return f"Erreur : {message}"
response = self.call_model(prompt)
valid, message = self.output_filter.filter(response)
if not valid:
return f"Erreur : {message}"
return response
Motif 2 : Garde-fous avancés
Les garde-fous avancés ajoutent l’assainissement, la limitation de débit et les budgets de jetons :
class AdvancedGuardrails:
def __init__(self):
self.sanitizer = PromptSanitizer()
self.input_validator = InputValidator(max_length=10000)
self.content_filter = ContentFilter()
self.output_filter = OutputFilter()
self.rate_limiter = RateLimiter(max_requests=10)
self.token_budget = TokenBudget(max_tokens=1000)
def process(self, prompt: str) -> str:
prompt = self.sanitizer.sanitize(prompt)
valid, message = self.input_validator.validate(prompt)
if not valid:
return f"Erreur : {message}"
valid, message = self.content_filter.filter(prompt)
if not valid:
return f"Erreur : {message}"
if not self.rate_limiter.allow():
return "Erreur : Limite de débit dépassée"
response = self.call_model(prompt)
valid, message = self.output_filter.filter(response)
if not valid:
return f"Erreur : {message}"
valid, message = self.token_budget.validate(response)
if not valid:
return f"Erreur : {message}"
return response
Quand les garde-fous comptent
Les garde-fous comptent lorsque vous construisez des systèmes destinés aux utilisateurs, gérez des données sensibles ou fonctionne en production. Ils comptent également lorsque vous avez des exigences de conformité — RGPD, HIPAA, SOC 2.
Ils ne comptent pas lorsque vous prototypiez, utilisez des modèles uniquement pour des outils internes ou ne gérez pas de données sensibles. Ignorez-les jusqu’à ce que vous en ayez besoin.
Le compromis est toujours capacité versus sécurité. Plus de garde-fous signifient moins d’échecs, mais aussi moins de capacités. Trouvez l’équilibre qui fonctionne pour votre système.
Compromis
| Stratégie | Sécurité | Capacité | Latence |
|---|---|---|---|
| Aucun garde-fou | La plus basse | La plus élevée | La plus basse |
| Validation des entrées | Élevée | Moyenne | Faible |
| Filtrage des sorties | Élevée | Moyenne | Faible |
| Mécanismes de sécurité | La plus élevée | La plus basse | La plus élevée |
| Conformité | La plus élevée | La plus basse | La plus élevée |
Related
- Stratégies de routage des modèles — routage basé sur les capacités, conscient des coûts et de la latence
- Optimisation des coûts pour les systèmes LLM — gestion du budget de jetons, modèles de repli, mise en cache
- Conception de systèmes multi-modèles — architecture pour plusieurs modèles
- Architecture LLM — pilier de conception système : routage, coût, garde-fous et orchestration