Aller au contenu

Guides

Tutoriel : évaluer une application RAG

Un parcours exécutable pour une application augmentée par la recherche, selon deux chemins : une application boîte noire existante dont vous enregistrez de l'extérieur la récupération, le contexte et les citations, et une application en étapes qu'Oloproof exécute étape par étape pour que oloproof diagnose puisse réexécuter les cas en échec sous des modifications contrôlées. Les deux s'exécutent en local sans identifiants de fournisseur.

Les concepts derrière chaque étape (étapes, étiquettes de pertinence, contexte de référence, les quatre étiquettes d'échec) se trouvent sur la page Évaluation RAG ; les termes cas, évaluateur, métrique, intervalle et porte sont dans Concepts fondamentaux. Cette page est le parcours pratique à travers eux.

Quel chemin est le vôtre

Votre applicationCheminCe que vous obtenezCe que vous n'obtenez pas
Un appel en entrée, une réponse en sortie (un service, un point de terminaison HTTP, une chaîne de framework que vous ne voulez pas découper)A, boîte noireMétriques de récupération, vérifications de citations, juges d'ancrage, porte, comparaisonInterventions contrôlées : diagnose ne réexécute rien
Une récupération et une génération que vous pouvez appeler séparémentB, en étapesTout ce qu'offre A, un cache par étape, et diagnose avec le contexte de référence, le top-k et un reclasseur à côté d'un témoinD'autres interventions que ces trois-là

Commencez par A en cas de doute. Il ne nécessite aucune modification de l'application, et passer à B plus tard conserve le jeu de données, les évaluateurs et la politique.

Prérequis

  • Python 3.11 ou ultérieur, et Oloproof installé (pip install oloproof).
  • Les projets d'exemple, livrés avec le paquet : blackbox_rag pour le chemin A et support_rag pour le chemin B. Copiez-en un dans un nouveau répertoire et travaillez-y :
oloproof init --example blackbox_rag my-rag
cd my-rag

Chaque commande ci-dessous s'exécute depuis le répertoire copié. Les exécutions, jugements et diagnostics sont stockés dans son .oloproof/.

Chemin A : une application existante comme boîte noire

Les fichiers

FichierCe que c'est
app.pysupport_api(question), qui tient lieu de votre application, et run(case), l'adaptateur
server.pyLa même application en HTTP, pour la variante HTTP ci-dessous
data/corpus.jsonlLa base de connaissances de 14 passages dans laquelle l'application cherche
data/support.jsonl15 cas : 13 avec étiquettes de pertinence et passages de référence, 2 sans l'un ni l'autre
oloproof.yamlLa suite : jeu de données, système, évaluateurs, segments
oloproof.http.yamlLa même suite contre le serveur HTTP
release.yamlLa politique de publication pour une seule exécution
compare.yamlLa politique pour comparer une exécution candidate à une référence

Ce que renvoie l'application

support_api se comporte comme une application que vous avez déjà : elle cherche, construit un prompt à partir des meilleures sources qui tiennent dans un budget de mots, répond et cite. Sa réponse porte déjà ce qu'elle a fait :

{
  "answer": "Team plans include five seats.",
  "cited": ["kb-03"],
  "sources": [{"id": "kb-03", "score": 3.0, "text": "Team plans include five seats. ..."}],
  "prompt_sources": [{"id": "kb-03", "score": 3.0, "text": "...", "rank": 1, "tokens": 17}],
  "skipped": [{"id": "kb-05", "rank": 3, "why": "top_k"}]
}

Les noms de champs de votre application seront différents. Ce qui compte, c'est qu'elle puisse vous dire, pour chaque question, les sources classées qu'elle a récupérées, celles qui ont atteint le modèle et celles qu'elle a citées. Si elle ne le peut pas, ajoutez-les d'abord à sa réponse ou à ses journaux : Oloproof mesure ce qui est enregistré et ne déduit jamais la récupération d'une réponse.

L'adaptateur

run appelle l'application sans la modifier et projette la réponse sur trois artefacts typés, les enregistrements que lisent les évaluateurs de récupération et de citation :

@system(
    name="support-rag-blackbox",
    version="tutorial",
    records=("retrieval/v1", "context/v1", "citations/v1"),
)
def run(case):
    response = support_api(str(case["question"]))
    recorder = current_case()
    recorder.retrieval(
        Retrieval(
            query=case["question"],
            depth=SEARCH_DEPTH,
            candidates=tuple(
                Passage(doc_id=s["id"], score=s["score"], text=s["text"])
                for s in response["sources"]
            ),
        )
    )
    recorder.context(
        Context(
            items=tuple(
                ContextItem(doc_id=i["id"], position=i["rank"], tokens=i["tokens"], text=i["text"])
                for i in response["prompt_sources"]
            ),
            dropped=tuple(
                DroppedItem(doc_id=i["id"], position=i["rank"], reason=i["why"])
                for i in response["skipped"]
            ),
            token_budget=PROMPT_WORD_BUDGET,
        )
    )
    recorder.citations(response["cited"])
    return {"answer": response["answer"], "citations": response["cited"]}
ArtefactFormeLu par
retrieval/v1query, depth, et candidates dans l'ordre où votre système de récupération les a renvoyés, chacun un Passage(doc_id, chunk_id, score, text)hit_rate, recall, mrr, ndcg
context/v1les items qui ont atteint le modèle (doc_id, position, tokens, text), les éléments dropped avec une reason valant top_k ou token_budget, et token_budgetcitation_validity, groundedness_judge, citation_support_judge
citations/v1ids, chacun un doc_id ou doc_id#chunk_idcitation_validity, citation_support_judge

Oloproof enregistre les positions telles quelles et ne reclasse jamais. Un artefact mal formé arrête l'exécution avec le code de sortie 2 au lieu d'être stocké. case est l'objet input du cas, donc case["question"] est la question du jeu de données.

Pour utiliser votre propre application, remplacez le corps de support_api par un appel à celle-ci (un appel de SDK, une requête HTTP) et gardez run. Faites pointer system.callable dans oloproof.yaml vers elle sous la forme module:function.

La variante HTTP

Un système HTTP ne peut pas appeler l'enregistreur, donc sa réponse porte les preuves à la place, déjà sous les trois formes ci-dessus, et la configuration indique où :

system:
  name: support-rag-http
  version: tutorial
  http:
    url: http://127.0.0.1:8766/answer
    output_path: result
    artifacts:
      retrieval/v1: evidence.retrieval
      context/v1: evidence.context
      citations/v1: evidence.citations

server.py sert exactement cela. Démarrez-le, puis exécutez contre lui :

python server.py 8766
oloproof run --config oloproof.http.yaml

L'entrée du cas est envoyée comme corps JSON. output_path extrait la sortie de la réponse, et chaque entrée artifacts enregistre un chemin pointé comme ce type ; un champ absent ou mal formé arrête l'exécution avec le code de sortie 2. Les résultats sont identiques au chemin par appelable ci-dessous. Dans votre propre service, l'objet de preuves est généralement un champ de débogage que vous activez pour le trafic d'évaluation.

Ce que déclare un cas

{"id":"seat_count","input":{"question":"How many seats does a team plan include?"},"expected":{"answer":"5 seats","relevant":[{"doc_id":"kb-03"}],"gold_context":[{"doc_id":"kb-03","text":"Team plans include five seats. ..."}]},"metadata":{"topic":"billing"}}
{"id":"office_hours","input":{"question":"What are the support office hours?"},"expected":{"answer":"09:00"},"metadata":{"topic":"account"}}
  • expected.relevant liste les passages qui répondent à la question. Les métriques de récupération le lisent. Un cas qui ne l'a pas, comme office_hours, en est exclu avec no_relevance_labels : il sort du dénominateur au lieu de compter comme une réussite ou un échec.
  • expected.gold_context est le texte du passage lui-même. Le chemin A ne l'utilise jamais ; le chemin B le substitue au contexte récupéré pendant le diagnostic.

Les cas sans étiquette sont normaux en pratique, car étiqueter la pertinence demande du travail. Ils comptent tout de même pour les vérifications de réponse et de citation.

Choisir les évaluateurs

evaluators:
  - {type: contains, criterion: answer_correct, field: answer, expected_field: answer}
  - {type: hit_rate, k: 2}
  - {type: recall, k: 2}
  - {type: citation_validity, require_citations: true}
slices: [metadata.topic]
min_slice_support: 4
  • contains vérifie que la réponse contient le texte attendu. C'est la vérification de la tâche : l'utilisateur a-t-il obtenu la bonne réponse. Utilisez plutôt un juge exact ou à grille quand la formulation varie.
  • hit_rate et recall à k: 2 mesurent la récupération à la profondeur que l'application met réellement dans le prompt. Une métrique de récupération à une profondeur que le modèle ne voit jamais décrit l'index, pas l'application.
  • citation_validity vérifie que chaque identifiant cité désigne un passage qui a atteint le modèle ; require_citations: true fait aussi échouer une réponse qui ne cite rien.
  • groundedness_judge et citation_support_judge (facultatifs) demandent à un modèle si la réponse est étayée par le contexte. Ils nécessitent un fournisseur, un modèle et des identifiants dans une variable d'environnement, et coûtent de l'argent par cas ; voir Juges pour ce qu'un juge doit franchir avant de pouvoir servir de porte.

Les segments relevant_position et context_truncated ne sont pas disponibles ici : ils comparent les positions au top-k de l'application, que seul un système en étapes déclare. Les demander arrête l'exécution avec slice 'relevant_position' compares relevant positions with top_k, so it needs a staged system.

La politique de publication

version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
  - id: answer-floor
    metric: answer_correct
    min: 0.70
  - id: retrieval-floor
    metric: hit_rate_at_2
    min: 0.80
  - id: citations-valid
    metric: citations_valid
    kind: observed_count
    max_failures: 0

Une règle min ne passe que lorsque tout l'intervalle franchit le plancher, échoue lorsque tout l'intervalle se situe en dessous, et vaut INSUFFICIENT_EVIDENCE sinon. Une règle observed_count décide sur les cas réellement exécutés, sans intervalle : « aucune citation invalide dans cette suite ». Voir Porte de CI.

L'exécuter

oloproof run
Run run_01M4FCBPE0G550CKCVGXCNEM2P [DECIDED/COMPLETE]
Gate: BLOCK (exit 1)
│ answer-floor    │ answer_correct  │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold    │
│ retrieval-floor │ hit_rate_at_2   │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold    │
│ citations-valid │ citations_valid │ FAIL                  │ observed_failures_exceed_limit │

│ answer_correct  │ 73.3%    │ [44.8%, 92.3%] │ 11 / 15 observed · 0 missing · 0 excluded │
│ hit_rate_at_2   │ 92.3%    │ [63.9%, 99.9%] │ 12 / 13 observed · 0 missing · 2 excluded │
│ recall_at_2     │ 92.3%    │ [63.9%, 99.9%] │ 12 / 13 observed · 0 missing · 2 excluded │
│ citations_valid │ 93.3%    │ [68.0%, 99.9%] │ 14 / 15 observed · 0 missing · 0 excluded │
Cache: execution 0 hit/15 miss; judgment 0 hit/56 miss

Comment le lire :

  • Gate: BLOCK (exit 1) : une règle a donné FAIL. La sortie 1 signifie un FAIL ; la sortie 3 signifie que la porte a bloqué sans FAIL (ici ce serait INSUFFICIENT_EVIDENCE) ; la sortie 0 signifie rien sur quoi la politique bloque. [DECIDED/COMPLETE] est l'état d'exécution : chaque cas a été exécuté.
  • citations-valid donne FAIL : une réponse ne citait rien, et require_citations compte cela comme invalide.
  • answer-floor vaut INSUFFICIENT_EVIDENCE, pas PASS, bien que 73.3 % dépasse 70 % : avec 15 cas, l'intervalle descend jusqu'à 44.8 %, donc les preuves ne peuvent pas montrer que le plancher est atteint.
  • hit_rate_at_2 indique 2 excluded : les deux cas sans étiquette. Son dénominateur est 13, pas 15.
  • La table Slices qui suit est exploratoire et ne sert jamais de porte ; un segment sous min_slice_support n'affiche pas d'intervalle.

Inspecter les échecs

L'identifiant de l'exécution figure sur la première ligne de sa sortie.

oloproof inspect RUN_ID --failures
4 of 15 cases failed, errored or did not finish

refund_review
  output: {"answer": "Every refund request on an annual plan is logged in the audit trail, and the same request is listed again on the day it was reviewed and approved."…
  answer_correct: failed

money_back
  output: {"answer": "I could not find that in the knowledge base.", "citations": []}
  answer_correct: failed
  hit_rate_at_2: failed
  recall_at_2: failed
  citations_valid: failed

security_review
  output: {"answer": "Security reviews during Enterprise onboarding include an access review and a written summary for the customer, and every review is scheduled with t…
  answer_correct: failed

seat_count
  output: {"answer": "Team plans include five seats.", "citations": ["kb-03"]}
  answer_correct: failed

oloproof inspect RUN_ID --case refund_review affiche l'entrée, les valeurs attendues, la sortie et chaque jugement d'un cas. Les artefacts enregistrés se trouvent dans le paquet exporté :

oloproof export RUN_ID

Chaque ligne de .oloproof/bundles/RUN_ID/cases.jsonl est l'enregistrement d'un cas ; son champ artifacts contient ce qui a été enregistré. Pour money_back, ce champ indique :

{"retrieval/v1": [{"candidates": [], "depth": 6, "query": "Where do I claim money back on a yearly subscription?"}], "context/v1": [{"dropped": [], "items": [], "source": "retrieval", "token_budget": 40}], "citations/v1": [{"ids": []}]}

Lecture des quatre échecs à partir des seules preuves enregistrées :

CasCe que montre l'enregistrementUne action suivante pertinente
money_backLa récupération n'a rien renvoyé : la question ne partage aucun mot avec le passage sur les remboursementsRéécriture de requête ou synonymes, mesurés par hit_rate_at_2
refund_review, security_reviewhit_rate_at_2 a réussi, mais la réponse venait d'un autre passageInspecter context/v1 : le passage pertinent a-t-il été retiré pour le budget ?
seat_countLe bon passage a été récupéré, conservé et cité ; la réponse dit « five », le cas attend « 5 »Corriger l'attente ou le format de la réponse, pas la récupération

Ce tableau est votre lecture de l'enregistrement. C'est une association entre un échec et une étape, pas une cause démontrée : rien n'a réexécuté le cas avec l'étape modifiée.

Ce que fait diagnose sur une boîte noire

oloproof diagnose RUN_ID --intervention gold-context --criterion answer_correct
Selected: 4 failed cases with gold context (observed; no population claim)
UNRESOLVED: 4 of 4, the system is not staged, so no case was re-executed
Diagnosis sha256:8809e100ab2ec1dad8ffeacc136cd510300081a0edf01ec11f881c543ed104bc
Cases: oloproof inspect sha256:8809e100ab2ec1dad8ffeacc136cd510300081a0edf01ec11f881c543ed104bc

Chaque cas est UNRESOLVED avec la raison intervention_unsupported. Oloproof ne peut pas donner à une boîte noire le passage de référence à la place de sa propre récupération, il ne fait donc pas semblant. Les interventions contrôlées nécessitent le chemin B.

Faire une modification candidate et comparer

L'enregistrement indique que money_back a échoué à la récupération. La modification candidate enrichit la question de synonymes avant la recherche. Dans app.py :

EXPAND_QUERY = True

Modifier le code change la version du système enregistrée pour l'exécution. Exécutez à nouveau, puis comparez le candidat à la référence :

oloproof run
oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yaml

L'exécution candidate seule : citations-valid donne désormais PASS, hit_rate_at_2 indique 100.0 % [75.2 %, 100.0 %], et la porte bloque toujours avec la sortie 3 car answer-floor et retrieval-floor restent INSUFFICIENT_EVIDENCE. La comparaison :

Comparison sha256:2feb024c… of run_01M4FCCJYCVVYA8NB4XZDV6YMB against run_01M4FCCHVBG9WHDP7G5HFX7RDT · 15 paired cases
answer_correct: +6.7 points [-26.5, +40.8] · 15 paired · 0 missing · 0 excluded
hit_rate_at_2: +7.7 points [-29.8, +45.5] · 13 paired · 0 missing · 2 excluded
  excluded 2: no_relevance_labels
recall_at_2: +7.7 points [-29.8, +45.5] · 13 paired · 0 missing · 2 excluded
  excluded 2: no_relevance_labels
citations_valid: +6.7 points [-26.5, +40.8] · 15 paired · 0 missing · 0 excluded
20 exploratory slice differences not shown; add --slices to list them
Decisions
  answers-not-worse  answer_correct  non-inferiority, margin 5.0 points  INSUFFICIENT_EVIDENCE  interval_overlaps_margin
    about 38 more paired cases would decide it, if the difference holds (53 in total at 7% discordance)
  citations-not-worse  citations_valid  non-inferiority, margin 2.0 points  INSUFFICIENT_EVIDENCE  interval_overlaps_margin
    about 68 more paired cases would decide it, if the difference holds (83 in total at 7% discordance)
Gate: BLOCK (exit 3)

La modification a corrigé le cas visé (une réponse de plus, +6.7 points sur 15 cas appariés). La comparaison ne peut toujours pas établir que le candidat n'est pas pire que la référence de plus que la marge : 15 cas appariés laissent un intervalle large d'environ 67 points. La ligne de planification indique combien de cas appariés supplémentaires trancheraient si la différence se maintenait. Une suite plus grande, et non une marge différente, est l'action suivante. Voir Comparer un candidat à une référence et Règles de comparaison.

Chemin B : une application en étapes avec diagnostic

Les fichiers en étapes

Le chemin B exécute l'exemple support_rag, décrit sur la page Évaluation RAG. Copiez-le :

oloproof init --example support_rag my-staged-rag
cd my-staged-rag
FichierCe que c'est
app.pySupportRag, une classe décorée avec @rag_system : retrieve(input, depth), generate(input, context), count_tokens(passage)
data/corpus.jsonl, data/support.jsonlLa base de connaissances, et 13 cas, chacun avec relevant et gold_context
oloproof.yamlsystem.rag pointe vers la classe et fixe depth, top_k, token_budget, index_version
release.yaml, compare.yamlLes mêmes politiques que pour le chemin A

La différence avec le chemin A tient à qui assemble le contexte. Ici, Oloproof appelle retrieve, garde les top_k premiers candidats, retire les passages au-delà de token_budget et passe le reste à generate. Parce qu'il tient les étapes séparées, il peut les mettre en cache séparément et réexécuter la génération avec un autre contexte. Pour adapter votre propre application, remplacez les corps de retrieve (appelez votre index, renvoyez Retrieval(candidates=[Passage(...)]) dans l'ordre de votre système de récupération) et de generate (appelez votre modèle avec les passages fournis). Donnez à index_version une valeur qui change quand votre index change : elle fait partie de l'identité de la récupération, et une valeur périmée réutilise des récupérations en cache contre un index qui ne les renvoie plus.

La même configuration autorise aussi les segments relevant_position et context_truncated, et un évaluateur ndcg sur toute la profondeur de récupération.

Exécuter la suite en étapes

oloproof run
Gate: BLOCK (exit 3)
│ answer-floor    │ answer_correct  │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold    │
│ retrieval-floor │ hit_rate_at_2   │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold    │
│ citations-valid │ citations_valid │ PASS                  │ observed_failures_within_limit │
│ answer_correct  │ 69.2%    │ [38.5%, 91.0%]  │ 9 / 13 observed · 0 missing · 0 excluded     │
│ hit_rate_at_2   │ 92.3%    │ [63.9%, 99.9%]  │ 12 / 13 observed · 0 missing · 0 excluded    │
│ recall_at_2     │ 92.3%    │ [63.9%, 99.9%]  │ 12 / 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    │
Cache: execution 0 hit/13 miss; judgment 0 hit/65 miss
Stages: retrieve 0 hit/13 miss; generate 0 hit/13 miss

La ligne Stages est le cache propre du système en étapes. Sortie 3 : rien n'a donné FAIL, mais deux règles manquent de preuves pour donner PASS.

Diagnostiquer avec le contexte de référence, à côté d'un témoin

oloproof diagnose RUN_ID --intervention gold-context --criterion answer_correct
Selected: 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:50a6124f…
Child runs: gold context run_…, control run_…
Cases: oloproof inspect sha256:50a6124f…

Deux exécutions filles sont créées à partir des cas en échec : l'une avec le gold_context du cas à la place du contexte récupéré, et un témoin qui les réexécute sans changement. Le témoin est ce qui rend la lecture sûre : un cas qui réussit lors d'une simple réexécution était instable, pas diagnostiqué. diagnose se termine avec 0 quoi qu'il trouve ; il ne décide rien sur la publication.

oloproof inspect DIAGNOSIS_ID
money_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 2

Lire les étiquettes

ÉtiquetteCe qui a été observéCe qu'elle n'établit pas
RETRIEVAL_MISSAucun passage pertinent n'a été récupéré, et le cas a réussi avec le passage de référenceQue la récupération soit la seule chose qui cloche, ou qu'une modification donnée de la récupération le corrigera
RANKED_OUTUn passage pertinent a été récupéré sous top_k, et le cas a réussi avec le passage de référenceQu'élargir le top-k aidera d'autres cas
CONTEXT_ASSEMBLY_LOSSUn passage pertinent dans le top-k a été retiré du contexte, et le cas a réussi avec le passage de référenceQuel budget suffirait
GENERATION_FAILURELe cas a encore échoué avec le passage de référence en mainQue le modèle, plutôt que le prompt ou l'attente, soit en faute
UNRESOLVEDRien n'a pu être conclu : le système n'est pas en étapes (intervention_unsupported), le cas a été récupéré sous le témoin (unstable_under_control), il n'a pas d'étiquettes de pertinence (no_relevance_labels), ou des preuves manquentQuoi que ce soit sur le cas

Chaque étiquette est une association entre un échec et une étape sous une intervention sur ces cas. Ce n'est pas une cause démontrée : « Implicated » et « Candidate experiment » sont les mots les plus forts qu'emploie la sortie, et les comptes ne décrivent que les cas sélectionnés (« no population claim »). seat_count est un bon rappel : il échoue avec le bon passage parce que la base de connaissances dit « five » et que le cas attend « 5 », ce qu'aucune modification de la récupération ne peut corriger.

Cas avec et sans passages de référence

Seuls les cas en échec qui déclarent expected.gold_context peuvent être réexécutés. Retirez le passage de référence de seat_count et de money_back (et l'étiquette de pertinence de money_back) et la même commande rapporte :

Selected: 2 failed cases with gold context (observed; no population claim)
Excluded: 2 failed cases, no_gold_context - declare the passages that would have answered the case in its `expected.gold_context`, as a list of `{doc_id, text}` objects; an intervention needs them to tell a retrieval failure from a generation one
Control: 0 of 2 passed when re-executed without the intervention
Recovered under gold context: 2 of 2
CONTEXT_ASSEMBLY_LOSS: 2 of 2, recovered; relevant evidence within top-k was left out of the context

Les cas exclus sont listés, pas retirés en silence. Notez aussi ce que retirer une étiquette de pertinence fait à l'exécution elle-même : hit_rate_at_2 est monté à 100.0 % (12 / 12 observés, 1 exclu), car le seul cas que la récupération manquait n'est plus mesuré. Les cas sans étiquette sortent du dénominateur ; ils ne comptent pas comme des réussites, et une métrique sur moins de cas peut paraître meilleure que l'application ne l'est. Étiquetez d'abord les cas difficiles.

Tester une correction avant de la faire : top-k et un reclasseur

Deux autres interventions rejouent la récupération enregistrée avec un réglage différent, de sorte que le système de récupération n'est pas rappelé :

oloproof diagnose RUN_ID --intervention top-k --top-k 4 --criterion answer_correct
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:50a6124f…): 3 of 4 recovered

Un reclasseur est une fonction (input, candidates) -> candidates que vous écrivez. Enregistrez-la sous rerank.py à côté de app.py :

"""A candidate reranker: shorter passages first, so more of them fit the token budget."""

from oloproof import Passage


def shortest_first(input: dict, candidates: list[Passage]) -> list[Passage]:
    return sorted(candidates, key=lambda passage: len((passage.text or "").split()))
oloproof diagnose RUN_ID --intervention reranker --reranker rerank:shortest_first --criterion answer_correct
Recovered under reranker rerank:shortest_first: 0 of 4
Confirmed under reranker rerank:shortest_first: 0 of 0 RANKED_OUT cases also recovered

Aucune ne récupère quoi que ce soit, ce que les étiquettes du contexte de référence prédisaient : aucun échec ici n'était un passage classé juste sous la coupure. Chaque rejeu reporte les étiquettes du contexte de référence, de sorte que les diagnostics se lisent ensemble.

Mener l'expérience que le diagnostic a nommée, et comparer

Portez token_budget à 120 dans oloproof.yaml, puis :

oloproof run
oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yaml
Stages: retrieve 13 hit/0 miss; generate 7 hit/6 miss

Chaque récupération a été réutilisée, car top_k et le budget 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 à nouveau.

answer_correct: +0.0 points [-33.6, +33.6] · 13 paired · 0 missing · 0 excluded
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 cas n'a changé de verdict, donc l'hypothèse proposée par le diagnostic n'est pas étayée pour ces cas. C'est un résultat utile. L'expérience suivante porte sur des fragments plus petits, ou sur les prompts des deux cas d'assemblage du contexte ; seat_count a besoin que son attente soit corrigée.

Dépannage

SymptômeCauseCorrection
Configuration error: slice 'relevant_position' ... needs a staged systemUn segment de position sur un système appelable ou HTTPRetirez le segment, ou passez au chemin B
L'exécution s'arrête avec la sortie 2 et malformed retrieval/v1 artifactUn champ que le schéma n'autorise pas, ou plus de candidats que depthNe projetez que les champs documentés ; fixez depth au moins au nombre renvoyé
citations_valid indique 0 / 0 observed · 15 missing et sa règle vaut INSUFFICIENT_EVIDENCE avec no_observationsL'adaptateur n'a pas enregistré citations/v1 (ou context/v1) ; chaque cas concerné est manquant, pas réussiEnregistrez les deux sur chaque chemin de l'adaptateur, y compris « pas de réponse » ; oloproof inspect RUN_ID --failures montre l'erreur par cas
Une métrique de récupération montre beaucoup d'excludedDes cas sans expected.relevantÉtiquetez-les, ou acceptez sciemment le dénominateur plus petit
diagnose indique UNRESOLVED ... not stagedChemin AAttendu ; utilisez le chemin B pour les interventions
diagnose refuse avec an intervention must re-execute the same systemLe code ou la configuration a changé depuis l'exécutionDiagnostiquez une exécution de la version actuelle, ou restaurez la version exécutée
Le diagnostic sélectionne moins de cas qu'il n'y a d'échecsDes cas en échec sans expected.gold_contextAjoutez les passages de référence ; les cas exclus sont nommés dans la sortie
Des récupérations sont réutilisées après un changement d'indexindex_version inchangéChangez index_version quand l'index change

Limites

  • Oloproof appelle votre application ; il ne l'héberge pas, ne l'isole pas et ne la réinitialise pas. Son index, ses caches et tout état qu'elle conserve vous appartiennent.
  • Sur une boîte noire, les interventions ne sont pas disponibles : diagnose étiquette chaque cas UNRESOLVED et ne réexécute rien.
  • Les interventions sont le contexte de référence, le top-k et un reclasseur. Il n'y a pas d'intervention sur le découpage, les plongements ou le prompt.
  • Les étiquettes de diagnostic décrivent les cas en échec sélectionnés sous une intervention à côté d'un témoin. Elles associent un échec à une étape ; elles ne démontrent pas une cause, et n'affirment rien sur les cas non sélectionnés.
  • Les métriques de récupération nécessitent des étiquettes de pertinence, et le diagnostic nécessite des passages de référence ; Oloproof ne crée ni les unes ni les autres.
  • Les exemples déterministes tiennent lieu d'un vrai système de récupération et d'un vrai modèle. Un modèle réel dans generate ou un évaluateur juge appelle un fournisseur, nécessite des identifiants et coûte de l'argent par cas.
  • Ce qui fonctionne où, SDK face au YAML face au navigateur, se trouve sur Ce qui fonctionne aujourd'hui.