File size: 5,434 Bytes
ee56f8e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
"""
Tool for validating that responses are properly grounded in context.
"""
import re
from typing import Dict, List, Any
from ..models.agent_models import GroundedResponse, RetrievedContext


class GroundingValidatorTool:
    """
    Tool for validating that responses are properly grounded in provided context.
    """

    def __call__(self, response: str, context: str, sources: List[str]) -> Dict[str, Any]:
        """
        Validate that the response is properly grounded in the provided context.

        Args:
            response: The response to validate
            context: The context that was provided to generate the response
            sources: List of sources used in the response

        Returns:
            Dictionary with validation results
        """
        # Check if response contains information from the context
        context_present = self._check_context_presence(response, context)

        # Check for hallucinations (information not in context)
        hallucinations = self._detect_hallucinations(response, context)

        # Validate source citations
        citations_valid = self._validate_citations(response, sources)

        # Calculate confidence score based on validation
        confidence_score = self._calculate_confidence_score(
            context_present, hallucinations, citations_valid
        )

        return {
            "is_valid": context_present and not hallucinations and citations_valid,
            "validation_details": {
                "context_present": context_present,
                "hallucinations_detected": len(hallucinations) > 0,
                "hallucinations": hallucinations,
                "citations_valid": citations_valid,
                "sources_mentioned": sources
            },
            "confidence_score": confidence_score
        }

    def _check_context_presence(self, response: str, context: str) -> bool:
        """
        Check if the response contains information from the provided context.

        Args:
            response: The response to validate
            context: The context that was provided

        Returns:
            True if response appears to contain information from context
        """
        # Convert to lowercase for comparison
        response_lower = response.lower()
        context_lower = context.lower()

        # Check if key terms from context appear in response
        context_words = set(context_lower.split())
        response_words = set(response_lower.split())

        # Find intersection of words
        common_words = context_words.intersection(response_words)

        # If there's a meaningful intersection, consider context present
        return len(common_words) > 3  # arbitrary threshold

    def _detect_hallucinations(self, response: str, context: str) -> List[str]:
        """
        Detect potential hallucinations in the response.

        Args:
            response: The response to validate
            context: The context that was provided

        Returns:
            List of potential hallucinations (claims not supported by context)
        """
        # For simplicity, this is a basic implementation
        # A more sophisticated implementation would use semantic similarity
        # or other advanced techniques
        hallucinations = []

        # Look for claims that are not supported by the context
        # This is a simplified approach - in practice, this would be more complex
        response_sentences = re.split(r'[.!?]+', response)
        context_lower = context.lower()

        for sentence in response_sentences:
            sentence = sentence.strip()
            if sentence and sentence.lower() not in context_lower:
                # This is a simplified check - in reality, we'd need more sophisticated
                # semantic analysis to determine if the sentence is supported by context
                pass  # Skip this basic check for now, as it's too simplistic

        return hallucinations

    def _validate_citations(self, response: str, sources: List[str]) -> bool:
        """
        Validate that sources are properly cited in the response.

        Args:
            response: The response to validate
            sources: List of sources that should be cited

        Returns:
            True if sources are properly cited in the response
        """
        response_lower = response.lower()
        cited_sources = 0

        for source in sources:
            if source.lower() in response_lower:
                cited_sources += 1

        # Require at least one source to be cited
        return cited_sources > 0

    def _calculate_confidence_score(self, context_present: bool, hallucinations: List[str], citations_valid: bool) -> float:
        """
        Calculate a confidence score based on validation results.

        Args:
            context_present: Whether context is present in response
            hallucinations: List of detected hallucinations
            citations_valid: Whether citations are valid

        Returns:
            Confidence score between 0 and 1
        """
        score = 0.0

        if context_present:
            score += 0.4  # 40% for context presence

        if not hallucinations:
            score += 0.4  # 40% for no hallucinations

        if citations_valid:
            score += 0.2  # 20% for valid citations

        return min(score, 1.0)  # Cap at 1.0