7. Optimization

Évaluation humaine : Méthodologie et outils

31 mars 2026
Équipe Ailog

Complétez l'évaluation automatisée par l'expertise humaine. Protocoles d'annotation, accord inter-annotateurs et outils de labeling pour RAG.

Évaluation humaine : Méthodologie et outils

L'évaluation automatisée avec RAGAS et les métriques LLM-as-judge offre reproductibilité et rapidité. Mais elle manque la nuance du jugement humain. Ce guide présente les méthodologies d'évaluation humaine pour compléter votre pipeline de qualité RAG.

Pourquoi l'évaluation humaine reste essentielle

Les limites de l'automatisation

AspectÉvaluation autoÉvaluation humaine
Nuances culturellesDifficileExcellent
Ton et styleApproximatifPrécis
Cas ambigusÉchoue souventJugement contextuel
Satisfaction réelleProxy indirectMesure directe
Découverte de bugsPatterns connusNouveaux problèmes

Quand privilégier l'humain ?

  • Lancement produit : Validation avant mise en production
  • Nouveaux domaines : Pas de ground truth automatique
  • Cas sensibles : Médical, juridique, financier
  • Debugging ciblé : Comprendre pourquoi une réponse échoue
  • Calibrage des métriques : Vérifier que les scores auto correspondent au jugement réel

Protocoles d'évaluation

1. Évaluation par critères (Likert)

Le plus courant. Chaque réponse est notée sur plusieurs dimensions.

DEVELOPERpython
from dataclasses import dataclass from enum import Enum from typing import Optional class Rating(Enum): TERRIBLE = 1 POOR = 2 ACCEPTABLE = 3 GOOD = 4 EXCELLENT = 5 @dataclass class EvaluationCriteria: relevance: Rating # La réponse répond-elle à la question ? accuracy: Rating # Les informations sont-elles correctes ? completeness: Rating # Manque-t-il des informations importantes ? clarity: Rating # La réponse est-elle claire et bien structurée ? helpfulness: Rating # La réponse aide-t-elle l'utilisateur ? comment: Optional[str] # Commentaire libre @dataclass class AnnotationTask: task_id: str question: str rag_answer: str contexts: list[str] ground_truth: Optional[str] annotator_id: str evaluation: Optional[EvaluationCriteria] = None

Grille d'évaluation type :

ScorePertinencePrécisionComplétude
5Répond parfaitement100% correctTout couvert
4Répond bienQuasi correctEssentiel couvert
3Répond partiellementQuelques erreurs mineuresLacunes mineures
2Hors sujet partielErreurs significativesLacunes importantes
1Ne répond pasFauxRien d'utile

2. Comparaison par paires (A/B)

Plus fiable pour des différences subtiles. L'annotateur choisit la meilleure réponse entre deux versions.

DEVELOPERpython
@dataclass class PairwiseTask: task_id: str question: str answer_a: str answer_b: str contexts: list[str] annotator_id: str preference: Optional[str] = None # "A", "B", ou "equal" confidence: Optional[int] = None # 1-5 reason: Optional[str] = None def calculate_win_rate(annotations: list[PairwiseTask]) -> dict: """Calcule le taux de victoire entre deux modèles""" wins_a = sum(1 for a in annotations if a.preference == "A") wins_b = sum(1 for a in annotations if a.preference == "B") ties = sum(1 for a in annotations if a.preference == "equal") total = len(annotations) return { "model_a_wins": wins_a / total, "model_b_wins": wins_b / total, "ties": ties / total }

3. Évaluation par erreurs (Error taxonomy)

Identifie les types d'erreurs spécifiques pour un debugging ciblé.

DEVELOPERpython
class ErrorType(Enum): HALLUCINATION = "hallucination" FACTUAL_ERROR = "factual_error" INCOMPLETE = "incomplete" OFF_TOPIC = "off_topic" WRONG_CONTEXT = "wrong_context" OUTDATED = "outdated" FORMATTING = "formatting" TONE = "tone" @dataclass class ErrorAnnotation: task_id: str errors: list[ErrorType] error_details: dict[ErrorType, str] severity: int # 1-5 fixable: bool def analyze_error_distribution(annotations: list[ErrorAnnotation]) -> dict: """Analyse la distribution des erreurs""" error_counts = {} for annotation in annotations: for error in annotation.errors: error_counts[error.value] = error_counts.get(error.value, 0) + 1 total_errors = sum(error_counts.values()) return { error: count / total_errors for error, count in sorted(error_counts.items(), key=lambda x: x[1], reverse=True) }

Accord inter-annotateurs

Pourquoi le mesurer ?

Si deux annotateurs sont en désaccord sur 50% des samples, vos annotations ne sont pas fiables. L'accord inter-annotateurs (IAA) mesure la cohérence.

Métriques d'accord

DEVELOPERpython
import numpy as np from sklearn.metrics import cohen_kappa_score def calculate_iaa(annotations_1: list[int], annotations_2: list[int]) -> dict: """Calcule plusieurs métriques d'accord inter-annotateurs""" # Pourcentage d'accord exact exact_agreement = sum( 1 for a, b in zip(annotations_1, annotations_2) if a == b ) / len(annotations_1) # Cohen's Kappa (corrige pour le hasard) kappa = cohen_kappa_score(annotations_1, annotations_2) # Accord à 1 point près close_agreement = sum( 1 for a, b in zip(annotations_1, annotations_2) if abs(a - b) <= 1 ) / len(annotations_1) # Corrélation de Pearson correlation = np.corrcoef(annotations_1, annotations_2)[0, 1] return { "exact_agreement": exact_agreement, "close_agreement": close_agreement, "cohens_kappa": kappa, "pearson_correlation": correlation }

Interprétation des scores

Cohen's KappaInterprétationAction
< 0.20Accord faibleRevoir les guidelines
0.20-0.40Accord modesteClarifier les critères
0.40-0.60Accord modéréAcceptable pour démarrer
0.60-0.80Accord substantielBon niveau
> 0.80Accord presque parfaitExcellent

Améliorer l'accord

DEVELOPERpython
class AnnotationGuidelines: """Structure pour les guidelines d'annotation""" def __init__(self): self.examples = {} self.edge_cases = [] self.calibration_set = [] def add_example(self, score: int, question: str, answer: str, explanation: str): """Ajoute un exemple de référence pour chaque score""" if score not in self.examples: self.examples[score] = [] self.examples[score].append({ "question": question, "answer": answer, "explanation": explanation }) def run_calibration(self, annotators: list[str], samples: list[dict]) -> dict: """Session de calibrage : tous annotent les mêmes samples""" results = {} for annotator in annotators: results[annotator] = self._get_annotations(annotator, samples) # Identifier les désaccords disagreements = [] for i, sample in enumerate(samples): scores = [results[a][i] for a in annotators] if max(scores) - min(scores) > 1: disagreements.append({ "sample_idx": i, "sample": sample, "scores": dict(zip(annotators, scores)) }) return {"disagreements": disagreements}

Plateforme d'annotation

Interface web simple

DEVELOPERpython
from fastapi import FastAPI, HTTPException from pydantic import BaseModel import uuid app = FastAPI() class AnnotationSubmission(BaseModel): task_id: str annotator_id: str relevance: int accuracy: int completeness: int clarity: int comment: str = "" tasks_db = {} annotations_db = {} @app.get("/task/{annotator_id}") async def get_next_task(annotator_id: str): """Retourne la prochaine tâche à annoter""" for task_id, task in tasks_db.items(): if not any( a["annotator_id"] == annotator_id for a in annotations_db.get(task_id, []) ): return task raise HTTPException(404, "No tasks available") @app.post("/annotate") async def submit_annotation(submission: AnnotationSubmission): """Soumet une annotation""" if submission.task_id not in tasks_db: raise HTTPException(404, "Task not found") annotation = { "id": str(uuid.uuid4()), "task_id": submission.task_id, "annotator_id": submission.annotator_id, "scores": { "relevance": submission.relevance, "accuracy": submission.accuracy, "completeness": submission.completeness, "clarity": submission.clarity }, "comment": submission.comment } if submission.task_id not in annotations_db: annotations_db[submission.task_id] = [] annotations_db[submission.task_id].append(annotation) return {"status": "success", "annotation_id": annotation["id"]}

Outils existants

OutilTypePrixForces
Label StudioOpen-sourceGratuitFlexible, self-hosted
ArgillaOpen-sourceGratuitSpécialisé NLP/RAG
ProdigyCommercial390 EURUX excellente, rapide
Scale AIServiceVariableAnnotateurs inclus

Échantillonnage intelligent

Stratégie d'échantillonnage

DEVELOPERpython
import random from collections import defaultdict class SmartSampler: def __init__(self, all_samples: list[dict]): self.samples = all_samples def stratified_sample(self, n: int, strata_key: str) -> list[dict]: """Échantillonnage stratifié par catégorie""" strata = defaultdict(list) for sample in self.samples: strata[sample.get(strata_key, "unknown")].append(sample) samples_per_stratum = n // len(strata) selected = [] for stratum_samples in strata.values(): selected.extend(random.sample( stratum_samples, min(samples_per_stratum, len(stratum_samples)) )) return selected[:n] def uncertainty_sample(self, n: int, ragas_scores: dict) -> list[dict]: """Échantillonne les samples avec scores RAGAS incertains""" uncertain = [ (i, sample) for i, sample in enumerate(self.samples) if 0.4 < ragas_scores.get(i, {}).get("faithfulness", 0) < 0.7 ] return [sample for _, sample in uncertain[:n]]

Taille d'échantillon recommandée

ObjectifTaille miniAnnotateursTemps estimé
Validation rapide5012h
Calibrage modèle10026h
Benchmark sérieux300324h
Production critique500+3+40h+

Intégration avec l'évaluation automatique

Pipeline hybride

DEVELOPERpython
class HybridEvaluationPipeline: def __init__(self, ragas_evaluator, human_platform): self.ragas = ragas_evaluator self.human = human_platform async def evaluate(self, samples: list[dict]) -> dict: # Étape 1 : Évaluation automatique auto_results = await self.ragas.evaluate(samples) # Étape 2 : Identifier les samples à valider humainement uncertain_indices = [ i for i, score in enumerate(auto_results["per_sample"]) if 0.4 < score["faithfulness"] < 0.7 ] # Étape 3 : Créer les tâches d'annotation human_tasks = [samples[i] for i in uncertain_indices] await self.human.create_tasks(human_tasks) # Étape 4 : Attendre les annotations human_results = await self.human.wait_for_completion() # Étape 5 : Combiner les résultats return self._merge_results(auto_results, human_results, uncertain_indices) def _merge_results(self, auto, human, human_indices): """Combine les scores auto et humains""" final_scores = auto["per_sample"].copy() for i, idx in enumerate(human_indices): human_score = human[i]["average_score"] auto_score = final_scores[idx]["faithfulness"] final_scores[idx]["final_score"] = 0.6 * human_score + 0.4 * auto_score final_scores[idx]["human_validated"] = True return final_scores

Checklist évaluation humaine

ÉtapeActionFait
GuidelinesRédiger avec exemples pour chaque score[ ]
CalibrageSession initiale avec tous les annotateurs[ ]
Pilote20 samples pour vérifier l'accord[ ]
ProductionLancer l'annotation complète[ ]
QualitéCalculer l'IAA régulièrement[ ]
FeedbackIntégrer les retours dans les guidelines[ ]

Pour aller plus loin

FAQ

Un minimum de 2 annotateurs par sample est recommande pour calculer l'accord inter-annotateurs. Pour les cas critiques (medical, juridique), visez 3 annotateurs et prenez la majorite. Un seul annotateur suffit uniquement pour un audit rapide en phase de developpement.
Utilisez une approche hybride : evaluez automatiquement avec RAGAS, puis focalisez l'evaluation humaine sur les samples incertains (scores entre 0.4 et 0.7). Cela reduit le volume a annoter de 60-80% tout en couvrant les cas critiques.
Un Kappa de 0.40-0.60 (accord modere) est acceptable pour demarrer l'annotation. Si vous etes en dessous de 0.40, organisez une session de calibrage avec vos annotateurs sur 20 samples communs avant de continuer.
Non pour l'evaluation de la qualite perçue (pertinence, clarte). Oui si vous evaluez la precision factuelle et que vos annotateurs ne sont pas experts du domaine. Dans ce cas, fournissez le ground truth comme reference mais demandez d'evaluer independamment avant de le consulter.
Pour les desaccords mineurs (1 point d'ecart sur une echelle de 5), prenez la moyenne. Pour les desaccords majeurs, organisez une discussion de reconciliation ou ajoutez un troisieme annotateur. Documentez les cas ambigus dans vos guidelines pour eviter les recurrences. ---

Évaluation humaine simplifiée avec Ailog

Mettre en place un pipeline d'évaluation humaine demande infrastructure et coordination. Avec Ailog, bénéficiez d'outils intégrés :

  • Interface d'annotation intuitive
  • Dashboard de progression en temps réel
  • Calcul IAA automatique
  • Échantillonnage intelligent des cas critiques
  • Rapports combinant scores auto et humains

Testez gratuitement et validez la qualité de votre RAG avec expertise humaine.

Tags

ragevaluationannotationqualitehuman-in-the-loop

Articles connexes

Ailog Assistant

Ici pour vous aider

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