6개의 오픈 소스 LLM 평가 프레임워크로 CI 게이트를 테스트했습니다. 머지 큐(merge queue)에서 살아남은 것은 단 두
요약
6개의 오픈 소스 LLM 평가 프레임워크를 실제 GitHub Actions 머지 큐 환경에서 테스트한 결과, 결정론적 체크와 속도가 핵심임을 밝힙니다. LLM-as-judge의 비결정론적 특성으로 인해 발생하는 CI 게이트의 불안정성 문제를 다룹니다.
핵심 포인트
- CI 게이트는 동일한 입력에 대해 항상 동일한 결과를 내는 결정론적 특성이 필수적임
- LLM-as-judge 기반 지표는 수치 드리프트로 인해 멀쩡한 PR을 차단할 위험이 있음
- 머지 큐 환경에서는 기능의 다양성보다 실행 속도와 안정성이 더 중요함
- Promptfoo와 DeepEval이 머지 큐 환경에서 가장 안정적인 성능을 보임
요약(TL;DR). 대부분의 "최고의 오픈 소스 LLM 평가 프레임워크" 모음집들은 기능(features)을 기준으로 순위를 매깁니다. 하지만 머지 큐(merge queue)가 중요하게 여기는 단 하나의 질문, 즉 "이 게이트가 두 번 실행했을 때 동일하게 통과하거나 실패하는가?"를 묻는 곳은 없습니다. 저는 이 프레임워크 중 6개를 실제 GitHub Actions 머지 큐에 연결하고 약 8개월 동안 프로덕션 PR(Pull Request)을 대상으로 실행했습니다. 깔끔하게 게이트 역할을 수행한 것들은 한 가지 공통된 속성을 공유했습니다. 바로 LLM-as-judge 점수는 비차단형 신호(non-blocking signals)로 유지하면서, 몇 초 안에 종료 코드(exit code)를 반환하는 결정론적 체크(deterministic checks)를 수행한다는 점입니다. 불안정한(flake) 것들은 그 반대였습니다. 거의 모든 지표가 judge 호출(judge call)이었고, 결과적으로 수치가 드리프트(drift)하면서 큐가 차단되었습니다. "우리 머지 큐에서 살아남은 순위"로 매기자, Promptfoo와 DeepEval이 앞서 나갔습니다. 먼저 요약된 목록을 살펴본 뒤, 도구별 노트, 그리고 어떤 경우에 이 중 그 무엇으로도 게이트를 설정해서는 안 되는지에 대해 설명하겠습니다.
순위를 결정지은 장애 사고
2년 전, 저는 0.8 임계값(threshold)을 가진 LLM-as-judge 지표를 우리 머지 큐에 적용했습니다. 데모에서는 깔끔해 보였습니다. 하지만 3주 후, 주말 동안 14개의 PR과 하나의 릴리스를 차단했습니다. judge가 동일하고 변경되지 않은 출력값에 대해 금요일에는 0.83을, 월요일에는 0.78을 부여했기 때문입니다. 동일한 프롬프트(prompt), 동일한 모델, 시드(seed)도 없었습니다. 저는 새벽 1시에 휴대폰으로 게이트를 중단시켰고, 우리는 문제없이 배포했습니다. 그 게이트가 "보호"하려 했던 회귀(regression)는 애초에 존재하지 않았습니다.
이것이 이 글 전체를 관통하는 관점입니다. CI 게이트의 임무는 단 하나입니다. 무언가 고장 났을 때는 실패하고, 그렇지 않을 때는 통과하며, 매번 동일한 방식으로 작동하는 것입니다. 평가 프레임워크(eval framework)가 세상에서 가장 좋은 지표를 가지고 있더라도, 그 지표가 흔들린다면(wobble) 나쁜 게이트가 될 수 있습니다. 기능 위주의 순위 목록들은 이를 놓치곤 합니다. 노트북(notebook) 환경에서는 비결정론(nondeterminism) 때문에 벌을 받는 일이 없기 때문입니다. 하지만 머지 큐는 새벽 1시, 팀 전체가 보는 앞에서 당신에게 벌을 줍니다.
품질 평가 (quality evals)가 쓸모없다고 주장하는 것이 아닙니다. 저 또한 그것들을 많이 실행합니다. 제가 주장하는 바는 머지 큐 (merge queue)는 매우 특수하고 가차 없는 환경이며, 그곳에 적합한 도구는 항상 가장 긴 메트릭 (metric) 목록을 가진 도구가 아니라는 점입니다. 깨끗한 PR을 차단하는 게이트는 팀원들이 이를 강제 머지 (force-merge)하도록 훈련시키며, 일단 팀이 일상적으로 게이트를 무시하고 강제 머지를 하기 시작하면 그 게이트는 더 이상 아무것도 보호하지 못하게 됩니다. 그저 모두가 무시하는 법을 배운 클릭 한 번을 추가할 뿐입니다. 이것이 제가 현재 가장 우선적으로 고려하는 실패 모드 (failure mode)입니다.
"머지 큐에서 살아남았다"는 의미
저를 괴롭혔던 순서대로 다섯 가지를 나열하면 다음과 같습니다:
- 결정론 (Determinism). 동일한 입력에는 동일한 판결이 내려져야 합니다. 고정된 시드 (fixed seed)가 없는 판사 모델 (judge call)은 결정론적이지 않으며, 어떤 임계값 튜닝 (threshold tuning)으로도 이를 완전히 해결할 수 없습니다.
- 속도 (Speed). 평가 실행 한 번에 4분이 추가되는데 하루에 40개의 PR을 머지해야 한다면, 당신은 큐 정체 (queue backup)를 구매한 셈입니다.
- 비용 (Cost). 판사 모델로 채점하는 메트릭은 실행당 토큰을 소모합니다. 여기에 PR 개수를 곱해 보세요. 어떤 달에는 아무도 예산에 반영하지 않은 실제 청구서가 되어 돌아옵니다.
- 연결 노력 (Wiring effort). 도구가 종료 코드 (exit code)를 반환하나요, 아니면 제가 그 주변에 통과/실패 (pass/fail) 로직을 직접 구축해야 하나요?
- 신호 품질 (Signal quality). 실패했을 때, 실제 회귀 (regression) 지점을 지목하나요, 아니면 단순히 수치가 낮아졌다고 보고하며 어깨를 으쓱할 뿐인가요?
아래의 모든 내용은 데모에서 메트릭이 얼마나 좋아 보이는가가 아니라, 이 기준들에 따라 등급이 매겨졌습니다. 다섯 가지 중 두 가지는 결정론과 그 부작용에 관한 것인데, 이는 저를 가장 잠 못 들게 했던 요소이기 때문입니다. 나머지 세 가지(속도, 연결, 신호)는 게이트가 작동하기 시작했을 때 이를 계속 유지할 가치가 있는지를 결정합니다.
실행 방식: 각 도구는 동일한 GitHub Actions 워크플로우 내에서 동일한 작은 골든 세트 (golden set, 지원 답변 기능을 위한 약 60개의 입력/기대 쌍)를 보호하며 머지 시 차단하도록 설정되었습니다. 저는 도구들을 하나씩 교체하며 세 가지 수치를 관찰했습니다: 변경되지 않은 입력에 대한 플레이크 비율 (flake rate), 실행당 추가된 시간, 그리고 월말의 토큰 비용입니다. 저는 변경되지 않은 입력에 대해 판결이 뒤집히지 않는 경우에만 차단 경로 (blocking path)에 메트릭을 유지했습니다. 이 규칙 하나만으로도 목록이 완전히 재편되었습니다.
모든 버전, 라이선스 및 메트릭(metric) 수는 2026년 중반 기준입니다. 모든 것은 계속 변하므로, 이를 신뢰하기 전에 각 리포지토리(repo)를 확인하십시오.
한눈에 보기
-
- Promptfoo (MIT). CI 훅 (CI hook): CLI 종료 코드 (exit code), JSON 출력, 그리고 유지 관리되는 GitHub Action. 메트릭 (Metrics): 수십 개의 어설션 (assertions), 결정론적 (deterministic) 및 등급 부여 (graded). LLM-judge: 선택 사항. 모든 스택에서 CLI 게이팅 (gating)을 수행하기에 가장 적합합니다.
-
- DeepEval (Apache-2.0). CI 훅 (CI hook): pytest 래퍼 (wrapper) (deepeval test run). 메트릭 (Metrics): 20개 이상. LLM-judge: 대부분의 경우 기본값. pytest 기반의 Python 게이트 (gates)에 가장 적합합니다.
-
- Future AGI (Apache-2.0). CI 훅 (CI hook): 자체 하네스 (harness)에서 evaluate() 호출. 메트릭 (Metrics): 50개 이상 (로컬 및 하이브리드 저지 (judge)). LLM-judge: 선택 사항. Python 또는 TypeScript에서 직접 구동하는 평가 SDK (eval SDK)로 가장 적합합니다.
-
- RAGAS (Apache-2.0). CI 훅 (CI hook): 스크립트에서 evaluate() 호출. 메트릭 (Metrics): 약 12개의 RAG 전용 메트릭. LLM-judge: 예, 대부분. RAG 품질 측정에 가장 적합합니다.
-
- Arize Phoenix (Elastic License 2.0). CI 훅 (CI hook): 스크립트에서 run_evals() 실행. 메트릭 (Metrics): 소수의 평가기 (evaluators). LLM-judge: 예. 트레이싱 (tracing)과 평가를 하나의 도구로 수행하기에 가장 적합합니다.
-
- MLflow evaluate (Apache-2.0). CI 훅 (CI hook): 스크립트에서 mlflow.evaluate() 실행. 메트릭 (Metrics): 12개 이상 (휴리스틱 (heuristic) 및 생성형 AI (genai)). LLM-judge: 선택 사항. 실험과 함께 기록되는 평가에 가장 적합합니다.
리포지토리 (Repo) 링크와 설치 명령어는 각 섹션에 포함되어 있습니다. 이제 세부 사항을 살펴보겠습니다.
결정을 내린 패턴
여섯 가지 도구가 모두 동일한 워크플로우에 포함되자, 순위 결정의 기준은 지표(metric)의 품질이 아니게 되었습니다. 결국 하나의 갈림길로 귀결되었습니다. 즉, 차단(blocking) 체크가 모델을 호출하느냐, 아니냐의 차이였습니다. 결정론적 체크(Deterministic checks; 문자열 일치, JSON-schema, 정규표현식(regex), 정확한 일치)는 매 실행마다 동일한 방식으로 통과하거나 실패했으며, 1초 미만으로 완료되었고 비용이 들지 않았습니다. 하지만 판단(Judge) 체크는 달랐습니다. 제가 실행한 모든 판단 기반 게이트(judge-based gate)는 8개월 동안 적어도 한 번은 임계값(threshold) 근처에서 표류(drift)했으며, 그중 두 개는 깨끗한 PR(Pull Request)을 적어도 한 번은 차단했습니다. 따라서 차단 경로(blocking path)에 결정론적 단언(deterministic assertions)을 배치하고, 판단 점수(judge scores)를 권고 경로(advisory lane)로 밀어낼 수 있게 해주는 도구들이 앞서 나갔습니다. 판단 우선 지표 세트(judge-first metric set)를 중심으로 구축된 도구들은 지표가 약해서가 아니라, 동일한 실행 사이에서 변동하는 점수는 머지 게이트(merge gate)를 유지할 수 없기 때문에 뒤처졌습니다. 여섯 가지 도구를 읽으면서 이 차이점을 염두에 두시기 바랍니다. 이는 왜 잘 알려진 RAG 라이브러리가 더 젊은 도구보다 아래에 위치하는지, 그리고 왜 추적 및 관측성(observability) 도구들의 지표가 괜찮음에도 불구하고 최하위에 위치하는지를 포함하여 전체 순위를 설명해 줍니다.
1. Promptfoo
Repo: github.com/promptfoo/promptfoo. License: MIT. Install: npm install -g promptfoo (또는 npx promptfoo로 실행).
정체: 명령줄 기반의 평가(eval) 및 레드팀(red-teaming) 도구입니다. YAML 파일에 프롬프트(prompts), 프로바이더(providers), 테스트 케이스를 기술하고, 명령어를 한 번 실행하면 통과/실패(pass/fail) 테이블을 얻을 수 있습니다. 2026년 중반 기준으로, 두 진영으로 나뉜 수십 가지의 내장 단언(assertions)을 제공합니다: 결정론적 단언(contains, equals, regex, is-json, starts-with, cost, latency)과 모델 등급 단언(model-graded; llm-rubric, factuality, answer-relevance, embedding에 의한 similarity)이 그것입니다.
이것이 CI를 제어(gate)하는 방식입니다. promptfoo eval은 어설션(assertion)이 실패하는 즉시 0이 아닌 종료 코드(nonzero exit code)를 반환합니다. 이것이 머지 큐(merge queue)에서 가장 중요한 핵심입니다. 0이 아닌 종료 코드는 곧 실패(red check)를 의미하며, 별도의 접착 코드(glue code)가 필요하지 않습니다. 또한 빌드 아티팩트(build artifact)로 보관할 수 있는 JSON, CSV 또는 HTML 파일을 생성하며, 평가 결과의 차이(eval diff)를 PR에 댓글로 남겨주는 관리되는 GitHub Action이 있어 리뷰어가 정확히 무엇이 변경되었는지 확인할 수 있습니다. 제 실행 결과, 결정론적 어설션(deterministic assertions)은 8개월 동안 단 한 번도 불안정하게 작동(flake)하지 않았습니다. 큐가 흔들렸던 유일한 주는 llm-rubric 어설션을 차단 경로(blocking path)에 두었을 때였으며, 해결책은 이를 다시 차단 경로 밖으로 옮기는 것이었습니다.
# promptfooconfig.yaml -> CI에서 다음 명령으로 실행: npx promptfoo eval -c promptfooconfig.yaml
prompts:
- "Answer the support question: {{question}}"
...
장점. 결정론적 어설션이 이 도구를 최상위에 위치시키는 이유입니다. contains 및 is-json은 불안정하게 작동하지 않고, 비용이 들지 않으며, 밀리초 단위로 실행됩니다. 또한 언어에 구애받지 않습니다(language-agnostic). Python 기반 팀, Go 기반 팀, TypeScript 기반 팀 모두 종료 코드를 사용하는 CLI이기 때문에 동일한 방식으로 연결할 수 있습니다. 설정(Config)은 보호 대상인 코드와 함께 버전 관리 시스템에 저장되므로, 잘못된 게이트(gate) 변경 사항도 동일한 diff에서 확인할 수 있습니다.
한계. 사례(case)가 수십 개를 넘어가면 YAML 파일이 방대해지며, 실행하기 전까지는 타입 안정성(type safety)을 보장받을 수 없습니다. 모델 기반 어설션(model-graded assertions)은 다른 모든 방식과 마찬가지로 판사(judge)의 비결정론적 특성을 그대로 가지고 있습니다. 따라서 머지를 차단하기 위해 llm-rubric에 의존한다면, 피하고자 했던 불안정성(flake)을 다시 도입하는 셈이 됩니다. 또한 Python 중심의 팀은 CI 이미지에 원치 않을 수도 있는 Node 의존성을 추가해야 합니다.
적합한 용도. 차단용 어설션은 결정론적으로 유지하고, 모델 기반 어설션은 권고 사항(advisory)으로 취급한다면, 어떤 스택에서든 프롬프트 및 출력 회귀(regression)를 제어하는 데 적합합니다.
2. DeepEval
Repo: github.com/confident-ai/deepeval. License: Apache-2.0. Install: pip install deepeval.
무엇인가: pytest와 유사한 느낌을 주도록 구축된 Python 평가 프레임워크입니다. 2026년 중반 기준으로 G-Eval (평문으로 정의하는 루브릭 메트릭), 답변 관련성 (answer relevancy), 충실도 (faithfulness), 환각 (hallucination), 문맥적 정밀도 및 재현율 (contextual precision and recall), 그리고 편향 (bias) 및 독성 (toxicity)과 같은 안전성 메트릭을 포함하여 20개 이상의 메트릭을 제공합니다. 이 중 대부분은 LLM이 판단합니다.
CI 게이트 방식: 일반적인 형태의 테스트 파일을 작성하고 deepeval test run test_file.py를 실행합니다. 내부적으로 pytest를 래핑(wrap)하고 있으므로, pytest의 종료 코드 (exit codes)와 JUnit XML 리포터를 그대로 사용할 수 있습니다. 만약 귀하의 CI가 이미 pytest를 이해하고 있다면, 이미 DeepEval도 이해하고 있는 것입니다. 이는 이 목록에 있는 모든 도구 중 Python 팀이 '그린 체크(통과)'를 받기 위한 가장 짧은 경로입니다. 제 실행 과정에서는 pytest 연결 설정을 완료하는 데 1시간도 채 걸리지 않았습니다. 테스트가 불안정하게 실패(flake)했을 때는 테스트 하네스 (harness)의 문제가 아니라, 판단 기반 메트릭 (judge-based metrics)들이 임계값 (thresholds) 근처에서 드리프트(drift)하며 발생한 문제였습니다.
# test_support_bot.py -> 실행 명령: deepeval test run test_support_bot.py
import pytest
from deepeval import assert_test
...
강점: pytest 래핑은 Python 팀이 가장 깔끔하게 진입할 수 있는 경로입니다. 메트릭별 임계값이 명시적이며 코드 내에 존재합니다. 이 목록 중 카탈로그가 가장 방대하여 메트릭을 직접 구현할 일이 거의 없으며, G-Eval을 사용하면 스코어러 (scorer)를 처음부터 작성하지 않고도 내부 규칙(예: "반드시 티켓 ID를 인용해야 함")을 인코딩할 수 있습니다.
한계: 주요 메트릭 대부분이 판단 모델 호출 (judge calls) 방식이므로, 제 장애 상황에서 나타난 불안정성(flakiness) 문제가 그대로 적용됩니다. 임계값을 느슨하게 유지하지 않으면 큐 (queue)가 그 대가를 치르게 됩니다. 대시보드와 공유 데이터셋이 필요해지면 동일한 팀에서 제공하는 호스팅 제품인 Confident AI로 유도되는 경향이 있습니다. 또한 카탈로그가 방대하다는 것은 관리해야 할 표면적도 넓다는 뜻입니다. 판단 모델 (judge-model)이 업그레이드되면, 사용자 측의 코드 변경 없이도 고정된 임계값 아래에서 점수가 변동될 수 있기 때문입니다.
적합한 대상: 이미 pytest로 게이팅을 수행하고 있으며 결정론적인 연결 (deterministic wiring)을 원하는 Python 팀. 단, 몇 가지 메트릭만 선택하고 판단 기반 메트릭은 임계값 근처에서 주의 깊게 다뤄야 합니다.
3. 미래의 AGI
Repo: github.com/future-agi/future-agi. License: Apache-2.0. Install: pip install ai-evaluation.
개요 (What it is). 오픈 소스 평가 SDK (fi.evals)입니다. 2026년 중반 기준으로, 이 프로젝트의 README에는 단 한 번의 evaluate() 호출로 실행 가능한 50개 이상의 평가 메트릭 (evaluation metrics)과 가드레일 스캐너 (guardrail scanners)가 나열되어 있으며, Python 및 TypeScript 클라이언트를 지원합니다. 이는 더 큰 오픈 소스 플랫폼의 일부이지만, CI 게이팅 (CI gating)만을 목적으로 한다면 평가 SDK만이 중요하므로, 저는 머지 큐 (merge queue) 앞에 이 SDK만을 배치했습니다.
CI 게이팅 방식 (How it gates CI). 전용 테스트 러너 (test runner)는 없습니다. Evaluator를 생성하고, 입력값에 대해 evaluate()를 호출한 뒤, 아래의 RAGAS와 동일한 형태인 반환된 점수에 대해 직접 assert(단언)를 작성해야 합니다. 게이트 (gate) 운영 시 알아두어야 할 한 가지 까다로운 점은, 문서화된 퀵스타트 (quickstart) 방식이 API 키로 인증하고 호스팅된 서비스(hosted service)를 대상으로 실행된다는 것입니다. 따라서 단순하게 설정하면 차단 경로 (blocking path)에 네트워크 호출이 포함됩니다. 이 SDK는 로컬 메트릭 실행 (local metric execution)도 지원하는데, 머지 게이트 (merge gate)를 위해서는 이 모드를 사용해야 합니다. 결정론적인 (deterministic) 로컬 메트릭은 판사 (judge) 모델처럼 불안정하게 작동(flake)하지 않으며, 실행당 토큰 비용을 발생시키지 않기 때문입니다. 하이브리드 판사 (hybrid judge) 모드를 켜면 이 목록에 있는 다른 모든 도구와 마찬가지로 비결정론적인 (nondeterminism) 특성을 그대로 물려받게 됩니다. 실제로 가장 많은 시간이 소요된 부분은 evaluate()를 감싸는 하네스 코드 (harness code)를 직접 작성해야 했다는 점인데, 이 도구는 러너 (runner)를 직접 제공하지 않기 때문입니다.
# pip install ai-evaluation (module: fi.evals)
from fi.evals import Evaluator
# 문서화된 퀵스타트는 API 키로 인증합니다 (호스팅된 실행)
...
강점 (Strengths). 메트릭을 로컬 모드에서 실행할 때, 게이트가 원하는 것은 결정론적인 점수입니다. 즉, 동일한 입력에 대해 동일한 점수가 나오며 PR당 토큰 비용이 발생하지 않습니다. 로컬 점수 산출과 판사 (judge) 점수 산출 모두에 대해 단일한 evaluate() 인터페이스를 제공하므로 하네스 (harness)를 작게 유지할 수 있습니다. 또한 TypeScript 클라이언트를 지원하므로, Node CI 작업이 Python을 호출(shelling out)하지 않고도 게이팅을 수행할 수 있는데, 이는 이 목록 내에서 드문 사례입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기