Repository: explodinggradients/ragas
Issue: Issue in Context Recall Metrics
Root cause: Documentation Deficiency


According to Ragas docs, Context Recall is calculated based on ground truth and context. But in github repo , it is using question, context and answer (model response not the ground truth). 
<img width="440" alt="Screenshot 2024-08-05 125043" src="https://github.com/user-attachments/assets/d50b7485-f60c-41c1-98ca-57b9d0502fa6">
<img width="649" alt="Screenshot 2024-08-05 125138" src="https://github.com/user-attachments/assets/0c3dd5ad-e79f-4133-95e7-8d497369dee1">

Can anyone explain or whether I am interpreting incorrectly?

<!-- Answer -->
The discrepancy you're noticing might be due to the specific implementation details in the code. The `ContextRecallClassificationAnswers.parse_obj` function is used to parse a list of dictionaries into a `ContextRecallClassificationAnswers` object. This function takes a list of dictionaries as its input parameter, where each dictionary represents a classification answer with keys such as `statement`, `reason`, and `attributed`. The expected output is a `ContextRecallClassificationAnswers` object that can be converted to a list of dictionaries using the `dicts` method.

Here is an example of how it works:

```python
classification = ContextRecallClassificationAnswers.parse_obj(
    [
        {
            "statement": "Albert Einstein, born on 14 March 1879, was a German-born theoretical physicist, widely held to be one of the greatest and most influential scientists of all time.",
            "reason": "The date of birth of Einstein is mentioned clearly in the context.",
            "attributed": 1,
        },
        {
            "statement": "He received the 1921 Nobel Prize in Physics for his services to theoretical physics.",
            "reason": "The exact sentence is present in the given context.",
            "attributed": 1,
        },
        {
            "statement": "He published 4 papers in 1905.",
            "reason": "There is no mention about papers he wrote in the given context.",
            "attributed": 0,
        },
        {
            "statement": "Einstein moved to Switzerland in 1895.",
            "reason": "There is no supporting evidence for this in the given context.",
            "attributed": 0,
        },
    ]
).dicts()
```

The input parameters are a list of dictionaries, and the expected output is a list of dictionaries representing the parsed classification answers <sup>[[1]](https://github.com/explodinggradients/ragas/blob/main/src/ragas/metrics/_context_recall.py)</sup>.

Additionally, recent updates in the Ragas codebase might have affected the calculation or interpretation of the Context Recall metric. Specifically, the `ContextEntityRecall` class calculates recall based on entities present in the ground truth and context. The method `_compute_score` calculates the recall by finding the intersection of entities in the context and ground truth and dividing by the number of entities in the ground truth <sup>[[2]](https://github.com/explodinggradients/ragas/blob/main/src/ragas/metrics/_context_entities_recall.py)</sup>.

This might explain the differences you're seeing between the documentation and the code implementation.


<!-- Dosu Comment Footer -->
*To continue talking to [Dosu](https://dosu.dev), mention @dosu.*


@dosu Can you justify the mismatch between doc and codebase?

<!-- Answer -->
The mismatch between the documentation and the codebase regarding the calculation of the Context Recall metric can be justified by examining the provided code and the updated documentation. The documentation states that the Context Recall metric is estimated by calculating True Positives (TP) and False Negatives (FN) using the annotated answer and retrieved context. However, the code implementation shows that the metric is calculated based on the presence of entities in the context and ground truth.

Here is the relevant code snippet that performs this calculation:

```python
class ContextEntityRecall(MetricWithLLM):
    """
    Calculates recall based on entities present in ground truth and context.
    Let CN be the set of entities present in context,
    GN be the set of entities present in the ground truth.

    Then we define can the context entity recall as follows:
    Context Entity recall = | CN ∩ GN | / | GN |

    If this quantity is 1, we can say that the retrieval mechanism has
    retrieved context which covers all entities present in the ground truth,
    thus being a useful retrieval. Thus this can be used to evaluate retrieval
    mechanisms in specific use cases where entities matter, for example, a
    tourism help chatbot.
    """

    def _compute_score(
        self, ground_truth_entities: t.Sequence[str], context_entities: t.Sequence[str]
    ) -> float:
        num_entities_in_both = len(
            set(context_entities).intersection(set(ground_truth_entities))
        )
        return num_entities_in_both / (len(ground_truth_entities) + 1e-8)
```

In this code:
- The recall is calculated based on the intersection of entities present in both the context and the ground truth.
- The score is calculated as the ratio of the number of common entities to the total number of entities in the ground truth.

This approach aligns with the description in the updated documentation but may differ from the traditional definition of context recall involving TP and FN. Therefore, the mismatch arises from the difference in the conceptual understanding of how context recall is calculated versus its actual implementation in the code <sup>[[1]](https://github.com/explodinggradients/ragas/blob/main/src/ragas/metrics/_context_recall.py)</sup><sup>[[2]](https://github.com/explodinggradients/ragas/blob/main/src/ragas/metrics/_context_entities_recall.py)</sup>.


<!-- Dosu Comment Footer -->
*To continue talking to [Dosu](https://dosu.dev), mention @dosu.*


@shahules786 can you take a look at this?

Hi @Anjalisoni99-byte I have raised a fix for this https://github.com/explodinggradients/ragas/pull/1173
To expand it little more on why question is needed, there could be examples in your evaluation dataset like
```
'question': Wha is the capital of France?
"ground_truth":"Paris"
"Context":"France is in western.......nd Paris it's capital"
```
Here by also providing a question along with ground_truth, the LLM gets a better idea of the answer itself and it can rephrase the ground_truth to "Pairs is the capital of France" and do the inference against the provided context. Checkout a of few shot examples [here](https://github.com/explodinggradients/ragas/blob/d2486f117fd827dcfc3e196d4cf7798573c55b09/src/ragas/metrics/_context_recall.py#L75)


Hi @jjmachan and @shahules786, Correct me if I am wrong,


The way I understood context recall is it only takes context and answer and doesn't require the question. 

Have a look at the instruction prompt used in the codebase of context recall,
  
```python
instruction="""Given a context, and an answer, analyze each sentence in the answer and classify if the sentence can be attributed to the given context or not. Use only "Yes" (1) or "No" (0) as a binary classification. Output json with reason."""
```

Checkout the few shot examples pointed in this comment [here](https://github.com/explodinggradients/ragas/issues/1168#issuecomment-2270353914)
Not only the instruction but also few shot examples influence results of LLMs.