Spaces:
Sleeping
Sleeping
File size: 4,772 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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | """EvalSample โ ํ์ดํ๋ผ์ธ ์คํ ํ ๊ฑด์ ๋ด๋ ๋ ์ฝ๋.
RAGAS ์ `EvalSample` ๊ฐ๋
์ ์ด ๋ ํฌ ํ๋๋ช
์ผ๋ก ์ฎ๊ธด ๊ฒ์ด๋ค. ๊ต์ด ๋ฌธ๋งฅ์์
`user_input`/`response` ๊ฐ์ RAGAS ์ด๋ฆ์ ์ด์ํด์ ์ฐ๋ฆฌ์(`input`/`output`)์ผ๋ก
๋ฐ๊ฟจ๋ค (2026-08-09 ๊ฒฐ์ , `.agents/01-plans/harness/2026-08-09-chosun-proofread-evalsample-migration.md` ยง9).
์ด ํ์ผ์ ๋ง์ด๊ทธ๋ ์ด์
๋จ๊ณ A ์ฐ์ถ๋ฌผ์ด๋ค. ๋จ๊ณ C ์์ `pipelines/*` ๊ฐ, ๋จ๊ณ D/E ์์
`core/runner.py`ยท`evaluators/*` ๊ฐ ์ด ํด๋์ค๋ฅผ ์ค์ ๋ก ์๋นํ๊ธฐ ์์ํ๋ค โ `input`/
`output`/`artifacts["step_outputs"]` ๋ ํ์ดํ๋ผ์ธ์ด ์ฑ์ฐ๊ณ , ๋ฌ๋๊ฐ ์ ์ฅ ์ง์ ์
๋ ๊ฑฐ์ 8+trace ํค dict ๋ก ํผ์น๋ค(`to_record()` ์๋ ํค ์ด๋ฆ์ด ๋ค๋ฅด๋ค โ ์ ์ฅ ์คํค๋ง
ํ์ ํธํ ๋๋ฌธ, `core/runner.py:build_eval_sample_from_result` ์ฐธ๊ณ ), ์ฑ์ ๊ธฐ๋
`required_fields` ๋ก ์ ์ธํ ํ๋๊ฐ ์ฑ์์ก๋์ง ํ์ธํ ๋ค ์ด ์ํ์ ๊ทธ๋๋ก ๋ฐ๋๋ค.
`from_row`/`resolve_field_map` ์ ์์ง ๋ฌ๋๊ฐ ๋ฐฐ์ ํ์ง ์์๋ค (์๋ ค์ง ํ๊ณ โ
`input_fields` ๊ฐ ์ฌ๋ฌ ๊ฐ์ธ ํ์คํฌ์์ ์ ๋๋ field_map ์ด ์ฒซ ํ๋๋ง ๋ด๊ธฐ ๋๋ฌธ,
ยง5-C ๋จ๊ณ ๋ด๋น์ ๋ฉ๋ชจ ์ฐธ๊ณ ).
"""
from dataclasses import dataclass, field
from typing import Any
# field_map ์ value ๋ก ์ฌ ์ ์๋ ๋ ํํ:
# - str: row ์ ์ปฌ๋ผ๋ช
ํ๋๋ฅผ ๊ทธ๋๋ก ๊ฐ๋ฆฌํจ๋ค.
# - dict[str, str]: ์ฌ๋ฌ ์ปฌ๋ผ์ {์๋ธํค: ์ปฌ๋ผ๋ช
} ์ผ๋ก ๋ฌถ์ด ํฉ์ฑ ์ ๋ต(golden_fields)์
# ๋ง๋ ๋ค. `runner.py` ์ `golden_fields` ์ฒ๋ฆฌ์ ๋์ผํ ๊ท์น์ด๋ค.
FieldMapValue = str | dict[str, str]
@dataclass
class EvalSample:
"""ํ๊ฐ ํ์ดํ๋ผ์ธ ํ ์ํ์ ์
๋ ฅยท์ค๊ฐ ์ฐ์ถ๋ฌผยท์ถ๋ ฅ์ ๋ด๋ ๋ ์ฝ๋.
Attributes:
input: ๊ต์ด์ด๋ฉด ์๋ฌธ, RAG ๋ฉด ์ง๋ฌธ, MCQ ๋ฉด ๋ฌธํญ+๋ณด๊ธฐ ๋ฑ ํ์คํฌ๋ง๋ค shape ์ด ๋ค๋ฅด๋ค.
contexts: RAG ๊ฒ์ ๊ฒฐ๊ณผ ๋ฆฌ์คํธ. ๊ต์ด/MCQ ๋ ์ฐ์ง ์์ None.
output: ํ์ดํ๋ผ์ธ์ด ์ฑ์ด๋ค. **์คํ
๋ง๋ค ๋ฎ์ด์ด๋ค** โ ์ต์ข
์คํ
์ ๊ฒฐ๊ณผ๋ง ๋จ๋๋ค.
์คํ
๋ณ ์ค๋
์์ด ํ์ํ๋ฉด `artifacts["step_outputs"]` ๋ฅผ ๋ณธ๋ค.
reference: ์ ๋ต(golden). ๋จ์ผ ๊ฐ์ด๊ฑฐ๋(golden_field), ์ฌ๋ฌ ์ปฌ๋ผ์ ๋ฌถ์
dict(golden_fields) ์ผ ์ ์๋ค.
artifacts: ์ค๊ฐ ์ฐ์ถ๋ฌผ ์ ์ฅ์. ๊ด๋ก์ `artifacts["step_outputs"]` ๋
{์คํ
์ด๋ฆ: ๊ทธ ์คํ
์งํ์ output} ๋ก ๋์ ๋๋ค.
"""
input: Any = None
contexts: list[Any] | None = None
output: Any = None
reference: Any = None
artifacts: dict[str, Any] = field(default_factory=dict)
@classmethod
def from_row(cls, row: dict[str, Any], field_map: dict[str, FieldMapValue]) -> "EvalSample":
"""๋ฐ์ดํฐ์
ํ + field_map ์ผ๋ก ์ํ์ ๋ง๋ ๋ค.
Args:
row: ๋ฐ์ดํฐ์
ํ ํ (jsonl ํ ์ค์ ํ์ฑํ dict).
field_map: EvalSample ํ๋๋ช
(`input`/`reference`/`contexts`) -> ๋ฐ์ดํฐ์
์ปฌ๋ผ๋ช
(str) ๋๋ {์๋ธํค: ์ปฌ๋ผ๋ช
} dict. `core.project_loader.resolve_field_map`
์ด task ์ค์ ์์ ์ด ๊ฐ์ ์ ๋ํ๋ค.
Returns:
field_map ์ ์๋ ํค๋ง ์ฑ์์ง EvalSample (์๋ ํค๋ dataclass ๊ธฐ๋ณธ๊ฐ ์ ์ง).
Raises:
TypeError: field_map ์ value ๊ฐ str/dict ๊ฐ ์๋ ๊ฒฝ์ฐ (์กฐ์ฉํ ๋ฌด์ํ์ง ์๋๋ค).
"""
kwargs: dict[str, Any] = {}
for sample_field in ("input", "reference", "contexts"):
if sample_field not in field_map:
continue
resolved = _resolve_field_value(field_map[sample_field], row)
if sample_field == "contexts" and not isinstance(resolved, list):
resolved = [resolved]
kwargs[sample_field] = resolved
return cls(**kwargs)
def to_record(self) -> dict[str, Any]:
"""JsonlStore ์ ์ฅ์ฉ ์ง๋ ฌํ. artifacts ๋ฅผ ํฌํจํด ํํํ dict ๋ก ํผ์น๋ค."""
return {
"input": self.input,
"contexts": self.contexts,
"output": self.output,
"reference": self.reference,
"artifacts": self.artifacts,
}
def _resolve_field_value(mapping: FieldMapValue, row: dict[str, Any]) -> Any:
"""field_map ์ value ํ๋(str ๋๋ dict)๋ฅผ row ์์ ์ค์ ๊ฐ์ผ๋ก ํ์ด๋ธ๋ค."""
if isinstance(mapping, dict):
return {sub_key: row.get(column, "") for sub_key, column in mapping.items()}
if isinstance(mapping, str):
return row.get(mapping, "")
raise TypeError(
f"field_map value ๋ str ๋๋ dict[str, str] ์ด์ด์ผ ํ๋ค: {mapping!r} ({type(mapping)})"
)
|