Skip to content

Guias

Avaliação de RAG

A versão em inglês desta página mudou desde que foi traduzida. A página em inglês é a atual. Ler em inglês

Uma resposta aumentada por recuperação pode estar errada por quatro razões diferentes: a passagem certa nunca foi recuperada; foi recuperada e ficou numa posição baixa demais; ficou numa posição alta o suficiente e depois foi retirada do contexto; ou chegou ao modelo e o modelo errou mesmo assim. Um único número de acurácia não consegue distingui-las. O Oloproof executa um sistema RAG como duas etapas que ele consegue ver, mede cada uma e reexecuta os casos que falharam sob mudanças controladas para descobrir qual razão se aplica.

examples/support_rag/ é o projeto que esta página executa. Ele não precisa de credenciais de provedor.

Um sistema em etapas

O sistema é uma classe com uma etapa de recuperação e uma etapa de geração, decorada com @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) retorna até depth candidatos, como Passage(doc_id=..., score=..., text=...), na ordem em que o seu mecanismo de recuperação os produziu. O Oloproof registra as posições e nunca os reordena. Em seguida, ele mantém os primeiros top_k, descarta as passagens que excedem token_budget e passa o que sobra para generate(input, context). count_tokens(passage) só é necessário com um token_budget; o Oloproof nunca estima tokens.

oloproof.yaml aponta para a classe e pode sobrescrever qualquer uma de suas configurações:

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-16

index_version faz parte da identidade da recuperação. Altere-o quando o índice mudar; caso contrário, recuperações em cache serão reutilizadas contra um índice que já não as retorna.

O que um caso declara

Um caso de RAG traz dois campos em expected de que nenhum outro tipo de caso precisa:

{"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 lista os documentos que respondem à pergunta. As métricas de recuperação o leem,

e um caso sem ele é excluído delas com no_relevance_labels.

  • expected.gold_context é o próprio texto da passagem. O diagnóstico o coloca no lugar do contexto

recuperado, e um caso que falhou sem ele não pode ser diagnosticado.

Os avaliadores

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: 4

hit_rate, recall, mrr e ndcg recebem k e derivam dele o nome do próprio critério: hit_rate_at_2. Uma entrada em expected.relevant também pode trazer um chunk_id e um grade, cujo padrão é 1; relevance_unit: chunk passa então a contar cada chunk como uma unidade própria, em vez de documentos inteiros. citation_validity verifica se cada id que uma resposta cita corresponde a uma passagem do contexto que ela recebeu, e com require_citations: true uma resposta que não cita nada falha. groundedness_judge e citation_support_judge são juízes LLM que leem o contexto montado e recebem um provider e um model como qualquer outro juiz.

oloproof run

Três linhas da tabela de métricas:

│ 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 elas, hit_rate_at_2 e recall_at_2 marcam ambas 92.3%, 12 de 13 observados, com um limite inferior de 63.9%: a passagem relevante de uma pergunta nunca foi recuperada.

Cache: execution 0 hit/13 miss; judgment 0 hit/65 miss
Stages: retrieve 0 hit/13 miss; generate 0 hit/13 miss

A linha Stages é o cache próprio do sistema em etapas. Recuperação e geração ficam em cache separadamente, então uma mudança na geração nunca reexecuta a recuperação.

Descobrindo o porquê

Quatro perguntas falharam. diagnose as reexecuta com a passagem de referência no lugar do contexto recuperado, ao lado de um controle que as reexecuta sem alterações:

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:b2846cbe04099225bda4bf21beb1738f54d6cc0e9a7ce210bb3eb467619cbd69
Child runs: gold context run_01M3C3REAAZDF9FD8N2C8BG17P, control run_01M3C3REAFVXK2VM4MYAQYH6KE
Cases: oloproof inspect sha256:b2846cbe04099225bda4bf21beb1738f54d6cc0e9a7ce210bb3eb467619cbd69

Um caso que passa com a passagem de referência e falha sem ela falhou antes do modelo. Um caso que falha mesmo com a passagem de referência em mãos é responsabilidade do modelo. O controle é o que torna essa leitura segura: um caso que se recupera numa simples reexecução era instável, não foi diagnosticado.

O id do diagnóstico lista o caso por trás de cada contagem:

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

Ele aponta um fator, nunca uma causa. "Implicated" e "candidate experiment" são as palavras mais fortes que ele usa, porque quatro casos que se recuperam sob uma intervenção não estabelecem por que falharam.

Testando uma correção antes de fazê-la

Duas outras intervenções reproduzem a recuperação registrada com uma configuração diferente, de modo que nenhuma chamada ao mecanismo de recuperação é feita. top-k amplia o corte:

oloproof diagnose RUN_ID --intervention top-k --top-k 4 --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 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:668084c3e99df84f7b63f138792aa96f48e5cb8251fee3acae8e1cc0500ba085

Nada se recuperou, que é o que os rótulos previam: nenhuma falha aqui era uma passagem classificada logo abaixo do corte. A reprodução leva adiante os rótulos do contexto de referência, de modo que os dois diagnósticos são lidos em conjunto. reranker recebe --reranker com um module:function e, em vez disso, reproduz a recuperação passando pelo seu reranker.

Executando o experimento

O diagnóstico indicou um orçamento de tokens maior. Aumente token_budget para 120 e execute novamente:

Cache: execution 0 hit/13 miss; judgment 0 hit/65 miss
Stages: retrieve 13 hit/0 miss; generate 7 hit/6 miss

Todas as recuperações foram reutilizadas, porque top_k e o orçamento de tokens estão fora da identidade da recuperação; só os seis casos cujo contexto mudou foram gerados de novo. Em seguida, compare, com a política de comparação do exemplo:

oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yaml
Comparison 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 excluded

Segue-se uma linha exploratória para cada segmento e métrica, e depois as decisões:

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)

O experimento não ajudou: nenhum dos treze mudou de veredito. E treze casos pareados não poderiam ter estabelecido uma mudança de nenhum tamanho que importe para um lançamento, que é a outra coisa que o intervalo diz.

Próximos passos

  • Segmentos trata de relevant_position e context_truncated.
  • Regras de comparação trata das regras pelas quais a última etapa foi decidida.
  • Juízes trata do que um juiz de fundamentação precisa cumprir antes de poder fazer gate.