<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Evaluation on lo0 — Tech Blog</title><link>https://blog.lo0.es/en/tags/evaluation/</link><description>Recent content in Evaluation on lo0 — Tech Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Tue, 16 Jun 2026 03:00:00 +0200</lastBuildDate><atom:link href="https://blog.lo0.es/en/tags/evaluation/index.xml" rel="self" type="application/rss+xml"/><item><title>LLM quality benchmarks: the contamination trap and the OSS tools for not fooling yourself</title><link>https://blog.lo0.es/en/posts/llm-quality-benchmarks-contamination-trap-oss-tools/</link><pubDate>Tue, 16 Jun 2026 03:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/llm-quality-benchmarks-contamination-trap-oss-tools/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>euros (N €)&lt;/strong>, decimals with a full stop. The dollar sign is not used
(on this site it is a formula delimiter).&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Post &lt;strong>B7&lt;/strong> of the benchmarking track. The &lt;a href="https://blog.lo0.es/en/posts/llm-inference-benchmarking-frameworks-metrics-state-of-the-art/">B1 introduction&lt;/a>
established the distinction between the three axes (performance, quality, energy). The &lt;a href="https://blog.lo0.es/en/posts/llm-benchmark-tool-catalogue-practical-deep-dive/">B2 tool catalogue&lt;/a>
covered the performance axis (TTFT, ITL, goodput). This article covers the &lt;strong>quality&lt;/strong> axis:
what each benchmark measures, how it is run, and why the leaderboard numbers do not always
say what they appear to say. The &lt;a href="https://blog.lo0.es/en/posts/evals-llm-layer-after-tracing/">LLMOps eval pipeline&lt;/a>
and &lt;a href="https://blog.lo0.es/en/posts/llm-as-judge-exam-marker-not-oracle/">LLM-as-judge&lt;/a> cover the system&amp;rsquo;s internal
evaluation layer; this article covers the evaluation of the &lt;strong>base model&lt;/strong> with standard
academic benchmarks.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>An inference engine with an 80 ms TTFT serving a low-quality model is worth nothing: the platform&amp;rsquo;s Pareto front has two independent axes. In 2025–2026, the most widely used static benchmarks (MMLU, HumanEval) have crossed the saturation threshold (HumanEval &amp;gt; 93% in frontier models, MMLU-Pro &amp;gt; 75% in the top-5), which invalidates their ability to discriminate at the high end. The main cause is not that the models are better: it is data contamination, with 18% n-gram overlap detected in Llama-2-70B against evaluation datasets. The OSS tools active in 2026 are &lt;strong>lm-evaluation-harness&lt;/strong> (EleutherAI, MIT, 60+ tasks, the ecosystem&amp;rsquo;s de facto engine), &lt;strong>HELM&lt;/strong> (Stanford, Apache 2.0, in maintenance since June 2026), &lt;strong>LiveBench&lt;/strong> (MIT, 1,000 questions refreshed monthly to avoid contamination), &lt;strong>lighteval&lt;/strong> (Hugging Face, MIT, 1,000+ tasks, vLLM support), and &lt;strong>LMArena/Chatbot Arena&lt;/strong> (Bradley-Terry over millions of human comparisons). OpenLLM Leaderboard v2 was archived in 2025 and replaced by the &lt;strong>OpenEvals&lt;/strong> ecosystem (200+ specialised leaderboards). Reproducing an eval number requires pinning: harness version, model version, prompt template version and number of shots.&lt;/p>
&lt;hr>
&lt;h2 id="quality--performance-the-independent-pareto-axis">Quality ≠ performance: the independent Pareto axis&lt;/h2>
&lt;p>The performance axis (TTFT, ITL, throughput) measures &lt;strong>how long&lt;/strong> the engine takes to produce tokens.
The quality axis measures &lt;strong>how correct&lt;/strong> those tokens are. They are orthogonal: a small model
served with vLLM at 3,000 tok/s can score 42% on MMLU-Pro; a frontier model may need
4× more GPU and score 72%. The product throughput × quality is the only number that matters for
a platform in production.&lt;/p>
&lt;div class="diagram" style="max-width:700px;margin:1rem auto;">
&lt;svg viewBox="0 0 700 320" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Pareto space of quality versus performance with four quadrants">
&lt;style>
.ax{fill:none;stroke:currentColor;stroke-width:1.4}
.tl{font:600 13px sans-serif;fill:currentColor}
.ts{font:11px sans-serif;fill:currentColor}
.dot{stroke:currentColor;stroke-width:1.2}
.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#arh)}
&lt;/style>
&lt;defs>
&lt;marker id="arh" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>
&lt;/marker>
&lt;/defs>
&lt;line class="ax" x1="60" y1="260" x2="660" y2="260"/>
&lt;line class="ax" x1="60" y1="260" x2="60" y2="30"/>
&lt;path class="ar" d="M650,260 L665,260"/>
&lt;path class="ar" d="M60,40 L60,25"/>
&lt;text x="670" y="264" class="ts">Performance (tok/s)&lt;/text>
&lt;text x="65" y="22" class="ts">Quality (MMLU-Pro %)&lt;/text>
&lt;text x="62" y="258" class="ts">0&lt;/text>
&lt;text x="55" y="155" class="ts" text-anchor="end">50&lt;/text>
&lt;text x="55" y="80" class="ts" text-anchor="end">72&lt;/text>
&lt;text x="200" y="270" class="ts">1000&lt;/text>
&lt;text x="460" y="270" class="ts">3000&lt;/text>
&lt;line class="ax" x1="58" y1="155" x2="62" y2="155"/>
&lt;line class="ax" x1="58" y1="80" x2="62" y2="80"/>
&lt;line class="ax" x1="200" y1="258" x2="200" y2="262"/>
&lt;line class="ax" x1="460" y1="258" x2="460" y2="262"/>
&lt;circle class="dot" cx="200" cy="155" r="10" fill="none"/>
&lt;text x="212" y="150" class="ts">Small fast model&lt;/text>
&lt;text x="212" y="163" class="ts">(42% / 3,000 tok/s) — useless if the task demands it&lt;/text>
&lt;circle class="dot" cx="460" cy="80" r="10" fill="none"/>
&lt;text x="472" y="75" class="ts">Optimised frontier model&lt;/text>
&lt;text x="472" y="88" class="ts">(72% / 3,000 tok/s) — Pareto zone&lt;/text>
&lt;circle class="dot" cx="180" cy="80" r="10" fill="none"/>
&lt;text x="192" y="75" class="ts">Slow frontier&lt;/text>
&lt;text x="192" y="88" class="ts">(72% / 800 tok/s)&lt;/text>
&lt;circle class="dot" cx="500" cy="200" r="10" fill="none"/>
&lt;text x="512" y="195" class="ts">Fast engine, bad model&lt;/text>
&lt;text x="512" y="208" class="ts">(35% / 4,000 tok/s) — unusable&lt;/text>
&lt;text x="330" y="300" class="tl" text-anchor="middle">Useful zone: quality ≥ task threshold ∧ performance ≥ production SLO&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>The operational consequence: select model and engine &lt;strong>first by quality&lt;/strong> (does it clear the minimum threshold for the task?) and &lt;strong>then by performance&lt;/strong> (does it meet the SLO within the GPU budget?). Inverting the order, picking the fastest engine and fitting the model into it, is the most frequent mistake in initial sizing.&lt;/p>
&lt;hr>
&lt;h2 id="reference-benchmarks-what-each-one-measures">Reference benchmarks: what each one measures&lt;/h2>
&lt;p>The table below collects the standard academic benchmarks in 2026, the capability each one measures, its test set size and the current saturation level in frontier models.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Benchmark&lt;/th>
&lt;th>Capability measured&lt;/th>
&lt;th>Format&lt;/th>
&lt;th>Test set&lt;/th>
&lt;th>Frontier saturation 2026&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>MMLU&lt;/strong>&lt;/td>
&lt;td>Multidisciplinary knowledge (57 subjects)&lt;/td>
&lt;td>4-choice MCQ&lt;/td>
&lt;td>14,079&lt;/td>
&lt;td>High: top-5 &amp;gt; 88%; insufficient separation&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>MMLU-Pro&lt;/strong>&lt;/td>
&lt;td>MMLU with 10 options, multistep reasoning&lt;/td>
&lt;td>10-choice MCQ&lt;/td>
&lt;td>12,032&lt;/td>
&lt;td>Medium: top-5 between 72–77%; still separates&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>GSM8K&lt;/strong>&lt;/td>
&lt;td>Primary-school maths reasoning (word problems)&lt;/td>
&lt;td>answer generation&lt;/td>
&lt;td>1,319&lt;/td>
&lt;td>Very high: top models &amp;gt; 97%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>MATH&lt;/strong>&lt;/td>
&lt;td>Competition maths (algebra, geometry, calculus, etc.)&lt;/td>
&lt;td>LaTeX generation&lt;/td>
&lt;td>5,000&lt;/td>
&lt;td>Medium-high: top models 85–92%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>GPQA Diamond&lt;/strong>&lt;/td>
&lt;td>PhD-level scientific reasoning (biology, physics, chemistry)&lt;/td>
&lt;td>4-choice MCQ&lt;/td>
&lt;td>198&lt;/td>
&lt;td>Emerging: top &amp;gt; 90% in 2026; still separates in 60–90%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>BBH&lt;/strong> (BIG-Bench Hard)&lt;/td>
&lt;td>23 tasks where earlier models fell below human level&lt;/td>
&lt;td>generation&lt;/td>
&lt;td>6,511&lt;/td>
&lt;td>Medium: useful for complex reasoning&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>IFEval&lt;/strong>&lt;/td>
&lt;td>Verifiable instruction following (format, length, language)&lt;/td>
&lt;td>generation + rules&lt;/td>
&lt;td>541&lt;/td>
&lt;td>Low: notable separation between models&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>HumanEval&lt;/strong>&lt;/td>
&lt;td>Python code generation (164 problems with signature)&lt;/td>
&lt;td>generation + execution&lt;/td>
&lt;td>164&lt;/td>
&lt;td>Very high: top &amp;gt; 93%; saturated&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>MBPP&lt;/strong>&lt;/td>
&lt;td>Code generation (500 problems without a function signature)&lt;/td>
&lt;td>generation + execution&lt;/td>
&lt;td>500&lt;/td>
&lt;td>High: top models 85–92%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>MuSR&lt;/strong>&lt;/td>
&lt;td>Multistep reasoning over long texts&lt;/td>
&lt;td>generation&lt;/td>
&lt;td>~1,000&lt;/td>
&lt;td>Low: useful for extended reasoning&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>TruthfulQA&lt;/strong>&lt;/td>
&lt;td>Truthfulness: resistance to asserting popular myths&lt;/td>
&lt;td>generation + MCQ&lt;/td>
&lt;td>817&lt;/td>
&lt;td>Medium: visible separation between families&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Note on saturation:&lt;/strong> a saturated benchmark does not distinguish models at the high end; it is only valuable as a &lt;em>floor check&lt;/em> (confirming the model has not regressed to the level of three years ago). For the frontier zone, the useful benchmarks in 2026 are MMLU-Pro, GPQA Diamond, IFEval and MuSR.&lt;/p>
&lt;hr>
&lt;h2 id="oss-evaluation-tools-data-sheets">OSS evaluation tools: data sheets&lt;/h2>
&lt;h3 id="lm-evaluation-harness-eleutherai">lm-evaluation-harness (EleutherAI)&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Field&lt;/th>
&lt;th>Data&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Repository&lt;/td>
&lt;td>github.com/EleutherAI/lm-evaluation-harness&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Licence&lt;/td>
&lt;td>MIT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Maintainer&lt;/td>
&lt;td>EleutherAI&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Active version (Jun 2026)&lt;/td>
&lt;td>0.4.x (&lt;code>main&lt;/code> branch)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Available tasks&lt;/td>
&lt;td>60+ benchmarks, hundreds of subtasks and variants&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Supported backends&lt;/td>
&lt;td>Hugging Face Transformers, vLLM, litellm, OpenAI-compatible API, GGUF&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Task format&lt;/td>
&lt;td>YAML + Python; each task defines dataset, template, metric and number of shots&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>What it measures&lt;/td>
&lt;td>MCQ accuracy, exact match, pass@k on code, perplexity, BLEU/ROUGE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>How it is invoked&lt;/td>
&lt;td>&lt;code>lm_eval --model hf --model_args pretrained=&amp;lt;model&amp;gt; --tasks mmlu,gsm8k --num_fewshot 5 --output_path results/&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Output&lt;/td>
&lt;td>JSON with per-task metrics + configuration metadata&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Use in the ecosystem&lt;/td>
&lt;td>Official engine of the OpenLLM Leaderboard (v1 and v2); de facto reference in research&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The harness is the industry standard for reproducible evaluation. Its strength is the breadth of tasks and the normalisation of the prompt format. Its limitation: the tasks are static, the datasets do not change between harness versions, which exposes it to the contamination problem if the model was trained on the same data.&lt;/p>
&lt;p>Full invocation with vLLM as the backend:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">lm_eval &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --model vllm &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --model_args &lt;span class="nv">pretrained&lt;/span>&lt;span class="o">=&lt;/span>meta-llama/Llama-3.1-70B-Instruct,dtype&lt;span class="o">=&lt;/span>bfloat16 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --tasks mmlu_pro,gpqa_diamond,ifeval,humaneval &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --num_fewshot &lt;span class="m">5&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --batch_size auto &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --output_path ./eval_results/ &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --log_samples
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="helm-stanford-crfm">HELM (Stanford CRFM)&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Field&lt;/th>
&lt;th>Data&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Repository&lt;/td>
&lt;td>github.com/stanford-crfm/helm&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Official site&lt;/td>
&lt;td>crfm.stanford.edu/helm&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Licence&lt;/td>
&lt;td>Apache 2.0&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Maintainer&lt;/td>
&lt;td>Stanford CRFM&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Status (Jun 2026)&lt;/td>
&lt;td>&lt;strong>Maintenance mode&lt;/strong> since 1 June 2026&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>What it measures&lt;/td>
&lt;td>Accuracy, calibration, robustness, fairness, toxicity, efficiency (latency/cost)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Task format&lt;/td>
&lt;td>Python + YAML; scenarios with multiple simultaneous metrics&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Supported backends&lt;/td>
&lt;td>API (OpenAI, Anthropic, Cohere, etc.) + local models via Hugging Face&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Active variants&lt;/td>
&lt;td>HELM Classic, HELM Capabilities, HELM Lite, MedHELM, HELM Safety&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Installation&lt;/td>
&lt;td>&lt;code>pip install crfm-helm&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>How it is invoked&lt;/td>
&lt;td>&lt;code>helm-run --conf-paths run_specs.conf --suite v1 --max-eval-instances 1000&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Output&lt;/td>
&lt;td>JSON + web UI with a reproducible leaderboard and per-prompt transparency&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>HELM is the tool that introduced &lt;strong>holistic&lt;/strong> evaluation (multiple metrics over the same scenario instead of a single number). In 2026, with HELM in maintenance mode, the ecosystem has fragmented into specialised leaderboards; but HELM remains the methodological reference for evaluations that need fairness and toxicity as well as accuracy.&lt;/p>
&lt;p>&lt;strong>HELM Capabilities&lt;/strong> (published March 2025): an active leaderboard with full prompt transparency, reproducible with the HELM framework. Available at crfm.stanford.edu/helm/capabilities.&lt;/p>
&lt;hr>
&lt;h3 id="livebench">LiveBench&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Field&lt;/th>
&lt;th>Data&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Repository&lt;/td>
&lt;td>github.com/LiveBench/LiveBench&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Official site&lt;/td>
&lt;td>livebench.ai&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Licence&lt;/td>
&lt;td>MIT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Maintainer&lt;/td>
&lt;td>NYU, UT Austin, UC Santa Barbara&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Paper&lt;/td>
&lt;td>arXiv:2406.19314&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Size&lt;/td>
&lt;td>1,000 active questions (monthly refresh, same total size)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Categories&lt;/td>
&lt;td>6: math, coding, reasoning, language, instruction following, data analysis&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tasks&lt;/td>
&lt;td>18 subtasks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>What it measures&lt;/td>
&lt;td>General capabilities over questions with objectively verifiable answers&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Anti-contamination&lt;/td>
&lt;td>Questions generated from recent sources: arXiv papers, news, IMDb, new datasets&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Scoring&lt;/td>
&lt;td>No LLM-as-judge: automatically verifiable answer (exact ground truth)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cadence&lt;/td>
&lt;td>New questions monthly; old ones retired to avoid accumulated memorisation&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>LiveBench is the architectural answer to the contamination problem: if the benchmark changes every month with questions based on sources published &lt;strong>after&lt;/strong> the cutoff of the model under evaluation, memorisation of the training set cannot inflate the result. The limitation is size (1,000 questions) and that it does not cover every domain with the same depth as MMLU.&lt;/p>
&lt;p>Execution:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/LiveBench/LiveBench
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> LiveBench
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install -e .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python livebench/gen_model_answer.py &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --model-path meta-llama/Llama-3.1-70B-Instruct &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --model-id llama3-70b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python livebench/gen_ground_truth_judgment.py &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --model-id llama3-70b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python livebench/show_livebench_result.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="lighteval-hugging-face">lighteval (Hugging Face)&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Field&lt;/th>
&lt;th>Data&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Repository&lt;/td>
&lt;td>github.com/huggingface/lighteval&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Licence&lt;/td>
&lt;td>MIT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Maintainer&lt;/td>
&lt;td>Hugging Face (Leaderboard &amp;amp; Evals Team)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Available tasks&lt;/td>
&lt;td>1,000+ tasks across multiple domains and languages&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Supported backends&lt;/td>
&lt;td>Hugging Face Accelerate (CPU/GPU/multi-GPU), Nanotron, vLLM, TGI, OpenAI-compatible endpoints&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>What it measures&lt;/td>
&lt;td>Accuracy, BERT score, BLEU, exact match; same as the harness + HF&amp;rsquo;s own tasks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Differentiator&lt;/td>
&lt;td>Designed to be the engine behind HF&amp;rsquo;s leaderboards; easy extension with custom tasks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Installation&lt;/td>
&lt;td>&lt;code>pip install lighteval&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>How it is invoked&lt;/td>
&lt;td>`lighteval accelerate &amp;ndash;model_args &amp;ldquo;pretrained=&lt;model>&amp;rdquo; &amp;ndash;tasks &amp;ldquo;lighteval&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Output&lt;/td>
&lt;td>JSON + direct integration with the Hugging Face Hub to publish results&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>lighteval is the internal successor to lm-evaluation-harness in the HF ecosystem: more modern, better vLLM and distributed backend support, and designed to run without leaving the HF stack. For teams already using the Hub to manage models, it is the lowest-friction option.&lt;/p>
&lt;hr>
&lt;h3 id="openllm-leaderboard-v2-archived-2025--openevals">OpenLLM Leaderboard v2 (archived 2025) → OpenEvals&lt;/h3>
&lt;p>The &lt;strong>OpenLLM Leaderboard v2&lt;/strong> evaluated more than 13,000 models between June 2024 and its closure in 2025, using six fixed benchmarks: &lt;strong>IFEval, MuSR, GPQA, MATH, BBH and MMLU-Pro&lt;/strong>, all run with lm-evaluation-harness. Its closure was a direct consequence of the saturation and gaming problem: as the benchmarks became well known, models were optimised for them.&lt;/p>
&lt;p>&lt;strong>Successor: OpenEvals&lt;/strong> (huggingface.co/spaces/OpenEvals/find-a-leaderboard)&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Aspect&lt;/th>
&lt;th>OpenLLM Leaderboard v2&lt;/th>
&lt;th>OpenEvals&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Architecture&lt;/td>
&lt;td>Single centralised leaderboard&lt;/td>
&lt;td>200+ specialised community leaderboards&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Benchmarks&lt;/td>
&lt;td>Fixed (6 tasks)&lt;/td>
&lt;td>Variable per leaderboard (math, code, medical, safety, etc.)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Models evaluated&lt;/td>
&lt;td>13,000+&lt;/td>
&lt;td>Distributed across leaderboards&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Eval engine&lt;/td>
&lt;td>lm-evaluation-harness&lt;/td>
&lt;td>lighteval (most) or harness&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Status&lt;/td>
&lt;td>Archived with an updated UI (Mar 2025)&lt;/td>
&lt;td>Active; you can create your own leaderboard&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Advantage&lt;/td>
&lt;td>Historical consistency&lt;/td>
&lt;td>Specialisation, speed of adaptation&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The archived collection is available for reference at &lt;code>huggingface.co/collections/OpenEvals/archived-open-llm-leaderboard-2024-2025&lt;/code>.&lt;/p>
&lt;hr>
&lt;h3 id="lmarena--chatbot-arena">LMArena / Chatbot Arena&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Field&lt;/th>
&lt;th>Data&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Site&lt;/td>
&lt;td>lmarena.ai (rebrand of LMSys Chatbot Arena, January 2026)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Methodology&lt;/td>
&lt;td>Anonymous pairwise comparisons: the user votes for the answer they prefer without knowing the model&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Statistical model&lt;/td>
&lt;td>&lt;strong>Bradley-Terry&lt;/strong> (maximum likelihood over the full history of match-ups; replaced classic Elo for greater stability)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Accumulated votes&lt;/td>
&lt;td>&amp;gt; 6 million (Jun 2026)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Variants&lt;/td>
&lt;td>Overall, Coding, Math, Hard Prompts, Multimodal — the specialised ones are more reliable&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Licence/access&lt;/td>
&lt;td>Free (web); vote data available under a CC licence&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Limitations&lt;/td>
&lt;td>Overall is biased by labs&amp;rsquo; selective disclosure and by format gaming; Bradley-Terry was recalibrated in mid-2025 (Style Control), shifting some models by ±20–40 Elo&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>LMArena is the only benchmark based on human preference at scale. Its advantage: it captures real perceived quality, not just MCQ accuracy. Its limitation: it is hard to reproduce on-premise (it requires a user interface and a panel of human voters). Useful as an external validation signal, not as a CI gate.&lt;/p>
&lt;hr>
&lt;h2 id="decision-table-which-tool-for-which-objective">Decision table: which tool for which objective&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Objective&lt;/th>
&lt;th>Tool&lt;/th>
&lt;th>Why&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Reproduce a standard academic eval&lt;/td>
&lt;td>lm-evaluation-harness&lt;/td>
&lt;td>De facto engine; maximum compatibility with papers&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Holistic evaluation (accuracy + fairness + toxicity)&lt;/td>
&lt;td>HELM (+ HELM Capabilities)&lt;/td>
&lt;td>The only framework measuring every axis at once&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Avoid contamination with recent questions&lt;/td>
&lt;td>LiveBench&lt;/td>
&lt;td>Monthly refresh; verifiable answers without a judge&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Native integration with HF Hub and vLLM&lt;/td>
&lt;td>lighteval&lt;/td>
&lt;td>vLLM backend, 1,000+ tasks, direct publishing to the Hub&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Compare against the public state of the art (OSS)&lt;/td>
&lt;td>OpenEvals + lighteval&lt;/td>
&lt;td>The successor ecosystem to the OpenLLM Leaderboard&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Human preference at scale&lt;/td>
&lt;td>LMArena&lt;/td>
&lt;td>Bradley-Terry over millions of real comparisons&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Custom domain eval (medical, legal, etc.)&lt;/td>
&lt;td>lighteval with a custom task&lt;/td>
&lt;td>Simpler to extend than the harness&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Fast CI gate (&amp;lt;10 min)&lt;/td>
&lt;td>lm-eval harness (subset)&lt;/td>
&lt;td>Individual tasks with &lt;code>--tasks ifeval,gpqa&lt;/code> + vLLM batching&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="the-data-contamination-trap">The data contamination trap&lt;/h2>
&lt;h3 id="what-it-is">What it is&lt;/h3>
&lt;p>A model that has seen a benchmark&amp;rsquo;s &lt;strong>test data&lt;/strong> during pretraining memorises answers instead of generalising. The benchmark measures memorisation, not capability. This is called &lt;strong>data contamination&lt;/strong> or &lt;strong>benchmark leakage&lt;/strong>.&lt;/p>
&lt;h3 id="scale-of-the-problem-20242025-data">Scale of the problem (2024–2025 data)&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Finding&lt;/th>
&lt;th>Source&lt;/th>
&lt;th>Figure&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>N-gram overlap in Llama-2-70B&lt;/td>
&lt;td>&amp;ldquo;Data Contamination or Genuine Generalization?&amp;rdquo; (2025)&lt;/td>
&lt;td>18.1% overlap with eval datasets&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Out-of-distribution drop in contaminated large models&lt;/td>
&lt;td>Ibid.&lt;/td>
&lt;td>−9.4 pp on reformulated prompts vs the original test&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>HumanEval saturated&lt;/td>
&lt;td>Artificial Analysis / multiple 2026 reports&lt;/td>
&lt;td>Top models &amp;gt; 93%; separation ≤ 2 pp between the best 5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPQA Diamond: appearance of indirect contamination&lt;/td>
&lt;td>mindstudio.ai analysis 2026&lt;/td>
&lt;td>&amp;ldquo;Data laundering&amp;rdquo;: questions leaked via online forums&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="detection-mechanisms">Detection mechanisms&lt;/h3>
&lt;p>&lt;strong>1. N-gram overlap (the classic method)&lt;/strong>&lt;/p>
&lt;p>Compute the fraction of test set n-grams that appear in the model&amp;rsquo;s pretraining corpus. A practical threshold: if more than 13% of the test&amp;rsquo;s 8-grams match the corpus, the result is compromised.&lt;/p>
\[
\text{contamination ratio} = \frac{|\{g \in \text{test n-grams}\} \cap \{g \in \text{train n-grams}\}|}{|\text{test n-grams}|}
\]
&lt;p>Limit: it only works if you have access to the pretraining corpus. For closed models, it is not applicable.&lt;/p>
&lt;p>&lt;strong>2. Canary strings (decoy insertion)&lt;/strong>&lt;/p>
&lt;p>Unique random strings are inserted into the evaluation dataset while it is being created. If the model reproduces those strings without having seen them in context, it confirms that it memorised them from pretraining. The technique was proposed in &amp;ldquo;Extracting Training Data from Large Language Models&amp;rdquo; (Carlini et al., 2021) and adopted in recent benchmarks.&lt;/p>
&lt;p>&lt;strong>3. Perturbation and reformulation&lt;/strong>&lt;/p>
&lt;p>Semantically equivalent variants of the test are generated (paraphrasing, changing proper nouns, translation and back-translation). A model that generalises keeps its accuracy; a model that memorises drops. The average drop detected in contaminated models is 9–15 pp on equivalent reformulated prompts.&lt;/p>
&lt;p>&lt;strong>4. LiveBench (monthly refresh)&lt;/strong>&lt;/p>
&lt;p>The architectural solution: if the questions are generated from sources published &lt;strong>after&lt;/strong> the model&amp;rsquo;s cutoff (recent arXiv papers, news, new data), memorisation from pretraining cannot help. The model has to reason over information it could never have seen.&lt;/p>
&lt;p>&lt;strong>5. Kernel divergence analysis&lt;/strong> (Savelka et al., 2025)&lt;/p>
&lt;p>A more sophisticated statistical method: it compares the embedding distribution of the test set with the distribution of the training corpus. An abnormally low divergence indicates that the benchmark and the training corpus come from the same source.&lt;/p>
&lt;h3 id="why-static-leaderboards-saturate">Why static leaderboards saturate&lt;/h3>
&lt;p>The cycle is predictable:&lt;/p>
&lt;ol>
&lt;li>A new benchmark is published (MMLU in 2021, GPQA in 2023).&lt;/li>
&lt;li>The labs use it as an &lt;em>evaluation target&lt;/em> during pretraining and fine-tuning.&lt;/li>
&lt;li>Performance on that benchmark rises quickly, faster than real capability.&lt;/li>
&lt;li>The benchmark stops discriminating at the high end.&lt;/li>
&lt;li>The community needs a new benchmark.&lt;/li>
&lt;/ol>
&lt;p>The average time between the publication of a benchmark and its saturation in frontier models has gone from ~36 months (MMLU: 2021–2024) to ~18 months (HumanEval: 2021–2023, GPQA: 2023–2025). Saturation is accelerating.&lt;/p>
&lt;hr>
&lt;h2 id="honest-methodology-reproducibility-and-sources-of-variance">Honest methodology: reproducibility and sources of variance&lt;/h2>
&lt;h3 id="the-four-parameters-that-must-be-pinned">The four parameters that must be pinned&lt;/h3>
&lt;p>An eval result is only reproducible if it declares:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Parameter&lt;/th>
&lt;th>Example&lt;/th>
&lt;th>Why it matters&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Harness version&lt;/td>
&lt;td>&lt;code>lm-evaluation-harness==0.4.3&lt;/code>&lt;/td>
&lt;td>Changes to the prompt template between versions invalidate the comparison&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Exact model version&lt;/td>
&lt;td>&lt;code>meta-llama/Llama-3.1-70B-Instruct@sha256:abc…&lt;/code>&lt;/td>
&lt;td>Silent model updates on the Hub change the results&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Prompt template&lt;/td>
&lt;td>&lt;code>mmlu_pro_cot_0shot&lt;/code> vs &lt;code>mmlu_pro_5shot&lt;/code>&lt;/td>
&lt;td>The difference between 0-shot and 5-shot can be 5–12 pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Number of shots&lt;/td>
&lt;td>&lt;code>--num_fewshot 5&lt;/code>&lt;/td>
&lt;td>The published standard varies by benchmark; 0-shot and 5-shot are not comparable&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="few-shot-vs-zero-shot-the-numerical-difference">Few-shot vs zero-shot: the numerical difference&lt;/h3>
&lt;p>Sensitivity to the number of shots varies dramatically by benchmark:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Benchmark&lt;/th>
&lt;th>Typical 0-shot&lt;/th>
&lt;th>Typical 5-shot&lt;/th>
&lt;th>Difference&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>MMLU-Pro&lt;/td>
&lt;td>62%&lt;/td>
&lt;td>72%&lt;/td>
&lt;td>+10 pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPQA Diamond&lt;/td>
&lt;td>55%&lt;/td>
&lt;td>65%&lt;/td>
&lt;td>+10 pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GSM8K&lt;/td>
&lt;td>70%&lt;/td>
&lt;td>82%&lt;/td>
&lt;td>+12 pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>IFEval&lt;/td>
&lt;td>68%&lt;/td>
&lt;td>71%&lt;/td>
&lt;td>+3 pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>HumanEval&lt;/td>
&lt;td>75%&lt;/td>
&lt;td>80%&lt;/td>
&lt;td>+5 pp&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Publishing an MMLU-Pro result in 0-shot without saying so, when the leaderboard standard is 5-shot, inflates the comparison by ~10 pp. Two results from the same model published with different numbers of shots &lt;strong>are not comparable&lt;/strong>.&lt;/p>
&lt;h3 id="prompt-sensitivity">Prompt sensitivity&lt;/h3>
&lt;p>Minor perturbations in the prompt template produce variations of up to ±8 pp on MCQ and ±15 pp on generation tasks. Sources of variance identified in the literature:&lt;/p>
&lt;ul>
&lt;li>Order of the options in MCQ (position bias): up to ±4 pp.&lt;/li>
&lt;li>Wording of the question (paraphrasing): ±3–8 pp.&lt;/li>
&lt;li>Presence or absence of &amp;ldquo;Let&amp;rsquo;s think step by step&amp;rdquo;: ±5–12 pp on reasoning.&lt;/li>
&lt;li>Language of the system instructions: ±3–10 pp in non-multilingual models.&lt;/li>
&lt;/ul>
&lt;h3 id="why-two-evals-give-different-results">Why two evals give different results&lt;/h3>
&lt;p>A common scenario: the paper reports MMLU-Pro 74.3% and the reproduction gives 71.8%. Frequent causes:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Cause&lt;/th>
&lt;th>Typical magnitude&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Different harness version (changed template)&lt;/td>
&lt;td>1–4 pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Model with a silent update&lt;/td>
&lt;td>0.5–3 pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Different context truncation&lt;/td>
&lt;td>0.5–2 pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sampling seed (for generation)&lt;/td>
&lt;td>0.3–1.5 pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tokenisation with a chat template vs without one&lt;/td>
&lt;td>2–8 pp&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The sum of these sources explains gaps of 2–6 pp that are not noise but systematic protocol differences.&lt;/p>
&lt;h3 id="the-confidence-interval-formula-for-comparing-two-models">The confidence interval formula for comparing two models&lt;/h3>
&lt;p>To know whether the difference between model A (accuracy \(p_A\)) and model B (\(p_B\)) over a test set of size \(n\) is statistically significant:&lt;/p>
\[
\Delta p \pm 1.96 \cdot \sqrt{\frac{p_A(1-p_A) + p_B(1-p_B)}{n}}
\]
&lt;p>Applied to GPQA Diamond (\(n = 198\)), if \(p_A = 0.90\) and \(p_B = 0.88\):&lt;/p>
\[
\Delta p = 0.02 \quad;\quad \text{margin} = 1.96 \cdot \sqrt{\frac{0.09 + 0.1056}{198}} \approx 1.96 \cdot 0.0314 \approx 0.062
\]
&lt;p>The 95% confidence interval is \([{-0.042},\ {+0.082}]\): it &lt;strong>crosses zero&lt;/strong>. A difference of 2 pp over 198 questions is not statistically distinguishable from noise. To distinguish 90% from 88% with 95% confidence you need:&lt;/p>
\[
n \approx \frac{(1.96)^2 \cdot (p_A(1-p_A) + p_B(1-p_B))}{(\Delta p)^2} \approx \frac{3.84 \cdot 0.1956}{0.0004} \approx 1{,}878 \text{ questions}
\]
&lt;p>GPQA Diamond is not that size. Differences of 1–2 pp on GPQA Diamond are statistically invisible.&lt;/p>
&lt;hr>
&lt;h2 id="the-quality-axis-in-the-scorecard">The quality axis in the scorecard&lt;/h2>
&lt;p>The platform&amp;rsquo;s scorecard has three columns: performance (goodput under SLO), quality (score on a reference benchmark), energy (J/token). The quality column is built like this:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Field&lt;/th>
&lt;th>Content&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Primary benchmark&lt;/td>
&lt;td>MMLU-Pro (5-shot) — separates in the 60–77% zone&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Reasoning benchmark&lt;/td>
&lt;td>GPQA Diamond (0-shot) — separates in the 55–90% zone&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Instruction benchmark&lt;/td>
&lt;td>IFEval (0-shot prompt-level accuracy)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Code benchmark&lt;/td>
&lt;td>MBPP (0-shot, pass@1) — less saturated than HumanEval&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Anti-contamination&lt;/td>
&lt;td>LiveBench global score (monthly)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tool&lt;/td>
&lt;td>lm-evaluation-harness (primary) + lighteval (secondary, HF Hub)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Pinned version&lt;/td>
&lt;td>&lt;code>harness==0.4.3&lt;/code>, &lt;code>lighteval==0.6.x&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Shots&lt;/td>
&lt;td>Declared explicitly per benchmark&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Reproducibility&lt;/td>
&lt;td>Output JSON stored with the model SHA&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Frequency&lt;/td>
&lt;td>Per model release; not in the CI of every PR (GPU-hour cost)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>A full eval over 4 benchmarks (MMLU-Pro 12,032 + GPQA 198 + IFEval 541 + MBPP 500) with a 70B model on a generic 2×H100 node takes on the order of &lt;strong>4–8 hours&lt;/strong> depending on batch size and number of shots. The GPU cost is real: at ~5 €/h per amortised card, a full eval costs on the order of &lt;strong>40–80 €&lt;/strong>. It is not run on every commit; it is run on every model release or major adapter release.&lt;/p>
&lt;hr>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/evals-llm-layer-after-tracing/">LLM evals: the layer after tracing&lt;/a> — the system&amp;rsquo;s internal evaluation layer (golden dataset, CI gate, calibrated judge). A necessary complement to the academic benchmarks in this article.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/llm-as-judge-exam-marker-not-oracle/">LLM-as-judge: fundamentals&lt;/a> — the LLM judge that evaluates outputs in production; different from an academic benchmark, but sharing the need for calibration and reproducibility.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/evaluating-rag-ragas-golden-dataset-metrics/">Evaluating a RAG: RAGAS and the golden dataset&lt;/a> — a specialisation of evaluation for RAG systems; faithfulness and context precision do not appear in the academic benchmarks in this article.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/llm-benchmark-tool-catalogue-practical-deep-dive/">Benchmark tool catalogue: data sheet by data sheet&lt;/a> — the performance axis (TTFT, ITL, goodput); the complementary half of this article in the scorecard.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/llm-inference-benchmarking-frameworks-metrics-state-of-the-art/">LLM benchmarking: state of the art&lt;/a> — the B1 introduction with the three axes and the track&amp;rsquo;s general methodology.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>EleutherAI · lm-evaluation-harness — &lt;a href="https://github.com/EleutherAI/lm-evaluation-harness">https://github.com/EleutherAI/lm-evaluation-harness&lt;/a>&lt;/li>
&lt;li>Stanford CRFM · HELM — &lt;a href="https://github.com/stanford-crfm/helm">https://github.com/stanford-crfm/helm&lt;/a>&lt;/li>
&lt;li>Stanford CRFM · HELM Capabilities (mar-2025) — &lt;a href="https://crfm.stanford.edu/2025/03/20/helm-capabilities.html">https://crfm.stanford.edu/2025/03/20/helm-capabilities.html&lt;/a>&lt;/li>
&lt;li>Stanford CRFM · HELM (sitio oficial) — &lt;a href="https://crfm.stanford.edu/helm/">https://crfm.stanford.edu/helm/&lt;/a>&lt;/li>
&lt;li>LiveBench · paper arXiv:2406.19314 — &lt;a href="https://arxiv.org/abs/2406.19314">https://arxiv.org/abs/2406.19314&lt;/a>&lt;/li>
&lt;li>LiveBench · sitio oficial — &lt;a href="https://livebench.ai">https://livebench.ai&lt;/a>&lt;/li>
&lt;li>LiveBench · GitHub — &lt;a href="https://github.com/LiveBench/LiveBench">https://github.com/LiveBench/LiveBench&lt;/a>&lt;/li>
&lt;li>NYU Center for Data Science · LiveBench blog — &lt;a href="https://nyudatascience.medium.com/livebench-challenging-language-models-with-contamination-free-questions-999b52967ec8">https://nyudatascience.medium.com/livebench-challenging-language-models-with-contamination-free-questions-999b52967ec8&lt;/a>&lt;/li>
&lt;li>Hugging Face · lighteval — &lt;a href="https://github.com/huggingface/lighteval">https://github.com/huggingface/lighteval&lt;/a>&lt;/li>
&lt;li>Hugging Face · OpenLLM Leaderboard (archivado) — &lt;a href="https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard">https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard&lt;/a>&lt;/li>
&lt;li>Hugging Face · OpenEvals (sucesor) — &lt;a href="https://huggingface.co/spaces/OpenEvals/find-a-leaderboard">https://huggingface.co/spaces/OpenEvals/find-a-leaderboard&lt;/a>&lt;/li>
&lt;li>Hugging Face · Archived Open LLM Leaderboard 2024-2025 — &lt;a href="https://huggingface.co/collections/OpenEvals/archived-open-llm-leaderboard-2024-2025">https://huggingface.co/collections/OpenEvals/archived-open-llm-leaderboard-2024-2025&lt;/a>&lt;/li>
&lt;li>LMArena · Chatbot Arena — &lt;a href="https://lmarena.ai">https://lmarena.ai&lt;/a>&lt;/li>
&lt;li>LMSys · blog Chatbot Arena Elo — &lt;a href="https://www.lmsys.org/blog/2023-05-03-arena/">https://www.lmsys.org/blog/2023-05-03-arena/&lt;/a>&lt;/li>
&lt;li>&amp;ldquo;A Statistical Framework for Ranking LLM-Based Chatbots&amp;rdquo; · arXiv:2412.18407 — &lt;a href="https://arxiv.org/pdf/2412.18407">https://arxiv.org/pdf/2412.18407&lt;/a>&lt;/li>
&lt;li>&amp;ldquo;Data Contamination or Genuine Generalization?&amp;rdquo; (2025) — &lt;a href="https://www.suaspress.org/ojs/index.php/AJNS/article/view/v2n2a03">https://www.suaspress.org/ojs/index.php/AJNS/article/view/v2n2a03&lt;/a>&lt;/li>
&lt;li>&amp;ldquo;When Benchmarks Leak: Inference-Time Decontamination&amp;rdquo; (2025) · arXiv:2601.19334 — &lt;a href="https://arxiv.org/pdf/2601.19334">https://arxiv.org/pdf/2601.19334&lt;/a>&lt;/li>
&lt;li>&amp;ldquo;How Contaminated Is Your Benchmark?&amp;rdquo; (2025) · arXiv:2502.00678 — &lt;a href="https://arxiv.org/pdf/2502.00678">https://arxiv.org/pdf/2502.00678&lt;/a>&lt;/li>
&lt;li>&amp;ldquo;When AI Benchmarks Plateau&amp;rdquo; (2026) · arXiv:2602.16763 — &lt;a href="https://arxiv.org/pdf/2602.16763">https://arxiv.org/pdf/2602.16763&lt;/a>&lt;/li>
&lt;li>GPQA · arXiv:2311.12022 — &lt;a href="https://arxiv.org/abs/2311.12022">https://arxiv.org/abs/2311.12022&lt;/a>&lt;/li>
&lt;li>GPQA Diamond leaderboard · Artificial Analysis — &lt;a href="https://artificialanalysis.ai/evaluations/gpqa-diamond">https://artificialanalysis.ai/evaluations/gpqa-diamond&lt;/a>&lt;/li>
&lt;li>&amp;ldquo;The Emperor&amp;rsquo;s New Clothes in Benchmarking?&amp;rdquo; (2025) · arXiv:2503.16402 — &lt;a href="https://arxiv.org/pdf/2503.16402">https://arxiv.org/pdf/2503.16402&lt;/a>&lt;/li>
&lt;li>lm-evaluation-harness · CLI reference — &lt;a href="https://lm-evaluation-harness.readthedocs.io/running_evals/cli_reference/">https://lm-evaluation-harness.readthedocs.io/running_evals/cli_reference/&lt;/a>&lt;/li>
&lt;li>crfm-helm · PyPI — &lt;a href="https://pypi.org/project/crfm-helm/">https://pypi.org/project/crfm-helm/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Evaluating a RAG system without fooling yourself: RAGAS, the golden dataset and the four metrics that matter</title><link>https://blog.lo0.es/en/posts/evaluating-rag-ragas-golden-dataset-metrics/</link><pubDate>Thu, 04 Jun 2026 09:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/evaluating-rag-ragas-golden-dataset-metrics/</guid><description>&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>A RAG pipeline fails in ways that user satisfaction cannot distinguish: the LLM can hallucinate even with good chunks, or retrieval can ignore key documents even though the LLM synthesises well what it receives. RAGAS breaks evaluation down into four orthogonal metrics, faithfulness, answer relevance, context precision and context recall, each pointing at a different sub-component. The golden dataset is the reference calibrator; without it the metrics have no anchor. The complete stack runs 100 % on-premise with vLLM as the judge and Langfuse for traceability.&lt;/p>
&lt;hr>
&lt;h2 id="the-master-analogy-the-quality-inspector-at-a-furniture-factory">The master analogy: the quality inspector at a furniture factory&lt;/h2>
&lt;p>Imagine you manufacture chairs. You could ask customers &amp;ldquo;is it comfortable?&amp;rdquo; and leave it there. But that question does not tell you what to fix when the answer is &amp;ldquo;no&amp;rdquo;. The quality inspector does not ask that: they measure the board with a Shore hardness test, check that each leg is exactly 45 cm, verify that the assembly manual includes the twelve screws from the BOM, and detect whether a low-density board slipped past the incoming filter.&lt;/p>
&lt;p>RAGAS is that inspector applied to RAG:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Faithfulness&lt;/strong> → does the board have the specified hardness? The LLM can only use the material (chunks) that retrieval hands it.&lt;/li>
&lt;li>&lt;strong>Context Precision&lt;/strong> → is the leg exactly the right length? Of the K retrieved chunks, how many are genuinely useful and how many are filler that confuses the assembler?&lt;/li>
&lt;li>&lt;strong>Context Recall&lt;/strong> → does the manual include all the screws? Of all the facts the correct answer should contain, how many appear in the retrieved chunks?&lt;/li>
&lt;li>&lt;strong>Noise Sensitivity&lt;/strong> → if the operator uses a medium-low density board, does it show in the final product? If you introduce irrelevant chunks, does the LLM start hallucinating?&lt;/li>
&lt;/ul>
&lt;p>Without measuring each dimension separately, the diagnosis is opaque: &amp;ldquo;the RAG is not working well&amp;rdquo; does not tell you whether to repair the embedder, the reranker, the prompt or the corpus.&lt;/p>
&lt;hr>
&lt;h2 id="the-problem-of-evaluating-rag">The problem of evaluating RAG&lt;/h2>
&lt;p>Classification has one uncomfortable virtue: if you predict 87 out of 100 labels correctly, accuracy = 0.87. There is no ambiguity. RAG does not enjoy that luxury.&lt;/p>
&lt;p>A RAG system can fail in at least three independent ways:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Retrieval correct, LLM hallucinates&lt;/strong>: the chunks contain the correct answer, but the LLM generates claims that are not in those chunks. Faithfulness low; context recall high.&lt;/li>
&lt;li>&lt;strong>LLM correct, retrieval fails&lt;/strong>: retrieval returns irrelevant chunks (low context precision) or incomplete ones (low context recall). If the LLM has enough parametric knowledge, it may look as though it is answering well, but in reality it is ignoring the context, which is a time bomb once the parametric knowledge goes stale.&lt;/li>
&lt;li>&lt;strong>Retrieval and LLM both correct, answer does not answer the question&lt;/strong>: the answer is faithful to the context and the chunks are relevant, but the question was a different one. Answer relevance low.&lt;/li>
&lt;/ol>
&lt;p>Each failure mode requires a different metric and a different corrective action. Using a single metric (BLEU, ROUGE, user satisfaction) mixes the signals and makes it impossible to prioritise improvement work.&lt;/p>
&lt;hr>
&lt;h2 id="the-four-ragas-metrics">The four RAGAS metrics&lt;/h2>
&lt;h3 id="1-faithfulness--fidelity-to-the-context">1. Faithfulness — fidelity to the context&lt;/h3>
&lt;p>&lt;strong>Question:&lt;/strong> how many claims in the generated answer are supported by the retrieved chunks?&lt;/p>
&lt;p>&lt;strong>Calculation:&lt;/strong>&lt;/p>
$$\text{Faithfulness} = \frac{|\text{claims supported by the context}|}{|\text{total claims in the answer}|}$$
&lt;p>The process uses an LLM-as-judge (see https://blog.lo0.es/en/posts/llm-as-judge-exam-marker-not-oracle/): first the atomic claims are extracted from the answer (&amp;ldquo;the model was released in 2023&amp;rdquo;, &amp;ldquo;it supports contexts of 128k tokens&amp;rdquo;, and so on), then the judge classifies each claim as &lt;em>supported&lt;/em> or &lt;em>not supported&lt;/em> by the chunks.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong> The generated answer has 5 claims. The judge determines that 4 are in the chunks and 1 is an unsupported extrapolation.&lt;/p>
$$\text{Faithfulness} = \frac{4}{5} = 0{.}80$$
&lt;p>&lt;strong>Warning sign:&lt;/strong> faithfulness &amp;lt; 0.85 indicates that the LLM is generating content that goes beyond the context, which is to say it is hallucinating with superficial backing.&lt;/p>
&lt;h3 id="2-answer-relevance--relevance-of-the-answer">2. Answer Relevance — relevance of the answer&lt;/h3>
&lt;p>&lt;strong>Question:&lt;/strong> does the answer actually answer the question as posed?&lt;/p>
&lt;p>&lt;strong>Intuition:&lt;/strong> An answer that answers the question well &amp;ldquo;implies&amp;rdquo; that question. If you generate N hypothetical questions from the answer and measure their semantic similarity to the original question, you get a relevance signal.&lt;/p>
&lt;p>&lt;strong>Calculation:&lt;/strong>&lt;/p>
$$\text{AnswerRelevance} = \frac{1}{N} \sum_{i=1}^{N} \cos(\vec{q}_{\text{original}}, \vec{q}_{i}^{\text{generated}})$$
&lt;p>where $\vec{q}$ are question embeddings.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong> For the question &amp;ldquo;Which Python versions does FastAPI support?&amp;rdquo; and an answer about web frameworks in general, the hypothetical questions generated will be along the lines of &amp;ldquo;what are the best web frameworks?&amp;rdquo;, with low cosine against the original question → low answer relevance.&lt;/p>
&lt;h3 id="3-context-precision--retrieval-precision">3. Context Precision — retrieval precision&lt;/h3>
&lt;p>&lt;strong>Question:&lt;/strong> of the K retrieved chunks, what proportion are genuinely relevant?&lt;/p>
&lt;p>&lt;strong>Calculation&lt;/strong> (weighted version):&lt;/p>
$$\text{ContextPrecision@K} = \frac{\sum_{k=1}^{K} \text{Precision@}k \cdot \mathbb{1}[\text{chunk}_k \text{ is relevant}]}{|\text{relevant chunks in top-K}|}$$
&lt;p>The most direct form: the LLM judge classifies each chunk as relevant or not for answering the question. Precision is the relevant fraction.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong> 5 chunks are retrieved. The judge considers 3 relevant and 2 noise.&lt;/p>
$$\text{ContextPrecision} = \frac{3}{5} = 0{.}60$$
&lt;p>&lt;strong>Warning sign:&lt;/strong> precision &amp;lt; 0.6 indicates that retrieval is contaminating the context with information that can contradict or dilute the correct answer.&lt;/p>
&lt;h3 id="4-context-recall--retrieval-recall">4. Context Recall — retrieval recall&lt;/h3>
&lt;p>&lt;strong>Question:&lt;/strong> of all the facts needed to build the correct answer (ground truth), what proportion are covered by the retrieved chunks?&lt;/p>
&lt;p>&lt;strong>Calculation:&lt;/strong>&lt;/p>
$$\text{ContextRecall} = \frac{|\text{ground-truth claims attributable to some chunk}|}{|\text{total claims in ground truth}|}$$
&lt;p>This metric &lt;strong>requires ground truth&lt;/strong>, which is to say you need the golden dataset.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong> The ground truth has 6 claims. The retrieved chunks cover 5 of them; the sixth is in a document that retrieval did not find.&lt;/p>
$$\text{ContextRecall} = \frac{5}{6} \approx 0{.}83$$
&lt;h3 id="5-noise-sensitivity--sensitivity-to-noise">5. Noise Sensitivity — sensitivity to noise&lt;/h3>
&lt;p>&lt;strong>Question:&lt;/strong> if you introduce irrelevant chunks into the context, how far does faithfulness fall?&lt;/p>
&lt;p>It is computed as the difference in faithfulness between the clean condition and the condition with controlled noise:&lt;/p>
$$\Delta\text{Faithfulness} = \text{Faithfulness}_{\text{clean}} - \text{Faithfulness}_{\text{noisy}}$$
&lt;p>A $\Delta > 0{.}15$ indicates that the prompt or the LLM are fragile against contaminated contexts, a problem that low context precision turns into a routine occurrence.&lt;/p>
&lt;hr>
&lt;h2 id="a-complete-worked-example">A complete worked example&lt;/h2>
&lt;h3 id="scenario">Scenario&lt;/h3>
&lt;p>&lt;strong>Question:&lt;/strong> &amp;ldquo;What latency guarantees does Redis offer for in-memory read operations with a 10 GB dataset?&amp;rdquo;&lt;/p>
&lt;p>&lt;strong>Answer generated by the RAG:&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;Redis guarantees sub-millisecond latencies for GET operations on datasets that fit in RAM. With a 10 GB dataset and clients on the same local network, P99 is typically below 1 ms. Redis also supports horizontal clustering to scale beyond the RAM of a single node. In addition, Redis has included native JSON support since version 7.2.&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>Ground truth (reference answer):&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;Redis operates entirely in memory, which guarantees sub-millisecond latencies for GET. On local networks with 10 GB datasets in RAM, P99 sits below 1 ms. Clustering allows scaling beyond the RAM of a single node.&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>Retrieved chunks (5 chunks, summarised fragments):&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>#&lt;/th>
&lt;th>Summarised content&lt;/th>
&lt;th>Relevant&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>C1&lt;/td>
&lt;td>&amp;ldquo;Redis operates in memory; GET has latencies &amp;lt; 1 ms on LAN&amp;rdquo;&lt;/td>
&lt;td>Yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>C2&lt;/td>
&lt;td>&amp;ldquo;Redis Cluster allows sharding to scale total RAM&amp;rdquo;&lt;/td>
&lt;td>Yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>C3&lt;/td>
&lt;td>&amp;ldquo;Redis Sentinel manages high availability through automatic failover&amp;rdquo;&lt;/td>
&lt;td>No&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>C4&lt;/td>
&lt;td>&amp;ldquo;Redis benchmarks: P50 = 0.3 ms, P99 = 0.9 ms on a 10 GB dataset&amp;rdquo;&lt;/td>
&lt;td>Yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>C5&lt;/td>
&lt;td>&amp;ldquo;Redis Stack adds modules: RedisJSON, RediSearch, RedisTimeSeries&amp;rdquo;&lt;/td>
&lt;td>No&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="step-by-step-calculation">Step-by-step calculation&lt;/h3>
&lt;p>&lt;strong>Faithfulness:&lt;/strong>&lt;/p>
&lt;p>Claims in the generated answer:&lt;/p>
&lt;ol>
&lt;li>&amp;ldquo;Redis guarantees sub-millisecond latencies for GET on in-RAM datasets&amp;rdquo; → &lt;strong>supported&lt;/strong> by C1, C4&lt;/li>
&lt;li>&amp;ldquo;With 10 GB on LAN, P99 &amp;lt; 1 ms&amp;rdquo; → &lt;strong>supported&lt;/strong> by C4&lt;/li>
&lt;li>&amp;ldquo;Redis supports horizontal clustering to scale RAM&amp;rdquo; → &lt;strong>supported&lt;/strong> by C2&lt;/li>
&lt;li>&amp;ldquo;Redis has included native JSON support since version 7.2&amp;rdquo; → &lt;strong>NOT supported&lt;/strong> by any chunk (C5 mentions RedisJSON as a Redis Stack module, not as native to Redis core)&lt;/li>
&lt;/ol>
$$\text{Faithfulness} = \frac{3}{4} = 0{.}75$$
&lt;p>Claim 4 is an extrapolation that mixes information from C5 imprecisely, a partial hallucination.&lt;/p>
&lt;p>&lt;strong>Context Precision:&lt;/strong>&lt;/p>
&lt;p>Relevant chunks: C1, C2, C4 (3 out of 5).&lt;/p>
$$\text{ContextPrecision} = \frac{3}{5} = 0{.}60$$
&lt;p>C3 and C5 are noise. C5 in particular contributed to the partial hallucination about JSON.&lt;/p>
&lt;p>&lt;strong>Context Recall:&lt;/strong>&lt;/p>
&lt;p>Ground-truth claims:&lt;/p>
&lt;ol>
&lt;li>&amp;ldquo;Redis operates in memory, GET &amp;lt; 1 ms&amp;rdquo; → attributable to C1 ✓&lt;/li>
&lt;li>&amp;ldquo;P99 &amp;lt; 1 ms on LAN with 10 GB&amp;rdquo; → attributable to C4 ✓&lt;/li>
&lt;li>&amp;ldquo;Clustering scales beyond the RAM of a single node&amp;rdquo; → attributable to C2 ✓&lt;/li>
&lt;/ol>
$$\text{ContextRecall} = \frac{3}{3} = 1{.}00$$
&lt;p>Retrieval found every chunk needed for the ground truth. The problem is not recall but precision (C3 and C5 contaminated the context).&lt;/p>
&lt;p>&lt;strong>Answer Relevance:&lt;/strong>&lt;/p>
&lt;p>The judge generates 3 hypothetical questions from the answer:&lt;/p>
&lt;ul>
&lt;li>&amp;ldquo;What latencies does Redis offer for in-memory reads?&amp;rdquo; — cos = 0.91&lt;/li>
&lt;li>&amp;ldquo;How does Redis scale horizontally?&amp;rdquo; — cos = 0.74&lt;/li>
&lt;li>&amp;ldquo;Which JSON modules does Redis include?&amp;rdquo; — cos = 0.52 (a drift caused by the hallucination)&lt;/li>
&lt;/ul>
$$\text{AnswerRelevance} = \frac{0{.}91 + 0{.}74 + 0{.}52}{3} = 0{.}72$$
&lt;p>The drift towards JSON reduced relevance. A tighter answer would have scored around 0.90.&lt;/p>
&lt;h3 id="summary-of-the-example">Summary of the example&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Metric&lt;/th>
&lt;th>Value&lt;/th>
&lt;th>Diagnosis&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Faithfulness&lt;/td>
&lt;td>0.75&lt;/td>
&lt;td>LLM extrapolated beyond the context&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Context Precision&lt;/td>
&lt;td>0.60&lt;/td>
&lt;td>Retrieval returned 2 irrelevant chunks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Context Recall&lt;/td>
&lt;td>1.00&lt;/td>
&lt;td>Retrieval captured everything needed&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Answer Relevance&lt;/td>
&lt;td>0.72&lt;/td>
&lt;td>Answer drifts off topic&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Main corrective action:&lt;/strong> improve the reranker so that C3 and C5 are filtered before they reach the LLM. The faithfulness and relevance problem is a direct consequence of the low precision, not of the LLM itself.&lt;/p>
&lt;hr>
&lt;h2 id="building-the-golden-dataset">Building the golden dataset&lt;/h2>
&lt;h3 id="what-it-is-and-why-it-matters">What it is and why it matters&lt;/h3>
&lt;p>The golden dataset is a set of &lt;code>(question, relevant chunks, correct answer)&lt;/code> tuples that acts as a reference calibrator. Without it, context recall cannot be computed (there is no ground truth) and the other metrics lack any interpretive anchor: is 0.75 faithfulness good or bad for this corpus and this domain?&lt;/p>
&lt;p>A well-built golden dataset lets you:&lt;/p>
&lt;ul>
&lt;li>Compare pipeline versions (embedder v1 vs v2, chunk size 512 vs 1024)&lt;/li>
&lt;li>Detect regressions in CI before deploying&lt;/li>
&lt;li>Stratify the analysis by question type&lt;/li>
&lt;/ul>
&lt;h3 id="llm-assisted-construction-pipeline">LLM-assisted construction pipeline&lt;/h3>
&lt;p>Purely manual construction is expensive. The standard pattern in 2026 is LLM assistance with human review of a sample:&lt;/p>
&lt;p>&lt;strong>Step 1 — Seed chunk selection.&lt;/strong> From the full corpus, select representative chunks through stratified sampling (by section, date, document type). For a technical corpus of 10,000 chunks, 500-1,000 seeds is a reasonable starting point.&lt;/p>
&lt;p>&lt;strong>Step 2 — Question generation.&lt;/strong> A powerful LLM (Llama-3.1-70B or similar) generates 2-3 questions per seed chunk using a prompt along these lines:&lt;/p>
&lt;pre tabindex="0">&lt;code>Given the following documentation fragment, generate specific questions
that can only be answered correctly using THIS fragment and not
general knowledge. The questions should be the ones an engineer looking
for operational information would ask.
Fragment: {chunk}
&lt;/code>&lt;/pre>&lt;p>&lt;strong>Step 3 — Reference answer generation.&lt;/strong> The same LLM, with access to the seed chunk (and to adjacent chunks if the question requires them), generates the reference answer.&lt;/p>
&lt;p>&lt;strong>Step 4 — Human review of a sample.&lt;/strong> Manually review 10-20 % of the generated dataset. The most common rejection criteria: trivial questions that any LLM answers without the corpus, answers the LLM padded with parametric knowledge instead of the chunks, and badly formulated or ambiguous questions.&lt;/p>
&lt;h3 id="minimum-size">Minimum size&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Use case&lt;/th>
&lt;th>Minimum pairs&lt;/th>
&lt;th>Notes&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Prototype / initial validation&lt;/td>
&lt;td>50-100&lt;/td>
&lt;td>Enough to spot coarse problems&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Technical corpus in production&lt;/td>
&lt;td>200-500&lt;/td>
&lt;td>Allows basic stratification&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Robust production with full stratification&lt;/td>
&lt;td>500-1,000+&lt;/td>
&lt;td>Needed to detect subtle regressions&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="stratifying-the-dataset">Stratifying the dataset&lt;/h3>
&lt;p>A flat golden dataset measures the average but hides the extreme cases. The minimum recommended stratification includes three question types:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Easy (single-hop):&lt;/strong> A single chunk contains all the information needed. The baseline that any decent RAG must beat.&lt;/li>
&lt;li>&lt;strong>Hard (multi-hop):&lt;/strong> The correct answer requires combining information from 2-4 different chunks. This is where the limits of the reranker and the synthesis prompt show up.&lt;/li>
&lt;li>&lt;strong>Adversarial:&lt;/strong> The question has a false premise, or the corpus does not contain the answer. A correct RAG must answer &amp;ldquo;I do not have enough information&amp;rdquo;, whereas a fragile RAG hallucinates confidently. This question type directly measures the risk of high-impact hallucination.&lt;/li>
&lt;/ul>
&lt;h3 id="the-goodhart-trap">The Goodhart trap&lt;/h3>
&lt;blockquote>
&lt;p>&amp;ldquo;When a measure becomes a target, it ceases to be a good measure.&amp;rdquo; — Charles Goodhart&lt;/p>
&lt;/blockquote>
&lt;p>If you optimise the embedder or the reranker using the golden dataset as a loss function, the dataset is corrupted as a metric: the system learns to perform well on those specific questions without improving on the general domain.&lt;/p>
&lt;p>The solution is the same as in supervised ML: separate the &lt;strong>dev set&lt;/strong> (for optimisation and iteration) from the &lt;strong>test set&lt;/strong> (for final evaluation, frozen and audited). The test set must never be used to make design decisions; only to report the state of the system at release time.&lt;/p>
&lt;hr>
&lt;h2 id="correlation-with-real-satisfaction">Correlation with real satisfaction&lt;/h2>
&lt;p>Field studies published by the Databricks teams (2024) and the RAGAS adoption analyses (2025) point to interpretable operational thresholds:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Metric range&lt;/th>
&lt;th>Observable symptom&lt;/th>
&lt;th>Corrective action&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Faithfulness &amp;lt; 0.75&lt;/td>
&lt;td>Users frequently report &amp;ldquo;made-up answers&amp;rdquo;&lt;/td>
&lt;td>Review the LLM prompt; increase citation instructions; lower the temperature&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Faithfulness 0.75-0.85&lt;/td>
&lt;td>Occasional hallucinations on peripheral topics&lt;/td>
&lt;td>Improve context precision to eliminate contaminating chunks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Faithfulness ≥ 0.85&lt;/td>
&lt;td>Correlates with positive NPS in field studies&lt;/td>
&lt;td>Hold steady; monitor for drift&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Context Precision &amp;lt; 0.60&lt;/td>
&lt;td>LLM includes contradictory information; inconsistent answers&lt;/td>
&lt;td>Tune the reranker; reduce K; review similarity thresholds&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Context Recall &amp;lt; 0.70&lt;/td>
&lt;td>Multi-hop questions fail; key information missing&lt;/td>
&lt;td>Review the chunking strategy; add larger chunks; enrich metadata&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Answer Relevance &amp;lt; 0.70&lt;/td>
&lt;td>Answers that are &amp;ldquo;correct but do not answer&amp;rdquo;&lt;/td>
&lt;td>Review the synthesis prompt; add an explicit instruction to stick to the question&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Low context precision is especially pernicious: irrelevant chunks are not neutral. They raise the probability that the LLM uses incorrect information as though it were relevant, degrading faithfulness in a chain reaction. It is the transmission belt by which a retrieval problem turns into an LLM problem.&lt;/p>
&lt;hr>
&lt;h2 id="diagram-the-continuous-evaluation-loop">Diagram: the continuous evaluation loop&lt;/h2>
&lt;figure>
&lt;svg viewBox="0 0 800 420" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="rag-eval-title rag-eval-desc" style="width:100%;max-width:800px;font-family:system-ui,sans-serif;">
&lt;title id="rag-eval-title">RAG evaluation loop with RAGAS&lt;/title>
&lt;desc id="rag-eval-desc">Circular diagram showing the flow from corpus to corrective action via retrieval, LLM, answer, RAGAS judge and metrics with alerts.&lt;/desc>
&lt;!-- Background -->
&lt;rect width="800" height="420" fill="#0f1117" rx="12"/>
&lt;!-- Node: Corpus -->
&lt;rect x="30" y="170" width="110" height="50" rx="8" fill="#1e2433" stroke="#4a5568" stroke-width="1.5"/>
&lt;text x="85" y="191" text-anchor="middle" fill="#e2e8f0" font-size="12" font-weight="600">Corpus&lt;/text>
&lt;text x="85" y="207" text-anchor="middle" fill="#718096" font-size="10">documents&lt;/text>
&lt;!-- Arrow Corpus → Retrieval -->
&lt;line x1="140" y1="195" x2="175" y2="195" stroke="#4a5568" stroke-width="1.5" marker-end="url(#arrow)"/>
&lt;!-- Node: Retrieval -->
&lt;rect x="175" y="170" width="120" height="50" rx="8" fill="#1e2433" stroke="#4a5568" stroke-width="1.5"/>
&lt;text x="235" y="191" text-anchor="middle" fill="#e2e8f0" font-size="12" font-weight="600">Retrieval&lt;/text>
&lt;text x="235" y="207" text-anchor="middle" fill="#718096" font-size="10">top-K chunks&lt;/text>
&lt;!-- Arrow Retrieval → LLM -->
&lt;line x1="295" y1="195" x2="330" y2="195" stroke="#4a5568" stroke-width="1.5" marker-end="url(#arrow)"/>
&lt;!-- Node: LLM -->
&lt;rect x="330" y="170" width="110" height="50" rx="8" fill="#1e2433" stroke="#4a5568" stroke-width="1.5"/>
&lt;text x="385" y="191" text-anchor="middle" fill="#e2e8f0" font-size="12" font-weight="600">LLM&lt;/text>
&lt;text x="385" y="207" text-anchor="middle" fill="#718096" font-size="10">synthesis&lt;/text>
&lt;!-- Arrow LLM → Answer -->
&lt;line x1="440" y1="195" x2="475" y2="195" stroke="#4a5568" stroke-width="1.5" marker-end="url(#arrow)"/>
&lt;!-- Node: Answer -->
&lt;rect x="475" y="170" width="110" height="50" rx="8" fill="#1e2433" stroke="#4a5568" stroke-width="1.5"/>
&lt;text x="530" y="191" text-anchor="middle" fill="#e2e8f0" font-size="12" font-weight="600">Answer&lt;/text>
&lt;text x="530" y="207" text-anchor="middle" fill="#718096" font-size="10">generated&lt;/text>
&lt;!-- Arrow Answer → RAGAS -->
&lt;line x1="585" y1="195" x2="620" y2="195" stroke="#4a5568" stroke-width="1.5" marker-end="url(#arrow)"/>
&lt;!-- Node: RAGAS Judge -->
&lt;rect x="620" y="170" width="130" height="50" rx="8" fill="#1a2640" stroke="#3b82f6" stroke-width="2"/>
&lt;text x="685" y="191" text-anchor="middle" fill="#93c5fd" font-size="12" font-weight="600">RAGAS Judge&lt;/text>
&lt;text x="685" y="207" text-anchor="middle" fill="#6b9fd4" font-size="10">LLM-as-judge&lt;/text>
&lt;!-- Arrow RAGAS → Metrics (downwards) -->
&lt;line x1="685" y1="220" x2="685" y2="285" stroke="#3b82f6" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
&lt;!-- Node: Metrics -->
&lt;rect x="615" y="285" width="140" height="60" rx="8" fill="#1a2a1a" stroke="#22c55e" stroke-width="1.5"/>
&lt;text x="685" y="305" text-anchor="middle" fill="#86efac" font-size="12" font-weight="600">Metrics&lt;/text>
&lt;text x="685" y="320" text-anchor="middle" fill="#4ade80" font-size="9">faithfulness · precision&lt;/text>
&lt;text x="685" y="333" text-anchor="middle" fill="#4ade80" font-size="9">recall · relevance&lt;/text>
&lt;!-- Arrow Metrics → Alert -->
&lt;line x1="615" y1="315" x2="510" y2="315" stroke="#22c55e" stroke-width="1.5" marker-end="url(#arrow-green)"/>
&lt;!-- Node: Grafana alert -->
&lt;rect x="390" y="285" width="120" height="60" rx="8" fill="#2a1a1a" stroke="#f97316" stroke-width="1.5"/>
&lt;text x="450" y="305" text-anchor="middle" fill="#fdba74" font-size="12" font-weight="600">Alert&lt;/text>
&lt;text x="450" y="320" text-anchor="middle" fill="#fb923c" font-size="9">Prometheus&lt;/text>
&lt;text x="450" y="333" text-anchor="middle" fill="#fb923c" font-size="9">Grafana&lt;/text>
&lt;!-- Arrow Alert → Action -->
&lt;line x1="390" y1="315" x2="285" y2="315" stroke="#f97316" stroke-width="1.5" marker-end="url(#arrow-orange)"/>
&lt;!-- Node: Action -->
&lt;rect x="155" y="285" width="130" height="60" rx="8" fill="#1a1a2a" stroke="#a855f7" stroke-width="1.5"/>
&lt;text x="220" y="305" text-anchor="middle" fill="#d8b4fe" font-size="12" font-weight="600">Corrective action&lt;/text>
&lt;text x="220" y="320" text-anchor="middle" fill="#c084fc" font-size="9">retrieval / chunking&lt;/text>
&lt;text x="220" y="333" text-anchor="middle" fill="#c084fc" font-size="9">prompt / fine-tuning&lt;/text>
&lt;!-- Arrow Action → Corpus (closing the loop) -->
&lt;line x1="155" y1="315" x2="85" y2="315" stroke="#a855f7" stroke-width="1.5"/>
&lt;line x1="85" y1="315" x2="85" y2="222" stroke="#a855f7" stroke-width="1.5" marker-end="url(#arrow-purple)"/>
&lt;!-- Golden Dataset (side input) -->
&lt;rect x="600" y="350" width="155" height="40" rx="6" fill="#1a2633" stroke="#64748b" stroke-width="1" stroke-dasharray="5,3"/>
&lt;text x="677" y="366" text-anchor="middle" fill="#94a3b8" font-size="10" font-weight="600">Golden Dataset&lt;/text>
&lt;text x="677" y="380" text-anchor="middle" fill="#64748b" font-size="9">ground truth for recall&lt;/text>
&lt;!-- Arrow Golden Dataset → Metrics -->
&lt;line x1="677" y1="350" x2="685" y2="347" stroke="#64748b" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-gray)"/>
&lt;!-- Langfuse label -->
&lt;rect x="460" y="130" width="90" height="28" rx="5" fill="#1a2633" stroke="#64748b" stroke-width="1" stroke-dasharray="4,2"/>
&lt;text x="505" y="148" text-anchor="middle" fill="#94a3b8" font-size="9">Langfuse tracing&lt;/text>
&lt;!-- Langfuse line above -->
&lt;line x1="505" y1="158" x2="505" y2="170" stroke="#64748b" stroke-width="1" stroke-dasharray="3,2"/>
&lt;!-- Marker definitions -->
&lt;defs>
&lt;marker id="arrow" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto">
&lt;path d="M0,0 L0,6 L8,3 z" fill="#4a5568"/>
&lt;/marker>
&lt;marker id="arrow-blue" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto">
&lt;path d="M0,0 L0,6 L8,3 z" fill="#3b82f6"/>
&lt;/marker>
&lt;marker id="arrow-green" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto">
&lt;path d="M0,0 L0,6 L8,3 z" fill="#22c55e"/>
&lt;/marker>
&lt;marker id="arrow-orange" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto">
&lt;path d="M0,0 L0,6 L8,3 z" fill="#f97316"/>
&lt;/marker>
&lt;marker id="arrow-purple" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto">
&lt;path d="M0,0 L0,6 L8,3 z" fill="#a855f7"/>
&lt;/marker>
&lt;marker id="arrow-gray" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto">
&lt;path d="M0,0 L0,6 L8,3 z" fill="#64748b"/>
&lt;/marker>
&lt;/defs>
&lt;/svg>
&lt;figcaption style="text-align:center;font-size:0.85em;color:#718096;margin-top:0.5em">The continuous evaluation loop: corpus → retrieval → LLM → RAGAS judge → metrics → alert → corrective action → corpus.&lt;/figcaption>
&lt;/figure>
&lt;hr>
&lt;h2 id="the-2026-oss-stack-for-running-ragas-on-premise">The 2026 OSS stack for running RAGAS on-premise&lt;/h2>
&lt;h3 id="ragas-apache-20">ragas (Apache 2.0)&lt;/h3>
&lt;p>The &lt;code>ragas&lt;/code> library supports asynchronous evaluation and multiple LLM backends. Integrating vLLM as the judge removes any need to send data to external APIs, which is critical in environments with sensitive data.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">ragas&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">evaluate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">ragas.metrics&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">faithfulness&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">answer_relevancy&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context_precision&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context_recall&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">OpenAIEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Judge LLM pointing at on-premise vLLM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">judge_llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;meta-llama/Llama-3.1-70B-Instruct&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://vllm-service:8000/v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;sk-local&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># vLLM ignores the value but requires the field&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAIEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;BAAI/bge-m3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://embedding-service:8001/v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;sk-local&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">evaluate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">golden_dataset&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># HuggingFace Dataset with standard columns&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metrics&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">faithfulness&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">answer_relevancy&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context_precision&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context_recall&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">judge_llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The dataset RAGAS expects has four columns: &lt;code>question&lt;/code>, &lt;code>answer&lt;/code>, &lt;code>contexts&lt;/code> (a list of strings), &lt;code>ground_truth&lt;/code>.&lt;/p>
&lt;h3 id="langfuse-for-eval-traceability">Langfuse for eval traceability&lt;/h3>
&lt;p>Each RAGAS evaluation is recorded in Langfuse as a &lt;em>dataset experiment&lt;/em>, linking the scores to the production spans (see https://blog.lo0.es/en/posts/llm-tracing-opentelemetry-genai/). This makes it possible to correlate a drop in faithfulness with the specific request that caused it. Without that link, the metrics are numbers with no actionable context.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langfuse&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Langfuse&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">lf&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Langfuse&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Create or retrieve the dataset in Langfuse&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">lf&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_or_create_dataset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;rag-golden-v3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Record the experiment scores&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">idx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">row&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">to_pandas&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">iterrows&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lf&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">score&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;ragas-faithfulness&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;faithfulness&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">trace_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">row&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;trace_id&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="c1"># linked to the production span&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="prometheus--grafana-for-operational-alerts">Prometheus + Grafana for operational alerts&lt;/h3>
&lt;p>RAGAS metrics are exposed as Prometheus gauges. A Grafana dashboard with thresholds configures alerts when faithfulness falls sustainedly below 0.80:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Prometheus alert rule&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>- &lt;span class="nt">alert&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">RAGFaithfulnessLow&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">expr&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">avg_over_time(rag_faithfulness_score[30m]) &amp;lt; 0.80&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">for&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">10m&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">severity&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">warning&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">annotations&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">summary&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;RAG faithfulness below threshold ({{ $value | humanize }})&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">description&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Review context precision and the reranker. Possible corpus drift.&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="running-ragas-against-on-premise-vllm--practical-considerations">Running RAGAS against on-premise vLLM — practical considerations&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Judge size:&lt;/strong> Llama-3.1-70B as the judge produces results comparable to GPT-4 on faithfulness and context evaluation, according to the RAGAS 0.2 benchmarks (2025). Smaller models (8B-13B) degrade judge quality on multi-hop questions.&lt;/li>
&lt;li>&lt;strong>Throughput:&lt;/strong> On on-premise hardware with 4×H100 SXM (320 GB, NVLink), a run of 200 evaluations with Llama-3.1-70B takes roughly 8-12 minutes with batch_size=8 and vLLM in continuous batching mode.&lt;/li>
&lt;li>&lt;strong>Cost per evaluation:&lt;/strong> With no external API, the marginal cost is electricity plus GPU amortisation. With 4×H100 at around 3 kW sustained, a run of 200 evaluations costs &amp;lt; 0.10 € in energy at a typical industrial tariff.&lt;/li>
&lt;li>&lt;strong>Recommended frequency:&lt;/strong> weekly offline eval over the full golden dataset plus sampled online eval (5-10 % of production requests) with a subset of metrics that do not require ground truth (faithfulness, answer relevance).&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="what-we-have-not-covered">What we have not covered&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Alternatives to RAGAS:&lt;/strong> TruLens (evaluation with modular feedbacks), DeepEval (programmatic assertions, pytest integration), ARES (Stanford framework with trained classifiers instead of LLM-as-judge), and the OpenAI evals framework. Each has different trade-offs in judge cost, reliability and ease of integration.&lt;/li>
&lt;li>&lt;strong>Continuous eval in production:&lt;/strong> automatically sampling real requests, anonymising them, running a subset of metrics without ground truth and using the result to detect system drift before users report it. It requires a data pipeline separate from the inference pipeline.&lt;/li>
&lt;li>&lt;strong>Multilingual eval:&lt;/strong> RAGAS with a judge in Spanish or Catalan over a non-English corpus has documented biases when the judge is a model trained fundamentally in English. The semantic similarity embeddings for answer relevance are especially sensitive to corpus language vs judge language.&lt;/li>
&lt;li>&lt;strong>A/B testing of RAG configurations:&lt;/strong> using RAGAS metrics as the success criterion in controlled experiments, chunk size 512 vs 1024, pure BM25 vs hybrid, cross-encoder reranker vs biencoder, with statistical significance computed over the golden dataset.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>https://blog.lo0.es/en/posts/llm-as-judge-exam-marker-not-oracle/ — the LLM judge pattern that RAGAS uses to measure faithfulness claim by claim&lt;/li>
&lt;li>https://blog.lo0.es/en/posts/evals-llm-layer-after-tracing/ — the general evals framework in which RAGAS is the RAG specialisation&lt;/li>
&lt;li>https://blog.lo0.es/en/posts/reranker-hybrid-retrieval-five-chunks-llm-reads/ — the retrieval layer whose context precision and recall these metrics measure&lt;/li>
&lt;li>https://blog.lo0.es/en/posts/rag-corpus-curation-active-librarian/ — the corpus quality that context recall reflects&lt;/li>
&lt;li>https://blog.lo0.es/en/posts/llm-tracing-opentelemetry-genai/ — the production spans where Langfuse records the RAGAS scores&lt;/li>
&lt;li>https://blog.lo0.es/en/posts/data-versioning-llmops-dvc-lakefs-reproducible-golden-dataset/ — the golden dataset is a data artefact that needs versioning just like the corpus&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="references">References&lt;/h2>
&lt;ol>
&lt;li>Es Shahul, et al. &lt;em>RAGAS: Automated Evaluation of Retrieval Augmented Generation&lt;/em>. arXiv:2309.15217 (2023). &lt;a href="https://arxiv.org/abs/2309.15217">https://arxiv.org/abs/2309.15217&lt;/a>&lt;/li>
&lt;li>RAGAS Documentation v0.2. &lt;em>Metrics Reference&lt;/em>. &lt;a href="https://docs.ragas.io/en/stable/concepts/metrics/">https://docs.ragas.io/en/stable/concepts/metrics/&lt;/a> (accessed June 2026)&lt;/li>
&lt;li>Langfuse. &lt;em>Dataset Experiments&lt;/em>. &lt;a href="https://langfuse.com/docs/datasets/overview">https://langfuse.com/docs/datasets/overview&lt;/a> (accessed June 2026)&lt;/li>
&lt;li>Databricks. &lt;em>LLM Quality Evaluation: From Lab to Production&lt;/em>. Databricks Engineering Blog (2024).&lt;/li>
&lt;li>Saad-Falcon, J. et al. &lt;em>ARES: An Automated Evaluation Framework for Retrieval-Augmented Generation Systems&lt;/em>. arXiv:2311.09476 (2023).&lt;/li>
&lt;li>Goodhart, C.A.E. &lt;em>Problems of Monetary Management: The U.K. Experience&lt;/em>. Papers in Monetary Economics. Reserve Bank of Australia (1975). Modern formulation of the law that bears his name.&lt;/li>
&lt;li>vLLM Project. &lt;em>OpenAI-Compatible Server&lt;/em>. &lt;a href="https://docs.vllm.ai/en/stable/serving/openai_compatible_server.html">https://docs.vllm.ai/en/stable/serving/openai_compatible_server.html&lt;/a> (accessed June 2026)&lt;/li>
&lt;/ol></description></item></channel></rss>