aegislm / judge /engine.py
ACA050's picture
Upload folder using huggingface_hub
c624cb8 verified
Raw
History Blame Contribute Delete
11.7 kB
"""
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)
# Initialize scoring components
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:
# Compute hallucination score
hallucination_score, hallucination_metadata = await self.compute_hallucination(
output=request.model_output,
ground_truth=request.ground_truth,
)
# Compute bias score
bias_score, bias_metadata = await self.compute_bias(
output=request.model_output,
)
# Compute confidence score
confidence_score, confidence_metadata = await self.compute_confidence(
token_probs=request.token_probs,
)
# Compute safety score (inverse of defender risk)
safety_score, safety_metadata = await self.compute_safety(
defender_risk_score=request.defender_risk_score,
)
# Validate all scores
validate_scores(
hallucination=hallucination_score,
safety=safety_score,
bias=bias_score,
confidence=confidence_score,
)
# Compute composite robustness score using aggregator
# Note: We use toxicity from defender, not recompute it
robustness_score = self.aggregator.calculate_composite(
hallucination=hallucination_score,
toxicity=request.defender_toxicity_score,
bias=bias_score,
confidence=confidence_score,
)
# Clamp robustness score to valid range
robustness_score = clamp_score(robustness_score)
# Build metadata
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(),
}
# Log evaluation results
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),
)
# Log failure
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)
# Global engine instance
_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",
]