Vai al contenuto

Guide

Tutorial: valutare un'applicazione RAG

Un percorso eseguibile per un'applicazione con generazione aumentata dal recupero, in due varianti: un'applicazione esistente a scatola nera di cui registri dall'esterno recupero, contesto e citazioni, e un'applicazione a stadi che Oloproof esegue stadio per stadio, così che oloproof diagnose possa rieseguire i casi falliti con modifiche controllate. Entrambe girano in locale senza credenziali di un provider.

I concetti dietro ogni passo (stadi, etichette di rilevanza, contesto gold, le quattro etichette di fallimento) sono nella pagina Valutazione RAG; i termini caso, valutatore, metrica, intervallo e gate sono in Concetti fondamentali. Questa pagina è il percorso pratico attraverso di essi.

Qual è il tuo percorso

La tua applicazionePercorsoChe cosa ottieniChe cosa non ottieni
Una chiamata in ingresso, una risposta in uscita (un servizio, un endpoint HTTP, una catena di un framework che non vuoi dividere)A, scatola neraMetriche di recupero, controlli sulle citazioni, giudici di aderenza, gate, confrontoInterventi controllati: diagnose non riesegue nulla
Recupero e generazione che puoi chiamare separatamenteB, a stadiTutto ciò che offre A, cache per stadio, e diagnose con contesto gold, top-k e un reranker accanto a un controlloInterventi diversi da quei tre

Se hai dubbi, parti da A. Non richiede alcuna modifica all'applicazione, e passare a B in seguito conserva dataset, valutatori e policy.

Prerequisiti

  • Python 3.11 o successivo, e Oloproof installato (pip install oloproof).
  • I progetti di esempio, distribuiti con il pacchetto: blackbox_rag per il percorso A e support_rag per il percorso B. Copiane uno in una nuova directory e lavora lì:
oloproof init --example blackbox_rag my-rag
cd my-rag

Ogni comando qui sotto si esegue dall'interno della directory copiata. Esecuzioni, giudizi e diagnosi vengono memorizzati lì, in .oloproof/.

Percorso A: un'applicazione esistente come scatola nera

I file

FileChe cos'è
app.pysupport_api(question), che fa le veci della tua applicazione, e run(case), l'adattatore
server.pyLa stessa applicazione via HTTP, per la variante HTTP qui sotto
data/corpus.jsonlLa base di conoscenza di 14 passaggi in cui l'applicazione cerca
data/support.jsonl15 casi: 13 con etichette di rilevanza e passaggi gold, 2 senza né le une né gli altri
oloproof.yamlLa suite: dataset, sistema, valutatori, slice
oloproof.http.yamlLa stessa suite contro il server HTTP
release.yamlLa policy di rilascio per una singola esecuzione
compare.yamlLa policy per confrontare un'esecuzione candidata con una baseline

Che cosa restituisce l'applicazione

support_api si comporta come un'applicazione che hai già: cerca, costruisce un prompt dalle fonti migliori che rientrano in un budget di parole, risponde e cita. La sua risposta contiene già ciò che ha fatto:

{
  "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"}]
}

I nomi dei campi della tua applicazione saranno diversi. Ciò che conta è che possa dirti, per ogni domanda, le fonti ordinate che ha recuperato, quelle che hanno raggiunto il modello e quelle che ha citato. Se non può, aggiungile prima alla sua risposta o ai suoi log: Oloproof misura ciò che è registrato e non deduce mai il recupero da una risposta.

L'adattatore

run chiama l'applicazione senza modificarla e mappa la risposta su tre artefatti tipizzati, i record letti dai valutatori di recupero e di citazione:

@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"]}
ArtefattoFormaLetto da
retrieval/v1query, depth e candidates nell'ordine in cui il tuo retriever li ha restituiti, ciascuno un Passage(doc_id, chunk_id, score, text)hit_rate, recall, mrr, ndcg
context/v1Gli items che hanno raggiunto il modello (doc_id, position, tokens, text), gli elementi dropped con un reason pari a top_k o token_budget, e token_budgetcitation_validity, groundedness_judge, citation_support_judge
citations/v1ids, ciascuno un doc_id o un doc_id#chunk_idcitation_validity, citation_support_judge

Oloproof registra le posizioni così come sono date e non riordina mai. Un artefatto malformato ferma l'esecuzione con codice di uscita 2 invece di essere memorizzato. case è l'oggetto input del caso, quindi case["question"] è la domanda presa dal dataset.

Per usare la tua applicazione, sostituisci il corpo di support_api con una chiamata a essa (una chiamata SDK, una richiesta HTTP) e conserva run. Fai puntare system.callable in oloproof.yaml a essa come module:function.

La variante HTTP

Un sistema HTTP non può chiamare il recorder, quindi è la sua risposta a portare l'evidenza, già nelle tre forme qui sopra, e la configurazione indica dove:

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 serve esattamente questo. Avvialo, poi esegui contro di esso:

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

L'input del caso viene inviato come corpo JSON. output_path estrae l'output dalla risposta, e ogni voce di artifacts registra un percorso puntato come quel tipo; un campo mancante o malformato ferma l'esecuzione con codice di uscita 2. I risultati sono identici a quelli del percorso callable qui sotto. Nel tuo servizio, l'oggetto di evidenza è di solito un campo di debug che abiliti per il traffico di valutazione.

Che cosa dichiara un caso

{"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 elenca i passaggi che rispondono alla domanda. Le metriche di recupero lo leggono. Un caso che non lo ha, come office_hours, ne è escluso con no_relevance_labels: esce dal denominatore invece di contare come successo o come fallimento.
  • expected.gold_context è il testo stesso del passaggio. Il percorso A non lo usa mai; il percorso B lo sostituisce al contesto recuperato durante la diagnosi.

I casi senza etichette sono normali nella pratica, perché etichettare la rilevanza richiede lavoro. Contano comunque per i controlli sulla risposta e sulle citazioni.

Scegliere i valutatori

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 verifica che la risposta contenga il testo atteso. È il controllo del compito: l'utente ha ricevuto la risposta giusta? Usa invece un giudice esatto o a rubrica quando la formulazione varia.
  • hit_rate e recall con k: 2 misurano il recupero alla profondità che l'applicazione mette davvero nel prompt. Una metrica di recupero a una profondità che il modello non vede mai descrive l'indice, non l'applicazione.
  • citation_validity verifica che ogni id citato nomini un passaggio che ha raggiunto il modello; require_citations: true fa fallire anche una risposta che non cita nulla.
  • groundedness_judge e citation_support_judge (facoltativi) chiedono a un modello se la risposta è supportata dal contesto. Richiedono un provider, un modello e credenziali in una variabile d'ambiente, e costano denaro per caso; vedi Giudici per ciò che un giudice deve superare prima di poter fare da gate.

Le slice relevant_position e context_truncated non sono disponibili qui: confrontano le posizioni con il top-k dell'applicazione, che solo un sistema a stadi dichiara. Richiederle ferma l'esecuzione con slice 'relevant_position' compares relevant positions with top_k, so it needs a staged system.

La policy di rilascio

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

Una regola min passa solo quando l'intero intervallo supera la soglia minima, fallisce quando l'intero intervallo sta sotto di essa, ed è INSUFFICIENT_EVIDENCE altrimenti. Una regola observed_count decide sui casi effettivamente eseguiti, senza intervallo: "nessuna citazione non valida in questa suite". Vedi Gate.

Eseguila

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

Come leggerlo:

  • Gate: BLOCK (exit 1): una regola è in FAIL. L'uscita 1 indica un FAIL; l'uscita 3 indica che il gate ha bloccato senza un FAIL (qui sarebbe INSUFFICIENT_EVIDENCE); l'uscita 0 indica che non c'è nulla su cui la policy blocca. [DECIDED/COMPLETE] è lo stato di esecuzione: ogni caso è stato eseguito.
  • citations-valid è in FAIL: una risposta non ha citato nulla, e require_citations lo conta come non valido.
  • answer-floor è INSUFFICIENT_EVIDENCE, non PASS, anche se 73.3% è sopra 70%: con 15 casi l'intervallo scende fino a 44.8%, quindi l'evidenza non può mostrare che la soglia è rispettata.
  • hit_rate_at_2 riporta 2 excluded: i due casi senza etichette. Il suo denominatore è 13, non 15.
  • La tabella Slices che segue è esplorativa e non fa mai da gate; una slice sotto min_slice_support non mostra alcun intervallo.

Esamina i fallimenti

L'id dell'esecuzione è sulla prima riga dell'output dell'esecuzione.

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 stampa l'input di un caso, i valori attesi, l'output e ogni giudizio. Gli artefatti registrati sono nel bundle esportato:

oloproof export RUN_ID

Ogni riga di .oloproof/bundles/RUN_ID/cases.jsonl è il record di un caso; il suo campo artifacts contiene ciò che è stato registrato. Per money_back, quel campo riporta:

{"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": []}]}

Leggere i quattro fallimenti dalla sola evidenza registrata:

CasoChe cosa mostra il recordUna prossima azione sensata
money_backIl recupero non ha restituito nulla: la domanda non condivide alcuna parola con il passaggio sui rimborsiRiscrittura della query o sinonimi, misurati da hit_rate_at_2
refund_review, security_reviewhit_rate_at_2 è passato, eppure la risposta proveniva da un altro passaggioEsamina context/v1: il passaggio rilevante è stato scartato per il budget?
seat_countIl passaggio giusto è stato recuperato, conservato e citato; la risposta dice "five", il caso si aspetta "5"Correggi l'aspettativa o il formato della risposta, non il recupero

Quella tabella è la tua lettura del record. È un'associazione tra un fallimento e uno stadio, non una causa dimostrata: nulla ha rieseguito il caso con lo stadio modificato.

Che cosa fa diagnose su una scatola nera

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

Ogni caso è UNRESOLVED con il motivo intervention_unsupported. Oloproof non può consegnare a una scatola nera il passaggio gold al posto del suo recupero, quindi non finge di farlo. Gli interventi controllati richiedono il percorso B.

Fai una modifica candidata e confronta

Il record dice che money_back è fallito nel recupero. La modifica candidata espande la domanda con sinonimi prima della ricerca. In app.py:

EXPAND_QUERY = True

Modificare il codice cambia la versione del sistema registrata per l'esecuzione. Esegui di nuovo, poi confronta il candidato con la baseline:

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

L'esecuzione candidata da sola: citations-valid ora è in PASS, hit_rate_at_2 riporta 100.0% [75.2%, 100.0%], e il gate blocca ancora con uscita 3 perché answer-floor e retrieval-floor restano INSUFFICIENT_EVIDENCE. Il confronto:

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 modifica ha corretto il caso a cui mirava (una risposta in più, +6.7 punti su 15 casi appaiati). Il confronto non riesce ancora a stabilire che il candidato non sia peggiore della baseline di più del margine: 15 casi appaiati lasciano un intervallo largo circa 67 punti. La riga di pianificazione dice quanti altri casi appaiati lo deciderebbero se la differenza reggesse. La prossima azione è una suite più grande, non un margine diverso. Vedi Confrontare due esecuzioni e Regole di confronto.

Percorso B: un'applicazione a stadi con diagnosi

I file a stadi

Il percorso B esegue l'esempio support_rag, descritto nella pagina Valutazione RAG. Copialo:

oloproof init --example support_rag my-staged-rag
cd my-staged-rag
FileChe cos'è
app.pySupportRag, una classe decorata con @rag_system: retrieve(input, depth), generate(input, context), count_tokens(passage)
data/corpus.jsonl, data/support.jsonlLa base di conoscenza, e 13 casi, ciascuno con relevant e gold_context
oloproof.yamlsystem.rag punta alla classe e imposta depth, top_k, token_budget, index_version
release.yaml, compare.yamlLe stesse policy del percorso A

La differenza rispetto al percorso A è chi assembla il contesto. Qui Oloproof chiama retrieve, conserva i primi top_k candidati, scarta i passaggi oltre token_budget e passa il resto a generate. Poiché tiene separati gli stadi, può metterli in cache separatamente e rieseguire la generazione con un contesto diverso. Per adattare la tua applicazione, sostituisci i corpi di retrieve (chiama il tuo indice, restituisci Retrieval(candidates=[Passage(...)]) nell'ordine del tuo retriever) e di generate (chiama il tuo modello con i passaggi forniti). Imposta index_version su qualcosa che cambia quando cambia il tuo indice: fa parte dell'identità del recupero, e un valore non aggiornato riusa recuperi in cache contro un indice che non li restituisce più.

La stessa configurazione consente anche le slice relevant_position e context_truncated, e un valutatore ndcg sull'intera profondità di recupero.

Esegui la suite a stadi

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 riga Stages è la cache propria del sistema a stadi. Uscita 3: nulla è in FAIL, ma a due regole manca l'evidenza per il PASS.

Diagnosi con contesto gold, accanto a un controllo

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…

Dai casi falliti si creano due esecuzioni figlie: una con il gold_context del caso al posto del contesto recuperato, e un controllo che li riesegue senza modifiche. Il controllo è ciò che rende sicura la lettura: un caso che passa con una semplice riesecuzione era instabile, non diagnosticato. diagnose esce con 0 qualunque cosa trovi; non decide nulla sul rilascio.

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

Leggere le etichette

EtichettaChe cosa è stato osservatoChe cosa non stabilisce
RETRIEVAL_MISSNessun passaggio rilevante è stato recuperato, e il caso è passato con il passaggio goldChe il recupero sia l'unica cosa sbagliata, o che una data modifica al recupero lo correggerà
RANKED_OUTUn passaggio rilevante è stato recuperato sotto top_k, e il caso è passato con il passaggio goldChe allargare il top-k aiuterà altri casi
CONTEXT_ASSEMBLY_LOSSUn passaggio rilevante entro il top-k è stato scartato dal contesto, e il caso è passato con il passaggio goldQuale budget basterebbe
GENERATION_FAILUREIl caso è fallito anche con il passaggio gold a disposizioneChe la colpa sia del modello, e non del prompt o dell'aspettativa
UNRESOLVEDNon si è potuto concludere nulla: il sistema non è a stadi (intervention_unsupported), il caso si è ripreso sotto il controllo (unstable_under_control), non ha etichette di rilevanza (no_relevance_labels), oppure manca evidenzaQualunque cosa riguardo al caso

Ogni etichetta è un'associazione tra un fallimento e uno stadio sotto un intervento su questi casi. Non è una causa dimostrata: "Implicated" e "Candidate experiment" sono le parole più forti che l'output usa, e i conteggi descrivono solo i casi selezionati ("no population claim"). seat_count è un buon promemoria: fallisce con il passaggio giusto perché la base di conoscenza dice "five" e il caso si aspetta "5", cosa che nessuna modifica al recupero può correggere.

Casi con e senza passaggi gold

Si possono rieseguire solo i casi falliti che dichiarano expected.gold_context. Rimuovi il passaggio gold da seat_count e money_back (e l'etichetta di rilevanza da money_back) e lo stesso comando riporta:

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

I casi esclusi vengono elencati, non scartati in silenzio. Nota anche che cosa fa all'esecuzione stessa la rimozione di un'etichetta di rilevanza: hit_rate_at_2 è salito a 100.0% (12 / 12 osservati, 1 escluso), perché l'unico caso mancato dal recupero non viene più misurato. I casi senza etichette escono dal denominatore; non contano come successi, e una metrica su meno casi può sembrare migliore di quanto sia l'applicazione. Etichetta prima i casi difficili.

Prova una correzione prima di farla: top-k e un reranker

Altri due interventi rigiocano il recupero registrato con un'impostazione diversa, quindi il retriever non viene chiamato di nuovo:

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 reranker è una funzione (input, candidates) -> candidates che scrivi tu. Salvala come rerank.py accanto a 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

Nessuno dei due recupera nulla, ed è ciò che le etichette del contesto gold prevedevano: nessun fallimento qui era un passaggio classificato appena sotto il taglio. Ogni riesecuzione porta avanti le etichette del contesto gold, così le diagnosi si leggono insieme.

Esegui l'esperimento indicato dalla diagnosi, e confronta

Porta token_budget a 120 in oloproof.yaml, poi:

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

Ogni recupero è stato riusato, perché top_k e il budget sono fuori dall'identità del recupero; sono stati generati di nuovo solo i sei casi il cui contesto è cambiato.

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'esperimento non ha aiutato: nemmeno un caso ha cambiato verdetto, quindi l'ipotesi offerta dalla diagnosi non è supportata per questi casi. È un risultato utile. Il prossimo esperimento sono chunk più piccoli, oppure i prompt dei due casi di assemblaggio del contesto; seat_count ha bisogno che la sua aspettativa venga corretta.

Risoluzione dei problemi

SintomoCausaSoluzione
Configuration error: slice 'relevant_position' ... needs a staged systemUna slice di posizione su un sistema callable o HTTPRimuovi la slice, o passa al percorso B
L'esecuzione si ferma con uscita 2 e malformed retrieval/v1 artifactUn campo che lo schema non consente, o più candidati di depthMappa solo i campi documentati; imposta depth almeno al numero restituito
citations_valid riporta 0 / 0 observed · 15 missing e la sua regola è INSUFFICIENT_EVIDENCE con no_observationsL'adattatore non ha registrato citations/v1 (o context/v1); ogni caso di questo tipo è mancante, non passatoRegistra entrambi su ogni percorso dell'adattatore, compreso "nessuna risposta"; oloproof inspect RUN_ID --failures mostra l'errore per caso
Una metrica di recupero mostra molti excludedCasi senza expected.relevantEtichettali, o accetta consapevolmente il denominatore più piccolo
diagnose dice UNRESOLVED ... not stagedPercorso APrevisto; usa il percorso B per gli interventi
diagnose rifiuta con an intervention must re-execute the same systemIl codice o la configurazione sono cambiati dopo l'esecuzioneDiagnostica un'esecuzione della versione attuale, o ripristina la versione che è stata eseguita
La diagnosi seleziona meno casi di quelli fallitiCasi falliti senza expected.gold_contextAggiungi i passaggi gold; i casi esclusi sono nominati nell'output
I recuperi vengono riusati dopo che l'indice è cambiatoindex_version non modificatoCambia index_version quando cambia l'indice

Limitazioni

  • Oloproof chiama la tua applicazione; non la ospita, non la isola in una sandbox e non la reimposta. Il suo indice, le sue cache e qualsiasi stato conservi sono tuoi.
  • Su una scatola nera gli interventi non sono disponibili: diagnose etichetta ogni caso come UNRESOLVED e non riesegue nulla.
  • Gli interventi sono contesto gold, top-k e un reranker. Non esiste alcun intervento su chunking, embedding o prompt.
  • Le etichette di diagnosi descrivono i casi falliti selezionati sotto un intervento accanto a un controllo. Associano un fallimento a uno stadio; non dimostrano una causa, e non affermano nulla sui casi non selezionati.
  • Le metriche di recupero richiedono etichette di rilevanza, e la diagnosi richiede passaggi gold; Oloproof non crea né le une né gli altri.
  • Gli esempi deterministici fanno le veci di un retriever e di un modello reali. Un modello reale in generate o un valutatore giudice chiama un provider, richiede credenziali e costa denaro per caso.
  • Che cosa funziona dove, SDK rispetto a YAML rispetto a browser, è in Che cosa funziona oggi.