Spaces:
Sleeping
Sleeping
| """Base evaluator class for all metrics.""" | |
| from abc import ABC, abstractmethod | |
| from typing import Optional | |
| import logging | |
| from datetime import datetime | |
| from ..types import ( | |
| QAPair, | |
| SystemOutput, | |
| EvaluationResult, | |
| EvaluationMetric, | |
| ) | |
| from ..utils.llm_client import LLMClient | |
| logger = logging.getLogger(__name__) | |
| class BaseEvaluator(ABC): | |
| """Abstract base class for all evaluators. | |
| Each evaluator measures a specific dimension of LLM output quality. | |
| They all follow the same pattern: | |
| 1. Receive question, reference answer, and system output | |
| 2. Use an LLM judge to score the output | |
| 3. Return EvaluationResult with score and reasoning | |
| """ | |
| def __init__(self, judge_client: LLMClient, model_name: Optional[str] = None): | |
| """Initialize evaluator with an LLM client. | |
| Args: | |
| judge_client: LLMClient instance (OpenAI or Anthropic) | |
| model_name: Name of the judge model (for logging/display) | |
| """ | |
| self.judge_client = judge_client | |
| self.model_name = model_name or "unknown-model" | |
| def metric(self) -> EvaluationMetric: | |
| """Return the metric this evaluator measures.""" | |
| pass | |
| def system_prompt(self) -> str: | |
| """Judge system prompt - defines the role and task.""" | |
| pass | |
| def format_prompt( | |
| self, | |
| qa_pair: QAPair, | |
| system_output: SystemOutput, | |
| ) -> str: | |
| """Format the evaluation prompt for the judge. | |
| Args: | |
| qa_pair: The question and reference answer | |
| system_output: The system's output to evaluate | |
| Returns: | |
| Formatted prompt for the judge | |
| """ | |
| pass | |
| async def parse_judge_response(self, response: str) -> tuple[float, str]: | |
| """Parse judge's response into score and reasoning. | |
| Args: | |
| response: Raw response from the judge LLM | |
| Returns: | |
| Tuple of (score_0_to_1, reasoning) | |
| """ | |
| pass | |
| async def evaluate( | |
| self, | |
| qa_pair: QAPair, | |
| system_output: SystemOutput, | |
| ) -> EvaluationResult: | |
| """Evaluate system output for this metric. | |
| This is the main entry point. It orchestrates: | |
| 1. Formatting the evaluation prompt | |
| 2. Calling the judge | |
| 3. Parsing the response | |
| 4. Returning a structured result | |
| Args: | |
| qa_pair: Question and reference answer | |
| system_output: Output from system under test | |
| Returns: | |
| EvaluationResult with score, reasoning, confidence | |
| """ | |
| try: | |
| # Format prompt for the judge | |
| user_prompt = self.format_prompt(qa_pair, system_output) | |
| # Get judge's evaluation | |
| logger.debug(f"Calling judge for {self.metric.value}") | |
| judge_response = await self.judge_client.generate( | |
| system_prompt=self.system_prompt, | |
| user_message=user_prompt, | |
| temperature=0.1, # Low temp for consistency | |
| max_tokens=500, | |
| ) | |
| # Parse the response | |
| score, reasoning = await self.parse_judge_response(judge_response) | |
| # Validate score is 0-1 | |
| if not (0 <= score <= 1): | |
| logger.warning(f"Score {score} out of range, clamping to [0, 1]") | |
| score = max(0, min(1, score)) | |
| # Return structured result | |
| result = EvaluationResult( | |
| metric=self.metric, | |
| score=score, | |
| raw_score=score, # Could store original if different | |
| reasoning=reasoning, | |
| judge_model=self.model_name, | |
| confidence=0.8, # Could estimate from judge's confidence | |
| timestamp=datetime.utcnow(), | |
| ) | |
| logger.info(f"Evaluation complete: {self.metric.value}={score:.2f}") | |
| return result | |
| except Exception as e: | |
| logger.error(f"Evaluation failed for {self.metric.value}: {e}") | |
| raise | |
| def __repr__(self) -> str: | |
| """String representation.""" | |
| return f"{self.__class__.__name__}(metric={self.metric.value}, judge={self.model_name})" | |