ElGraph.Eval (ElGraph v0.3.0)

Copy Markdown View Source

경량 평가 하네스 (트렌드 보고서 Tier 3.8).

데이터셋의 각 케이스를 그래프에 invoke하고 스코어러로 채점한다. 풀 eval 프레임워크가 아니라 ElGraph의 차별점(결정적 실행·체크포인트)을 활용한 회귀 평가 훅이다 — ElGraph.invoke/3 공개 API 위의 순수 오케스트레이션이라 어떤 그래프에도 붙는다.

cases = [%{input: %{n: 2}, expect: %{n: 4}}, ...]
ElGraph.Eval.run(graph, cases)
#=> %{total: ..., passed: ..., pass_rate: ..., results: [...]}

스코어러는 (invoke_result, case) -> %{pass: boolean, score: number}. 기본은 :expect 맵이 결과 상태의 부분집합인지 본다. LLM-judge는 llm_judge/2로 만든다.

Summary

Functions

베이스라인 요약과 후보 요약을 비교해 회귀/개선을 가려낸다.

LLM-as-judge 스코어러를 만든다. 판정 모델이 PASS로 시작하는 답을 내면 통과로 본다.

JSONL 데이터셋을 로드한다 — 한 줄당 JSON 객체 하나, 빈 줄은 건너뛴다.

체크포인트 재생(time-travel) 평가.

데이터셋을 평가하고 요약을 반환한다.

Types

case_spec()

@type case_spec() :: %{
  :input => map(),
  optional(:expect) => map(),
  optional(any()) => any()
}

scorer()

@type scorer() :: (term(), case_spec() ->
               %{:pass => boolean(), optional(:score) => number()})

Functions

compare(baseline, candidate)

@spec compare(map(), map()) :: map()

베이스라인 요약과 후보 요약을 비교해 회귀/개선을 가려낸다.

케이스는 :id가 있으면 id로, 없으면 인덱스로 매칭한다. 회귀는 베이스라인에서 통과했지만 후보에서 실패한 케이스, 개선은 그 반대다. 반환: %{pass_rate_delta, regressions, improvements}.

llm_judge(arg, criteria)

@spec llm_judge(
  {module(), term()},
  String.t()
) :: scorer()

LLM-as-judge 스코어러를 만든다. 판정 모델이 PASS로 시작하는 답을 내면 통과로 본다.

load_jsonl(path)

@spec load_jsonl(String.t()) :: [map()]

JSONL 데이터셋을 로드한다 — 한 줄당 JSON 객체 하나, 빈 줄은 건너뛴다.

최상위 "input"/"expect" 키를 atom 키(:input/:expect)로 바꾸고, 그 안쪽 맵의 키도 atom으로 변환한다(String.to_atom). 따라서 로드된 케이스는 run/3의 기본 스코어러와 바로 호환된다 — 그래프 상태 키(atom)와 일치. 신뢰된 데이터셋 전용이다(String.to_atom은 atom 테이블에 쌓인다).

replay_eval(graph, arg, thread_id, step, scenarios, opts \\ [])

@spec replay_eval(
  ElGraph.Graph.t(),
  {module(), term()},
  String.t(),
  non_neg_integer(),
  [map()],
  keyword()
) :: map()

체크포인트 재생(time-travel) 평가.

thread의 step 체크포인트를 가져와 각 시나리오마다 분기(fork)·재실행하고 채점한다. 시나리오의 :resume 맵은 분기 시작 상태에 병합돼 "만약 이 step에서 상태가 달랐다면"을 결정적으로 재현한다. 각 시나리오는 새 thread_id로 돌아 원본 thread는 보존된다. 실행은 공개 API(ElGraph.Executor.resume_from/3, 체크포인터 get/3)만 쓴다.

시나리오 맵은 :resume 외에 스코어러 필드(:expect 등)를 갖는다. 반환은 run/3와 같은 요약(%{total, passed, pass_rate, metrics, results})이다.

run(graph, cases, opts \\ [])

@spec run(ElGraph.Graph.t(), [case_spec()], keyword()) :: map()

데이터셋을 평가하고 요약을 반환한다.

옵션:

  • :score — 스코어러 교체 (기본은 :expect 부분집합 검사).
  • :max_concurrency — 동시 평가 케이스 수 (기본 1 = 순차). >1이면 Task.async_stream으로 병렬 채점하되 결과는 데이터셋 순서를 유지한다. 한 케이스가 크래시·타임아웃해도 실패로 채점될 뿐 전체 실행을 멈추지 않는다.

요약 맵: %{total, passed, pass_rate, metrics, results}. :metrics는 케이스별 :score에 대한 집계 %{pass_rate, mean_score, min_score, max_score, median_score}다.