Guides
Évaluation RAG
La version anglaise de cette page a changé depuis sa traduction. La page anglaise est la version à jour. La lire en anglais
Une réponse augmentée par la recherche peut être fausse pour quatre raisons différentes : le bon passage n'a jamais été récupéré ; il a été récupéré mais classé trop bas ; il a été classé assez haut puis retiré du contexte ; ou il a atteint le modèle et le modèle s'est trompé malgré tout. Un chiffre d'exactitude unique ne permet pas de les distinguer. Oloproof exécute un système RAG en deux étapes qu'il peut observer, mesure chacune, et réexécute les cas en échec sous des modifications contrôlées pour déterminer laquelle de ces raisons s'applique.
examples/support_rag/ est le projet que cette page exécute. Il ne nécessite aucun identifiant de fournisseur.
Un système en étapes
Le système est une classe dotée d'une étape de récupération et d'une étape de génération, décorée avec @rag_system :
from oloproof import Passage, Retrieval, rag_system
@rag_system(
name="support-rag",
version="slice-b-example",
depth=6,
top_k=2,
token_budget=40,
index_version="kb-2026-09-16",
)
class SupportRag:
def retrieve(self, input, depth):
...
return Retrieval(query=input["question"], depth=depth, candidates=tuple(passages))
def generate(self, input, context):
...
return {"answer": answer, "citations": [best.doc_id]}
def count_tokens(self, passage):
return len((passage.text or "").split())retrieve(input, depth) renvoie jusqu'à depth candidats, sous forme de Passage(doc_id=..., score=..., text=...), dans l'ordre produit par votre moteur de recherche. Oloproof enregistre les positions et ne reclasse jamais. Il conserve ensuite les top_k premiers, retire les passages qui dépassent token_budget, et passe ce qui reste à generate(input, context). count_tokens(passage) n'est nécessaire qu'avec un token_budget ; Oloproof n'estime jamais les tokens.
oloproof.yaml pointe vers la classe et peut remplacer n'importe lequel de ses paramètres :
system:
name: support-rag
version: slice-b-example
rag:
object: app:SupportRag
depth: 6
top_k: 2
token_budget: 40
index_version: kb-2026-09-16index_version fait partie de l'identité de la récupération. Changez-la lorsque l'index change, sinon des récupérations en cache seront réutilisées face à un index qui ne les renvoie plus.
Ce que déclare un cas
Un cas RAG porte sous expected deux champs dont aucun autre type de cas n'a besoin :
{"id":"refund_annual","input":{"question":"How long do refunds take for annual plans?"},"expected":{"answer":"14 days","relevant":[{"doc_id":"kb-01"}],"gold_context":[{"doc_id":"kb-01","text":"Refunds for annual plans are issued within 14 days of an approved request. Support reviews each request on the same business day."}]},"metadata":{"topic":"billing"}}- expected.relevant liste les documents qui répondent à la question. Les métriques de
récupération le lisent, et un cas qui en est dépourvu en est exclu avec no_relevance_labels.
- expected.gold_context est le texte même du passage. Le diagnostic le substitue au contexte
récupéré, et un cas en échec qui en est dépourvu ne peut pas être diagnostiqué.
Les évaluateurs
evaluators:
- {type: contains, criterion: answer_correct, field: answer, expected_field: answer}
- {type: hit_rate, k: 2}
- {type: recall, k: 2}
- {type: ndcg, k: 6}
- {type: citation_validity}
slices: [metadata.topic, relevant_position, context_truncated]
min_slice_support: 4hit_rate, recall, mrr et ndcg prennent k et en tirent le nom de leur critère : hit_rate_at_2. Une entrée de expected.relevant peut aussi porter un chunk_id et un grade, qui vaut 1 par défaut ; relevance_unit: chunk compte alors chaque fragment comme une unité à part entière plutôt que des documents entiers. citation_validity vérifie que chaque identifiant cité par une réponse désigne un passage du contexte qui lui a été fourni, et avec require_citations: true, une réponse qui ne cite rien échoue. groundedness_judge et citation_support_judge sont des juges LLM qui lisent le contexte assemblé, et prennent un provider et un model comme tout autre juge.
oloproof runTrois lignes du tableau des métriques :
│ answer_correct │ 69.2% │ [38.5%, 91.0%] │ 9 / 13 observed · 0 missing · 0 excluded │
│ ndcg_at_6 │ 0.866 │ [0.506, 0.990] │ mean of 13 observed · 0 missing · 0 excluded │
│ citations_valid │ 100.0% │ [75.2%, 100.0%] │ 13 / 13 observed · 0 missing · 0 excluded │Entre elles, hit_rate_at_2 et recall_at_2 affichent toutes deux 92,3 %, 12 sur 13 observés, avec une borne inférieure de 63,9 % : le passage pertinent d'une question n'a jamais été récupéré.
Cache: execution 0 hit/13 miss; judgment 0 hit/65 miss
Stages: retrieve 0 hit/13 miss; generate 0 hit/13 missLa ligne Stages est le cache propre du système en étapes. La récupération et la génération sont mises en cache séparément, de sorte qu'une modification de la génération ne relance jamais la récupération.
Trouver pourquoi
Quatre questions ont échoué. diagnose les réexécute avec le passage de vérité terrain à la place du contexte récupéré, à côté d'un témoin qui les réexécute sans modification :
oloproof diagnose RUN_ID --intervention gold-context --criterion answer_correctSelected: 4 failed cases with gold context (observed; no population claim)
Control: 0 of 4 passed when re-executed without the intervention
Recovered under gold context: 3 of 4
RETRIEVAL_MISS: 1 of 4, recovered; no relevant evidence was retrieved
CONTEXT_ASSEMBLY_LOSS: 2 of 4, recovered; relevant evidence within top-k was left out of the context
GENERATION_FAILURE: 1 of 4, still failed with the gold context
Implicated: context budget, in 2 of the 3 recovered failures.
Candidate experiment: a larger token budget. This is a hypothesis to test, not an established cause.
Candidate experiment: smaller chunks. This is a hypothesis to test, not an established cause.
Diagnosis sha256:b2846cbe04099225bda4bf21beb1738f54d6cc0e9a7ce210bb3eb467619cbd69
Child runs: gold context run_01M3C3REAAZDF9FD8N2C8BG17P, control run_01M3C3REAFVXK2VM4MYAQYH6KE
Cases: oloproof inspect sha256:b2846cbe04099225bda4bf21beb1738f54d6cc0e9a7ce210bb3eb467619cbd69Un cas qui réussit avec le passage de vérité terrain et échoue sans lui a échoué en amont du modèle. Un cas qui échoue alors qu'il dispose du passage de vérité terrain relève du modèle. Le témoin est ce qui rend cette lecture sûre : un cas qui se rétablit sur une simple réexécution était instable, pas diagnostiqué.
L'identifiant du diagnostic liste le cas derrière chaque décompte :
oloproof inspect DIAGNOSIS_IDmoney_back: RETRIEVAL_MISS, relevant_not_retrieved, strength intervention_recovery, best relevant position none
refund_review: CONTEXT_ASSEMBLY_LOSS, relevant_dropped_from_context, strength intervention_recovery, best relevant position 2
seat_count: GENERATION_FAILURE, fails_with_gold_context, strength intervention_non_recovery, best relevant position 1
security_review: CONTEXT_ASSEMBLY_LOSS, relevant_dropped_from_context, strength intervention_recovery, best relevant position 2Il nomme un facteur, jamais une cause. « Implicated » et « candidate experiment » sont les termes les plus forts qu'il emploie, car le rétablissement de quatre cas sous une intervention n'établit pas pourquoi ils ont échoué.
Tester un correctif avant de l'appliquer
Deux autres interventions rejouent la récupération enregistrée avec un paramètre différent, si bien qu'aucun appel au moteur de recherche n'est effectué. top-k élargit la coupure :
oloproof diagnose RUN_ID --intervention top-k --top-k 4 --criterion answer_correctSelected: 4 failed cases with gold context (observed; no population claim)
Control: 0 of 4 passed when re-executed without the intervention
Recovered under top-k 4: 0 of 4
Confirmed under top-k 4: 0 of 0 RANKED_OUT cases also recovered
Labels from gold context (diagnosis sha256:b2846cbe04099225bda4bf21beb1738f54d6cc0e9a7ce210bb3eb467619cbd69): 3 of 4 recovered
RETRIEVAL_MISS: 1 of 4, recovered; no relevant evidence was retrieved
CONTEXT_ASSEMBLY_LOSS: 2 of 4, recovered; relevant evidence within top-k was left out of the context
GENERATION_FAILURE: 1 of 4, still failed with the gold context
Diagnosis sha256:668084c3e99df84f7b63f138792aa96f48e5cb8251fee3acae8e1cc0500ba085
Child runs: gold context run_01M3C3REAAZDF9FD8N2C8BG17P, control run_01M3C3REAFVXK2VM4MYAQYH6KE, top-k 4 run_01M3C3RKHTGGV37079V11JJ78M, replay control run_01M3C3RKJ076K7MSVQ94J4ZE2P
Cases: oloproof inspect sha256:668084c3e99df84f7b63f138792aa96f48e5cb8251fee3acae8e1cc0500ba085Rien ne s'est rétabli, ce que les étiquettes prédisaient : aucun échec ici ne tenait à un passage classé juste sous la coupure. Le rejeu reporte les étiquettes du contexte de vérité terrain, de sorte que les deux diagnostics se lisent ensemble. reranker prend --reranker avec un module:function et rejoue plutôt la récupération à travers votre reclasseur.
Mener l'expérience
Le diagnostic a désigné un budget de tokens plus grand. Portez token_budget à 120 et relancez :
Cache: execution 0 hit/13 miss; judgment 0 hit/65 miss
Stages: retrieve 13 hit/0 miss; generate 7 hit/6 missToutes les récupérations ont été réutilisées, car top_k et le budget de tokens sont hors de l'identité de la récupération ; seuls les six cas dont le contexte a changé ont été générés de nouveau. Comparez ensuite, avec la politique de comparaison de l'exemple :
oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yamlComparison sha256:d801ed897f1fa1b196ca1e3f73a1ef4ffe6845d6ee8bddaea8a44da94d27a75b of run_01M3C3RYJZ4PAJ8P4P9NT71360 against run_01M3C3R5Y9D18WGSZQFA7N4XRT · 13 paired cases
answer_correct: +0.0 points [-33.6, +33.6] · 13 paired · 0 missing · 0 excluded
hit_rate_at_2: +0.0 points [-33.6, +33.6] · 13 paired · 0 missing · 0 excluded
recall_at_2: +0.0 points [-33.6, +33.6] · 13 paired · 0 missing · 0 excluded
ndcg_at_6: +0.0 points [-33.6, +33.6] · 13 paired · 0 missing · 0 excluded
citations_valid: +0.0 points [-33.6, +33.6] · 13 paired · 0 missing · 0 excludedUne ligne exploratoire suit pour chaque segment et chaque métrique, puis viennent les décisions :
Decisions
answers-not-worse answer_correct non-inferiority, margin 5.0 points INSUFFICIENT_EVIDENCE interval_overlaps_margin
citations-not-worse citations_valid non-inferiority, margin 2.0 points INSUFFICIENT_EVIDENCE interval_overlaps_margin
Gate: BLOCK (exit 3)L'expérience n'a pas aidé : aucun des treize cas n'a changé de verdict. Et treize cas appariés n'auraient pu établir un changement d'aucune ampleur qui compte pour une publication, ce qui est l'autre chose que dit l'intervalle.
Pour aller plus loin
- Segments couvre relevant_position et context_truncated.
- Règles de comparaison couvre les règles qui ont tranché la dernière
étape.
- Juges couvre ce qu'un juge d'ancrage doit franchir avant de pouvoir servir de
gate.