math-api / src /services /evaluation_service.py
False45's picture
feat: complete Phase 11 & 12, finalize Hackathon submission with ADK, LangGraph, and React
8a1fc38
Raw History Blame Contribute Delete
3.49 kB
"""
Evaluation Service — Offline LLM-as-judge pipeline for answering quality.
"""
import logging
from typing import Dict, List, Any
from backend.src.config import settings
logger = logging.getLogger("math_assistant.eval")
class EvaluationService:
"""Uses LLM-as-judge to evaluate generated math answers against reference solutions."""
def __init__(self, judge_model: str = "gemini-2.5-flash"):
self.judge_model = judge_model
def evaluate_answer(self, question: str, generated_answer: str, reference_answer: str) -> Dict[str, Any]:
"""Ask the judge model to score the generated answer from 1 to 5."""
from google import genai
from google.genai import types
api_key = settings.gemini_api_key
if not api_key:
return {"score": 0, "reasoning": "Missing API Key for evaluation"}
client = genai.Client(api_key=api_key)
prompt = f"""You are an expert mathematics teacher grading a student's answer.
Question: {question}
Reference/Correct Answer:
{reference_answer}
Student's Answer to evaluate:
{generated_answer}
Grade the student's answer from 1 to 5 based on:
1. Accuracy: Is the final answer correct?
2. Steps: Are the steps logical and mathematically sound?
Format your response EXACTLY as follows:
SCORE: [1-5]
REASONING: [1-2 sentences explaining why]
"""
config = types.GenerateContentConfig(
temperature=0.1,
max_output_tokens=150,
)
try:
response = client.models.generate_content(
model=self.judge_model,
contents=prompt,
config=config
)
text = response.text.strip()
score = 0
reasoning = text
for line in text.split('\n'):
if line.startswith("SCORE:"):
try:
score = int(line.replace("SCORE:", "").strip())
except:
pass
elif line.startswith("REASONING:"):
reasoning = line.replace("REASONING:", "").strip()
return {"score": score, "reasoning": reasoning}
except Exception as e:
logger.error(f"Evaluation failed: {e}")
return {"score": 0, "reasoning": f"Evaluation error: {e}"}
def run_eval_pipeline(self, test_cases: List[Dict[str, str]]) -> Dict[str, Any]:
"""Run evaluation on a batch of test cases."""
results = []
total_score = 0
for case in test_cases:
res = self.evaluate_answer(
question=case['question'],
generated_answer=case['generated_answer'],
reference_answer=case['reference_answer']
)
total_score += res['score']
results.append({
"question": case['question'],
"score": res['score'],
"reasoning": res['reasoning']
})
avg_score = total_score / len(test_cases) if test_cases else 0
return {
"average_score": avg_score,
"total_evaluated": len(test_cases),
"results": results
}