개발한 LLM 애플리케이션이 자체 평가 벤치마크에서 94점을 기록했습니다. 자신 있게 프로덕션에 배포합니다. 3주 뒤, 벤치마크에서 전혀 테스트되지 않았던 특정 엣지 케이스에서 모델이 존재하지 않는 출처를 당당하게 지어내는(citation fabrication/hallucination) 현상이 고객에 의해 발견됩니다.

이것이 바로 ’단일 평가 방식의 함정(single-method evaluation trap)’입니다. 아무리 정교하게 설계된 평가자(evaluator)라 할지라도 사각지대는 반드시 존재합니다. 해결책은 더 뛰어난 단일 평가자를 찾는 것이 아닙니다. 서로의 사각지대를 상호 보완해 주는 평가자들의 시스템을 구축하는 것입니다.

5가지 평가 방법

Langfuse는 5가지 평가 방법을 제공합니다. 대다수의 팀은 이 중 하나만 선택합니다. 하지만 프로덕션 환경의 실패를 실제로 사전에 잡아내는 팀들은 이 5가지 방식을 모두 계층형 아키텍처(layered architecture)로 유기적으로 결합합니다.

┌──────────────────────────────────────────────────────────┐
│              EVALUATION MATURITY LADDER                   │
│                                                          │
│  Level 0: "We log traces"                                │
│  Level 1: "We run one LLM-as-a-Judge evaluator"          │
│  Level 2: "We combine automated + manual scoring"        │
│  Level 3: "We have a full evaluation pipeline"           │
│  Level 4: "Evaluation drives deployment decisions"       │
│                                                          │
│  Most teams stall at Level 1.                            │
└──────────────────────────────────────────────────────────┘

각 방법의 역할, 활용 시점, 그리고 이들이 어떻게 결합되는지 살펴보겠습니다.

방법 1: LLM-as-a-Judge — 확장 가능한 시맨틱(Semantic) 평가 계층

LLM-as-a-Judge는 프로덕션 모니터링의 핵심 주력(workhorse)입니다. 평가 기준(rubric, 루브릭)을 정의하고, 판사 역할을 할 모델(Judge model)을 선정한 뒤, 모든 출력(또는 샘플링된 출력)을 자동으로 채점하도록 설정합니다.

Judge 모델은 입력값, 출력값, 그리고 평가 루브릭을 전달받아 구조화된 점수와 그에 대한 추론 근거(reasoning)를 반환합니다. 전형적인 평가 프롬프트는 다음과 같은 형태입니다:

Rate the helpfulness of this response on a scale of 1-5.

Criteria:
- 1: Completely unhelpful, irrelevant, or harmful
- 3: Partially helpful but missing key information
- 5: Fully addresses the question with accurate, actionable information

User question: {{input}}
Response: {{output}}

점수 유형(Score types)은 생각보다 훨씬 중요합니다. 유용성(helpfulness)이나 충실도(faithfulness)와 같은 연속적인 평가 차원에는 수치형(Numeric) 점수를 사용하십시오. 품질 분류 체계에 매핑되는 이산 레이블(correct, partially_correct, incorrect)이 필요한 경우에는 범주형(Categorical) 점수를 사용합니다. 응답에 개인건강정보(PHI)가 포함되어 있는가? 스코프를 위반했는가? 사용자가 어시스턴트의 의견에 동의하지 않는가? 와 같은 이진 정책 판단에는 불리언(Boolean) 점수를 사용하십시오.

옵저베이션 수준(Observation-level)으로의 전환은 필수적입니다. 기존 트레이스 수준(trace-level) 평가자는 지원 중단(deprecated)되었습니다. 옵저베이션 수준 평가자는 작업 단위(operation-level)의 정밀도를 제공합니다. 즉, 전체 워크플로우를 통째로 채점하는 대신 최종 LLM 호출만 평가하거나, 검색(retrieval) 단계만 평가하거나, 특정 도구 호출(tool invocation)만을 골라 평가할 수 있습니다. 이는 비용을 절감하고 평가 시그널의 순도를 높입니다. 다만 전체 요청/응답 컨텍스트가 필요한 경우, 해당 요약 정보를 담고 있는 논리적 루트(root) 옵저베이션을 타깃팅해야 하는 트레이드오프가 있습니다.

정확도 수치는 신뢰할 만합니다. 강력한 판사 모델(GPT-4o 클래스, Claude Sonnet, Gemini Pro 등)은 인간 평가자와 80~90%의 일치율(agreement)을 달성합니다. 이는 훈련된 두 명의 인간 평가자 간 상호 일치율(inter-annotator agreement)에 필적하는 수준입니다. 핵심 요구사항은 점수가 안정적으로 파싱될 수 있도록 판사 모델이 구조화된 출력(Structured Outputs)을 지원해야 한다는 점입니다.

비용 또한 충분히 감당 가능한 수준입니다. 일반적인 평가 1회당 비용은 약 $0.01~$0.10 수준입니다. 일일 10,000건의 요청에 대해 5% 샘플링을 적용하면 지속적인 품질 모니터링 비용은 하루 $5~$50에 불과합니다. 대부분의 엔지니어링 팀은 이것이 전체 시스템 스택에서 가장 비용 효율적인 품질 시그널임을 체감합니다.

LLM-as-a-Judge 단독 사용이 실패하는 경우

세 가지 대표적인 실패 모드(failure modes)가 있습니다:

  1. 루브릭 드리프트(Rubric drift) — 루브릭이 새로운 유형의 실패를 포괄하지 못합니다. 루브릭에 해당 엣지 케이스가 정의되어 있지 않기 때문에, Judge 모델은 결함이 있는 출력임에도 “정상(correct)“으로 판정합니다.
  2. 판사 모델 편향(Judge bias) — 판사 모델 자체의 체계적인 선호(장황함 편향/verbosity bias, 위치 편향/position bias 등)가 애플리케이션의 결함 패턴과 결합되어 왜곡된 점수를 유발합니다.
  3. 결정론적 검증의 누락(Deterministic checks missed) — JSON 유효성 검사, 스키마 준수 여부, 특정 키워드의 정확한 포함 여부 등은 시맨틱(의미론적) Judge 모델에게 맡기기에는 과도할 뿐 아니라 신뢰하기 어렵습니다.

바로 이 지점에서 나머지 4가지 방법이 개입해야 합니다.

방법 2: 코드 평가자(Code Evaluators) — 결정론적 안전 기반(Deterministic Floor)

코드 평가자는 Langfuse 내부에서 커스텀 Python 또는 TypeScript 로직을 직접 실행합니다. LLM을 거치지 않으므로 모호성이 없으며, 평가 건당 비용도 들지 않습니다. 다음과 같은 객관적이고 이진적인(binary) 검사에 활용됩니다:

  • 출력이 유효한 JSON 형식인가?
  • 필수 필드를 모두 포함하고 있는가?
  • 응답이 기대했던 함수로의 도구 호출을 포함하고 있는가?
  • 출력 길이가 허용 범위 내에 있는가?
  • 정규식(Regex) 패턴과 일치하는가?

함수 계약(Function contract)이 매우 깔끔합니다. EvaluationContext를 받아 하나 이상의 점수를 포함한 EvaluationResult를 반환하는 evaluate 함수를 작성하면 됩니다:

def evaluate(ctx: EvaluationContext) -> EvaluationResult:
    output = ctx.observation.output
    is_valid_json = False
    try:
        json.loads(output)
        is_valid_json = True
    except (json.JSONDecodeError, TypeError):
        pass

    return EvaluationResult(
        scores=[Score(
            name="JSON Valid",
            value=is_valid_json,
            data_type="BOOLEAN",
            comment="Output is valid JSON." if is_valid_json else "Output is not valid JSON.",
        )]
    ]

제약 조건은 엄격하며 의도적으로 설계되었습니다. 표준 라이브러리(Standard library)만 허용되며 서드파티 패키지는 사용할 수 없습니다. 네트워크 접근이 차단되며 2초의 런타임 제한이 적용됩니다. 이는 코드 평가자가 수집(ingest) 시점에 일치하는 모든 옵저베이션에 대해 즉각 실행되기 때문입니다. 만약 느리거나 외부 의존성을 가진다면 데이터 수집 파이프라인 전체에 병목(blocking)이 발생할 것입니다.

코드 평가자가 수행해야 할 역할에 비추어 보면 2초의 제한 시간은 매우 넉넉합니다. 코드 평가자가 2초나 걸리는 작업을 수행하고 있다면, 사용 방식이 잘못된 것입니다. 정규식 매칭, JSON 파싱, 필드 존재 여부 확인, 문자열 포함 검사 등은 마이크로초(µs) 단위로 완료되는 연산입니다. 더 무거운 로직이 필요하다면 자체 파이프라인에서 실행한 뒤 Scores API를 통해 결과를 수집해야 합니다.

LLM-as-a-Judge와의 결합 방식

그 핵심 패턴은 ’심층 방어(defense in depth)’입니다:

Observation arrives
    → Code Evaluator: "Is the output structurally valid?"
        (FAIL → score immediately, skip LLM judge)
    → LLM-as-a-Judge: "Is the output semantically correct?"
        (scores on rubric dimensions)

코드 평가자는 게이트키퍼(gatekeeper) 역할을 수행합니다. 출력이 기본적인 구조적 유효성 검사조차 통과하지 못했다면, 굳이 $0.05를 들여 Judge 모델에게 그 응답이 “유용한지” 물어볼 이유가 없습니다. 이것이 가장 비용 효율적인 평가 트리아지(evaluation triage, 선별 분류)입니다.

방법 3: 어노테이션 큐(Annotation Queues) — 그라운드 트루스(Ground Truth) 팩토리

자동화된 평가자에게는 반드시 캘리브레이션(calibration, 보정 및 검증)이 필요합니다. 사람이 직접 검증한 그라운드 트루스(Ground Truth, 정답 레이블)와 비교해 본 적이 없다면 LLM 판사의 점수를 결코 신뢰할 수 없습니다. 어노테이션 큐는 바로 이러한 그라운드 트루스를 구축하기 위한 전문 워크플로우 엔진입니다.

배치 중심(batch-oriented)이자 키보드 친화적인 워크플로우를 제공합니다. 큐를 생성하고, Score Config(어노테이션할 평가 차원)를 연결한 뒤, 트레이스나 옵저베이션을 추가하고 도메인 전문가를 배정하여 채점을 진행합니다. 단축키(이동을 위한 →/←, 점수 선택용 1–9, 다음 항목 진행을 위한 Cmd+Enter)를 지원하므로 한 세션에서 수백 개의 항목을 신속하게 라벨링할 수 있습니다.

진정한 가치는 캘리브레이션 데이터에 있습니다. 100~200개의 항목에 대한 인간 어노테이션이 완료되면, LLM 판사의 점수를 인간 평가자의 점수와 정량적으로 비교할 수 있습니다. 할루시네이션(환각) 판사 모델이 인간 리뷰어와 72%의 일치율을 보인다면 해당 평가자를 어느 정도 신뢰할 수 있는지 명확히 알 수 있습니다. 반면 94%의 일치율을 보인다면 프로덕션급 평가자를 확보한 것입니다. 이러한 정밀 캘리브레이션은 어노테이션 워크플로우 없이는 결코 얻을 수 없습니다.

수정된 출력(Corrected outputs)의 가치는 과소평가되어 있습니다. 어노테이션 큐에서는 모델이 ’마땅히 생성했어야 하는 정답’을 첨부할 수 있습니다. 이는 회귀 테스트(regression testing)를 위한 결정적인 자산이 됩니다. 정제된 수정 출력 데이터셋을 구축해 두면, 프롬프트나 기반 모델을 변경할 때마다 해당 데이터셋을 기반으로 즉시 실험을 실행하여 품질 저하를 사전에 검증할 수 있습니다.

방법 4: UI를 통한 수동 채점(Manual Scores via UI) — 애드혹 비상구(Ad-Hoc Escape Hatch)

모든 평가가 대규모 배치 워크플로우에 맞는 것은 아닙니다. 특정 트레이스를 디버깅하다가 문제점을 즉시 표시해 두고 싶을 때가 있고, 이해관계자가 특정 대화를 검토하면서 플래그를 달고 싶을 때도 있습니다.

UI를 통한 수동 채점은 어노테이션 큐를 보완하는 경량화된 수단입니다. 트레이스를 열고, Annotate 버튼을 클릭한 뒤, 정의된 Score Config를 선택하여 평가를 기록하기만 하면 됩니다. 큐 생성, 배치 구성, 워크플로우 오버헤드가 전혀 없습니다.

사용 사례는 좁지만 매우 중요합니다. Score Config 생성 외에는 사전 설정이 전혀 필요 없는 유일한 방법입니다. 이제 막 평가 체계를 도입하기 시작한 팀에게는 인간 평가 점수를 시스템에 신속하게 축적하는 가장 빠른 지름길입니다.

실험 통합은 숨겨진 강력한 기능입니다. UI나 SDK를 통해 실험(experiments)을 실행할 때, 비교(compare) 뷰에서 결과를 보며 즉시 어노테이션을 수행할 수 있습니다. 검토하는 동안 입력값, 출력값, 자동 채점 점수 등 실험의 전체 컨텍스트가 유지되며, 점수를 추가함에 따라 요약 지표(Summary metrics)가 실시간으로 갱신됩니다. 이로써 스프레드시트로 번거롭게 진행하던 실험 분석이 완전히 통합된 엔지니어링 워크플로우로 전환됩니다.

방법 5: API/SDK를 통한 점수 수집(Scores via API/SDK) — 통합 백본(Integration Backbone)

외부 파이프라인, 커스텀 스크립트, 브라우저 피드백 위젯, CI 빌드 작업 등 시스템 외부의 모든 데이터는 Scores API를 통해 Langfuse로 수집됩니다. 바로 이 방법 덕분에 나머지 4가지 방법이 유기적으로 조합될 수 있습니다.

3가지 연결 계층(Attachment levels). 점수는 트레이스(전체적인 품질), 옵저베이션(특정 연산), 또는 세션(대화 세션 전체)에 유연하게 연결될 수 있습니다. 이를 통해 유스케이스에 가장 부합하는 정밀도로 점수를 기록할 수 있습니다.

4가지 데이터 타입이 모든 채점 패러다임을 지원합니다:

타입 입력값 유스케이스
Numeric (수치형) float 연속적인 차원 평가 (0.0-1.0)
Categorical (범주형) string 이산형 레이블 (“correct”, “partial”, “wrong”)
Boolean (불리언) float (0/1) 이진 결정 (true/false)
Text (텍스트) string (1-500자) 리뷰어의 자유 형식 메모

프론트엔드 피드백을 위한 브라우저 SDK. @langfuse/browser 패키지를 사용하면 공개 키(public key) 하나만으로 좋아요/싫어요(thumbs up/down), 별점 평가, “답변 오류 신고” 플래그와 같은 사용자 피드백을 직접 캡처할 수 있습니다. 백엔드 프록시가 필요 없으며, 각 점수는 인제스천(ingestion) API를 통해 즉시 전송됩니다.

멱등성(Idempotency)을 통한 중복 점수 방지. 점수는 id, name, toDate(timestamp) 세 가지 필드로 고유하게 식별됩니다. 세 필드가 모두 일치해야만 덮어쓰기(overwrite)가 발생합니다. 평가를 재실행하는 파이프라인에서는 안정적인 id(예: trace_id-score_name)를 멱등성 키로 사용하는 것이 필수적입니다. 이를 설정하지 않으면 파이프라인 재실행 시 중복 점수가 지속적으로 누적됩니다.

Score Config 강제를 통한 데이터 표준화. 점수 생성 시 configId를 참조하면 사전 정의된 허용 범위(수치형), 허용된 카테고리 목록(범주형), 또는 데이터 타입 제약 조건을 기준으로 검증이 수행됩니다. 이는 무분별한 자유 텍스트 데이터의 난립을 방지하고, 쿼리 및 분석이 가능한 체계적인 품질 데이터베이스를 구축하는 핵심 차별점입니다.

복합 계층 아키텍처(The Composed Architecture)

5가지 평가 방법은 서로를 대체하는 대안이 아닙니다. 상호 보완적인 ’계층(Layers)’입니다:

┌────────────────────────────────────────────────────────────┐
│                  PRODUCTION OBSERVATION                     │
│                                                            │
│  1. Code Evaluator runs first (structural gatekeeper)      │
│     → FAIL: attach boolean score, done                     │
│     → PASS: continue                                       │
│                                                            │
│  2. LLM-as-a-Judge scores on semantic rubrics              │
│     → Attach numeric/categorical scores                    │
│                                                            │
│  3. Sampling selects observations for human review         │
│     → Annotation Queue scores calibrate the judge          │
│                                                            │
│  4. External pipelines ingest additional scores via API    │
│     → User feedback, CI checks, custom metrics             │
│                                                            │
│  5. All scores converge in a single quality dashboard      │
│     → Trends, correlations, drift detection                │
│                                                            │
└────────────────────────────────────────────────────────────┘

실험 루프(The Experiment Loop)

개발 단계에서도 라이브 트래픽 대신 ’실험(Experiment)’을 통해 동일한 계층 스택을 따릅니다:

  1. 테스트 데이터셋 구축 — 테스트 입력과 기대 출력을 포함한 데이터셋 생성
  2. 실험 실행 — UI 또는 SDK를 통해 각 데이터셋 항목에 대해 애플리케이션 실행
  3. 코드 평가자 검증 — 구조적 유효성 확인 (자동, 즉각적)
  4. LLM-as-a-Judge 채점 — 시맨틱 품질 평가 (자동, 수 분 소요)
  5. 비교 뷰에서 인간 어노테이션 — 정밀 타깃팅된 그라운드 트루스 확보 (수동, 집중 검토)
  6. 실험 실행 결과 비교 — 프롬프트 변경이 점수를 개선했는가, 아니면 저하시켰는가?

이 루프가 바로 프로덕션에 배포되기 전 성능 퇴행(regression)을 사전에 포착하는 핵심 단계입니다. 여기서 가장 중요한 통찰은 5단계를 절대 건너뛰지 않는 것입니다. 인간의 캘리브레이션이 결여된 자동 점수는 의미 없는 숫자에 불과합니다.

게임 체인저: 옵저베이션 수준(Observation-Level)의 정밀도

Langfuse 평가 시스템에서 단일 아키텍처 개선으로 가장 결정적인 변화는 트레이스 수준에서 옵저베이션 수준 평가자로의 전환입니다.

트레이스는 입도가 거칩니다(coarse-grained). 단일 트레이스 내에 5번의 LLM 호출, 3번의 검색 단계, 2번의 도구 호출이 포함되어 있을 수 있습니다. 전체 트레이스를 대상으로 “유용성(helpfulness)“을 채점하면 최종 응답의 품질과 중간 단계들의 개별 품질이 뒤섞여 버립니다.

옵저베이션은 매우 정밀합니다(fine-grained). 최종 LLM 생성에 대해서는 유용성(helpfulness)을, 검색 단계에 대해서는 관련성(relevance)을, 도구 호출에 대해서는 인자 유효성(argument validity)을 정확히 타깃팅하여 평가할 수 있습니다. 동일한 트레이스 내에서도 서로 다른 작업에 대해 각기 다른 평가자가 독립적으로 동작합니다.

복합 필터링을 통한 정밀 타깃팅. 옵저베이션 필터(타입, 이름, 메타데이터)와 트레이스 필터(userId, sessionId, 태그, 버전)를 결합할 수 있습니다. 예를 들어, “plan=enterprise 속성을 가진 사용자의 customer-support 태그 대화에서 생성된 모든 LLM 세부 작업에 대해 유용성을 평가하라”와 같은 조건을 단 하나의 평가자 설정으로 지정할 수 있습니다.

마이그레이션 기한은 엄연한 현실입니다. 기존 트레이스 수준 평가자는 지원 중단(deprecated)되었으며 Langfuse v4 제거 로드맵에 포함되어 있습니다. 아직 트레이스 수준 평가자를 사용하고 있다면 문서화된 업그레이드 가이드를 참고하여 안정적인 옵저베이션 수준 API로 이전해야 합니다.

과거 데이터 소급 평가(Backfill Capability)

옵저베이션 수준 LLM-as-a-Judge는 과거 이력 데이터(historical data)에 대해서도 실행할 수 있습니다. 이는 다음과 같은 의미를 갖습니다:

  1. 평가 체계가 전혀 없는 상태로 애플리케이션을 먼저 출시함
  2. 3개월 뒤, 새롭게 발견된 실패 모드에 대응하는 평가자를 설계함
  3. 과거에 수집된 일치하는 모든 옵저베이션에 점수를 소급하여 채점함

실행 워크플로우는 간단합니다: Traces 테이블을 열고 원하는 기간으로 필터링한 뒤, 대상 행을 선택하고 Actions → Evaluate를 클릭합니다. 평가자가 선택된 옵저베이션들을 대상으로 실행되며 점수가 소급 적용됩니다.

이는 모니터링을 설정한 시점뿐만 아니라 배포의 전체 라이프사이클에 걸쳐 품질을 지속적으로 입증해야 하는 규제 산업 환경(의료, 제약, 금융 등)에서 독보적인 위력을 발휘합니다.

평가 시스템 자체의 디버깅

LLM-as-a-Judge가 실행될 때마다 langfuse-llm-as-a-judge 환경에 전체 실행 트레이스가 생성됩니다. 코드 평가자 역시 실행될 때마다 langfuse-code-eval 환경에 트레이스를 생성합니다. 두 환경 모두 기본 트레이싱 뷰에서는 숨겨져 있으며, 명시적으로 필터를 적용하여 확인할 수 있습니다.

덕분에 평가 과정 자체에 대한 완벽한 가시성을 확보할 수 있습니다. Judge 모델에 전달된 실제 프롬프트, 반환된 응답 원문, 토큰 사용량, 지연 시간 및 발생한 에러를 투명하게 들여다볼 수 있습니다. 평가자가 예상치 못한 점수를 산출한다면, 실행 트레이스를 통해 그 원인을 명확하게 진단할 수 있습니다.

일반적인 실행 상태(Status):

상태 (Status) 의미 (Meaning) 조치 사항 (Action)
Completed 평가가 성공적으로 완료됨 필요 없음
Error 평가 실행 실패 실행 트레이스 상세를 확인하여 에러 분석
Delayed LLM 제공업체의 Rate limit(호출 속도 제한)에 도달함 백오프(backoff) 알고리즘을 통한 자동 재시도
Pending 대기열(Queue)에 등록되어 실행 대기 중 대기하거나 큐 깊이(queue depth) 확인

프로덕션 구축 시 절대 건너뛰지 말아야 할 요소

프로덕션 LLM 애플리케이션의 평가 시스템을 구축할 때 권장하는 최소 기능 스택(Minimum Viable Stack)은 다음과 같습니다:

  1. 최종 LLM 출력을 검증하는 최소 1개 이상의 LLM-as-a-Judge 평가자 — 확장 가능한 핵심 품질 시그널
  2. 구조적 유효성을 검증하는 최소 1개 이상의 코드 평가자 — JSON, 스키마, 필수 필드 점검
  3. 200개 이상의 인간 어노테이션 샘플을 확보한 어노테이션 큐 — 캘리브레이션 벤치마크 데이터셋
  4. 사용자 피드백 수집을 위한 Scores API 연동 — UI상의 좋아요/싫어요는 시스템에서 얻을 수 있는 가장 가성비 높은 품질 시그널

어노테이션 큐를 생략하면 아무런 검증 없이 자동화된 점수를 맹신하게 됩니다. 코드 평가자를 생략하면 기초적인 구조 검사조차 통과하지 못하는 불량 출력에 귀중한 LLM 판사 호출 비용을 낭비하게 됩니다. API 연동을 생략하면 실제 사용자의 만족도에 대해 눈을 감고 비행하는 것과 같습니다.

5가지 평가 방식은 취향대로 고르는 메뉴판이 아닙니다. 상호 결합되는 ’스택(Stack)’입니다. 각 계층이 유기적으로 맞물릴 때 비로소 다른 계층들의 신뢰성이 완성됩니다.

리서치 노트

[[langfuse-evaluation-methods-comprehensive-report]]

관련 기사