Spaces:
Sleeping
Sleeping
Download solar_eval/evaluators/llm_judge.py from dev-strender/proofread-demo: direct link, hf CLI and curl.
- Browser
- Download file 4.79 kB
-
https://huggingface.co/spaces/dev-strender/proofread-demo/resolve/dd41633552f36d3c639676ca2e005abe50eb72af/solar_eval/evaluators/llm_judge.py
- Command line
-
hf download hf://spaces/dev-strender/proofread-demo@dd41633552f36d3c639676ca2e005abe50eb72af/solar_eval/evaluators/llm_judge.py
-
curl -L -o llm_judge.py https://huggingface.co/spaces/dev-strender/proofread-demo/resolve/dd41633552f36d3c639676ca2e005abe50eb72af/solar_eval/evaluators/llm_judge.py
4.79 kB
| from typing import Any | |
| from solar_eval.evaluators.base import BaseEvaluator | |
| from solar_eval.evaluators.scoring import ( | |
| error_count_to_severity, | |
| score_to_severity, | |
| severity_to_score, | |
| weighted_average, | |
| ) | |
| from solar_eval.models.sample import EvalSample | |
| from solar_eval.providers.base import BaseProvider | |
| class LLMJudgeEvaluator(BaseEvaluator): | |
| """LLM-as-Judge evaluator with weighted categories. | |
| Ported from chosun-projects body evaluation. | |
| Categories have weights and are scored by error count -> severity -> score. | |
| """ | |
| required_fields = frozenset({"input", "output", "reference"}) | |
| def __init__( | |
| self, | |
| categories: list[dict[str, Any]], | |
| judge_prompts: dict[str, str] | None = None, | |
| ) -> None: | |
| self.categories = categories # [{name, weight}] | |
| self.weights = {c["name"]: c["weight"] for c in categories} | |
| self.judge_prompts = judge_prompts or {} | |
| async def evaluate( | |
| self, | |
| sample: EvalSample, | |
| provider: BaseProvider | None = None, | |
| judge_model: str = "gpt-4o", | |
| ) -> dict[str, Any]: | |
| if provider is None: | |
| raise ValueError("LLMJudgeEvaluator requires a provider") | |
| scores = {} | |
| details = {} | |
| for category in self.categories: | |
| cat_name = category["name"] | |
| system_prompt = self.judge_prompts.get(cat_name, self._default_prompt(cat_name)) | |
| user_content = ( | |
| f"Korean (Source):\n{sample.input.get('original', '')}\n\n" | |
| f"Golden Reference:\n{sample.reference}\n\n" | |
| f"Model Output:\n{sample.output}" | |
| ) | |
| # F1: judge ํธ์ถ ์คํจ๋ฅผ ์ผ์ผ ๋ง์ (perfect/1.0)์ผ๋ก ์ฑ์ ํ์ง ์๋๋ค -- ์ฌ๊ธฐ์ | |
| # ์ก์ง ์๊ณ ๊ทธ๋๋ก ์ ํํ๋ค. ์นดํ ๊ณ ๋ฆฌ ํ๋๊ฐ ์ฃฝ์ผ๋ฉด ์ด ์ํ์ judge ๊ฒฐ๊ณผ | |
| # ์ ์ฒด๊ฐ ๋ฌดํจ๋ผ, ๋จ์ ์นดํ ๊ณ ๋ฆฌ๋ฅผ ๊ณ์ ๋๋ ๋์ ์ฆ์ ์ค๋จํ๋ค. ์คํจ๋ฅผ | |
| # eval_results ๋ฐ์ผ๋ก ๋นผ๋ ๊ฒ(eval_failures)์ ํธ์ถ์(runner/CLI)์ ์ฑ ์์ด๋ค. | |
| result = await provider.chat_completion_json( | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_content}, | |
| ], | |
| model=judge_model, | |
| ) | |
| count_key = self._get_count_key(cat_name) | |
| error_count = result.get(count_key, result.get("error_count", 0)) | |
| severity = error_count_to_severity(error_count) | |
| score = severity_to_score(severity) | |
| scores[cat_name] = score | |
| details[cat_name] = { | |
| "error_count": error_count, | |
| "severity": severity, | |
| "score": score, | |
| } | |
| overall_score = weighted_average(scores, self.weights) | |
| overall_severity = score_to_severity(overall_score, tiers=7) | |
| return { | |
| "score": overall_score, | |
| "severity": overall_severity, | |
| "category_scores": scores, | |
| "details": details, | |
| } | |
| def aggregate(self, results: list[dict[str, Any]]) -> dict[str, Any]: | |
| if not results: | |
| return {"overall_score": 0.0, "scores": {}} | |
| # Average per category | |
| cat_scores: dict[str, list[float]] = {c["name"]: [] for c in self.categories} | |
| overall_scores: list[float] = [] | |
| for r in results: | |
| overall_scores.append(r["score"]) | |
| for cat_name, score in r.get("category_scores", {}).items(): | |
| if cat_name in cat_scores: | |
| cat_scores[cat_name].append(score) | |
| avg_scores = {k: sum(v) / len(v) if v else 0.0 for k, v in cat_scores.items()} | |
| overall = sum(overall_scores) / len(overall_scores) | |
| return { | |
| "overall_score": overall, | |
| "scores": avg_scores, | |
| "severity": score_to_severity(overall, tiers=7), | |
| "num_samples": len(results), | |
| } | |
| def _get_count_key(self, category: str) -> str: | |
| key_map = { | |
| "factual_accuracy": "error_count", | |
| "content_addition": "addition_count", | |
| "content_omission": "omission_count", | |
| "style_appropriateness": "violation_count", | |
| } | |
| return key_map.get(category, "issue_count") | |
| def _default_prompt(self, category: str) -> str: | |
| return ( | |
| f"You are an expert translation quality evaluator. " | |
| f"Evaluate the '{category}' of the model output compared to the golden reference. " | |
| f"BE EXTREMELY CONSERVATIVE. Only count OBVIOUS errors. WHEN IN DOUBT, DON'T COUNT. " | |
| f"Return JSON with a single field 'error_count' (integer)." | |
| ) | |