dev.to

How to Make RAG Show Its Evidence: Claim Verification at 15M-Document Scale

RAG가 근거를 드러내게 만드는 법: 1,500만 문서 규모의 주장 검증

문서가 늘수록 RAG는 오류를 드러내기보다 그럴듯한 답을 내놓기 쉽습니다. 이 글은 검색, 인용 강제, 주장별 검증, 답변 유보의 네 단계로 이를 줄이는 설계를 제안하고, 소규모 SQuAD v2 실험에서 답변 위험 감소와 커버리지 손실을 함께 보고합니다.

에디터 노트

실험이 뒷받침하는 범위는 네 단계 설계의 1,500만 문서 성능이 아니라 검증과 유보가 답변 위험을 낮추는 메커니즘입니다. SQuAD v2에서는 부정확한 답변 비율이 100%에서 11.6%로 줄었지만 커버리지도 100%에서 46.8%로 떨어졌고, 정답 문단을 미리 제공한 단일 모델 실험입니다. 따라서 수치는 효과의 증거이지 운영 환경의 예상치가 아닙니다. 댓글은 유보를 제품의 최종 응답으로 지키지 않으면 자동 재시도가 다시 환각을 부를 수 있다고 지적합니다. 또 검색된 출처 자체가 오래됐거나 틀린 경우에는 인용 검증만으로 사실성을 보장하지 못합니다. 원문도 출처 신뢰성과 최신성을 별도 통제로 남겨 둡니다. 이 설계의 성패는 검증기뿐 아니라 유보 이후의 호출 흐름과 원본 데이터 확인까지 평가하는 데 달려 있습니다.

AI 요약

문서 규모가 커진 RAG는 검색 오류나 근거 부족을 로그에 남기지 않고 그럴듯한 문장으로 감출 수 있습니다. 저자는 이를 줄이기 위해 검색, 근거 인용 강제, 주장별 검증, 답변 유보를 차례로 적용하는 네 단계 구조를 제안합니다. 다만 1,500만 문서 환경에서 전체 구조를 벤치마크한 것은 아니며, 직접 측정한 실험은 소규모 검증·유보 실험입니다.

네 단계 설계

검색 단계에서는 MinHash와 LSH로 중복 문서를 줄이고, 문서 맥락을 덧붙인 청크를 임베딩합니다. Dense 검색과 BM25를 Reciprocal Rank Fusion(RRF)으로 합친 뒤, 약 150개 후보를 cross-encoder로 재정렬해 20개 안팎을 남기는 방식을 제안합니다. 문서 수와 벡터 수를 구분해야 한다고 강조합니다. 1,500만 개의 1,024차원 float32 벡터는 원시 벡터만 약 61GB이며, 문서마다 여러 청크를 만들면 실제 벡터 수는 훨씬 커집니다. 온디스크 인덱스와 양자화는 메모리를 줄이지만 재현율 손실을 실제 데이터에서 측정해야 합니다.

생성 단계에서는 각 사실 문장에 검색된 청크 ID를 붙이게 하고, 서버가 인용 ID가 실제 검색 결과에 포함됐는지 확인합니다. 이어 원자적 주장마다 인용한 청크만 근거로 삼아 함의 여부를 판정합니다. 검증에 실패한 주장은 제거하거나 답변 전체를 유보합니다. 검색 점수, 검증을 통과한 주장 비율, 여러 생성 결과의 일관성을 보정 데이터로 조정한 임계값과 함께 써서 유보 여부를 결정하는 방안도 제시합니다. 검증 모델 역시 오류를 내므로 무오류를 보장하지는 않습니다.

측정과 한계

저자는 SQuAD v2의 1,200개 테스트 질문으로 검증·유보 구조를 시험했습니다. 정답 근거가 주어진 설정에서 무조건 답변한 기준선은 커버리지 100%, 답변 위험 52.4%, 답변 불가능 질문에 대한 부정확한 답변 비율 100%였습니다. 검증·유보를 적용하면 각각 54.3%, 20.6%, 18.3%가 됐고, 보정된 유보를 더하면 46.8%, 15.3%, 11.6%로 바뀌었습니다. 부정확한 답변을 줄이는 대신 답변 가능한 질문에도 답하지 않는 비율이 늘었습니다. 이 실험은 단일 소형 추출형 QA 모델과 정답 문단을 사용했으므로 1,500만 문서 규모의 종단간 성능을 입증하지 않습니다.

저자는 검색 실패율, 주장별 근거 일치도, 검색 문맥 정밀도·재현율, 유보 보정을 함께 측정하고, 커버리지와 답변 위험을 나란히 보고하라고 제안합니다. 검증기의 오수락·오거부도 측정해야 하며, 20개 질문에서 오류가 없었다는 결과만으로 낮은 오류율을 주장할 수 없다고 설명합니다.

dev.to 반응

  • @salparvez — 어떤 수치가 인용 논문에서 왔고 어떤 수치를 직접 시험했는지 서두에서 밝힌 점이 좋습니다. RAG 글에서는 드문 구분입니다. 그럴듯한 오답을 "확인할 수 없었습니다"로 바꾸는 것이 맞는 기본값입니다. 데모가 덜 인상적으로 보이더라도 그렇습니다.
  • @max_quimby — RAG는 큰 소리로 실패하는 게 아니라 그럴듯하게 실패한다는 표현을 더 많은 팀이 제품 출시 전에 새겼으면 합니다. 유창한 오답은 아무도 경보를 받지 않는다는 점에서 충돌보다 큰 비용을 낳습니다. 실제 측정값과 설계 제안을 조심스럽게 구분한 점도 좋습니다. 제가 보기엔 유보 단계가 특히 과소평가됩니다. "확인할 수 없었습니다"가 안전한 답이 되려면 제품 경험도 이를 실패가 아닌 정식 답변으로 다뤄야 합니다. 빈 응답을 자동 재시도하는 후속 호출이 붙자 유보율이 조용히 무너지는 일을 봤습니다. 시스템은 유보를 허용했지만 루프가 환각 답변이 나올 때까지 다시 물었습니다. 검증 단계의 근거 임계값은 어디에 뒀나요? 주장과 표현이 다르다는 이유로 정확한 답을 유보하지 않도록 어떻게 했나요? 정밀도와 유보 사이의 균형이 이런 파이프라인의 성패를 좌우합니다.
  • @squeaky_spirit — 검색 전에 출처 이력의 변화를 확인하는 단계를 추가하겠습니다. 어떤 게시물은 기록이 20개라고 했지만 원본의 목록 API에는 30개가 나왔습니다. 누군가 첫 페이지를 전체라고 읽었을 뿐, 내용을 지어낸 것은 아니었습니다. 모든 검색기는 그 문장을 뒷받침하는 인용을 찾아낼 수 있습니다. 따라서 게시물에서 인용한 숫자가 아니라 원본 기록에서 숫자를 다시 가져와야 하고, 둘이 다르면 유보해야 합니다. 네 단계는 검색된 텍스트가 뒷받침하지 않는 답변을 잡습니다. 이 단계는 검색된 텍스트가 뒷받침하지만 현실과는 다른 내용을 잡습니다.

원문: dev.to / 번역·요약: Trawling