OPEN SOURCE
software/src/models/evaluation.py
このサイトで読み、必要なファイルを保存できます。
日常訳はAIを用いて作成された翻訳です。人間の聖書学・原語の専門家による全訳の検証は確認されていません。既存の翻訳や原語資料と照らし合わせてお読みください。
1"""Data models for translation evaluation."""23from __future__ import annotations45from datetime import datetime6from typing import TYPE_CHECKING78from pydantic import BaseModel, Field, computed_field910if TYPE_CHECKING:11pass1213# Evaluation dimension names14DIMENSIONS = [15"theological_accuracy",16"source_fidelity",17"naturalness",18"readability",19"glossary_consistency",20"literary_quality",21# Style-based dimensions22"style_adherence",23"formality_level",24"archaic_avoidance",25]2627# Thresholds for flagging28THEOLOGICAL_ACCURACY_THRESHOLD = 80.029SOURCE_FIDELITY_THRESHOLD = 70.030ANY_DIMENSION_THRESHOLD = 60.031CONFIDENCE_THRESHOLD = 70.0323334class EvaluationScore(BaseModel):35"""Score for a single evaluation dimension."""3637dimension: str = Field(..., description="Name of the evaluation dimension")38score: float = Field(..., ge=0, le=100, description="Score as percentage (0-100)")39confidence: float = Field(40..., ge=0, le=100, description="Confidence in the score (0-100)"41)42reasoning: str = Field(..., description="Brief explanation for the score")434445class VerseEvaluation(BaseModel):46"""Complete evaluation for a single verse."""4748verse_number: int = Field(..., ge=1, description="Verse number")49evaluator_model: str = Field(..., description="Model used for evaluation")50timestamp: datetime = Field(51default_factory=datetime.utcnow, description="When evaluation was performed"52)53scores: dict[str, EvaluationScore] = Field(54..., description="Scores by dimension name"55)56flags: list[str] = Field(57default_factory=list, description="Flags for issues found"58)59notes: str | None = Field(None, description="Additional notes")6061@computed_field62@property63def overall_score(self) -> float:64"""Calculate weighted average of all dimension scores."""65if not self.scores:66return 0.067total = sum(s.score for s in self.scores.values())68return round(total / len(self.scores), 1)6970def compute_flags(self) -> list[str]:71"""Compute flags based on scores and thresholds."""72flags = []7374# Check theological accuracy75if "theological_accuracy" in self.scores:76ta = self.scores["theological_accuracy"]77if ta.score < THEOLOGICAL_ACCURACY_THRESHOLD:78flags.append("theological_concern")79if ta.confidence < CONFIDENCE_THRESHOLD:80flags.append("low_confidence_theology")8182# Check source fidelity83if "source_fidelity" in self.scores:84sf = self.scores["source_fidelity"]85if sf.score < SOURCE_FIDELITY_THRESHOLD:86flags.append("source_fidelity_concern")8788# Check any dimension below threshold89for dim, score in self.scores.items():90if score.score < ANY_DIMENSION_THRESHOLD:91flags.append(f"low_{dim}")9293return flags949596class ChapterEvaluation(BaseModel):97"""Aggregated evaluation for a chapter."""9899book: str = Field(..., description="Book name")100chapter: int = Field(..., ge=1, description="Chapter number")101evaluator_model: str = Field(..., description="Model used for evaluation")102evaluated_at: datetime = Field(103default_factory=datetime.utcnow, description="When evaluation was performed"104)105verses: dict[int, VerseEvaluation] = Field(106default_factory=dict, description="Evaluations by verse number"107)108109@computed_field110@property111def aggregate_scores(self) -> dict[str, float]:112"""Calculate average scores per dimension across all verses."""113if not self.verses:114return {}115116dimension_totals: dict[str, list[float]] = {}117for verse_eval in self.verses.values():118for dim, score in verse_eval.scores.items():119if dim not in dimension_totals:120dimension_totals[dim] = []121dimension_totals[dim].append(score.score)122123return {124dim: round(sum(scores) / len(scores), 1)125for dim, scores in dimension_totals.items()126}127128@computed_field129@property130def overall_score(self) -> float:131"""Calculate overall average score for the chapter."""132if not self.verses:133return 0.0134total = sum(v.overall_score for v in self.verses.values())135return round(total / len(self.verses), 1)136137@computed_field138@property139def flagged_verses(self) -> list[int]:140"""Get list of verse numbers that have flags."""141return [v_num for v_num, v_eval in self.verses.items() if v_eval.flags]142143@computed_field144@property145def flagged_count(self) -> int:146"""Count of flagged verses."""147return len(self.flagged_verses)148149150class BookEvaluationSummary(BaseModel):151"""Summary evaluation for an entire book."""152153book: str = Field(..., description="Book name")154total_chapters: int = Field(..., description="Total chapters evaluated")155total_verses: int = Field(..., description="Total verses evaluated")156aggregate_scores: dict[str, float] = Field(157..., description="Average scores per dimension"158)159overall_score: float = Field(..., description="Overall average score")160flagged_verses: list[str] = Field(161..., description="List of flagged verse references (book:chapter:verse)"162)163evaluated_at: datetime = Field(164default_factory=datetime.utcnow, description="When summary was generated"165)166