AI SystemsINTERMEDIATEDifficulty: INTERMEDIATE~12h build

RAG Evaluation Lab

A harness that tells you whether your retrieval changes actually helped

By Renjith ·

Prerequisites

  • Python and pandas or Polars
  • An existing document corpus to test against
  • Roughly 50 questions with known correct source passages

The problem

Almost every RAG system in production was tuned the same way: someone changed the chunk size, asked it three questions they remembered, decided it felt better, and shipped.

The reason is not laziness. It is that a RAG pipeline has at least six independent knobs — chunk size, overlap, embedding model, retrieval depth, reranking, prompt — and no obvious way to attribute a change in output quality to any one of them. So people stop attributing and start guessing.

What was built

A harness that treats a RAG configuration as an experiment. You define configurations declaratively, run them all against a fixed question set, and get a comparison table with retrieval metrics and generation metrics reported separately.

That separation is the point. A bad answer has two very different causes — the right passage was never retrieved, or it was retrieved and the model ignored it — and they demand opposite fixes. A single quality score hides which one you have.

How it works

Retrieval first, always#

Run the retrieval metrics before touching anything else. In every corpus tested here, retrieval recall explained more of the variance in final answer quality than every generation-side change combined.

If recall@5 is 0.62, no prompt engineering will save you. The answer is not in the context. Prompt work at that point is rearranging furniture in a room with no floor.

evaluate.pypython
def retrieval_metrics(results: list[Result], k: int = 5) -> dict[str, float]:
    hits = [r for r in results if r.gold_chunk_id in r.retrieved_ids[:k]]

    reciprocal_ranks = []
    for r in results:
        if r.gold_chunk_id in r.retrieved_ids[:k]:
            reciprocal_ranks.append(1 / (r.retrieved_ids.index(r.gold_chunk_id) + 1))
        else:
            reciprocal_ranks.append(0.0)

    return {
        "hit_rate": len(hits) / len(results),
        "mrr": sum(reciprocal_ranks) / len(results),
    }

What the sweep actually showed#

Across three corpora — technical documentation, a policy handbook, and a set of transcripts — the ranking of what mattered was consistent and not what the internet says:

Change Typical effect on recall@5
Adding a reranker +0.11 to +0.18
Respecting document structure when chunking +0.06 to +0.14
Switching embedding model +0.02 to +0.05
Tuning chunk size within 256–768 ±0.02, mostly noise

Chunk size is the most-discussed parameter and, within a sensible range, close to the least important. Reranking is the least-discussed and consistently the largest single gain.

One golden set is not a benchmark

These numbers come from three corpora and one annotator. Treat them as a hypothesis to test on your corpus, which is exactly what the harness is for. Publishing a comparison table like this without saying that is how the field ended up full of confident, unreproducible claims.

Why DuckDB#

Results live in a single DuckDB file committed alongside the corpus. No service to run, no schema migration to manage, and analytical queries across hundreds of runs return instantly. For an experiment log that one person queries interactively, anything more is overhead.

Build steps

  1. 1

    Build the golden set

    Fifty questions, each annotated with the passage that actually answers it. This is the slowest step and the one that determines whether anything downstream means anything.

  2. 2

    Define configurations as data

    A YAML matrix of chunking, embedding and retrieval settings. Configurations as data rather than code branches is what makes the sweep reproducible.

  3. 3

    Measure retrieval alone

    Recall@k, MRR and hit rate — computed with no generation involved at all. This is the cheapest and most diagnostic signal in the whole pipeline.

  4. 4

    Measure generation given perfect retrieval

    Feed the known-correct passage directly and score faithfulness and answer relevance. This isolates the generation half completely.

  5. 5

    Run the full pipeline and compare

    The gap between the isolated scores and the end-to-end score tells you exactly where the loss is occurring.

  6. 6

    Store every run

    Results go to DuckDB with the full configuration. Six weeks later, "did we already try 256-token chunks with reranking?" is a query, not an argument.

Lessons learned

Separate retrieval from generation before optimising either. A combined score cannot tell you which half is broken, and the fixes are opposite.

The golden set is the project. Everything else is scaffolding around fifty carefully annotated questions.

Reranking is underrated; chunk size is overrated. The community discourse has these almost exactly backwards.

Log configurations, not just scores. Without the full configuration stored beside the result, the log is a list of numbers you cannot act on.

Limitations

Fifty questions is enough to catch a regression and far too few to make a general claim. Confidence intervals on these metrics are wide.

Faithfulness scoring uses a language model as judge, which has its own biases — it tends to reward fluent answers that stay close to the source wording.

Only tested on English text corpora. Multilingual and heavily tabular corpora behave differently and are not covered.

Future improvements

Automatic golden-set expansion by mining questions from real user logs, with human confirmation.

Cost and latency reported alongside quality, since a configuration that is 3% better and 4× more expensive is usually the wrong choice.

Per-question regression alerts, so a configuration change that breaks a specific question class is visible immediately.

Premium version

RAG Evaluation System

A production-ready evaluation harness: configuration sweeps, retrieval and generation metrics measured separately, and CI regression gates.

Related course

Production AI Engineering: RAG, Agents, Evals & MCP

Retrieval that holds up, agents that fail safely, evaluation that catches regressions, and MCP for tool integration.

Related builds and resources

AI Systems$149

RAG Evaluation System

A production-ready evaluation harness: configuration sweeps, retrieval and generation metrics measured separately, and CI regression gates.

v2.1.0Python · Ragas · DuckDB
AI & LLMFree

RAG Chunking Calculator

Work out how many chunks a corpus produces, what retrieval depth fits your context window, and what indexing will cost.

840 runs
UDEMY$119

Production AI Engineering: RAG, Agents, Evals & MCP

Retrieval that holds up, agents that fail safely, evaluation that catches regressions, and MCP for tool integration.

AdvancedDifficulty: Advanced★ 4.914h480 studentsPython · LangGraph · Ragas
AI Engineer Lab

Why Your RAG Evaluation Is Wrong

Most RAG evaluations measure the wrong thing, on the wrong data, with a judge that rewards the wrong behaviour. Here is what to measure instead.

IntermediateDifficulty: Intermediate3 min readPython · Ragas · OpenAI API
Video
Evaluation

Six RAG Chunking Strategies Compared

Fixed, recursive, semantic, structural, sentence-window and parent-document chunking measured on retrieval recall across three corpora.

IntermediateDifficulty: IntermediatePython · Ragas · pgvector
Notebook
Video

Building a RAG Evaluation Harness From Scratch

Why retrieval and generation must be measured separately, and how to build the harness that does it.

Watch