File size: 3,174 Bytes
9c84f9d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from abc import ABC, abstractmethod
from typing import Any

from solar_eval.models.sample import EvalSample
from solar_eval.providers.base import BaseProvider


class BaseEvaluator(ABC):
    """Abstract base for all evaluators.

    `required_fields` λŠ” `evaluate()` κ°€ ν•„μš”λ‘œ ν•˜λŠ” `EvalSample` ν•„λ“œ μ΄λ¦„μ˜
    집합이닀. μ§€ν‘œλ§ˆλ‹€ μš”κ΅¬ ν•„λ“œκ°€ μ •λ§λ‘œ λ‹€λ₯΄λ‹€ -- κ΅μ—΄μ˜ `lcs_diff` λŠ” 원문·정닡·
    좜λ ₯ μ…‹ λ‹€ ν•„μš”ν•˜μ§€λ§Œ(3-way), RAG 의 `faithfulness` λŠ” 원문이 ν•„μš” μ—†λŠ” 식
    (`.agents/01-plans/harness/2026-08-09-chosun-proofread-evalsample-migration.md` Β§3).
    μ„œλΈŒν΄λž˜μŠ€λŠ” 이 값을 클래슀 μ†μ„±μœΌλ‘œ μ„ μ–Έν•œλ‹€. ꡬ성에 따라 μš”κ΅¬ ν•„λ“œκ°€ λ‹¬λΌμ§€λŠ”
    경우(예: ν•˜μœ„ 평가기λ₯Ό μ‘°ν•©ν•˜λŠ” `CompositeEvaluator`)λŠ” `@property` 둜 μ˜€λ²„λΌμ΄λ“œ
    해도 λœλ‹€ -- `validate_required_fields` λŠ” `self.required_fields` 둜만 μ ‘κ·Όν•œλ‹€.
    """

    required_fields: frozenset[str] = frozenset()

    @abstractmethod
    async def evaluate(
        self,
        sample: EvalSample,
        provider: BaseProvider | None = None,
        judge_model: str = "gpt-4o",
    ) -> dict[str, Any]:
        """`sample` ν•œ 건을 μ±„μ ν•œλ‹€. `{score, details}` λ₯Ό λ°˜ν™˜ν•œλ‹€.

        채점에 μ‹€νŒ¨ν•˜λ©΄(judge 호좜 μ˜ˆμ™Έ, 잘λͺ»λœ 응닡 λ“±) κ·Έ μ‹€νŒ¨λŠ” **μ˜ˆμ™Έλ‘œ μ „νŒŒν•œλ‹€** --
        점수(0.0 이든 1.0 이든)둜 μΉ˜ν™˜ν•΄ λ°˜ν™˜ν•˜μ§€ μ•ŠλŠ”λ‹€. fail-open(μž₯μ• λ₯Ό 만점으둜
        λ‘”κ°‘μ‹œν‚€λŠ” 것)을 λ§Œλ“€μ§€ μ•ŠλŠ” 것이 이 κ³„μ•½μ˜ 핡심이닀. 이 μ˜ˆμ™Έλ₯Ό μž‘μ•„ μ‹€νŒ¨
        μƒ˜ν”Œμ„ μ§‘κ³„μ—μ„œ λΆ„λ¦¬ν•˜λŠ” 것은 호좜자(runner.py/CLI)의 μ±…μž„μ΄λ‹€ -- μ„œλΈŒν΄λž˜μŠ€λŠ”
        κ·Έλƒ₯ λ˜μ§€κΈ°λ§Œ ν•˜λ©΄ λœλ‹€.
        """
        ...

    @abstractmethod
    def aggregate(self, results: list[dict[str, Any]]) -> dict[str, Any]:
        """`evaluate()` κ°€ μ„±κ³΅ν•œ κ²°κ³Όλ“€λ§Œ λͺ¨μ•„ 전체 점수둜 μ§‘κ³„ν•œλ‹€.

        `results` μ—λŠ” μ‹€νŒ¨ν•œ μƒ˜ν”Œμ΄ μ„žμ—¬ λ“€μ–΄μ˜€μ§€ μ•ŠλŠ”λ‹€(ν˜ΈμΆœμžκ°€ 이미 κ±ΈλŸ¬λ‚Έλ‹€) --
        μ—¬κΈ°μ„œ μ‹€νŒ¨λ₯Ό λ³„λ„λ‘œ μ²˜λ¦¬ν•  ν•„μš”κ°€ μ—†λ‹€.
        """
        ...

    def validate_required_fields(self, sample: EvalSample) -> None:
        """`required_fields` κ°€ `sample` 에 μ±„μ›Œμ Έ μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.

        채점을 μ‹œμž‘ν•˜κΈ° 전에 ν˜ΈμΆœν•œλ‹€(λŸ¬λ„ˆ/CLI 의 evaluate ν˜ΈμΆœλΆ€). 값이 `None`
        이면 "μ±„μ›Œμ§€μ§€ μ•ŠμŒ"으둜 λ³Έλ‹€ -- 빈 λ¬Έμžμ—΄/빈 λ¦¬μŠ€νŠΈλŠ” μœ νš¨ν•œ κ°’(예: ꡐ정할
        λ‚΄μš©μ΄ μ—†λŠ” 원문)이라 ν†΅κ³Όμ‹œν‚¨λ‹€. 쑰용히 λ„˜κΈ°μ§€ μ•Šκ³  μ¦‰μ‹œ μ‹€νŒ¨μ‹œμΌœ, μ–΄λŠ
        ν•„λ“œκ°€ μ™œ μ—†λŠ”μ§€κ°€ λ‘œκ·Έμ— λ°”λ‘œ λ“œλŸ¬λ‚˜κ²Œ ν•œλ‹€.

        Raises:
            ValueError: `required_fields` 쀑 ν•˜λ‚˜ 이상이 `sample` μ—μ„œ `None` 인 경우.
        """
        missing = sorted(f for f in self.required_fields if getattr(sample, f, None) is None)
        if missing:
            raise ValueError(
                f"{type(self).__name__} requires sample field(s) {missing} to be filled "
                f"(required_fields={sorted(self.required_fields)})"
            )