RAG 품질은 어떻게 검증할까 — 30개 질문과 8개 후속 대화로 만든 회귀 평가

완전관리형 Knowledge Base와 우리가 만든 pgvector 파이프라인을 비교할 때, 실패 원인을 두 번 잘못 짚었다. 처음에는 파서가 표를 읽지 못한다고 봤고, 다음에는 청킹이 행을 잘랐다고 생각했다. 문서를 직접 뒤져 보니 수두 등교중지 기준이 적힌 행은 인덱스에 있었다. 자연어 질문으로 그 행이 상위에 나오지 않은 것이 문제였다.
최종 답변만 읽었다면 세 실패 유형을 구분할 수 없었다. 파싱 실패도, 청킹 실패도, 검색 순위 실패도 사용자에게는 똑같은 "답을 찾지 못했다"로 보인다. RAG 평가는 답변을 채점하는 일이 아니라 실패한 단계를 찾는 일에서 시작한다.
답변이 틀렸다고 모델부터 바꾸면 안 된다
RAG는 적어도 세 층으로 나눠 봐야 한다.
| 평가 층 | 묻는 질문 | 남겨야 할 증거 |
|---|---|---|
| 검색 | 정답 근거가 상위 k개 안에 들어왔나 | 청크 ID, 순위, 점수, 문서 버전 |
| 생성 | 반환된 근거만으로 정확하고 빠짐없이 답했나 | 답변, 인용, 프롬프트·모델 버전 |
| 대화 | 생략된 의도를 앞선 턴에서 복원했나 | 대화 턴, 재작성된 질의, 세션 상태 |
검색이 실패했는데 모델을 바꾸면 비용만 늘 수 있다. 반대로 정답 근거가 들어왔는데 답이 틀렸다면 검색 파라미터를 만져도 고쳐지지 않는다. 먼저 어느 층이 깨졌는지 분리해야 한다.
평가셋은 질문 목록이 아니라 판정 계약이다
우리는 발열 대응 절차, 질병별 등교중지 기간, 위기경보 단계별 조치, 보고 체계처럼 현장에서 실제로 나오는 질문 가운데 단일 질의 30개를 골랐다. 후속 질의 8세트는 별도로 구성했다.
이 평가셋으로 실행한 세 RAG 구성의 실제 응답과 근거는 학교 보건 문서 RAG 벤치마크에서 질의별로 확인할 수 있다.
질문만 저장하면 재현할 수 없다. 각 사례에는 다음 정보가 함께 있어야 한다.
- 익명화한 사례 ID와 질문 또는 대화 턴
- 평가에 사용한 근거 문서와 버전
- 정답을 뒷받침하는 청크나 표의 행
- 기대 행동: 답변, 답변 거절 또는 담당자 이관
- 오류가 났을 때의 위험도
아래는 기존 비교에서 공개한 질문과 근거 문구로 구조만 다시 만든 자체 평가 하네스 예시다. Bedrock JSONL 스키마가 아니며 실제 사용자 로그나 내부 원본 ID를 포함하지 않는다. 실제 평가셋에서는 근거 문서 버전도 함께 저장한다.
{"id":"exclusion-followup-example",
"turns":["인플루엔자 등교중지는?","그럼 수두는?"],
"expectedSourceIds":["disease-table:varicella"],
"referenceAnswer":"모든 수포에 가피가 형성될 때까지, 발진 후 최소 5일",
"requiredBehaviors":["resolve_previous_intent","cite_source"],
"risk":"high"}
수두 기준 문구는 질병관리청이 공개한 등교·등원 중지 안내에서도 확인할 수 있다.1
평가셋은 운영 중 발견한 실패를 흡수하며 자라야 한다. 흔한 질문만 모으면 데모는 통과해도 근거 없는 질문, 오래된 문서, 모호한 표현 앞에서 무너진다. 답해야 하는 사례와 거절해야 하는 사례를 나눠 두어야 거절 횟수가 아니라 올바른 거절을 판정할 수 있다.
검색은 생성과 떼어 먼저 평가한다
정답 근거가 하나인 질문이라면 첫 지표는 복잡할 필요가 없다. 그 청크가 상위 k개에 있으면 Hit@k, 없으면 실패다. 순위도 함께 남긴다. 같은 청크가 2위에서 18위로 밀렸다면 현재 k값에서는 아직 통과하더라도 회귀 신호다. 필요한 근거가 여러 개인 질문에는 정답으로 표시한 근거 중 실제로 회수한 비율을 계산한다.
표 문서는 청크 구조도 검사해야 한다. 제목, 열 헤더와 대상 행이 함께 남아 있는지 본다. 셀 값만 회수하면 기간의 단위나 조건이 사라질 수 있다. 우리 비교에서도 수두 기준은 인덱스에 있었지만 "수두는 며칠 쉬어요?" 같은 표현으로 안정적으로 상위에 오지 않았다. 검색 결과 수를 20개로 늘리고 리랭커를 켠 뒤에도 해당 행은 상위 결과에 나타나지 않았다. 없는 후보는 리랭킹할 수 없다.
Amazon Bedrock RAG Evaluation은 검색 전용과 검색·생성 통합 평가를 나누며, 검색 전용 평가에서는 문맥 관련성과 문맥 커버리지를 제공한다.23 Context Coverage는 기대 청크 목록과 검색 결과를 직접 대조하지 않는다. 정답 응답인 referenceResponses에 필요한 정보가 검색 문맥에 있는지를 판단한다. BYOI 데이터의 referenceContexts는 선택 항목이며 기본 지표에는 쓰이지 않는다.4 특정 공문서의 특정 행을 반드시 찾아야 한다면 정답 청크 ID와 순위는 자체 회귀 테스트에서 별도로 검사해야 한다.
검색을 통과한 뒤 생성 품질을 본다
검색이 정답 근거를 반환한 사례만 떼어 생성 단계를 본다. 관리형 경로와 직접 구축 경로를 비교할 때도 동일한 Claude 모델과 프롬프트를 사용했다. 그래야 답의 차이를 검색 경로의 차이로 해석할 수 있었다.
생성 평가는 다음 질문으로 나눴다.
- 근거에 있는 내용만 답했나
- 기간과 예외 조건을 빠뜨리지 않았나
- 인용한 문서가 실제 주장과 연결되나
- 근거가 없을 때 답을 만들지 않고 거절하나
Bedrock의 retrieve-and-generate 평가는 정확성, 완전성, 유용성, 논리적 일관성, 근거성, 인용 정밀도·커버리지와 거절 등을 제공한다.3 이 점수들은 대량 비교에 유용하지만 단위 테스트처럼 명확한 통과·실패 판정은 아니다. 특히 Refusal은 응답이 얼마나 거절·회피하는지를 점수화할 뿐, 그 거절이 적절한지는 판정하지 않는다. 답해야 하는 사례와 거절해야 하는 사례를 먼저 분리해야 한다.
**LLM 평가자(LLM judge)**도 모델이다. 내장 지표를 쓰면 평가 모델 ID와 공개된 판정 프롬프트의 확인일을 기록한다.5 프롬프트까지 버전 관리해야 한다면 팀이 소유하는 custom metric을 쓴다.6 한국어 고위험 사례에서는 사람이 판정한 작은 기준셋과 결과가 일치하는지도 먼저 확인해야 한다. 평균 점수가 올라도 필수 질문 하나가 근거 없이 답하기 시작했다면 배포를 막는 편이 맞다.
후속 질문은 별도 세트로 분리한다
단일 질의를 잘 맞힌다고 대화도 잘하는 것은 아니다. "인플루엔자 등교중지는?" 다음에 "그럼 수두는?"라고 물으면 두 번째 문장에는 무엇의 기간을 묻는지 적혀 있지 않다. 직전 턴에서 "등교중지 기간"을 복원한 뒤 검색해야 한다.
두 경로 모두 8개 후속 질의 세트에서 생략된 의도를 복원했다. 이 결과는 전체 RAG 품질이 같다는 뜻이 아니다. 대화 이력 전달은 당시 병목이 아니었다는 뜻이다. 검색 순위와 표 근거 회수는 여전히 갈렸다.
Bedrock의 retrieve-and-generate 평가셋은 대화당 최대 5턴을 담을 수 있지만, retrieve-only 평가는 단일 턴만 지원한다.7 후속 질문 회귀는 생성 포함 평가나 애플리케이션 자체 하네스에서 따로 보존해야 한다.
실패를 한 줄의 오답으로 기록하고 끝내지 않는다
한 사례가 실패하면 아래 분류 중 하나를 붙인다.
| 실패 유형 | 확인할 지점 | 첫 조치 |
|---|---|---|
| 문서·파싱 | 원문 구조와 표가 추출됐나 | 파서 출력 비교 |
| 청크 경계 | 제목·헤더·행이 함께 남았나 | 청크 원문 검사 |
| 후보 회수 | 정답 청크가 top-k에 있나 | 질의·필터·검색 방식 비교 |
| 리랭킹 | 후보 안에서 순위가 개선됐나 | 변경 전후 순위 비교 |
| 생성·근거성 | 근거를 왜곡하거나 누락했나 | 같은 근거로 모델·프롬프트 비교 |
| 대화 맥락 | 생략된 의도를 복원했나 | 질의 재작성과 세션 상태 확인 |
| 테스트 하네스 | 올바른 API와 설정을 호출했나 | 요청·응답·지원 범위 확인 |
마지막 항목은 실제로 우리를 한 번 속였다. 완전관리형 Knowledge Base 경로가 30문항 모두 실패했을 때 서비스 품질 문제로 판단했지만, 원인은 이 관리형 경로가 지원하지 않는 RetrieveAndGenerate API를 호출한 테스트 코드였다.8 서비스 실패와 테스트 코드 오류를 분리하지 않으면 자신 있게 틀린 결론을 낼 수 있다. 자세한 비교 과정은 pgvector와 관리형 Knowledge Base 비교에 남겼다.
같은 세트로 한 변수씩 바꾼다
회귀 평가에서는 질문뿐 아니라 실행 조건도 고정한다.
- 코퍼스·문서 버전
- 파서·청커 버전
- 임베딩 모델·인덱스 버전
- 검색 방식·필터·k값·리랭커
- 생성 모델·프롬프트 버전
- 평가셋·LLM 평가자 버전
Smart Parser를 명시해 같은 문서를 다시 적재하자 30개 중 6개가 "못 찾음"에서 정답으로 바뀌었다. 다만 Smart Parser는 원래 기본값이었으므로 이 재실행만으로 원인을 확정할 수는 없다. 다음에는 검색 결과 수와 리랭커를 바꿨지만 수두 기준이 적힌 행은 여전히 상위 결과에 나타나지 않았다. 같은 질문을 다시 돌렸기 때문에 어떤 변경이 무엇을 고쳤고 무엇을 못 고쳤는지 구분할 수 있었다.
여러 변수를 한꺼번에 바꾸면 점수가 올라도 이유를 모른다. 한 번에 설정값 하나만 바꾸고 같은 평가셋을 실행해 사례별 차이를 남긴다. 그래야 다음 변경에서 회귀 여부를 비교할 기준선이 생긴다.
평균보다 치명적인 실패를 먼저 본다
하나의 평균 점수는 보고하기 쉽지만 출시 결정을 흐릴 수 있다. 고위험 질문 한 건의 근거 없는 답변과 저위험 질문 여러 건의 표현 차이를 같은 무게로 더하면 안 된다.
통과 조건은 다음처럼 설계할 수 있다.
- 필수 근거 사례에서 정답 청크를 놓치면 차단
- 근거 없는 사례에서 답을 만들어 내면 차단
- 기준 버전이 통과한 고위험 사례가 후보 버전에서 실패하면 차단
- 나머지 품질 점수·지연 시간·요청당 비용은 사례별 차이와 함께 검토
구체적인 합격선은 도메인과 위험도에 맞춰 팀이 정해야 한다. 중요한 것은 평균 하나를 올리는 일이 아니라, 어떤 실패가 출시를 막는지 실행 전에 합의하는 것이다.
Bedrock 평가는 회귀 파이프라인의 한 층이다
Bedrock은 S3의 JSONL 평가셋을 받아 Knowledge Base나 외부 RAG 결과를 평가한다. 외부 추론 결과를 가져오는 BYOI를 쓰면 pgvector나 다른 환경에서 만든 검색·생성 결과도 동일한 평가 모델로 비교할 수 있다.9 평가 작업당 최대 1,000개 프롬프트를 받고 결과를 S3에 저장한다. CreateEvaluationJob은 비동기로 실행되므로 CI에서는 GetEvaluationJob으로 완료 상태를 확인하고 결과 파일을 읽어 기준 버전과 비교해야 한다.1011
서비스가 맡아 주는 부분과 우리가 유지할 부분은 다르다.
| Bedrock 평가에 맡길 것 | 자체 하네스에 남길 것 |
|---|---|
| 문맥 관련성·커버리지의 LLM 평가 | 정답 청크 ID, Hit@k와 순위 |
| 정확성·완전성·근거성·인용 평가 | 치명 사례의 이진 차단 조건 |
| 동일한 평가 모델로 외부 RAG 결과 비교 | 지연 시간·토큰 비용·검색 비용·버전별 차이 |
| S3에 저장되는 평가 리포트 | 사람이 판정한 기준셋으로 평가 모델 보정 |
RAG Evaluation 비용은 judge가 사용한 입력·출력 토큰에 해당 모델의 표준 온디맨드 요금이 붙는다. Bedrock Knowledge Base를 직접 평가하면 적용되는 Knowledge Bases 사용료가 추가되고, retrieve-and-generate 작업에는 생성 모델 추론 비용도 발생한다.12 평가 비용도 버전별로 남겨야 회귀 검사가 커지면서 생기는 운영비를 통제할 수 있다.
CI로 옮길 최소 형태
파서, 청커, 인덱스, 검색 설정, 모델이나 프롬프트가 바뀌면 같은 평가셋을 실행한다. 기준 버전과 후보 버전의 사례별 결과를 비교하고, 치명 사례가 회귀하면 병합이나 배포를 막는다. LLM 평가자의 평균 점수만 저장하지 말고 검색 청크, 답변, 인용, 실패 유형과 각 구성요소의 버전을 함께 남긴다.
평가셋 전체를 튜닝에 쓰지 않는 것도 중요하다. 자주 확인하는 개발 세트와 마지막에만 보는 보류(holdout) 세트를 나누지 않으면 30개 질문에만 잘 맞는 시스템이 된다. 운영에서 새 실패를 발견하면 익명화한 재현 사례로 추가하고, 기존 사례는 이유 없이 지우지 않는다.
이 절차는 AI PoC를 운영 시스템으로 넘기는 게이트의 품질 평가와 자동 회귀 검사를 RAG에 맞게 옮겼다. 평가셋은 보고서용 벤치마크가 아니라 배포를 멈출 수 있는 제품 명세다.
평가가 알려준 것
검색과 생성을 분리하지 않았다면 파싱·청킹·검색·API 호출 오류를 계속 같은 문제로 취급했을 것이다. 30개 단일 질의와 8개 후속 질의 세트의 가장 큰 가치는 점수 하나가 아니었다. 실패를 재현하고, 원인을 분류하고, 같은 조건에서 다시 확인할 수 있게 됐다.
좋은 RAG 평가는 "모델이 똑똑한가"를 묻지 않는다. 필요한 근거를 찾았나, 그 근거만으로 답했나, 바뀐 시스템이 어제 통과한 사례를 오늘 깨뜨렸나를 묻는다.
업무별 평가셋과 RAG 회귀 파이프라인 설계는 데이터 & ML 엔지니어링에서, 평가 결과를 출시·승인 기준으로 연결하는 일은 AX 컨설팅에서 함께 다룬다.
참고 자료
본문의 Bedrock 평가 기능과 제한은 아래 공식 문서를 기준으로 2026년 7월 16일 확인했다. 30개 단일 질의, 8개 후속 질의 세트와 설정 변경 결과는 우리 코퍼스에서 직접 관찰한 값이다.
출처와 각주12개펼치기접기
Footnotes
-
질병관리청, 봄철 수두 및 유행성이하선염 증가, 학교생활 시 감염병 조심하세요. 수두의 등교·등원 중지 기간을 안내한다. ↩
-
Amazon Bedrock, Evaluate the performance of RAG sources using Amazon Bedrock evaluations. Retrieve-only와 retrieve-and-generate 평가를 구분한다. ↩
-
Amazon Bedrock, RAG evaluation metrics. 검색·생성 평가의 기본 지표와 의미. ↩ ↩2
-
Amazon Bedrock, Create a prompt dataset for retrieve-only RAG evaluation jobs.
referenceResponses, 선택적referenceContexts, BYOI 검색 결과 형식을 설명한다. ↩ -
Amazon Bedrock, Evaluator prompts used in a RAG evaluation job. 평가 모델에 전달되는 내장 judge 프롬프트를 공개한다. ↩
-
Amazon Bedrock, Create a prompt for a custom metric. 팀이 소유하는 평가 프롬프트와 점수 기준을 정의하는 방법. ↩
-
Amazon Bedrock, Create a prompt dataset for a RAG evaluation. JSONL, 최대 1,000개 프롬프트, 대화 턴 제한. ↩
-
Amazon Bedrock API, RetrieveAndGenerate. 완전관리형 Knowledge Base에서는 이 API를 지원하지 않는다고 명시한다. ↩
-
AWS, Evaluate models or RAG systems using Amazon Bedrock Evaluations — now generally available. 외부 RAG 결과를 가져오는 BYOI와 인용 지표. ↩
-
Amazon Bedrock API, CreateEvaluationJob. 비동기 평가 작업 생성 API. ↩
-
Amazon Bedrock API, GetEvaluationJob. 평가 작업 상태와 결과 위치 조회 API. ↩
-
AWS, Amazon Bedrock pricing — Model Evaluation. judge 토큰과 Knowledge Base 사용 비용의 과금 기준. ↩
