GuideIntermédiaire

Audit trail RAG : Tracer les requetes et reponses

17 mars 2026
18 min de lecture
Equipe Ailog

Guide pour implementer un audit trail complet dans votre systeme RAG : logging, tracabilite, conformite et debugging.

Audit trail RAG : Tracer les requetes et reponses

Un audit trail complet est essentiel pour la conformite, le debugging et l'amelioration continue de votre systeme RAG. Ce guide montre comment implementer un logging exhaustif.

Pourquoi un audit trail ?

BesoinSolution
Conformite RGPDProuver l'acces aux donnees
DebuggingReproduire les problemes
AmeliorationAnalyser les patterns d'usage
SecuriteDetecter les anomalies

Quoi logger ?

DEVELOPERpython
from dataclasses import dataclass from datetime import datetime from typing import List, Dict, Optional @dataclass class RAGAuditLog: # Identifiants request_id: str session_id: str user_id: Optional[str] # Requete query: str query_timestamp: datetime # Retrieval retrieved_doc_ids: List[str] retrieval_scores: List[float] retrieval_latency_ms: float # Generation model_used: str prompt_tokens: int completion_tokens: int generation_latency_ms: float # Reponse response: str response_timestamp: datetime # Metadata ip_address: Optional[str] user_agent: Optional[str] feedback_score: Optional[int] # Erreurs error: Optional[str] error_type: Optional[str]

Implementation

DEVELOPERpython
import json import logging from uuid import uuid4 from datetime import datetime class RAGAuditLogger: def __init__(self, log_path: str = "rag_audit.jsonl"): self.log_path = log_path self.logger = logging.getLogger("rag_audit") # Handler fichier JSONL handler = logging.FileHandler(log_path) handler.setFormatter(logging.Formatter("%(message)s")) self.logger.addHandler(handler) self.logger.setLevel(logging.INFO) def log_request(self, audit_log: RAGAuditLog): log_entry = { "request_id": audit_log.request_id, "session_id": audit_log.session_id, "user_id": audit_log.user_id, "query": audit_log.query, "query_timestamp": audit_log.query_timestamp.isoformat(), "retrieved_docs": audit_log.retrieved_doc_ids, "retrieval_scores": audit_log.retrieval_scores, "retrieval_latency_ms": audit_log.retrieval_latency_ms, "model": audit_log.model_used, "prompt_tokens": audit_log.prompt_tokens, "completion_tokens": audit_log.completion_tokens, "generation_latency_ms": audit_log.generation_latency_ms, "response": audit_log.response, "response_timestamp": audit_log.response_timestamp.isoformat(), "ip_address": audit_log.ip_address, "error": audit_log.error } self.logger.info(json.dumps(log_entry)) # Integration dans le pipeline RAG class AuditedRAGPipeline: def __init__(self, retriever, generator, audit_logger: RAGAuditLogger): self.retriever = retriever self.generator = generator self.audit = audit_logger def query(self, query: str, session_id: str, user_id: str = None) -> str: request_id = str(uuid4()) query_time = datetime.now() try: # Retrieval retrieval_start = datetime.now() docs = self.retriever.search(query, k=5) retrieval_latency = (datetime.now() - retrieval_start).total_seconds() * 1000 # Generation gen_start = datetime.now() response = self.generator.generate(query, docs) gen_latency = (datetime.now() - gen_start).total_seconds() * 1000 # Log self.audit.log_request(RAGAuditLog( request_id=request_id, session_id=session_id, user_id=user_id, query=query, query_timestamp=query_time, retrieved_doc_ids=[d.id for d in docs], retrieval_scores=[d.score for d in docs], retrieval_latency_ms=retrieval_latency, model_used=self.generator.model_name, prompt_tokens=response.usage.prompt_tokens, completion_tokens=response.usage.completion_tokens, generation_latency_ms=gen_latency, response=response.text, response_timestamp=datetime.now(), ip_address=None, user_agent=None, feedback_score=None, error=None, error_type=None )) return response.text except Exception as e: # Log l'erreur aussi self.audit.log_request(RAGAuditLog( request_id=request_id, session_id=session_id, user_id=user_id, query=query, query_timestamp=query_time, retrieved_doc_ids=[], retrieval_scores=[], retrieval_latency_ms=0, model_used="", prompt_tokens=0, completion_tokens=0, generation_latency_ms=0, response="", response_timestamp=datetime.now(), ip_address=None, user_agent=None, feedback_score=None, error=str(e), error_type=type(e).__name__ )) raise

Stockage et retention

DEVELOPERpython
# Pour les gros volumes: utiliser un data lake import boto3 class S3AuditLogger: def __init__(self, bucket: str, prefix: str = "rag-audit/"): self.s3 = boto3.client("s3") self.bucket = bucket self.prefix = prefix def log_request(self, audit_log: RAGAuditLog): # Partitionner par date date_str = audit_log.query_timestamp.strftime("%Y/%m/%d") key = f"{self.prefix}{date_str}/{audit_log.request_id}.json" self.s3.put_object( Bucket=self.bucket, Key=key, Body=json.dumps(audit_log.__dict__, default=str), ContentType="application/json" ) # Politique de retention retention_policy = { "logs_raw": "90 days", "logs_aggregated": "1 year", "pii_removed": "5 years" }

Analyse des logs

DEVELOPERpython
import pandas as pd def analyze_rag_performance(log_file: str) -> Dict: logs = [] with open(log_file) as f: for line in f: logs.append(json.loads(line)) df = pd.DataFrame(logs) return { "total_requests": len(df), "avg_retrieval_latency_ms": df["retrieval_latency_ms"].mean(), "avg_generation_latency_ms": df["generation_latency_ms"].mean(), "p95_total_latency_ms": (df["retrieval_latency_ms"] + df["generation_latency_ms"]).quantile(0.95), "error_rate": df["error"].notna().mean(), "avg_docs_retrieved": df["retrieved_docs"].apply(len).mean(), "top_queries": df["query"].value_counts().head(10).to_dict() }

Integration avec Ailog

Ailog inclut un audit trail complet :

  • Logs temps reel : Chaque requete tracee
  • Dashboard analytics : Visualisation des patterns
  • Export : JSONL, CSV pour compliance
  • Retention configurable : Selon vos besoins RGPD

Voir les logs sur Ailog

FAQ

Au minimum : identifiant de requête, timestamp, requête utilisateur (hashée si sensible), IDs des documents récupérés avec leurs scores, modèle LLM utilisé, tokens consommés, latence de chaque étape, et statut de la réponse. Pour la conformité RGPD, ajoutez les accès aux données personnelles et les consentements.
La durée dépend de vos obligations réglementaires. Pour la conformité RGPD, conservez les logs d'accès aux données personnelles pendant au moins 1 an. Pour le debugging, 90 jours suffisent généralement. Les métriques agrégées peuvent être conservées plus longtemps (3-5 ans) pour l'analyse de tendances.
Utilisez un logging asynchrone pour ne pas bloquer les requêtes. Écrivez d'abord dans un buffer local, puis transférez vers le stockage permanent en batch. Pour les gros volumes, partitionnez par date et utilisez un format compact comme JSONL. Les solutions de data lake (S3, GCS) sont idéales pour l'archivage longue durée.
Non, pour des raisons de confidentialité. Stockez un hash de la requête pour la traçabilité tout en préservant l'anonymat. Si vous avez besoin du contenu pour le debugging, chiffrez-le et limitez l'accès aux administrateurs autorisés. Supprimez ou anonymisez les requêtes contenant des données sensibles détectées.
Analysez les requêtes sans résultat pertinent pour identifier les lacunes de votre base de connaissances. Examinez les requêtes avec feedback négatif pour améliorer le retrieval. Surveillez les latences pour optimiser les performances. Les patterns de requêtes fréquentes peuvent guider la création de contenu ou l'ajout de FAQ.

Guides connexes

Tags

RAGsecuriteauditloggingtracabiliteconformite

Articles connexes

Ailog Assistant

Ici pour vous aider

Salut ! Pose-moi des questions sur Ailog et comment intégrer votre RAG dans vos projets !