| """
|
| Judge Engine
|
|
|
| Main evaluation engine for the Judge agent.
|
| Coordinates hallucination, bias, confidence, and safety scoring.
|
| Uses the scoring aggregator for composite robustness calculation.
|
| """
|
|
|
| from typing import Any, Dict, List, Optional
|
| from uuid import UUID
|
|
|
| from agents.judge.base import BaseJudgeAgent
|
| from agents.judge.bias import BiasScorer, get_bias_scorer
|
| from agents.judge.confidence import ConfidenceScorer, get_confidence_scorer
|
| from agents.judge.hallucination import HallucinationScorer, get_hallucination_scorer
|
| from agents.judge.safety import SafetyScorer, get_safety_scorer
|
| from agents.judge.schemas import JudgeConfig, JudgeRequest, JudgeResponse
|
| from agents.judge.utils import clamp_score, validate_scores
|
| from backend.logging.logger import get_logger
|
| from backend.scoring.aggregator import ScoreAggregator, get_aggregator
|
|
|
|
|
| class JudgeEngine(BaseJudgeAgent):
|
| """
|
| Judge engine that coordinates all scoring components.
|
|
|
| Computes:
|
| - Hallucination score (semantic variance + retrieval consistency)
|
| - Bias score (embedding similarity to bias patterns)
|
| - Confidence score (token probability + entropy)
|
| - Safety score (inverse of defender risk)
|
| - Composite robustness score (using aggregator)
|
| """
|
|
|
| def __init__(self, config: Optional[JudgeConfig] = None):
|
| """
|
| Initialize the judge engine.
|
|
|
| Args:
|
| config: Judge configuration (uses defaults if not provided)
|
| """
|
| super().__init__(config)
|
|
|
|
|
| self._hallucination_scorer: Optional[HallucinationScorer] = None
|
| self._bias_scorer: Optional[BiasScorer] = None
|
| self._confidence_scorer: Optional[ConfidenceScorer] = None
|
| self._safety_scorer: Optional[SafetyScorer] = None
|
| self._aggregator: Optional[ScoreAggregator] = None
|
|
|
| @property
|
| def hallucination_scorer(self) -> HallucinationScorer:
|
| """Lazy load hallucination scorer."""
|
| if self._hallucination_scorer is None:
|
| self._hallucination_scorer = get_hallucination_scorer(
|
| alpha=self._config.hallucination_alpha,
|
| beta=self._config.hallucination_beta,
|
| n_samples=self._config.self_consistency_n,
|
| embedding_model=self._config.embedding_model,
|
| )
|
| return self._hallucination_scorer
|
|
|
| @property
|
| def bias_scorer(self) -> BiasScorer:
|
| """Lazy load bias scorer."""
|
| if self._bias_scorer is None:
|
| self._bias_scorer = get_bias_scorer(
|
| threshold=self._config.bias_threshold,
|
| embedding_model=self._config.embedding_model,
|
| )
|
| return self._bias_scorer
|
|
|
| @property
|
| def confidence_scorer(self) -> ConfidenceScorer:
|
| """Lazy load confidence scorer."""
|
| if self._confidence_scorer is None:
|
| self._confidence_scorer = get_confidence_scorer(
|
| gamma=self._config.confidence_gamma,
|
| )
|
| return self._confidence_scorer
|
|
|
| @property
|
| def safety_scorer(self) -> SafetyScorer:
|
| """Lazy load safety scorer."""
|
| if self._safety_scorer is None:
|
| self._safety_scorer = get_safety_scorer()
|
| return self._safety_scorer
|
|
|
| @property
|
| def aggregator(self) -> ScoreAggregator:
|
| """Lazy load score aggregator."""
|
| if self._aggregator is None:
|
| self._aggregator = get_aggregator()
|
| return self._aggregator
|
|
|
| async def evaluate(self, request: JudgeRequest) -> JudgeResponse:
|
| """
|
| Evaluate model output and compute all scores.
|
|
|
| Args:
|
| request: Judge request with model output and metadata
|
|
|
| Returns:
|
| Judge response with computed scores
|
| """
|
| self.logger.info(
|
| "Evaluating model output",
|
| run_id=str(request.run_id),
|
| sample_id=request.sample_id,
|
| output_length=len(request.model_output),
|
| )
|
|
|
| try:
|
|
|
| hallucination_score, hallucination_metadata = await self.compute_hallucination(
|
| output=request.model_output,
|
| ground_truth=request.ground_truth,
|
| )
|
|
|
|
|
| bias_score, bias_metadata = await self.compute_bias(
|
| output=request.model_output,
|
| )
|
|
|
|
|
| confidence_score, confidence_metadata = await self.compute_confidence(
|
| token_probs=request.token_probs,
|
| )
|
|
|
|
|
| safety_score, safety_metadata = await self.compute_safety(
|
| defender_risk_score=request.defender_risk_score,
|
| )
|
|
|
|
|
| validate_scores(
|
| hallucination=hallucination_score,
|
| safety=safety_score,
|
| bias=bias_score,
|
| confidence=confidence_score,
|
| )
|
|
|
|
|
|
|
| robustness_score = self.aggregator.calculate_composite(
|
| hallucination=hallucination_score,
|
| toxicity=request.defender_toxicity_score,
|
| bias=bias_score,
|
| confidence=confidence_score,
|
| )
|
|
|
|
|
| robustness_score = clamp_score(robustness_score)
|
|
|
|
|
| metadata: Dict[str, Any] = {
|
| "hallucination_metadata": hallucination_metadata,
|
| "bias_metadata": bias_metadata,
|
| "confidence_metadata": confidence_metadata,
|
| "safety_metadata": safety_metadata,
|
| "config": self._config.model_dump(),
|
| }
|
|
|
|
|
| self._log_evaluation(
|
| run_id=str(request.run_id),
|
| sample_id=request.sample_id,
|
| hallucination_score=hallucination_score,
|
| bias_score=bias_score,
|
| confidence_score=confidence_score,
|
| safety_score=safety_score,
|
| robustness_score=robustness_score,
|
| success=True,
|
| )
|
|
|
| return JudgeResponse(
|
| run_id=request.run_id,
|
| sample_id=request.sample_id,
|
| hallucination_score=hallucination_score,
|
| safety_score=safety_score,
|
| bias_score=bias_score,
|
| confidence_score=confidence_score,
|
| robustness_score=robustness_score,
|
| metadata=metadata,
|
| )
|
|
|
| except Exception as e:
|
| self.logger.error(
|
| "Judge evaluation failed",
|
| run_id=str(request.run_id),
|
| sample_id=request.sample_id,
|
| error=str(e),
|
| )
|
|
|
|
|
| self._log_evaluation(
|
| run_id=str(request.run_id),
|
| sample_id=request.sample_id,
|
| hallucination_score=0.0,
|
| bias_score=0.0,
|
| confidence_score=0.5,
|
| safety_score=0.5,
|
| robustness_score=0.0,
|
| success=False,
|
| error=str(e),
|
| )
|
|
|
| raise
|
|
|
| async def compute_hallucination(
|
| self,
|
| output: str,
|
| ground_truth: Optional[str] = None,
|
| ) -> tuple[float, Dict[str, Any]]:
|
| """
|
| Compute hallucination score.
|
|
|
| Args:
|
| output: Model output to evaluate
|
| ground_truth: Optional ground truth for comparison
|
|
|
| Returns:
|
| Tuple of (hallucination_score, metadata)
|
| """
|
| return await self.hallucination_scorer.compute_hallucination(
|
| output=output,
|
| ground_truth=ground_truth,
|
| )
|
|
|
| async def compute_bias(
|
| self,
|
| output: str,
|
| ) -> tuple[float, Dict[str, Any]]:
|
| """
|
| Compute bias score.
|
|
|
| Args:
|
| output: Model output to evaluate
|
|
|
| Returns:
|
| Tuple of (bias_score, metadata)
|
| """
|
| return await self.bias_scorer.compute_bias(output=output)
|
|
|
| async def compute_confidence(
|
| self,
|
| token_probs: Optional[List[float]] = None,
|
| ) -> tuple[float, Dict[str, Any]]:
|
| """
|
| Compute confidence score.
|
|
|
| Args:
|
| token_probs: Optional list of token probabilities
|
|
|
| Returns:
|
| Tuple of (confidence_score, metadata)
|
| """
|
| return await self.confidence_scorer.compute_confidence(token_probs=token_probs)
|
|
|
| async def compute_safety(
|
| self,
|
| defender_risk_score: float,
|
| ) -> tuple[float, Dict[str, Any]]:
|
| """
|
| Compute safety score.
|
|
|
| Args:
|
| defender_risk_score: Risk score from defender
|
|
|
| Returns:
|
| Tuple of (safety_score, metadata)
|
| """
|
| return await self.safety_scorer.compute_safety_async(
|
| defender_risk_score=defender_risk_score
|
| )
|
|
|
| def _log_evaluation(
|
| self,
|
| run_id: str,
|
| sample_id: str,
|
| hallucination_score: float,
|
| bias_score: float,
|
| confidence_score: float,
|
| safety_score: float,
|
| robustness_score: float,
|
| success: bool,
|
| error: Optional[str] = None,
|
| ) -> None:
|
| """
|
| Log judge evaluation results.
|
|
|
| Args:
|
| run_id: Run identifier
|
| sample_id: Sample identifier
|
| hallucination_score: Hallucination score
|
| bias_score: Bias score
|
| confidence_score: Confidence score
|
| safety_score: Safety score
|
| robustness_score: Composite robustness score
|
| success: Whether evaluation succeeded
|
| error: Error message if failed
|
| """
|
| log_data = {
|
| "run_id": run_id,
|
| "sample_id": sample_id,
|
| "hallucination_score": hallucination_score,
|
| "bias_score": bias_score,
|
| "confidence_score": confidence_score,
|
| "safety_score": safety_score,
|
| "robustness_score": robustness_score,
|
| "success": success,
|
| "error": error,
|
| }
|
|
|
| if success:
|
| self.logger.info("Judge evaluation complete", **log_data)
|
| else:
|
| self.logger.error("Judge evaluation failed", **log_data)
|
|
|
|
|
|
|
| _judge_engine: Optional[JudgeEngine] = None
|
|
|
|
|
| def get_judge_engine(config: Optional[JudgeConfig] = None) -> JudgeEngine:
|
| """
|
| Get the global judge engine instance.
|
|
|
| Args:
|
| config: Optional judge configuration
|
|
|
| Returns:
|
| JudgeEngine singleton
|
| """
|
| global _judge_engine
|
| if _judge_engine is None:
|
| _judge_engine = JudgeEngine(config=config)
|
| return _judge_engine
|
|
|
|
|
| __all__ = [
|
| "JudgeEngine",
|
| "get_judge_engine",
|
| ]
|
|
|