Retrieval-Augmented Generation (RAG) systems are often evaluated by measuring how well their retrieved context supports their generated answers, but the actual scoring mechanism is surprisingly flexible and can lead to wildly different conclusions if not understood.

Let’s see this in action. Imagine a RAG system that answers questions about a company’s internal documentation.

User Query: "What is the PTO policy for new hires?"

RAG System’s Process:

  1. Retrieval: The system searches its knowledge base (e.g., a vector database of HR documents) for documents relevant to "PTO policy" and "new hires."
  2. Context Augmentation: It might retrieve snippets like:
    • "New employees accrue 1.5 days of PTO per month, totaling 18 days per year." (Document A)
    • "PTO begins accruing on the first day of employment." (Document B)
    • "Company holidays are separate from PTO." (Document C)
  3. Generation: A language model uses the retrieved snippets to construct an answer.

The Evaluation Challenge: How do we score how good this retrieved context is for generating the answer? This is where MLflow RAG Evaluation shines. It allows us to define metrics that probe the relationship between the retrieved documents and the final answer.

MLflow RAG Evaluation Framework

MLflow’s RAG evaluation framework provides a structured way to assess RAG pipelines by defining specific metrics and running them against your data. It breaks down evaluation into several key components:

  • Data: Your test set of queries, ideally with ground truth answers.
  • Retriever: The component that fetches relevant documents.
  • Generator: The LLM that produces the final answer based on retrieved context.
  • Metrics: The specific measurements you want to perform.

Key Metrics for RAG Evaluation

MLflow supports a growing list of metrics, but let’s focus on some core ones for evaluating the retrieval aspect:

  1. Context Precision: Measures how many of the retrieved documents are actually relevant to the query.

    • Command: mlflow.evaluate(data=..., targets="answer", model_type="rag", registry_model_version="my_rag_model:1", features=["query"], targets=["answer"], extra_args={"retrieval_metric": "context_precision"})
    • Why it works: This metric directly asks if the retriever found useful information. A high score means the retriever isn’t wasting the LLM’s attention on irrelevant noise.
  2. Context Recall: Measures what proportion of the truly relevant documents in your knowledge base were retrieved.

    • Command: mlflow.evaluate(data=..., targets="answer", model_type="rag", registry_model_version="my_rag_model:1", features=["query"], targets=["answer"], extra_args={"retrieval_metric": "context_recall"})
    • Why it works: This is critical for completeness. A high score means the retriever found all the necessary pieces of information, even if some irrelevant ones also came back (which Context Precision would penalize).
  3. Faithfulness: Measures how well the generated answer is supported by the retrieved context. This is an LLM-based metric where another LLM judges if the answer "hallucinates" or contradicts the provided context.

    • Command: mlflow.evaluate(data=..., targets="answer", model_type="rag", registry_model_version="my_rag_model:1", features=["query"], targets=["answer"], extra_args={"generation_metric": "faithfulness"})
    • Why it works: Even if you retrieve perfectly relevant documents, the generator might still invent facts. Faithfulness directly penalizes this.
  4. Answer Relevance: Measures how relevant the generated answer is to the original query. This is also an LLM-based metric.

    • Command: mlflow.evaluate(data=..., targets="answer", model_type="rag", registry_model_version="my_rag_model:1", features=["query"], targets=["answer"], extra_args={"generation_metric": "answer_relevance"})
    • Why it works: This is the ultimate user-facing metric. Does the answer actually answer the question, regardless of how good the context was or how well it followed the context?

Putting it Together in MLflow

You typically use MLflow’s evaluate API. You’ll need to provide your RAG model (or components) and a dataset. The dataset should include your queries and, ideally, ground truth answers for metrics that require them.

import mlflow
from datasets import load_dataset

# Load your RAG model (e.g., a saved MLflow model)
# For simplicity, we'll assume a model is registered
rag_model_uri = "models:/my_rag_model/1" # Replace with your model URI

# Load your evaluation dataset
# This dataset should have 'query' and 'answer' columns
eval_dataset = load_dataset("json", data_files="eval_data.jsonl")

# MLflow Evaluate call
results = mlflow.evaluate(
    data=eval_dataset["train"],  # Use the 'train' split, or your chosen split
    model_uri=rag_model_uri,
    targets="answer",  # The column containing ground truth answers
    features=["query"], # The column containing user queries
    model_type="rag",
    # Specify the retrieval metrics you want to compute
    extra_args={
        "retrieval_metric": "context_precision,context_recall",
        # Specify generation metrics
        "generation_metric": "faithfulness,answer_relevance"
    }
)

# The results object contains the evaluation metrics.
# You can view them in the MLflow UI or programmatically:
print(results.metrics)

The Surprising Nuance: Metric Dependencies

Many RAG evaluation metrics are not independent. For instance, you can achieve perfect "Faithfulness" by retrieving no context and having the LLM state "I cannot answer this question based on the provided context." This would be a faithful answer, but utterly useless if the context did exist. Similarly, high "Context Precision" is meaningless if "Context Recall" is zero, meaning you missed all the vital documents. The power comes from looking at these metrics in aggregate and understanding their trade-offs.

The next challenge is often correlating these retrieval metrics with the quality of the generated answer for complex, multi-turn conversations.

Want structured learning?

Take the full Mlflow course →