이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 프롬프트 버전에는 프롬프트 문자열 외에 무엇을 기록해야 하는가?
- eval은 모델 비교가 아니라 운영 회귀 테스트로 어떻게 쓰이는가?
- prompt, context, schema 변경이 품질 회귀를 만들었는지 어떻게 확인하는가?
- 개인 프로젝트에서도 가능한 최소 eval은 무엇인가?
개요
Prompt versioning과 eval regression은 AI 기능의 변경 관리를 위한 장치다. 프롬프트, 모델, context 조립 방식, retrieval 설정, output schema, tool 목록이 바뀌면 사용자에게 보이는 품질이 달라진다.
전통적인 백엔드 코드에서는 테스트가 회귀를 잡는다. AI 기능에서도 같은 사고가 필요하다. 단지 테스트 대상이 deterministic function이 아니라 모델을 포함한 실행 파이프라인이라는 점이 다르다.
왜 백엔드 개발자에게 중요한가
AI 기능의 회귀는 배포 직후 컴파일 오류처럼 터지지 않는다. 며칠 뒤 특정 질문 유형에서 답변 품질이 낮아지거나, 분류 결과가 조금씩 틀어지거나, 파싱 실패율이 올라가는 식으로 나타난다.
백엔드 개발자는 다음 변경을 모두 release artifact로 봐야 한다.
- prompt text
- prompt variable contract
- model name
- temperature와 max token
- structured output schema
- retrieval top-k와 reranker
- chunking 전략
- tool 목록과 tool schema
- guardrail 정책
이 중 하나라도 바뀌면 eval을 돌릴 이유가 있다.
핵심 원리
좋은 prompt version은 문자열 이름보다 많은 정보를 담는다.
{
"prompt_version": "support-summary-v4",
"model": "gpt-4.1-mini",
"schema_version": "summary-result-v2",
"retrieval_profile": "support-doc-top8-rerank-v1",
"tools": ["ticket.lookup.readonly"],
"released_at": "2026-06-30",
"eval_dataset": "support-summary-regression-2026-06",
"eval_pass_rate": 0.94
}이 기록이 있어야 장애 때 “어떤 변경 이후부터 실패했는가”를 추적할 수 있다.
Eval Dataset 설계
eval dataset은 대표 질문만 모으면 부족하다. 다음 유형을 포함한다.
- 정상 요청
- 모호한 요청
- 문서에 근거가 없는 요청
- 권한 없는 문서를 물어보는 요청
- 오래된 정책과 최신 정책이 충돌하는 요청
- JSON schema를 깨기 쉬운 요청
- prompt injection 시도
- 긴 입력과 빈 입력
- provider refusal이 나올 수 있는 요청
운영 로그에서 dataset을 만들 수 있지만 그대로 쓰면 안 된다. 개인정보, 토큰, 내부 문서, 고객명, 계정 정보는 제거하거나 합성 데이터로 바꿔야 한다.
코드로 이해하기
다음은 작은 eval runner 예시다. 기존 Python 메모의 list comprehension과 dict 사용을 실제 eval 집계에 연결한 형태다.
from dataclasses import dataclass
from collections import Counter
@dataclass(frozen=True)
class EvalCase:
case_id: str
question: str
expected_sources: set[str]
must_contain: list[str]
@dataclass(frozen=True)
class EvalResult:
case_id: str
passed: bool
failure_type: str | None
def judge_case(case: EvalCase, answer: dict) -> EvalResult:
answer_text = answer.get("answer", "")
source_ids = set(answer.get("source_ids", []))
if not case.expected_sources.issubset(source_ids):
return EvalResult(case.case_id, False, "missing_source")
missing_terms = [
term for term in case.must_contain
if term not in answer_text
]
if missing_terms:
return EvalResult(case.case_id, False, "missing_required_term")
return EvalResult(case.case_id, True, None)
def summarize_results(results: list[EvalResult]) -> dict:
failures = Counter(
result.failure_type
for result in results
if not result.passed
)
return {
"total": len(results),
"passed": sum(result.passed for result in results),
"failures": dict(failures),
}핵심은 eval이 점수 하나로 끝나지 않는다는 것이다. 실패 유형이 있어야 검색 문제인지, 생성 문제인지, 출력 형식 문제인지 구분할 수 있다.
백엔드 설계 판단
eval을 CI에 넣을 때는 비용과 속도를 고려해야 한다.
- PR마다 작은 smoke eval을 돌린다.
- release 전에는 대표 regression eval을 돌린다.
- 모델 변경 전에는 더 넓은 dataset을 돌린다.
- 비용이 큰 eval은 nightly batch로 돌린다.
- 고객-facing 핵심 기능은 human review 샘플을 둔다.
eval gate를 너무 엄격하게 잡으면 개발이 멈추고, 너무 느슨하면 의미가 없다. 기능별로 실패 허용치를 다르게 둔다. 법률, 의료, 결제, 보안처럼 위험이 큰 영역은 더 엄격해야 한다.
장애 상황과 대응
프롬프트 변경 후 장애가 발생하면 다음 순서로 본다.
- prompt version이 바뀌었는가?
- model 또는 parameter가 바뀌었는가?
- schema version이 바뀌었는가?
- retrieval profile이 바뀌었는가?
- eval dataset에서 같은 실패가 재현되는가?
- 운영 로그에서 특정 사용자군, 문서군, 언어, 요청 길이에 집중되는가?
eval이 없으면 장애 대응은 감으로 변한다. eval이 있으면 rollback하거나 targeted fix를 만들 수 있다.
인프라 협업 포인트
eval도 비용과 인프라를 쓴다.
- nightly eval이 provider rate limit을 잡아먹지 않게 분리한다.
- eval trace와 production trace를 구분한다.
- eval dataset에 민감정보가 들어가지 않게 저장소와 접근 권한을 관리한다.
- 대량 eval은 queue와 worker로 돌리고, 실패 결과만 오래 보존하는 방식을 검토한다.
개인 프로젝트 최소 기준
- 대표 질문 10개를 JSON이나 YAML로 저장한다.
- 프롬프트 변경 때마다 직접 실행해 결과를 비교한다.
- schema validation 실패와 source 누락 정도는 자동 검사한다.
- eval 결과를 README나 문서에 짧게 기록한다.
기업 운영 수준 기준
- prompt/model/context/schema 변경마다 regression eval을 남긴다.
- eval dataset은 버전 관리하고 개인정보 제거 절차를 둔다.
- eval 결과를 배포 승인 조건과 연결한다.
- 실패 유형별 대시보드를 둔다.
- 중요한 기능은 human review loop를 둔다.
실전 팁
- eval case는 실제 장애와 CS 문의에서 계속 추가한다.
- “좋은 답변인지”만 묻는 judge보다 source, schema, 금지 행동처럼 관찰 가능한 기준을 많이 둔다.
- LLM-as-a-judge를 쓰더라도 중요한 판정은 규칙 기반 검사와 섞는다.
- eval dataset이 너무 깨끗하면 운영 문제를 못 잡는다. 모호하고 지저분한 입력도 포함한다.
위험 신호!
- prompt 변경이 PR 없이 production에 반영된다.
- eval 결과가 평균 점수 하나로만 관리된다.
- 운영 로그를 개인정보 제거 없이 eval dataset에 넣는다.
- fallback model은 eval하지 않는다.
- schema 변경과 prompt 변경을 동시에 배포해 회귀 원인을 찾기 어렵다.
확인 질문
확인 질문
- prompt version에 프롬프트 문자열 외에 무엇을 기록해야 하는가?
- 모델, schema, retrieval 설정, tool 목록, eval dataset, eval 결과, 배포 시점을 함께 기록해야 한다.
- eval이 운영 회귀 테스트인 이유는 무엇인가?
- 프롬프트와 context, 모델, schema 변경이 중요한 케이스에서 계속 기준을 만족하는지 확인하기 때문이다.
- eval dataset을 운영 로그에서 만들 때 주의할 점은 무엇인가?
- 개인정보와 민감정보를 제거하고, 권한 정보와 내부 문서를 그대로 노출하지 않게 해야 한다.
Spring AI 적용 연결
- Spring AI 프로젝트에서는 prompt version, model profile, advisor 조합을 함께 기록해야 회귀 원인을 좁힐 수 있다. 테스트 구조는 10. Evaluation Testing Regression에서 이어서 본다.
- prompt version이 런타임 호출 경계에 어떻게 남아야 하는지는 03. Spring Boot 계층 설계와 AI Gateway를 함께 본다.