fos-blog/study
01 / 홈02 / 카테고리03 / 시리즈
01 / 홈02 / 카테고리03 / 시리즈

카테고리

  • AI 페이지로 이동
    • RAG 페이지로 이동
    • langgraph 페이지로 이동
    • agents.md
    • BMAD Method — AI 에이전트로 애자일 개발하는 방법론
    • Claude Code 메모리: CLAUDE.md와 .claude/rules를 규칙으로 쓰는 법
    • Claude Code의 Skill 시스템 - 개발자를 위한 AI 자동화의 새로운 차원
    • Claude Code를 5주 더 쓴 결과 — 스킬·CLAUDE.md를 키워가는 방식
    • Claude Code를 11일 동안 쓴 결과 — 데이터로 본 나의 사용 패턴
    • Claude Code 멀티 에이전트 — Teams
    • AI 에이전트와 디자인의 새 컨벤션 — DESIGN.md, Google Stitch, Claude Design
    • Docling — IBM Research 의 문서 파싱 toolkit 상세 정리
    • 하네스 엔지니어링 실전 — 4인 에이전트 팀으로 코딩 파이프라인 구축하기
    • 하네스 엔지니어링 — 오래 실행되는 AI 에이전트를 위한 설계
    • 멀티모달 LLM (Multimodal Large Language Model)
    • AI 에이전트와 함께 MVP 만들기 — dooray-cli 사례
    • 스킬 문서를 신경망처럼 학습시킨다 — Microsoft SkillOpt 분석
  • ai 페이지로 이동
    • agent 페이지로 이동
    • [초안] AI 제품 백엔드 안정성 — 지연·비용·권한·관측·도구 실패·폴백/재시도/사람 에스컬레이션
    • [초안] LLM 평가 프레임워크: 골든셋, 회귀 테스트, LLM-as-a-judge, 사람 피드백 루프
  • algorithm 페이지로 이동
    • live-coding 페이지로 이동
    • 분산 계산을 위한 알고리즘
  • apartment 페이지로 이동
    • 구리 럭키아파트 24평 인테리어 레퍼런스 모음
  • architecture 페이지로 이동
    • [초안] 시니어 백엔드를 위한 API 설계 실전 스터디 팩 — REST · 멱등성 · 페이지네이션 · 버전 전략
    • [초안] API Versioning과 Backward Compatibility: 시니어 백엔드 관점 정리
    • 캐시 설계 전략 총정리
    • [초안] CJ푸드빌 디지털 채널 면접: 슬롯 도메인 경험을 커머스 도메인 설계 능력으로 번역하기
    • [초안] 커머스 Spring 서비스에 Clean/Hexagonal Architecture를 실용적으로 적용하기
    • [초안] 커머스 도메인 모델링: 주문·재고·노출의 세 축을 분리해서 설계하기
    • 커머스 주문 상태와 데이터 정합성 기본기 — CJ푸드빌 면접 대비
    • [초안] 쿠폰/프로모션 동시성과 정합성 기본기 — 선착순·중복 사용 방지·발급/사용/복구
    • [초안] DDD와 도메인 모델링: 시니어 백엔드 관점의 전술/전략 패턴 실전 가이드
    • [초안] Decorator & Chain of Responsibility — 행동을 체인으로 조립하는 두 가지 방식
    • 디자인 패턴
    • [초안] 분산 아키텍처 완전 정복: Java 백엔드 시니어 인터뷰 대비 실전 가이드
    • [초안] 분산 트랜잭션과 Outbox 패턴 — 왜 2PC를 피하고 어떻게 대신할 것인가
    • 분산 트랜잭션
    • [초안] e-Commerce 주문·결제 도메인 모델링: 상태머신, 멱등성, Outbox/Saga 실전 정리
    • [초안] F&B 쿠폰·프로모션·멤버십·포인트 설계
    • [초안] F&B · e-Commerce 디지털 채널 도메인 한 장 정리 — CJ푸드빌 디지털 채널 백엔드 면접 대비
    • [초안] F&B 주문/매장/픽업 상태머신 설계 — CJ푸드빌 디지털 채널 백엔드 관점
    • [초안] F&B 이커머스 결제·환불·정산 운영 가이드
    • [초안] Hexagonal / Clean Architecture를 Spring 백엔드에 적용하기
    • [초안] 대규모 커머스 트래픽 처리 패턴 — 1,600만 고객과 올영세일을 버티는 설계
    • [초안] 레거시 JSP/jQuery 화면과 신규 API가 공존하는 백엔드 운영 전략
    • [초안] MSA 서비스 간 통신: Redis [Cache-Aside](../database/redis/cache-aside.md) × Kafka 이벤트 하이브리드 설계
    • [초안] Observability 입문: 시니어 백엔드가 장애를 탐지하고 대응하는 방식
    • [초안] Outbox / Inbox Pattern 심화 — 분산 메시징의 정합성 문제를 DB 트랜잭션으로 풀어내기
    • [초안] 결제 도메인 멱등성과 트랜잭션 재시도 기본기
    • [초안] 시니어 백엔드를 위한 Resilience 패턴 실전 가이드 — Timeout, Retry, Circuit Breaker, Bulkhead, Backpressure
    • [초안] REST API 버저닝과 모바일 앱 하위 호환성 — CJ푸드빌 디지털 채널 백엔드 관점
    • [초안] Spring Batch vs Event-Driven — 같은 비동기처럼 보이지만 전혀 다른 두 패러다임
    • [초안] Strategy Pattern — 분기문을 없애는 설계, 시니어 백엔드 인터뷰 핵심 패턴
    • [초안] 시니어 백엔드를 위한 시스템 설계 입문 스터디 팩
    • [초안] 템플릿 메서드 패턴 - 백엔드 처리 골격을 강제하는 가장 오래되고 가장 위험한 패턴
    • [초안] 대규모 트래픽 중 무중단 마이그레이션 — Feature Flag + Shadow Mode 실전
  • database 페이지로 이동
    • mysql 페이지로 이동
    • opensearch 페이지로 이동
    • redis 페이지로 이동
    • 김영한의-실전-데이터베이스-설계 페이지로 이동
    • [초안] DB Connection Pool Saturation과 Thread Pool 격리
    • 커넥션 풀 크기는 얼마나 조정해야 할까?
    • 인덱스 - DB 성능 최적화의 핵심
    • [초안] JPA N+1과 커머스 조회 모델: 주문/메뉴/쿠폰 도메인에서 살아남기
    • [초안] MyBatis 기본기 — XML Mapper, resultMap, 동적 SQL, 운영 패턴 정리
    • [초안] MyBatis와 JPA/Hibernate 트레이드오프 — 레거시 백엔드를 다루는 시니어 관점
    • 역정규화 (Denormalization)
    • 데이터 베이스 정규화
  • devops 페이지로 이동
    • docker 페이지로 이동
    • k8s 페이지로 이동
    • k8s-in-action 페이지로 이동
    • observability 페이지로 이동
    • [초안] 커머스/F&B 채널 장애 첫 5분과 관측성 기본기
    • Envoy Proxy
    • [초안] F&B / e-Commerce 운영 장애 대응과 모니터링 — 백엔드 관점 정리
    • Graceful Shutdown
    • [초안] 시니어 백엔드를 위한 SLO와 Error Budget 기반 장애 대응
  • finance 페이지로 이동
    • industry-cycle 페이지로 이동
    • investing 페이지로 이동
  • http 페이지로 이동
    • HTTP Connection Pool
    • HTTPS는 어떻게 안전한가 — TLS, 인증서, 그리고 termination
  • interview 페이지로 이동
    • [초안] AI 서비스 팀 경험 기반 시니어 백엔드 면접 질문 뱅크 — Spring Batch RAG / gRPC graceful shutdown / 전략 패턴 / 12일 AI 웹툰 MVP
    • [초안] 커머스/F&B 면접 답변집 — 슬롯 도메인 경험을 주문·결제·쿠폰·매장 설계로 매핑하기
    • [초안] F&B / e-Commerce 운영 모니터링과 장애 대응 인터뷰 정리
    • Observability — 면접 답변 프레임
    • [초안] 시니어 Java 백엔드 면접 마스터 플레이북 — 김병태
    • [초안] NSC 슬롯팀 경험 기반 질문 은행 — 도메인 모델링·동시성·성능·AI 협업
  • java 페이지로 이동
    • concurrency 페이지로 이동
    • jdbc 페이지로 이동
    • opentelemetry 페이지로 이동
    • spring 페이지로 이동
    • spring-batch 페이지로 이동
    • 더_자바_코드를_조작하는_다양한_방법 페이지로 이동
    • [초안] Java 동시성 락 정리 — 커머스 메뉴/프로모션 정책 캐시 갱신 관점
    • [초안] JVM 튜닝 실전: 메모리 구조부터 Virtual Threads, GC 튜닝, 프로파일링까지
    • Java의 로깅 환경
    • MDC (Mapped Diagnostic Context)
    • Java StampedLock — 읽기 폭주에도 쓰기가 밀리지 않는 락
    • Virtual Thread와 Project Loom
  • javascript 페이지로 이동
    • typescript 페이지로 이동
    • AbortController
    • Async Iterator와 제너레이터
    • CommonJS와 ECMAScript Modules
    • 제너레이터(Generator)
    • Http Client
    • Node 백엔드 운영 패턴 — Streams 백프레셔, pipe/pipeline, 멱등성 vs 분산 락
    • Node.js
    • npm vs pnpm — 어떤 기준으로 선택했나
    • `setImmediate()`
  • kafka 페이지로 이동
    • [초안] Kafka 기본 개념 — 토픽, 파티션, 오프셋, 복제
    • Kafka를 사용하여 **데이터 정합성**은 어떻게 유지해야 할까?
    • [초안] Kafka 실전 설계: 파티션 전략, 컨슈머 그룹, 전달 보장, 재시도, 순서 보장 트레이드오프
    • 메시지 전송 신뢰성
  • linux 페이지로 이동
    • fsync — 리눅스 파일 동기화 시스템 콜
    • tmux — Terminal Multiplexer
  • network 페이지로 이동
    • L2(스위치)와 L3(라우터)의 역할 차이
    • L4와 VIP(Virtual IP Address)
    • IP Subnet
  • python 페이지로 이동
    • Python async/await — CompletableFuture·Reactor 와 다른 점, 그리고 blocking I/O 함정
    • Python 의존성 관리 — Java Maven/Gradle 사용자가 만나는 첫 충격
    • FastAPI 기초 — Spring Boot 사용자가 빠르게 익히는 법
    • GPU·CUDA·MPS 기초 — 자바 백엔드 개발자가 처음 만나는 그림
    • Multi-process GPU 워크로드 — 자바 ThreadPool 사용자가 만나는 모델 차이
    • Java 개발자를 위한 Python 심화 — OOP·데코레이터·컨텍스트 매니저
    • PyTorch 기초 — 텐서, 디바이스, 그리고 모델 로딩이 무거운 이유
    • Java 개발자를 위한 Python 문법 핵심
    • ML 서비스 성능 분석 워크플로 — 자바 백엔드 트러블슈팅과 다른 점
    • OCR 동작 원리 — Layout · Text · Post-process 3단계
    • Python 서버의 RSS 가 안 줄어드는 이유 — gc.collect 의 한계와 malloc_trim
  • rabbitmq 페이지로 이동
    • [초안] RabbitMQ Basics — 실전 백엔드 관점에서 정리하는 메시지 브로커 기본기
    • [초안] RabbitMQ vs Kafka — 백엔드 메시징 선택 기준과 실전 운영 관점
  • security 페이지로 이동
    • [초안] 시니어 백엔드를 위한 보안 / 인증 스터디 팩 — Spring Security, JWT, OAuth2, OWASP Top 10
    • [초안] Spring Security 6.x OAuth2 + JWT 상용 인증 설계 — Grant 선택, Resource Server, Refresh Rotation, 로그아웃
  • task 페이지로 이동
    • ai-service-team 페이지로 이동
    • nsc-slot 페이지로 이동
    • sb-dev-team 페이지로 이동
    • the-future-company 페이지로 이동
  • testing 페이지로 이동
    • [초안] 시니어 Java 백엔드를 위한 테스트 전략 완전 정리 — 피라미드부터 TestContainers, 마이크로벤치, Contract까지
  • travel 페이지로 이동
    • 오사카 3박 4일 일정표: 우메다 쇼핑, USJ, 난바·도톤보리, 오사카성
  • web 페이지로 이동
    • [초안] HTTP / Cookie / Session / Token 인증 기본기 — 레거시 JSP와 모바일 API가 공존하는 백엔드 관점
FOS-BLOG · FOOTERall systems normal·v0.1 · 2026.04.27·seoul, kr
Ffos-blog/study

개발 학습 기록을 정리하는 블로그입니다. 공부하면서 기록하고, 기록하면서 다시 배웁니다.

visitors
01site
  • Home↗
  • Posts↗
  • Categories↗
  • About↗
02policy
  • 소개/about
  • 개인정보처리방침/privacy
  • 연락처/contact
03categories
  • AI↗
  • Algorithm↗
  • DB↗
  • DevOps↗
  • Java/Spring↗
  • JS/TS↗
  • React↗
  • Next.js↗
  • System↗
04connect
  • GitHub@jon890↗
  • Source repositoryjon890/fos-study↗
  • RSS feed/rss.xml↗
  • Newsletter매주 1 회 · 한 편의 글→
© 2026 FOS Study. All posts MIT-licensed.
built with·Next.js·Tailwind v4·Geist·Pretendard·oklch
fos-blog/AI/[초안] LLM 평가 프레임워크: 골든셋, …
ai

[초안] LLM 평가 프레임워크: 골든셋, 회귀 테스트, LLM-as-a-judge, 사람 피드백 루프

LLM 기반 기능은 한 가지 곤란한 성질을 가진다. 같은 입력에도 출력이 매번 달라지고, "좋아졌다"가 숫자로 잘 잡히지 않는다. 프롬프트 한 줄을 고치거나 모델 버전을 올렸을 때, 그게 정말 개선인지 아니면 어떤 케이스를 조용히 망가뜨린 퇴행인지 눈으로는 알 수 없다. 그래서 LLM 제품의 신뢰성은 모델 자체보다 평가 체계(evaluation) 에서 갈린...

2026.06.07·8 min read·60 views

왜 이 주제가 중요한가

LLM 기반 기능은 한 가지 곤란한 성질을 가진다. 같은 입력에도 출력이 매번 달라지고, "좋아졌다"가 숫자로 잘 잡히지 않는다. 프롬프트 한 줄을 고치거나 모델 버전을 올렸을 때, 그게 정말 개선인지 아니면 어떤 케이스를 조용히 망가뜨린 퇴행인지 눈으로는 알 수 없다.

그래서 LLM 제품의 신뢰성은 모델 자체보다 평가 체계(evaluation) 에서 갈린다. 평가가 없으면 모든 변경이 "느낌상 더 나아진 것 같다"로 머물고, 평가가 있으면 변경을 측정 가능한 의사결정으로 바꿀 수 있다.

이 문서는 운영 무용담이 아니라 평가를 어떻게 설계하는가의 관점에서 네 가지 축을 한 흐름으로 엮는다.

  • 골든셋(golden set) — 무엇을 정답으로 둘 것인가
  • 회귀 테스트(regression test) — 변경이 기존 품질을 깨지 않았는지 어떻게 자동으로 잡는가
  • LLM-as-a-judge — 정답이 고정되지 않는 출력을 모델로 어떻게 채점하는가
  • 사람 피드백 루프(human feedback loop) — 사람을 어디에, 얼마의 비용으로 끼워 넣는가

에이전트처럼 여러 단계를 밟는 워크플로의 궤적(trajectory) 평가와 위험 게이트는 별도 주제이므로, 그쪽은 Agentic Workflow 평가와 Risk Gate 설계로 넘긴다. 여기서는 단일 호출·단일 응답 수준의 평가 기반을 다룬다.

전체 그림 — 네 축이 어떻게 맞물리는가

평가 프레임워크는 따로 노는 도구 묶음이 아니라 하나의 순환이다.

text
golden set (고정된 평가 데이터)
   │
   ▼
runner (프롬프트/모델 버전마다 출력 생성)
   │
   ▼
scorer (정확 매칭 + 메트릭 + LLM-as-a-judge)
   │
   ▼
regression gate (이전 버전 점수와 비교 → 통과/차단)
   │
   ▼
human review (낮은 점수·judge 불확실 구간만 샘플 검수)
   │
   └──→ 발견한 새 실패 케이스를 golden set에 환류

핵심은 데이터(golden set)와 채점(scorer)을 분리하고, 변경 판단(regression gate)을 자동화하며, 사람(human)을 전수가 아니라 선택 지점에만 넣는다는 점이다. 이 분리가 안 되면 평가가 매번 즉흥적인 수동 검수로 회귀한다.

1. 골든셋 — 무엇을 정답으로 둘 것인가

골든셋은 "이 입력에는 이런 출력이 나와야 한다"를 고정해 둔 평가용 데이터셋이다. 학습 데이터와 다르다. 학습은 모델을 바꾸지만, 골든셋은 모델을 바꾸지 않고 판정만 한다.

좋은 골든셋의 조건은 크기보다 분포다.

  • 대표성 — 실제 트래픽의 주요 의도/카테고리 분포를 반영한다. 쉬운 케이스만 모으면 항상 100점이 나와 변별력이 없다.
  • 난이도 혼합 — 평이한 케이스, 경계 케이스, 알려진 실패 케이스를 의도적으로 섞는다.
  • 부정 케이스 포함 — "거절해야 하는 입력", "모른다고 답해야 하는 입력"을 반드시 넣는다. 정상 응답만 평가하면 환각·과잉응답을 못 잡는다.
  • 라벨의 종류 명시 — 정답이 한 개로 고정되는지(분류·추출), 여러 정답이 허용되는지(요약·생성)에 따라 채점 방식이 달라진다.

각 케이스는 단순 입력/정답 쌍을 넘어, 무엇을 기준으로 통과로 볼지를 함께 적는다.

json
{
  "id": "refund-policy-001",
  "category": "policy_qa",
  "input": "결제 후 3일 지났는데 환불 되나요?",
  "reference": "기간 조건에 따라 다르며, 구체 조건은 정책 문서를 인용해 안내해야 함",
  "rubric": [
    "환불 가능 여부를 단정하지 않고 조건을 설명한다",
    "근거가 되는 정책 출처를 인용한다",
    "모르는 부분은 추측하지 않는다"
  ],
  "must_not": ["무조건 환불 가능하다고 단정", "정책에 없는 기간 임의 생성"],
  "difficulty": "boundary"
}

흔한 실수:

  • 데이터 오염(leakage) — 골든셋 입력이 프롬프트 예시(few-shot)에 그대로 들어가면 점수가 부풀려진다. 평가셋과 프롬프트 예시는 분리한다.
  • 버전 관리 부재 — 골든셋도 코드처럼 버전을 찍는다. 데이터가 조용히 바뀌면 어제와 오늘의 점수를 비교할 수 없다.
  • 정답을 한 표현으로 못박기 — 생성 과제에서 정답 문자열 하나만 두면, 의미가 같아도 표현이 다르면 다 틀리게 나온다. 이 경우 rubric(채점 기준표) 기반 채점으로 넘어가야 한다.

2. 회귀 테스트 — 변경이 품질을 깼는지 자동으로 잡기

회귀 테스트의 목적은 "프롬프트/모델/검색 파이프라인을 바꿨을 때, 기존에 잘 되던 케이스가 깨지지 않았는가"를 자동으로 확인하는 것이다. 일반 단위 테스트와 다른 점은 출력이 비결정적이라 정확 일치(exact match)만으로는 부족하다는 데 있다.

설계 포인트는 세 가지다.

  • 메트릭 게이팅 — 케이스별 pass/fail이 아니라 골든셋 전체의 집계 지표(정확도, rubric 통과율 등)에 임계값을 둔다. 단건 흔들림에 CI가 과민 반응하지 않게 한다.
  • 카테고리별 분해 — 전체 점수만 보면 "환불 카테고리가 무너졌는데 다른 카테고리가 올라가서 총점은 유지"되는 퇴행을 놓친다. 카테고리별로 따로 본다.
  • 비결정성 흡수 — temperature가 0이 아니면 같은 입력도 점수가 흔들린다. 케이스를 여러 번 돌려 평균을 쓰거나, 평가 시점에는 temperature를 낮춰 분산을 줄인다.

CI에 붙일 때는 "이전 버전 대비 임계 이상 하락하면 차단"이 기본형이다.

yaml
# 예: PR마다 평가 실행 후 임계값 게이트
eval-gate:
  run: python run_eval.py --golden golden_v3.json --out report.json
  rules:
    - metric: overall_pass_rate
      min: 0.85                 # 절대 하한
    - metric: overall_pass_rate
      max_drop_vs_main: 0.03    # main 대비 3%p 이상 하락 시 실패
    - metric: policy_qa_pass_rate
      max_drop_vs_main: 0.0     # 핵심 카테고리는 하락 불허

스냅샷 비교(이전 출력과 텍스트 diff)도 보조로 쓸 수 있지만, 생성 과제에서는 표현만 바뀌어도 diff가 터지므로 메트릭 게이팅을 주로 두고 스냅샷은 참고용으로 둔다.

3. LLM-as-a-judge — 정답이 고정되지 않는 출력 채점

요약·설명·대화처럼 정답이 한 개로 안 떨어지는 과제는, 사람이 매번 채점하면 느리고 비싸다. 그래서 모델이 모델의 출력을 채점하는 LLM-as-a-judge를 쓴다. 다만 judge도 LLM이라 같은 약점을 공유한다는 점을 전제로 설계해야 한다.

채점 방식은 크게 둘이다.

  • Pointwise — 출력 하나에 rubric 기준으로 점수/통과 여부를 매긴다. 기준이 명확할 때 안정적이다.
  • Pairwise — 두 출력 중 어느 쪽이 더 나은지 고른다. 절대 점수보다 상대 비교가 사람과 더 잘 맞는 경우가 많다. A/B 비교나 모델 교체 판단에 적합하다.

judge가 가진 알려진 편향:

  • Position bias — pairwise에서 먼저 제시된 답을 선호한다. 순서를 바꿔 두 번 물어 일관성을 확인한다.
  • Verbosity bias — 길고 장황한 답을 더 좋게 본다. rubric에 "간결성"을 명시하거나 길이를 통제한다.
  • Self-preference — 같은 계열 모델이 만든 답을 선호하는 경향이 있다. 가능하면 채점 모델과 생성 모델 계열을 분리한다.

가장 중요한 원칙은 judge 자체를 검증해야 한다는 것이다. judge를 믿기 전에, 사람이 라벨링한 소규모 셋에서 judge 판정과 사람 판정의 일치도(agreement)를 측정한다. 일치도가 낮으면 점수가 아니라 rubric을 고친다.

text
judge 신뢰성 점검 절차
1. 사람이 라벨링한 케이스 50~100건 준비
2. 동일 케이스를 judge로 채점
3. 사람 vs judge 일치율 / 상관 측정
4. 불일치 케이스 원인 분석 → rubric 문장 구체화
5. 목표 일치율 도달 후에야 judge를 대규모로 사용

rubric은 추상어("좋은 답인가")를 피하고, 통과 조건을 검증 가능한 문장으로 쪼갠다. 앞의 골든셋 예시에 넣은 rubric 배열이 그대로 judge 입력이 된다 — 골든셋과 judge가 같은 기준을 공유하게 설계하는 것이 핵심이다.

에이전트 궤적처럼 출력이 단일 응답이 아닌 경우의 judge 사용과 risk gate 결합은 Agentic Workflow 평가 문서에서 이어진다.

4. 사람 피드백 루프 — 사람을 어디에 넣는가

사람 검수는 가장 정확하지만 가장 비싸다. 그래서 설계의 핵심은 "사람을 전수 검수에 쓰지 않고, 자동 평가가 불확실한 지점에만 배치"하는 것이다.

사람을 끼우면 좋은 지점:

  • judge가 낮은 신뢰도로 판정한 구간 — 통과/실패 경계 점수, judge가 순서에 따라 답이 바뀐 케이스.
  • 새 실패 유형 발굴 — 자동 평가가 통과시켰지만 실제로는 이상한 출력. 여기서 나온 케이스를 골든셋에 환류한다.
  • rubric 보정 — 사람과 judge가 자주 엇갈리는 카테고리의 기준을 다시 쓴다.

피드백을 받는 방식도 비용 차이가 크다.

  • 명시적 피드백 — 사람이 직접 점수/라벨을 단다. 정확하지만 느리다.
  • 암묵적 피드백 — 사용자의 재질문, 복사, 이탈 같은 행동 신호. 싸지만 노이즈가 많고 해석이 필요하다.

가장 중요한 건 루프가 닫혀야 한다는 것이다. 사람이 찾은 실패가 골든셋의 새 케이스로 들어가고, 그 케이스가 다음 회귀 테스트에서 다시 검사되어야 같은 실패가 재발하지 않는다. 피드백을 모으기만 하고 골든셋에 환류하지 않으면, 평가는 점점 현실과 멀어진다.

Bad vs Improved — 평가 설계의 차이

나쁜 설계:

  • 출시 직전 팀원이 손으로 20개쯤 돌려 보고 "괜찮네" 하고 배포한다.
  • 정답을 문자열 하나로 고정해 의미가 같아도 틀렸다고 나온다.
  • judge 점수를 검증 없이 그대로 믿는다.
  • 프롬프트 예시에 평가 케이스가 섞여 점수가 부풀려진다.

개선된 설계:

  • 카테고리·난이도가 분포된 버전 관리되는 골든셋을 둔다.
  • 생성 과제는 rubric 기반 judge로 채점하고, judge는 사람 라벨과의 일치도로 먼저 검증한다.
  • 변경마다 CI에서 카테고리별 회귀 게이트를 통과해야 머지된다.
  • 사람 검수에서 나온 실패를 골든셋에 환류해 루프를 닫는다.

작은 실습 환경

거창한 플랫폼 없이도 로컬에서 평가 골격을 만들 수 있다. JSON 골든셋 + 러너 + 간단한 채점으로 시작한 뒤 judge를 붙이는 순서가 좋다.

bash
# 1. 골든셋과 러너만으로 시작
mkdir llm-eval && cd llm-eval
python -m venv .venv && source .venv/bin/activate
pip install pytest
# golden.json 작성 후
pytest -q test_eval.py

가장 단순한 형태의 평가 러너는 다음과 같다. 실제 모델 호출부는 call_model로 추상화해 두고, 채점은 rubric 통과 비율을 집계한다.

python
import json
 
def call_model(prompt: str) -> str:
    # 실제로는 LLM API 호출. 테스트에서는 stub로 대체.
    ...
 
def judge(output: str, rubric: list[str]) -> float:
    # 실제로는 judge 모델 호출. 여기서는 rubric 통과 개수 비율을 반환한다고 가정.
    ...
 
def run_eval(golden_path: str) -> dict:
    cases = json.load(open(golden_path, encoding="utf-8"))
    by_category: dict[str, list[float]] = {}
    for c in cases:
        out = call_model(c["input"])
        score = judge(out, c["rubric"])
        by_category.setdefault(c["category"], []).append(score)
    return {
        cat: sum(scores) / len(scores)
        for cat, scores in by_category.items()
    }
 
if __name__ == "__main__":
    report = run_eval("golden.json")
    for cat, avg in report.items():
        print(f"{cat}: {avg:.2f}")

출력은 카테고리별 평균으로 떨어진다.

text
policy_qa: 0.88
refund: 0.79
small_talk: 0.95

이 숫자를 이전 버전과 비교하는 한 줄을 CI에 추가하면, 그 순간부터 "느낌"이 아니라 "지표"로 변경을 판단하게 된다.

면접 답변 프레임

평가 설계 질문은 화려한 운영 규모가 아니라 사고의 분리를 본다. 다음 골격으로 답하면 운영 경험을 과장하지 않고도 설계 감각을 보여줄 수 있다.

  • "LLM 기능은 비결정적이라 회귀를 눈으로 못 잡습니다. 그래서 먼저 분포가 잡힌 골든셋을 만들고, 변경마다 그 셋으로 회귀를 봅니다."
  • "정답이 고정되는 과제는 정확 매칭, 생성 과제는 rubric 기반 judge로 나눠 채점합니다. judge는 그대로 믿지 않고 사람 라벨과의 일치도로 먼저 검증합니다."
  • "사람은 전수가 아니라 judge가 불확실한 구간에만 넣고, 거기서 나온 실패를 골든셋에 환류해 루프를 닫습니다."
  • "에이전트처럼 단계가 여러 개면 결과만이 아니라 궤적과 위험 행동도 따로 평가해야 합니다." (여기서 risk gate 주제로 자연스럽게 확장)

면접에서 함정은 "정확도 몇 %를 봤냐"가 아니라 "그 정확도를 어떻게 신뢰하느냐"를 되물을 때다. 이때 judge 검증과 데이터 오염 방지를 언급하면 한 단계 위의 답이 된다.

체크리스트

  • 골든셋이 실제 트래픽 분포를 반영하고, 부정 케이스(거절·모름)를 포함하는가
  • 골든셋이 버전 관리되고, 프롬프트 예시와 분리되어 데이터 오염이 없는가
  • 정답 고정 과제와 생성 과제의 채점 방식을 구분했는가
  • 회귀 게이트가 전체 점수만이 아니라 카테고리별로 하락을 감지하는가
  • 비결정성(temperature)으로 인한 점수 분산을 통제했는가
  • LLM-as-a-judge를 사람 라벨과의 일치도로 먼저 검증했는가
  • judge의 position/verbosity/self-preference 편향을 완화하는 장치가 있는가
  • 사람 검수가 전수가 아니라 불확실 구간에 집중되는가
  • 사람이 찾은 실패가 골든셋으로 환류되어 루프가 닫히는가
on this page
  • 01왜 이 주제가 중요한가
  • 02전체 그림 — 네 축이 어떻게 맞물리는가
  • 031. 골든셋 — 무엇을 정답으로 둘 것인가
  • 042. 회귀 테스트 — 변경이 품질을 깼는지 자동으로 잡기
  • 053. LLM-as-a-judge — 정답이 고정되지 않는 출력 채점
  • 064. 사람 피드백 루프 — 사람을 어디에 넣는가
  • 07Bad vs Improved — 평가 설계의 차이
  • 08작은 실습 환경
  • 09면접 답변 프레임
  • 10체크리스트

이런 글도

  • 스킬 문서를 신경망처럼 학습시킨다 — Microsoft SkillOpt 분석
    나는 Claude Code 위에서 30개가 넘는 개인 스킬(skill)을 운영한다. 블로그 글 작성, 이력서 갱신, 주간보고, 사내 결재 자동화 같은 반복 워크플로우를 각각 SKILL.md 한 장으로 정의해두고 쓴다. 이 스킬들은 시간이 지나면서 점점 커진다. 한 번 실수하면 "이런 함정이 있더라"를 문서에 적어두고, 다음에 같은 실수를 피하는 식이다. 그...
    🤖 ai
    ai
    2026.06.12
  • Claude Code 메모리: CLAUDE.md와 .claude/rules를 규칙으로 쓰는 법
    진행 기간: 2026.06 Claude Code로 한 레포를 오래 다루다 보면 "매번 같은 걸 다시 설명하는" 순간이 온다. PR 본문은 이렇게 써라, Dooray 업무 제목은 이 형식이다, 한국어로 풀어 써라. 이걸 어디에 적어둬야 Claude가 실제로 지키는지 — 그게 이 글의 주제다. 나는 그동안 이런 규칙을 프로젝트 안 .claude/skills/s...
    🤖 ai
    ai
    2026.06.08
  • [초안] AI 제품 백엔드 안정성 — 지연·비용·권한·관측·도구 실패·폴백/재시도/사람 에스컬레이션
    LLM을 붙인 제품의 백엔드는 일반 CRUD 백엔드와 안정성 모델이 다르다. 일반 백엔드는 "DB가 살아 있으면 같은 입력에 같은 출력"이 기본 전제다. AI 제품 백엔드는 그렇지 않다. - 응답이 비결정적이다 — 같은 입력에도 모델 출력이 매번 달라진다. 그래서 "정상 응답"의 정의 자체를 우리가 따로 만들어야 한다. - 지연이 크고 가변적이다 — 모델...
    🤖 ai
    ai
    2026.06.07
  • [초안] Agentic Workflow 상태 관리 — LangGraph로 보는 State Graph, Checkpoint, Human-in-the-loop, Tool 권한 경계
    도구를 한 번 부르고 끝나는 에이전트는 함수 호출과 다를 게 없다. 진짜 어려움은 에이전트가 여러 step에 걸쳐 오래 살아 있을 때 생긴다. 사용자가 중간에 답을 주길 기다려야 하고, 외부 API가 죽으면 재시도해야 하고, 프로세스가 재시작돼도 진행 중이던 작업을 이어가야 한다. 이때 시스템이 다뤄야 하는 것은 모델의 출력이 아니라 워크플로의 상태(sta...
    🤖 ai
    ai
    2026.06.07

댓글 (0)