← All extensions
RAG Score banner

RAG Score

ivyx

Scores a retrieval against the documents a case declared relevant so eval suites can gate on recall

RAG Score

Measures how good your search actually is. You write down which documents should come back for a question, and this counts how many of them did — no model is asked anything, so the number is repeatable and costs nothing.

What you can do

  • Gate a pipeline on retrieval quality, the same way a test suite gates a build
  • Compare two chunking or embedding choices on the same questions
  • Catch a search that quietly stopped working when an index went stale
  • See which expected document was missed, and where the first good one ranked

Requirements

  • The Evals hub, which finds this engine and runs the suite
  • Vectors, and a built index for the corpora your questions search

Getting started

Write a few questions with their answers in a .jsonl file — one line each, naming the documents that should come back. Point an eval suite at your search and list rag as its engine, then run it from the Evals panel. The first run tells you what your search is worth today; every run after that tells you whether a change helped.

Start with ten questions you already know the answers to. A small honest set beats a large guessed one.

The metrics

Metric Meaning
rag.recall Share of the declared documents that came back
rag.precision Share of the returned hits that were declared
rag.mrr Reciprocal rank of the first relevant hit; 0 when none came back
rag.hit@1 1 when the top hit was declared relevant
rag.retrieved How many hits were considered. A counter — never threshold it
rag.mode.semantic 1 when the retrieval ran semantically

Threshold rag.mode.semantic in every suite. vectors.query falls back to keyword search when a corpus has no index, and on that path it echoes the corpora it was asked for — so a suite that does not check can report a healthy recall for a pipeline that never embedded anything.

The suite and its dataset

suites:
  - id: rag-code
    target:
      kind: capability
      id: vectors.query
    dataset: .punica/evals/rag-code.jsonl
    engines: [rag]
    thresholds:
      rag.recall: 0.8
      rag.mode.semantic: 1

Each dataset line is the retrieval's own input, plus what should come back:

{ "id": "gateway-approval",
  "input": { "query": "how does the gateway decide an approval",
             "corpora": ["code"], "topK": 10 },
  "expect": { "relevant": ["src/gateway.ts", "docs/approvals.md"] } }

Options

Option Default Meaning
match path Which field of a hit an expected value names — path, id or corpus
k every hit Score only the first k hits, so one dataset reads at several k without re-running

match is one field, never a chain. A resolver that tried id, then path, then a prefix would make a wrong match indistinguishable from a right one.

When the hits carry none of the named field, or a case declares nothing relevant, the engine says so instead of reporting a recall of zero: an absent field is a broken suite, not a bad retrieval.

Two things it does not do

It asks no model anything. Recall is counted against documents a person declared, so the number is reproducible, costs no tokens, and the dataset never leaves the machine.

It does not measure groundedness. Whether an answer is supported by what was retrieved needs a judge, and an LLM-as-judge metric belongs in its own engine rather than inside a deterministic gate.