Skip to content
techpotions
rag-evaluation · retrieval-metrics · faithfulness · ci-for-ai · langchainOctober 23, 20266 min read

How to Evaluate a RAG Pipeline Before Users Find the Gaps

Stop eyeballing RAG answers and start measuring what breaks. This guide splits retrieval and generation metrics, shows how to build a golden question set, wire recall@k, faithfulness, and cost into CI.

Cover illustration for “How to Evaluate a RAG Pipeline Before Users Find the Gaps”

Learning how to evaluate a RAG pipeline – really evaluate it – starts by splitting retrieval from generation, then measuring both with the rigor your users deserve. When we built Parcoursup Zen, an AI orientation platform matching French students to 23,000+ post‑bac formations, a retrieval slip wasn’t a minor UX annoyance; it could push a student toward a dead‑end degree or miss the perfect fit entirely. So we treated evaluation as a first‑class engineering discipline, not an afterthought.

Building a golden question set before you measure anything

Without ground truth, your metrics are floating numbers. For Parcoursup Zen we created a 240‑case evaluation suite with domain experts. Every case pairs a student profile (interests, grades, location) with a known set of formation IDs that should appear in the top‑10 results. This golden set gates every prompt or embedding change and is the only reason we can trust that our pipeline won’t silently degrade during admission season.

How to evaluate a RAG pipeline: retrieval metrics that catch silent drift

The takeaway: Track recall@k and mean reciprocal rank (MRR) on your golden set. A chunk‑size tweak that passes a vibe check can tank recall by double digits — unless you measure it.

In Parcoursup Zen, retrieval quality is non‑negotiable: a science‑profile student must see the relevant engineering programmes in the top 5, not art history. We instrument every retriever call against the golden suite:

Python
def evaluate_retrieval(test_cases, retriever, k=10):
    recall_at_k = []
    mrr = []
    for case in test_cases:
        retrieved = retriever.get_relevant_docs(case['query'], top_k=k)
        retrieved_ids = [doc.id for doc in retrieved]
        hits = set(case['relevant_doc_ids']) & set(retrieved_ids)
        recall_at_k.append(len(hits) / len(case['relevant_doc_ids']))
        for rank, doc_id in enumerate(retrieved_ids, start=1):
            if doc_id in case['relevant_doc_ids']:
                mrr.append(1.0 / rank)
                break
        else:
            mrr.append(0.0)
    return {'recall@k': np.mean(recall_at_k), 'mrr': np.mean(mrr)}

The Braintrust evaluation guide recommends pairing recall with MRR to penalise late‑appearing relevant documents.

Metric

What it catches

Why it matters for Parcoursup Zen

Recall@10

Are all crucial formations present in the top 10?

A missing programme equals a missed opportunity

MRR

Is the best match ranked first?

Students scan the first few suggestions; rank‑1 accuracy drives trust

Precision@5

How much noise appears among the top suggestions?

Irrelevant results erode confidence fast

How to evaluate a RAG pipeline: generation‑side metrics when answers must be truthful

The takeaway: A fluent answer that hallucinates facts is worse than silence. Use an LLM‑as‑judge to score faithfulness and citation accuracy against the retrieved context.

In Parcoursup Zen, when the model explains why a formation fits a student, every claim must be traceable to the formation’s official description. A fabricated “high employment rate” could mislead a family. We built a faithfulness check that feeds the retrieved chunks and the answer to a judge prompt:

Python
prompt = """
Given the retrieved context:
{context}

And the answer:
{answer}

Is the answer fully supported by the context? Reply with YES or NO and a brief reason.
"""

The Ragas tutorial and Hugging Face’s evaluation cookbook provide formalised faithfulness scoring. For stricter compliance, we also validate that citations in the answer point to correct source identifiers.

Generation metric

Definition

How we use it

Faithfulness

% of claims explicitly grounded in retrieved docs

Must stay ≥ 95% on our eval suite; a drop triggers a block

Citation accuracy

Whether cited document IDs exist and support the adjacent statement

Every formation justification links to the right source

Answer relevance

How precisely the answer addresses the query

Catching off‑topic tendencies before they hurt users

Cost and latency: the operational metrics nobody budgets for

The takeaway: A perfect answer that costs 10× more than expected can sink your margins. In Parcoursup Zen, keeping the average AI cost below €4 per student was a hard requirement; we track token consumption and wall‑clock time for every interaction.

We instrumented the LangChain pipeline with a custom callback handler:

Python
from langchain.callbacks import BaseCallbackHandler
import time, tiktoken

class CostLatencyTracker(BaseCallbackHandler):
    def on_llm_start(self, serialized, prompts, **kwargs):
        self.start_time = time.time()
    def on_llm_end(self, response, **kwargs):
        latency = time.time() - self.start_time
        tokens = response.llm_output['token_usage']['total_tokens']
        log_metric('llm_latency_ms', latency * 1000, tags={'model': response.model_name})
        log_metric('tokens_per_query', tokens)

These metrics feed a dashboard that alerts if per‑query cost drifts upward after a prompt engineering sprint. The Redis RAG evaluation guide highlights similar infrastructure‑level observability.

Metric

Target (Parcoursup Zen)

Why

Latency p95

< 2 seconds

Students won't wait longer for matching

Avg AI cost per student

< €4

Keeps premium tier profitable

Token‑per‑query ratio

stable

Prevents accidental cost bloat from verbose prompts

Wire your evaluation into CI so every change reports its own grade

The takeaway: An evaluation suite is useless if it only runs when someone remembers. We made the 240‑case Parcoursup Zen eval a gated step in our GitHub Actions pipeline — a chunking parameter change must prove it doesn’t degrade recall or faithfulness before it can merge.

YAML
jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run RAG evaluation suite
        run: |
          pip install -r requirements-eval.txt
          python -m pytest tests/eval_suite.py --junitxml=results.xml
      - name: Assert metric thresholds
        uses: dorny/test-reporter@v1
        with:
          name: RAG Eval
          path: results.xml
          fail-on-error: true
          fail-on-empty: true

Our eval suite reports whether recall@10 ≥ 0.85 and faithfulness ≥ 0.95 — if a new chunking strategy drops recall by even three points, the CI job fails and the pull request turns red. This is the safety net that let us ship changes weekly through peak admission season without breaking a single student session.

If you want a team that builds RAG systems with evaluation baked into the CI pipeline from day zero, our AI agent development company designs these safeguards as part of the core architecture. (Book a deep dive on our AI services to see how we instrument it.)

FAQ

What’s the most critical metric for evaluating a RAG pipeline?

It depends on the stakes. For retrieval‑heavy apps like Parcoursup Zen, recall@k and MRR are life‑critical — missing a relevant formation can change a student’s future. For generative tasks, faithfulness is non‑negotiable: a fluent hallucination erodes trust. Most production systems need all three.

How often should I re‑evaluate my RAG pipeline?

On every change. We run our 240‑case evaluation suite in CI on every pull request that touches embeddings, chunking, or prompts. A periodic manual audit on fresh real‑world queries also catches distribution shift.

Can I evaluate a RAG pipeline without a golden dataset?

You can bootstrap with synthetic queries generated by an LLM and ask a judge model to score effectiveness, but that inherits biases. Domain‑expert‑curated golden sets, like the 240‑profile suite we built for Parcoursup Zen, give the highest signal and catch edge cases no synthetic generator will invent.

Written by
techpotions
All entries
Best Next.js Templates: What Each Costs You
The weekly

One email a week, from the workshop.

What we published, what we shipped, and the free packs as they land. No drip sequence, no webinar, unsubscribe in one click.