평가 하네스
JamJet eval로 출력 품질을 점수화하고, 회귀 테스트를 실행하며, CI를 제어하세요.
Eval Harness
JamJet은 출력 품질을 측정하고 보장하기 위한 내장 평가 시스템을 제공합니다 — 빠른 임시 검증부터 전체 CI 회귀 테스트 스위트까지 지원합니다.
평가가 중요한 이유
LLM 출력은 확률적입니다. 동일한 워크플로우가 대부분의 입력에서는 훌륭한 결과를 생성하지만 엣지 케이스에서는 실패할 수 있습니다. JamJet eval은 다음을 제공합니다:
- LLM-as-judge — 별도의 모델이 평가 기준에 따라 출력 품질을 점수화
- Assertions — 구조적 검사(길이, 필드 존재 여부, 형식)
- 지연 시간 + 비용 게이트 — CI에서 SLA 적용
- 회귀 테스트 스위트 — 프로덕션 배포 전 회귀 감지
인라인 평가(워크플로우)
워크플로우에 eval 노드를 추가하여 출력을 점수화하고 실패 시 재시도합니다:
nodes:
check-quality:
type: eval
scorers:
- type: llm_judge
rubric: "답변이 정확하고 완전하며 200단어 이하인가?"
min_score: 4 # 1-5 척도
model: claude-haiku-4-5-20251001
- type: assertion
check: "len(output.answer) > 0"
- type: latency
max_ms: 5000
on_fail: retry_with_feedback
max_retries: 2
next: endon_fail: retry_with_feedback로 설정하면, 평가자의 피드백이 자동으로 다음 모델 호출의 프롬프트에 주입되어 자체 개선 루프가 생성됩니다.
데이터셋 평가(CLI)
배치 평가를 위해 JSONL 데이터셋을 생성합니다:
{"id": "q1", "input": {"query": "JamJet이 무엇인가요?"}, "expected": {"topic": "runtime"}}
{"id": "q2", "input": {"query": "어떻게 설치하나요?"}, "expected": {"topic": "install"}}
{"id": "q3", "input": {"query": "어떤 모델을 지원하나요?"}, "expected": {}}실행:
jamjet eval run dataset.jsonl \
--workflow workflow.yaml \
--rubric "답변이 정확하고 유용한가?" \
--min-score 4 \
--assert "len(output.answer) >= 50" \
--latency-ms 3000 \
--concurrency 10 \
--fail-below 0.9Running 50 eval rows... ████████████████████ 50/50
┌─────────┬────────────┬───────┬──────────┬────────────────────┐
│ Row │ Status │ Score │ Latency │ Note │
├─────────┼────────────┼───────┼──────────┼────────────────────┤
│ q1 │ ✓ passed │ 4.8 │ 512ms │ │
│ q2 │ ✓ passed │ 4.2 │ 623ms │ │
│ q3 │ ✗ failed │ 2.1 │ 891ms │ Answer too vague │
└─────────┴────────────┴───────┴──────────┴────────────────────┘
Results: 49/50 passed (98.0%) — above threshold 90.0% ✓종료 코드는 통과 시 0, 실패 시 1 — CI에서 바로 사용 가능합니다.
CI 통합
GitHub Actions 워크플로우에 추가하세요:
- name: Run eval suite
run: |
jamjet eval run evals/core.jsonl \
--workflow workflow.yaml \
--rubric "Is the answer accurate and complete?" \
--fail-below 0.85
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
JAMJET_URL: http://localhost:7700tip: CI 작업에서 평가를 실행하기 전에 JamJet 개발 런타임을 시작하세요:
jamjet dev &실행 후sleep 2로 초기화 대기.
Python 평가 API
커스텀 평가 로직을 위해 Python 평가 패키지를 사용하세요:
import asyncio
from jamjet.eval import EvalDataset, EvalRunner
from jamjet.eval.scorers import LlmJudgeScorer, AssertionScorer, LatencyScorer
dataset = EvalDataset.from_file("evals/core.jsonl")
runner = EvalRunner(
workflow_path="workflow.yaml",
runtime_url="http://localhost:7700",
scorers=[
LlmJudgeScorer(
rubric="Is the answer accurate and helpful?",
model="claude-haiku-4-5-20251001",
min_score=4,
),
AssertionScorer(check="len(output['answer']) >= 50"),
LatencyScorer(max_ms=3000),
],
concurrency=10,
)
results = asyncio.run(runner.run(dataset))
runner.print_summary(results)
# 전체 통과율 확인
pass_rate = sum(1 for r in results if r.passed) / len(results)
assert pass_rate >= 0.9, f"Eval failed: {pass_rate:.0%} pass rate"커스텀 스코어러
BaseScorer를 상속하여 직접 스코어러를 작성하세요:
from jamjet.eval.scorers import BaseScorer, ScorerResult
class ExactMatchScorer(BaseScorer):
async def score(
self,
output: dict,
*,
expected: dict,
duration_ms: float,
cost_usd: float,
input_data: dict,
) -> ScorerResult:
answer = output.get("answer", "")
expected_answer = expected.get("answer", "")
passed = answer.strip().lower() == expected_answer.strip().lower()
return ScorerResult(
scorer="exact_match",
passed=passed,
score=1.0 if passed else 0.0,
message=None if passed else f"Expected '{expected_answer}', got '{answer}'",
)스코어러 유형
LLM 평가자
별도 모델을 사용하여 1–5점 척도로 출력을 평가합니다:
- type: llm_judge
rubric: "Is the answer accurate, complete, and under 200 words?"
min_score: 4 # 1–5 (5 = 완벽)
model: claude-haiku-4-5-20251001평가자는 입력, 출력, 평가 기준을 받아 score (1–5)와 reason을 포함한 JSON 객체를 반환합니다.
어서션
output과 expected에 대해 평가되는 Python 표현식:
- type: assertion
check: "len(output.answer) > 0"
# 다중 어서션
- type: assertion
check: "'sources' in output"
- type: assertion
check: "output.confidence >= 0.7"지연 시간
실행이 시간 제한 내에 완료되었는지 확인합니다:
- type: latency
max_ms: 3000비용
실행이 비용 제한 내에 유지되었는지 확인합니다:
- type: cost
max_usd: 0.05출력 형식
추가 분석을 위해 결과를 파일로 저장합니다:
jamjet eval run dataset.jsonl \
--workflow workflow.yaml \
--output results.json{
"summary": {
"total": 50,
"passed": 47,
"failed": 3,
"pass_rate": 0.94,
"avg_latency_ms": 612,
"avg_cost_usd": 0.0003
},
"rows": [
{
"id": "q1",
"passed": true,
"scorers": [
{ "scorer": "llm_judge", "passed": true, "score": 4.8 }
],
"duration_ms": 512,
"cost_usd": 0.00023
}
]
}