Guardrails : valider entrées et sorties
Qu’est-ce qu’un guardrail ?
Un guardrail est une couche de validation qui s’interpose entre l’utilisateur et le modèle (en entrée) ou entre le modèle et l’utilisateur (en sortie). Contrairement à la modération qui détecte le contenu offensant, les guardrails valident la conformité métier : le message est-il dans le périmètre attendu ? La réponse contient-elle des informations interdites ? L’action demandée est-elle autorisée ?
En 2026, les guardrails sont devenus un composant standard des architectures d’agents IA, indispensables dès qu’un modèle a accès à des outils ou des données sensibles.
Architecture d’un système de guardrails
Le pipeline de validation
from dataclasses import dataclass
from enum import Enum
class Decision(Enum):
AUTORISER = "autoriser"
BLOQUER = "bloquer"
MODIFIER = "modifier"
@dataclass
class ResultatValidation:
decision: Decision
raison: str
contenu_modifie: str | None = None
class Guardrail:
"""Classe de base pour un guardrail."""
def valider(self, contenu: str, contexte: dict) -> ResultatValidation:
raise NotImplementedError
class PipelineGuardrails:
"""Enchaîne plusieurs guardrails en séquence."""
def __init__(self):
self.guardrails_entree: list[Guardrail] = []
self.guardrails_sortie: list[Guardrail] = []
def ajouter_entree(self, guardrail: Guardrail):
self.guardrails_entree.append(guardrail)
def ajouter_sortie(self, guardrail: Guardrail):
self.guardrails_sortie.append(guardrail)
def valider_entree(self, message: str, contexte: dict) -> ResultatValidation:
for g in self.guardrails_entree:
resultat = g.valider(message, contexte)
if resultat.decision == Decision.BLOQUER:
return resultat
if resultat.decision == Decision.MODIFIER:
message = resultat.contenu_modifie
return ResultatValidation(Decision.AUTORISER, "Toutes les validations passées")
def valider_sortie(self, reponse: str, contexte: dict) -> ResultatValidation:
for g in self.guardrails_sortie:
resultat = g.valider(reponse, contexte)
if resultat.decision == Decision.BLOQUER:
return resultat
if resultat.decision == Decision.MODIFIER:
reponse = resultat.contenu_modifie
return ResultatValidation(Decision.AUTORISER, "OK", reponse)
Guardrail de périmètre (Topic Guard)
class GuardrailPerimetre(Guardrail):
"""Vérifie que le message reste dans le périmètre autorisé."""
def __init__(self, sujets_autorises: list[str], sujets_interdits: list[str]):
self.sujets_autorises = sujets_autorises
self.sujets_interdits = sujets_interdits
def valider(self, contenu: str, contexte: dict) -> ResultatValidation:
contenu_lower = contenu.lower()
# Vérifier les sujets interdits
for sujet in self.sujets_interdits:
if sujet.lower() in contenu_lower:
return ResultatValidation(
Decision.BLOQUER,
f"Sujet hors périmètre détecté : {sujet}"
)
return ResultatValidation(Decision.AUTORISER, "Dans le périmètre")
Guardrail de longueur et de format
class GuardrailFormat(Guardrail):
"""Valide le format et la longueur des messages."""
def __init__(self, max_tokens: int = 4000, max_lignes: int = 100):
self.max_tokens = max_tokens
self.max_lignes = max_lignes
def valider(self, contenu: str, contexte: dict) -> ResultatValidation:
# Estimation grossière des tokens (1 token ~ 4 caractères en français)
tokens_estimes = len(contenu) // 4
if tokens_estimes > self.max_tokens:
return ResultatValidation(
Decision.BLOQUER,
f"Message trop long : ~{tokens_estimes} tokens (max {self.max_tokens})"
)
nb_lignes = contenu.count("\n") + 1
if nb_lignes > self.max_lignes:
return ResultatValidation(
Decision.BLOQUER,
f"Trop de lignes : {nb_lignes} (max {self.max_lignes})"
)
return ResultatValidation(Decision.AUTORISER, "Format valide")
Guardrail anti-fuite de données (sortie)
import re
class GuardrailAntiExfiltration(Guardrail):
"""Empêche la fuite de données sensibles dans les sorties."""
def __init__(self):
self.patterns_sensibles = {
"cle_api": r"(sk|pk|api[_-]?key)[_-][a-zA-Z0-9]{16,}",
"email_interne": r"[a-zA-Z0-9.]+@(entreprise|internal|corp)\.\w+",
"ip_privee": r"\b(10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3})\b",
"chemin_serveur": r"(/data/|/etc/|/home/|C:\\Users\\)",
}
def valider(self, contenu: str, contexte: dict) -> ResultatValidation:
for type_donnee, pattern in self.patterns_sensibles.items():
if re.search(pattern, contenu):
# Masquer au lieu de bloquer
contenu_nettoye = re.sub(pattern, "[MASQUÉ]", contenu)
return ResultatValidation(
Decision.MODIFIER,
f"Données sensibles masquées : {type_donnee}",
contenu_nettoye,
)
return ResultatValidation(Decision.AUTORISER, "Aucune fuite détectée")
Exemple complet d’intégration
from openai import OpenAI
def creer_assistant_securise():
"""Crée un assistant avec un pipeline complet de guardrails."""
client = OpenAI()
pipeline = PipelineGuardrails()
# Guardrails d'entrée
pipeline.ajouter_entree(GuardrailFormat(max_tokens=2000))
pipeline.ajouter_entree(GuardrailPerimetre(
sujets_autorises=["produits", "commandes", "livraison", "retours"],
sujets_interdits=["politique", "religion", "concurrent"],
))
# Guardrails de sortie
pipeline.ajouter_sortie(GuardrailAntiExfiltration())
def traiter(message: str) -> str:
contexte = {"role_utilisateur": "client"}
# Validation entrée
check_entree = pipeline.valider_entree(message, contexte)
if check_entree.decision == Decision.BLOQUER:
return f"Je ne peux pas traiter cette demande : {check_entree.raison}"
message_valide = check_entree.contenu_modifie or message
# Appel au modèle
response = client.chat.completions.create(
model="gpt-5.4",
messages=[
{"role": "system", "content": "Assistant service client Acme Corp."},
{"role": "user", "content": message_valide},
],
)
reponse = response.choices[0].message.content
# Validation sortie
check_sortie = pipeline.valider_sortie(reponse, contexte)
if check_sortie.decision == Decision.BLOQUER:
return "Je ne suis pas en mesure de répondre à cette question."
return check_sortie.contenu_modifie or reponse
return traiter
Guardrails avancés : validation par LLM
Pour les cas complexes, un second modèle peut servir de juge :
def guardrail_llm(message: str, regle: str) -> ResultatValidation:
"""Utilise un modèle rapide pour valider la conformité."""
client = OpenAI()
response = client.chat.completions.create(
model="o4-mini",
messages=[
{"role": "system", "content": f"""Vous êtes un validateur de conformité.
Règle à vérifier : {regle}
Répondez UNIQUEMENT par JSON : {{"conforme": true/false, "raison": "..."}}"""},
{"role": "user", "content": f"Message à valider :\n{message}"},
],
)
import json
resultat = json.loads(response.choices[0].message.content)
if resultat["conforme"]:
return ResultatValidation(Decision.AUTORISER, resultat["raison"])
return ResultatValidation(Decision.BLOQUER, resultat["raison"])
Points clés à retenir
- Les guardrails valident la conformité métier, pas seulement le contenu offensant
- L’architecture en pipeline permet de combiner plusieurs couches de validation
- Chaque guardrail a une responsabilité unique : périmètre, format, exfiltration, etc.
- Les guardrails de sortie sont aussi importants que ceux d’entrée
- Pour les cas complexes, un LLM rapide peut servir de validateur