def _simple_evaluation(
        self, query: str, results: List[Dict[str, Any]], expected_content_keywords: List[str]
    ) -> Dict[str, Any]:
        """Simple keyword-based evaluation as fallback.

        Args:
            query: The search query
            results: List of search results
            expected_content_keywords: Keywords that should appear in good results

        Returns:
            Dict with evaluation metrics
        """
        # No results - return poor score
        if not results:
            return {
                "relevance": 0.0,
                "completeness": 0.0,
                "diversity": 0.0,
                "relevant_results": 0,
                "score": 0.0,
                "feedback": "No results provided",
            }

        # If no keywords provided, check if query terms appear in results
        if not expected_content_keywords:
            expected_content_keywords = [term.lower() for term in query.split() if len(term) > 3]

        relevant_count = 0
        keyword_matches = 0

        for result in results:
            # Convert result to string for simple text matching
            content = json.dumps(result).lower()
            query_terms_present = any(
                term.lower() in content for term in query.split() if len(term) > 3
            )
            keywords_present = any(
                keyword.lower() in content for keyword in expected_content_keywords
            )

            # Count as relevant if either query terms or expected keywords are present
            if query_terms_present or keywords_present:
                relevant_count += 1

            # Count total keyword matches
            keyword_matches += sum(
                1 for keyword in expected_content_keywords if keyword.lower() in content
            )

        # Calculate metrics
        total_possible_matches = len(expected_content_keywords) * len(results)
        completeness = keyword_matches / total_possible_matches if total_possible_matches > 0 else 0
        relevance = relevant_count / len(results) if results else 0

        # Fixed diversity estimate (can't easily determine with simple matching)
        diversity = 0.5

        # Calculate weighted score
        score = relevance * 0.5 + completeness * 0.3 + diversity * 0.2

        # Generate feedback
        if relevant_count == 0:
            feedback = "No relevant results found containing query terms or expected keywords."
        elif relevance < 0.5:
            feedback = (
                f"Low relevance ({relevance:.2f}): "
                f"Less than half of results contain relevant content."
            )
        elif completeness < 0.3:
            feedback = (
                f"Low completeness ({completeness:.2f}): Expected keywords not well represented."
            )
        else:
            feedback = f"Found {relevant_count} relevant results out of {len(results)}."

        return {
            "relevance": relevance,
            "completeness": completeness,
            "diversity": diversity,
            "relevant_results": relevant_count,
            "score": score,
            "feedback": feedback,
        }
Simple keyword-based evaluation as fallback.

        Args:
            query: The search query
            results: List of search results
            expected_content_keywords: Keywords that should appear in good results

        Returns:
            Dict with evaluation metrics
        
backend/tests/helpers/llm_judge.py