Before you start
You need Python 3, a pipeline that exposes its retrieved chunks and its answer, such as the one from lesson 1, and an API key for the judge model. Install Ragas with pip install ragas. The examples on the Ragas pages call OpenAI, and the testset guide shows how to wrap AWS, Google Cloud, Azure or any other LangChain provider with LangchainLLMWrapper.
One version note. The Ragas pages this lesson follows are dated 9 December 2025. They still say the older ragas.metrics classes, such as LLMContextRecall, form a legacy API that "will be deprecated in version 0.4" and removed in 1.0. PyPI shows that 0.4.0 had already shipped on 3 December 2025, and on 5 November 2026 the newest release was 0.4.3, from 13 January 2026. The metric code below uses the ragas.metrics.collections API the docs recommend for new projects. Pin the version you test with, for example pip install ragas==0.4.3, so an upgrade cannot shift your scores between two runs.
Test retrieval and the answer separately
A wrong answer has two possible causes. The retriever may not have found the passage, or the model may have ignored or distorted it. Ragas splits its metrics along that line, so a score tells you which half to fix. Context recall and context precision grade the retrieved chunks. Faithfulness and answer relevancy grade the response. Noise sensitivity looks at both.
| Metric | Question it answers | Fields it needs | Better |
|---|---|---|---|
| Context recall | Do the retrieved chunks support every claim in the reference answer? | user_input, retrieved_contexts, reference | Higher |
| Context precision | Are relevant chunks ranked above irrelevant ones? | user_input, retrieved_contexts, reference | Higher |
| Faithfulness | Does the retrieved context support every claim in the response? | user_input, retrieved_contexts, response | Higher |
| Answer relevancy | Does the response address the question? | user_input, response | Higher |
| Noise sensitivity | How often does the response make a claim the reference does not support? | user_input, retrieved_contexts, response, reference | Lower |
Build a small evaluation set
Ragas wants four fields for each question. user_input is the question, retrieved_contexts the chunks your retriever returned, response the answer your model gave and reference a golden answer. Its getting-started guide collects them by running each query through the pipeline and loading the rows into an EvaluationDataset. It calls the reference optional, but three of the metrics above take one.
Anthropic's guidance on building evals carries over. Mirror the real mix of your task. Include edge cases such as irrelevant or nonexistent input, overly long input and ambiguous cases where even humans would struggle to agree. Prefer more questions with automated grading over fewer that people grade by hand.
from ragas import EvaluationDataset
# sample_queries and expected_responses: your own questions and reference answers
dataset = []
for question, reference in zip(sample_queries, expected_responses):
chunks = retrieve(question) # your retrieval step: a list of chunk texts
response = answer(question, chunks) # your generation step
dataset.append(
{
"user_input": question,
"retrieved_contexts": chunks,
"response": response,
"reference": reference,
}
)
evaluation_dataset = EvaluationDataset.from_list(dataset)- Write down the questions your users ask, including a few your documents cannot answer.
- Write a reference answer for each, one or two sentences taken from your documents.
- Run each question through your pipeline and keep the chunks it retrieved and the response it gave.
- Load the rows into an
EvaluationDataset, or keep them as a list of dicts. - Freeze the set. Add a row whenever you meet a new failure, and never drop a row to make a score look better.
Score retrieval: recall and precision
Context recall asks whether the retriever missed anything. Ragas breaks the reference into claims and checks each one against the retrieved context, so the score is the claims the context supports divided by the claims in the reference. Because it compares against something, it always needs a reference.
Context precision asks whether the useful chunks come first. It is the mean of precision@k over the retrieved chunks, where precision@k counts the true positives among the first k chunks. Order matters, and the Ragas page shows how much. With one relevant chunk and one irrelevant chunk, its Context Utilization score halves when the irrelevant chunk comes first.
1.0
Context Utilization with the relevant chunk ranked first, rounded from 0.9999999999
Source: Ragas: Context Precision (read November 4, 2026)
0.5
Context Utilization with an irrelevant chunk ranked first, rounded from 0.49999999995
Source: Ragas: Context Precision (read November 4, 2026)
from openai import AsyncOpenAI
from ragas.llms import llm_factory
from ragas.metrics.collections import ContextPrecision, ContextRecall
client = AsyncOpenAI() # reads OPENAI_API_KEY
llm = llm_factory("gpt-4o-mini", client=client) # the judge model
recall = ContextRecall(llm=llm)
precision = ContextPrecision(llm=llm)
recalls, precisions = [], []
for row in dataset:
recalls.append(
recall.score(
user_input=row["user_input"],
retrieved_contexts=row["retrieved_contexts"],
reference=row["reference"],
).value
)
precisions.append(
precision.score(
user_input=row["user_input"],
reference=row["reference"],
retrieved_contexts=row["retrieved_contexts"],
).value
)
print("context recall", sum(recalls) / len(recalls))
print("context precision", sum(precisions) / len(precisions))- No reference answer:
ContextUtilizationcompares each chunk with the generated response instead. - No LLM calls:
NonLLMContextRecallandNonLLMContextPrecisionWithReferencecompare the retrieved chunks withreference_contextsthrough a string similarity measure such as Levenshtein distance. The precision one needs therapidfuzzpackage. - By ID:
IDBasedContextRecallandIDBasedContextPrecisioncompareretrieved_context_idswithreference_context_ids. In the docs, 1 of 4 reference IDs retrieved gives a recall of 0.25, and 2 of 4 retrieved IDs found in the reference gives a precision of 0.5.
Score the answer: faithfulness and relevancy
Faithfulness splits the response into statements and checks each against the retrieved context. The Ragas example gives a context saying Einstein was born on 14 March 1879. The answer "Einstein was born in Germany on 20th March 1879" splits into two statements. The first is supported and the second is not, so the score is 1 of 2, one half.
Answer relevancy works from the question and the response alone. A model writes artificial questions from the response, three by default, and the score is the mean cosine similarity between their embeddings and the embedding of the real question. By design it ignores factual accuracy and penalizes answers that are incomplete or carry unnecessary detail. The score usually falls between 0 and 1, but cosine similarity can go as low as -1, so Ragas does not guarantee the range.
from ragas.embeddings.base import embedding_factory
from ragas.metrics.collections import AnswerRelevancy, Faithfulness
embeddings = embedding_factory("openai", model="text-embedding-3-small", client=client)
faithfulness = Faithfulness(llm=llm)
relevancy = AnswerRelevancy(llm=llm, embeddings=embeddings)
faithful, relevant = [], []
for row in dataset:
faithful.append(
faithfulness.score(
user_input=row["user_input"],
response=row["response"],
retrieved_contexts=row["retrieved_contexts"],
).value
)
relevant.append(
relevancy.score(user_input=row["user_input"], response=row["response"]).value
)
print("faithfulness", sum(faithful) / len(faithful))
print("answer relevancy", sum(relevant) / len(relevant))NoiseSensitivityis the share of claims in the response that the reference does not support, so lower is better. Ragas computes it over the relevant chunks by default and over the irrelevant ones withmode="irrelevant".- Ragas's example gives a score of 0.333, one incorrect claim out of three, for a response that adds a claim the reference never makes.
- Faithfulness has a variant,
FaithfulnesswithHHEM, that replaces the claim check with Vectara's HHEM-2.1-Open classifier, which Ragas describes as small, free and open source.
Read the scores and pick a lever
A low score points at one half of the pipeline. Low context recall means the passage never reached the model. Low context precision with good recall means it was found but ranked below noise. Low faithfulness with good retrieval points at generation, and LlamaIndex lists prompt changes among the first things to try for hallucinations or badly formatted output.
| Low score | What it means | Levers to try |
|---|---|---|
| Context recall | The retrieved chunks miss what the reference says | Chunk size and overlap, contextual retrieval, hybrid search, another embedding model |
| Context precision | Relevant chunks sit below irrelevant ones | Rerank the candidates, or pass fewer chunks |
| Faithfulness | The response claims more than the chunks support | Prompt changes, citations |
| Answer relevancy | The response is incomplete or carries unneeded detail | Prompt changes |
When the judge needs checking
Most of the metrics above use a model as the judge. Anthropic ranks grading methods by speed, reliability and scale and says to choose the fastest, most reliable and most scalable one that works. Code-based grading is the fastest and most reliable but lacks nuance. Human grading is the most flexible and slow and expensive. LLM-based grading is fast and flexible, and Anthropic says to test it for reliability first, then scale.
- Write a detailed, clear rubric, for example that the answer must mention a named product in its first sentence, or it counts as incorrect.
- Make the output specific. Tell the judge to answer only "correct" or "incorrect", or to score from 1 to 5.
- Let the judge reason first. Anthropic says a grader with thinking turned on does better, particularly on complex judgments.
| Code-based Our pick | Human | LLM-based | |
|---|---|---|---|
| Fast and scalable | Yes | No | Yes |
| Handles nuanced judgments | No | Yes | Yes |
| Reliable as it comes | Yes | Yes | Partly |
Our pick: Code-based. Use it wherever a string match or an ID check can decide, as in the ID-based retrieval metrics, and keep the LLM judge for what code cannot settle.
Source: Claude Docs: Define success criteria and build evaluations (read November 4, 2026)
Generate more test questions
Writing questions by hand does not scale, so Ragas can generate them from your documents. Its guide describes two steps. It builds a knowledge graph from your documents and enriches it with extractors such as entities and keyphrases, then it turns the graph into scenarios that become questions with reference answers. Export the result with to_pandas() and keep the questions you judge fit. We'd read each generated question and its reference before trusting it, since a model wrote both.
The concepts page sorts RAG queries into four kinds. The default distribution is half single-hop specific questions and a quarter each of multi-hop abstract and multi-hop specific.
import openai
from langchain_community.document_loaders import DirectoryLoader
from langchain_openai import ChatOpenAI
from ragas.embeddings import OpenAIEmbeddings
from ragas.llms import LangchainLLMWrapper
from ragas.testset import TestsetGenerator
docs = DirectoryLoader("docs/", glob="**/*.md").load()
generator_llm = LangchainLLMWrapper(ChatOpenAI(model="gpt-4o"))
generator_embeddings = OpenAIEmbeddings(client=openai.OpenAI())
generator = TestsetGenerator(llm=generator_llm, embedding_model=generator_embeddings)
dataset = generator.generate_with_langchain_docs(docs, testset_size=10)
dataset.to_pandas()- Single-hop specific: one source answers a fact, such as the year Einstein published the theory of relativity.
- Single-hop abstract: one source, but the answer is interpretive, such as how the theory changed our view of time and space.
- Multi-hop specific: two or more sources, such as which scientist influenced Einstein's work and what theory they proposed.
- Multi-hop abstract: several sources combined into a broad answer, such as how theories of relativity have evolved since the original publication.
Shreya Shankar on checking the judge, Hamel Husain on generic metrics
Shreya Shankar, an incoming assistant professor of computer science at Carnegie Mellon by her X bio, called evals "arguably the hardest part of LLMOps" on X on 22 April 2024. Models make mistakes, so we check them with other models, and she asked who validates the validators. Her team's paper names an effect it calls criteria drift: people need criteria to grade outputs, but grading outputs helps them define the criteria.
So grade a sample of your rows by hand before you trust faithfulness, or a judge of your own, on the rest. Each row where you and the judge disagree points to a criterion the rubric lacks. Expect the rubric to change as you read, as it did for the people in the paper's study.
Hamel Husain, who works on evals at OpenAI by his X bio, warned on X on 4 June 2024 against generic evals such as conciseness, toxicity and factual correctness run on data nobody reads. In July 2026 he wrote on X about tests he and a co-author ran on real production data, of tools that scan traces with AI. The tools often caught issues people missed but missed problems that need domain expertise and taste, so he advises using them with you in the loop. Before you change a setting, read the rows behind your lowest faithfulness and context recall.
Many people run these on their data without looking at it. It’s a comfort blanket that has almost nill value if run blindly.
Screenshots of X's own embeds of the posts, taken on 5 November 2026, with the time in UTC. X's embed leaves out the paper link at the end of Shreya Shankar's post. Select one to open the post on X.
Common mistakes
- Scoring only the final answer. Without the retrieved chunks you cannot tell a retrieval miss from a generation error.
- Asking for context recall without a reference. It always needs one.
- Treating answer relevancy as accuracy. It ignores factual accuracy by design, so read it beside faithfulness.
- Reading one run on a handful of questions as a trend, when a single flipped answer moves every mean.
- Switching the judge model between two runs you want to compare. Keep the judge fixed so only your pipeline changes.
- Copying an old tutorial that imports classes from
ragas.metricsand callssingle_turn_ascore. The docs call that the legacy API.
Try it yourself
- Write ten questions for a set of documents you know, two of them unanswerable, and add reference answers.
- For three questions, mark by hand which chunk IDs hold the answer and compute ID-based recall as found IDs over reference IDs.
- Rerun the set after you change the chunk size from lesson 3. Compare the means, then the per-question rows.
- Move an irrelevant chunk to the front of
retrieved_contextsfor one row and watch context precision fall.
Next
This is the last lesson of the RAG course. The next course, advanced prompt engineering, starts the AI engineering path: its first lesson teaches a model with examples, which is also how you steer the answers your RAG pipeline writes. To compare other people's work with your own questions, browse the agents on Mindket.
Check yourself
Your pipeline gives a wrong answer, and context recall for that question is 0.0. Where do you look first?
Show answer
B. Retrieval, because the right passage never reached the model Context recall is the share of the reference answer's claims that the retrieved context supports, so zero means the chunks hold none of them.
Which Ragas metric counts the claims in the response that the retrieved context supports?
Show answer
D. Faithfulness Faithfulness divides the claims in the response that the retrieved context supports by all the claims in the response.
The relevant chunk moves from first place to second behind an irrelevant one. Which score falls?
Show answer
A. Context precision Context precision rewards relevant chunks ranked above irrelevant ones. Recall only asks whether the information was retrieved at all.
What is Anthropic's advice on LLM-based grading?
Show answer
C. Test the grader for reliability first, then scale it Anthropic calls LLM grading fast, flexible and scalable, and says to test it for reliability first. It also asks for detailed rubrics.
Frequently asked questions
Which metrics should I use to evaluate RAG?
Start with the four in the table above, and add noise sensitivity when wrong claims keep slipping in. Pair every retrieval metric with an answer metric, so you can see which half of the pipeline failed.
How big should a RAG evaluation set be?
No source sets a minimum. Ragas's guides use five hand-written questions and a generated set of ten, and Anthropic's advice is volume over quality. Start small and add every failure you meet as a new row.
How do I evaluate RAG without reference answers?
Score what you can today and write references as you go. Two of the four main scores never read one, and ContextUtilization swaps it for the generated response. A usable reference is short: a sentence or two copied from the document that holds the answer.
Can I trust an LLM as the judge?
Check it first. Anthropic says to test LLM-based grading for reliability before you scale it, and to give the judge a clear rubric, a constrained output and room to reason. Where a string match or an ID check can decide, use that instead.
What is the difference between faithfulness and answer relevancy?
Faithfulness asks whether the response sticks to the retrieved context. Answer relevancy asks whether it answers the question, and it ignores accuracy. A response that restates a chunk perfectly but answers a different question scores high on the first and low on the second.
What is a good score?
The Ragas pages give no pass mark. Scores usually run from 0 to 1, and for noise sensitivity lower is better, so use them to compare two versions of your pipeline on the same questions.
Can I generate test questions automatically?
Yes. Ragas's TestsetGenerator builds questions and reference answers from your documents, and its default mix leans on single-hop specific questions. Review each one first.
Sources
- Ragas Docs: Installation https://docs.ragas.io/en/stable/getstarted/install/
- Ragas Docs: Evaluate a simple RAG system https://docs.ragas.io/en/stable/getstarted/rag_eval/
- Ragas Docs: Context Recall https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_recall/
- Ragas Docs: Context Precision https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_precision/
- Ragas Docs: Faithfulness https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/faithfulness/
- Ragas Docs: Response Relevancy https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/answer_relevance/
- Ragas Docs: Noise Sensitivity https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/noise_sensitivity/
- Ragas Docs: Testset Generation for RAG (guide) https://docs.ragas.io/en/stable/getstarted/rag_testset_generation/
- Ragas Docs: Testset Generation for RAG (concepts) https://docs.ragas.io/en/stable/concepts/test_data_generation/rag/
- Claude Docs: Define success criteria and build evaluations https://platform.claude.com/docs/en/test-and-evaluate/develop-tests
- LlamaIndex Docs: Basic strategies https://developers.llamaindex.ai/python/framework/optimizing/basic_strategies/basic_strategies/
- PyPI: ragas release history https://pypi.org/project/ragas/
- Shreya Shankar on X: Who validates the validators? https://x.com/sh_reya/status/1782425960366940333
- arXiv: Who Validates the Validators? Aligning LLM-Assisted Evaluation of LLM Outputs with Human Preferences https://arxiv.org/abs/2404.12272
- Hamel Husain on X: Generic evals https://x.com/HamelHusain/status/1798024532995052010
- Hamel Husain on X: Do Automated Evals Work? (14 July 2026) https://x.com/HamelHusain/status/2077042379392213377


