데이터셋 직렬화

데이터셋 직렬화 (Dataset Serialization)

데이터셋을 다양한 형식으로 저장하고 불러오는 방법을 배워요. 커스텀 평가자와 IDE 통합을 지원해요.

Pydantic Evals는 데이터셋을 파일로 직렬화하는 두 가지 형식을 지원해요:

  • YAML (.yaml, .yml) - 사람이 읽기 쉬워 버전 관리에 좋아요
  • JSON (.json) - 구조화되어 있고 기계가 읽기 좋아요

두 형식 모두 다음을 지원해요:

  • IDE 자동 완성과 검증을 위한 자동 JSON 스키마 생성
  • 커스텀 평가자 직렬화/역직렬화
  • 제네릭 매개변수로 타입 안전하게 불러오기

출처: 문서

본문

YAML 형식

YAML은 가독성과 간결한 문법 덕분에 대부분의 사용 사례에 권장되는 형식이에요.

기본 예시

from typing import Any

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import EqualsExpected, IsInstance

# 타입 매개변수로 데이터셋 만들기
dataset = Dataset[str, str, Any](
    name='my_tests',
    cases=[
        Case(
            name='test_1',
            inputs='hello',
            expected_output='HELLO',
        ),
    ],
    evaluators=[
        IsInstance(type_name='str'),
        EqualsExpected(),
    ],
)

# YAML로 저장
dataset.to_file('my_tests.yaml')

이렇게 하면 두 개의 파일이 만들어져요:

  1. my_tests.yaml - 데이터셋
  2. my_tests_schema.json - IDE 지원용 JSON 스키마

YAML 출력

# yaml-language-server: $schema=my_tests_schema.json
name: my_tests
cases:
- name: test_1
  inputs: hello
  expected_output: HELLO
evaluators:
- IsInstance: str
- EqualsExpected

IDE용 JSON 스키마

첫 번째 줄이 스키마 파일을 참조해요:

# yaml-language-server: $schema=my_tests_schema.json

이렇게 하면 다음이 가능해져요:

  • ✅ VS Code, PyCharm 및 기타 편집기에서 자동 완성
  • ✅ 편집 중 인라인 검증
  • ✅ 필드에 대한 문서 툴팁
  • ✅ 잘못된 데이터에 대한 오류 강조

편집기 지원

yaml-language-server 주석은 다음에서 지원돼요:

  • VS Code (YAML 확장 포함)
  • JetBrains IDE (PyCharm, IntelliJ 등)
  • YAML 언어 서버를 지원하는 대부분의 편집기

자세한 내용은 YAML Language Server 문서를 참고해요.

YAML에서 불러오기

from pathlib import Path
from typing import Any

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import EqualsExpected, IsInstance

# 먼저 데이터셋을 만들고 저장
Path('my_tests.yaml').parent.mkdir(exist_ok=True)
dataset = Dataset[str, str, Any](
    name='my_tests',
    cases=[Case(name='test_1', inputs='hello', expected_output='HELLO')],
    evaluators=[IsInstance(type_name='str'), EqualsExpected()],
)
dataset.to_file('my_tests.yaml')

# 타입 매개변수로 데이터셋 불러오기
dataset = Dataset[str, str, Any].from_file('my_tests.yaml')


def my_task(text: str) -> str:
    return text.upper()


# 평가 실행
report = dataset.evaluate_sync(my_task)

JSON 형식

JSON 형식은 프로그래밍 방식의 생성이나 엄격한 구조가 필요할 때 유용해요.

기본 예시

from typing import Any

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import EqualsExpected

dataset = Dataset[str, str, Any](
    name='my_tests',
    cases=[
        Case(name='test_1', inputs='hello', expected_output='HELLO'),
    ],
    evaluators=[EqualsExpected()],
)

# JSON으로 저장
dataset.to_file('my_tests.json')

JSON 출력

{
  "$schema": "my_tests_schema.json",
  "name": "my_tests",
  "cases": [
    {
      "name": "test_1",
      "inputs": "hello",
      "expected_output": "HELLO"
    }
  ],
  "evaluators": [
    "EqualsExpected"
  ]
}

맨 위의 $schema 키가 YAML과 유사하게 IDE 지원을 가능하게 해요.

JSON에서 불러오기

from typing import Any

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import EqualsExpected

# 먼저 데이터셋을 만들고 저장
dataset = Dataset[str, str, Any](
    name='my_tests',
    cases=[Case(name='test_1', inputs='hello', expected_output='HELLO')],
    evaluators=[EqualsExpected()],
)
dataset.to_file('my_tests.json')

# JSON에서 불러오기
dataset = Dataset[str, str, Any].from_file('my_tests.json')

스키마 생성

자동 스키마 생성

기본적으로 to_file()은 데이터셋과 함께 JSON 스키마 파일을 만들어요:

from typing import Any

from pydantic_evals import Case, Dataset

dataset = Dataset[str, str, Any](name='my_tests', cases=[Case(inputs='test')])

# my_tests.yaml과 my_tests_schema.json 둘 다 생성
dataset.to_file('my_tests.yaml')

커스텀 스키마 위치

from pathlib import Path
from typing import Any

from pydantic_evals import Case, Dataset

dataset = Dataset[str, str, Any](name='my_tests', cases=[Case(inputs='test')])

# 디렉토리 만들기
Path('data').mkdir(exist_ok=True)

# 커스텀 스키마 파일명 (데이터셋 파일 위치 기준)
dataset.to_file(
    'data/my_tests.yaml',
    schema_path='my_schema.json',
)

# 스키마 파일 없음
dataset.to_file('my_tests.yaml', schema_path=None)

스키마 경로 템플릿

{stem}을 사용해 데이터셋 파일명을 참조해요:

from typing import Any

from pydantic_evals import Case, Dataset

dataset = Dataset[str, str, Any](name='my_tests', cases=[Case(inputs='test')])

# my_tests.yaml과 my_tests.schema.json 생성
dataset.to_file(
    'my_tests.yaml',
    schema_path='{stem}.schema.json',
)

수동 스키마 생성

데이터셋을 저장하지 않고 스키마를 생성해요:

import json
from typing import Any

from pydantic_evals import Dataset

# 특정 데이터셋 타입에 대한 스키마를 딕셔너리로 가져오기
schema = Dataset[str, str, Any].model_json_schema_with_evaluators()

# 수동으로 저장
with open('custom_schema.json', 'w', encoding='utf-8') as f:
    json.dump(schema, f, indent=2)

커스텀 평가자

커스텀 평가자는 직렬화와 역직렬화 중 특별한 처리가 필요해요.

요구사항

커스텀 평가자는 반드시:

  1. @dataclass로 장식돼야 해요
  2. Evaluator를 상속해야 해요
  3. to_file()from_file() 둘 다에 전달돼야 해요

완전한 예시

from dataclasses import dataclass
from typing import Any

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import Evaluator, EvaluatorContext


@dataclass
class CustomThreshold(Evaluator):
    """Check if output length exceeds a threshold."""

    min_length: int
    max_length: int = 100

    def evaluate(self, ctx: EvaluatorContext) -> bool:
        length = len(str(ctx.output))
        return self.min_length <= length <= self.max_length


# 커스텀 평가자로 데이터셋 만들기
dataset = Dataset[str, str, Any](
    name='custom_threshold_tests',
    cases=[
        Case(
            name='test_length',
            inputs='example',
            expected_output='long result',
            evaluators=[
                CustomThreshold(min_length=5, max_length=20),
            ],
        ),
    ],
)

# 커스텀 평가자 타입으로 저장
dataset.to_file(
    'dataset.yaml',
    custom_evaluator_types=[CustomThreshold],
)

저장된 YAML

# yaml-language-server: $schema=dataset_schema.json
cases:
- name: test_length
  inputs: example
  expected_output: long result
  evaluators:
  - CustomThreshold:
      min_length: 5
      max_length: 20

커스텀 평가자로 불러오기

from dataclasses import dataclass
from typing import Any

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import Evaluator, EvaluatorContext


@dataclass
class CustomThreshold(Evaluator):
    """Check if output length exceeds a threshold."""

    min_length: int
    max_length: int = 100

    def evaluate(self, ctx: EvaluatorContext) -> bool:
        length = len(str(ctx.output))
        return self.min_length <= length <= self.max_length


# 먼저 데이터셋을 만들고 저장
dataset = Dataset[str, str, Any](
    name='custom_threshold_tests',
    cases=[
        Case(
            name='test_length',
            inputs='example',
            expected_output='long result',
            evaluators=[CustomThreshold(min_length=5, max_length=20)],
        ),
    ],
)
dataset.to_file('dataset.yaml', custom_evaluator_types=[CustomThreshold])

# 커스텀 평가자 레지스트리로 불러오기
dataset = Dataset[str, str, Any].from_file(
    'dataset.yaml',
    custom_evaluator_types=[CustomThreshold],
)

중요

custom_evaluator_typesto_file()from_file() 둘 다에 전달해야 해요.

  • to_file(): 평가자를 JSON 스키마에 포함
  • from_file(): 역직렬화를 위해 평가자 등록

평가자 직렬화 형식

평가자는 세 가지 형태로 직렬화될 수 있어요:

1. 이름만 (매개변수 없음)

evaluators:
- EqualsExpected
- IsInstance: str  # 기본 매개변수 사용

2. 단일 매개변수 (짧은 형식)

evaluators:
- IsInstance: str
- Contains: "required text"
- MaxDuration: 2.0

3. 여러 매개변수 (Dict 형식)

evaluators:
- CustomThreshold:
    min_length: 5
    max_length: 20
- LLMJudge:
    rubric: "Response is accurate"
    model: "openai:gpt-5"
    include_input: true

형식 비교

기능 YAML JSON
사람이 읽기 쉬움 ✅ 훌륭함 ⚠️ 좋음
주석 ✅ 가능 ❌ 불가
간결함 ✅ 예 ⚠️ 장황함
기계 파싱 ✅ 좋음 ✅ 훌륭함
IDE 지원 ✅ 예 ✅ 예
버전 관리 ✅ 깔끔한 diff ⚠️ 시끄러운 diff

권장: 대부분의 경우 YAML을, 프로그래밍 방식 생성에는 JSON을 사용해요.

고급: 평가자 직렬화 이름

직렬화된 파일에서 평가자가 표시되는 방식을 커스터마이즈해요:

from dataclasses import dataclass

from pydantic_evals.evaluators import Evaluator, EvaluatorContext


@dataclass
class VeryLongDescriptiveEvaluatorName(Evaluator):
    @classmethod
    def get_serialization_name(cls) -> str:
        return 'ShortName'

    def evaluate(self, ctx: EvaluatorContext) -> bool:
        return True

YAML에서:

evaluators:
- ShortName  # VeryLongDescriptiveEvaluatorName 대신

문제 해결

IDE에서 스키마를 찾지 못함

문제: YAML 파일에 자동 완성이 표시되지 않음

해결 방법:

  1. YAML 첫 줄의 스키마 경로를 확인해요:

    # yaml-language-server: $schema=correct_schema_name.json
    
  2. 같은 디렉토리에 스키마 파일이 존재하는지 확인해요

  3. IDE에서 언어 서버를 재시작해요

  4. YAML 확장 설치 (VS Code: Red Hat의 "YAML")

커스텀 평가자를 찾지 못함

문제: ValueError: Unknown evaluator name: 'CustomEvaluator'

해결 방법: 불러올 때 custom_evaluator_types를 전달해요:

from dataclasses import dataclass
from typing import Any

from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import Evaluator, EvaluatorContext


@dataclass
class CustomEvaluator(Evaluator):
    def evaluate(self, ctx: EvaluatorContext) -> bool:
        return True


# 먼저 커스텀 평가자로 만들고 저장
dataset = Dataset[str, str, Any](
    name='custom_eval_tests',
    cases=[Case(inputs='test', evaluators=[CustomEvaluator()])],
)
dataset.to_file('tests.yaml', custom_evaluator_types=[CustomEvaluator])

# 커스텀 평가자 타입으로 불러오기
dataset = Dataset[str, str, Any].from_file(
    'tests.yaml',
    custom_evaluator_types=[CustomEvaluator],  # 필수!
)

형식 추론 실패

문제: ValueError: Cannot infer format from extension

해결 방법: 형식을 명시적으로 지정해요:

from typing import Any

from pydantic_evals import Case, Dataset

dataset = Dataset[str, str, Any](name='my_tests', cases=[Case(inputs='test')])

# 특이한 확장자용 명시적 형식
dataset.to_file('data.txt', fmt='yaml')
dataset_loaded = Dataset[str, str, Any].from_file('data.txt', fmt='yaml')

스키마 생성 오류

문제: 커스텀 평가자가 스키마 생성에 실패함

해결 방법: 평가자가 올바른 dataclass인지 확인해요:

from dataclasses import dataclass

from pydantic_evals.evaluators import Evaluator, EvaluatorContext


# ✅ 올바름
@dataclass
class MyEvaluator(Evaluator):
    value: int

    def evaluate(self, ctx: EvaluatorContext) -> bool:
        return True


# ❌ 오류: @dataclass 누락
class BadEvaluator(Evaluator):
    def __init__(self, value: int):
        self.value = value

    def evaluate(self, ctx: EvaluatorContext) -> bool:
        return True

다음 단계

더 알아보기 (Learn more)