LangChain RAG 복습 05: 검색 결과를 평가하고 출처를 남기는 법
메뉴

AI Backend

LangChain RAG 복습 05: 검색 결과를 평가하고 출처를 남기는 법

LangChain RAG 학습 흐름을 따라 retrieval evaluation, answer faithfulness, citation, source attribution으로 검색 결과와 답변 근거를 점검하는 방법을 정리합니다.

LangChain RAG 복습 05: 검색 결과를 평가하고 출처를 남기는 법 hero image
Markdown약 1845 tokens

답변이 맞아 보여도 검색 결과를 따로 봐야 한다

검색 품질을 높이는 전략을 어느 정도 붙이고 나면 자연스럽게 답변 생성으로 넘어가고 싶어진다. 앞선 흐름에서는 hybrid search, parent document retrieval, multi-vector retrieval로 "어떤 단위로 찾고 어떤 단위로 읽을 것인가"를 분리했다. 검색으로 이 글에 처음 들어왔다면, 여기서는 retriever가 이미 후보 문서를 돌려준 다음부터 읽으면 된다. 바로 최종 답변만 보면 RAG의 품질을 판단하기 어렵기 때문이다.

이번에도 1편부터 이어 온 가상의 학습용 문서 sample-office-guide.md를 사용한다. 실제 내부 문서가 아니라 검색 흐름을 설명하기 위해 만든 예시다. 사용자가 이렇게 묻는다고 하자.

# 이번 질문은 장비 대여 위치와 회의실 예약 조건을 함께 확인한다.회의실 발표용 모니터를 쓰려면 어디에서 빌리고, 예약은 언제까지 해야 하나요?

좋은 답변은 그럴듯한 문장으로 끝나지 않는다. 답변이 실제 검색 결과에서 온 것인지, 두 조건이 각각 어느 section에서 온 것인지, 출처를 다시 열어 확인할 수 있는지까지 남겨야 한다. RAG에서 "맞는 답변"은 모델이 잘 쓴 문장이 아니라, 검색 후보와 답변 문장이 서로 연결되는 상태에 가깝다.

그래서 5편의 흐름은 답변을 만들기 전과 후를 나누어 본다. 답변 전에는 retriever 후보를 평가하고, 답변 후에는 문장이 context 밖으로 나갔는지 확인한다. 마지막에는 사용자가 확인할 수 있는 citation과 source attribution을 남긴다.

검색 평가는 답변 전 후보 문서를 읽는 일이다

retrieval evaluation은 답변이 예쁘게 생성됐는지 보는 단계가 아니다. 모델 호출 전에 "질문을 해결할 후보 문서가 제대로 올라왔는가"를 보는 단계다. 이 평가를 건너뛰면 답변이 틀렸을 때 원인을 분리하기 어렵다. 검색이 틀렸는지, prompt가 부족했는지, 모델이 context를 잘못 읽었는지 한꺼번에 섞인다.

예를 들어 retriever가 아래 후보를 반환했다고 하자.

순위sourcesection확인할 점
1sample-office-guide.mdequipment모니터와 장비 대여 위치가 들어 있다
2sample-office-guide.mdreservation회의실 예약 마감 시간이 들어 있다
3sample-office-guide.mdsecurity질문과 직접 관련이 낮다

이 결과는 나쁘지 않다. 질문이 두 가지 업무 조건을 묻기 때문에 equipmentreservation이 함께 올라온 것은 좋은 신호다. 반대로 security는 prompt에 넣기 전에 제외할 수 있다. 평가의 핵심은 "상위 k개가 모두 관련 있어야 한다"가 아니라, 답변에 필요한 근거가 충분히 올라왔고 불필요한 후보를 알아볼 수 있는지다.

작은 평가 기록은 아래처럼 남길 수 있다.

# 검색 후보를 답변 전 단계에서 읽고, prompt에 넣을 문서와 제외할 문서를 나눈다.retrieved_docs = retriever.invoke(question) selected_docs = [    doc for doc in retrieved_docs    if doc.metadata["section"] in {"equipment", "reservation"}] rejected_docs = [    {        "source": doc.metadata["source"],        "section": doc.metadata["section"],        "reason": "질문이 묻는 장비 대여나 예약 조건과 직접 관련이 낮음",    }    for doc in retrieved_docs    if doc.metadata["section"] not in {"equipment", "reservation"}]

이 코드는 실제 평가 프레임워크를 대체하려는 예제가 아니다. 중요한 것은 답변 전에 후보를 읽는 자리를 만드는 것이다. 그 자리가 있어야 k를 늘릴지, metadata filter를 먼저 걸지, multi-query를 쓸지, reranking을 넣을지 판단할 수 있다.

self-query는 metadata 조건을 검색 전에 분리한다

질문에는 본문 의미와 metadata 조건이 섞여 들어올 때가 많다. 예를 들어 사용자가 이렇게 묻는다.

# 질문 안에는 section 조건과 실제 의미 검색 조건이 함께 들어 있다.회의실 안내 중에서 모니터 대여와 예약 마감 시간만 알려줘.

여기서 회의실 안내 중에서는 문서 범위를 좁히는 조건이고, 모니터 대여와 예약 마감 시간은 실제로 찾고 싶은 내용이다. self-query 계열 접근은 이런 조건을 검색 전에 분리하려는 생각에 가깝다. 질문 전체를 embedding 검색에 던지는 대신, metadata filter로 먼저 범위를 줄이고 본문 검색은 남은 의미 조건에 집중하게 만든다.

이 글의 예시에서는 단순하게 생각해도 충분하다.

질문 요소처리 방식이유
회의실 안내metadata filter 후보문서나 section 범위를 좁힐 수 있다
모니터 대여본문 검색장비 section의 실제 문장을 찾아야 한다
예약 마감 시간본문 검색예약 section의 조건 문장을 찾아야 한다

self-query를 적용할 때 조심할 점은 필터가 너무 강해지는 것이다. 사용자가 회의실 안내라고 말했지만 실제 답변에는 equipment section도 필요할 수 있다. 그래서 필터 결과를 닫힌 정답처럼 쓰지 말고, 원 질문과 분리된 조건을 다시 비교해야 한다.

답변 충실성은 문장을 context와 대조한다

검색 후보가 좋아도 답변이 항상 근거를 충실히 따르는 것은 아니다. answer faithfulness는 답변이 제공된 context 안에서만 말하고 있는지 보는 관점이다.

예시 답변을 보자.

# 이 답변은 마지막 문장이 context 밖 정보를 추가했는지 확인해야 한다.발표용 모니터는 안내 데스크에서 대여 기록 후 사용할 수 있습니다.회의실 예약은 사용 시작 1시간 전까지 가능합니다.단, 모든 장비 대여는 팀장 승인이 필요합니다.

앞의 두 문장은 equipment, reservation 후보에서 온 내용이라고 볼 수 있다. 문제는 마지막 문장이다. sample-office-guide.md의 검색 후보에 팀장 승인 조건이 없었다면 이 문장은 빼야 한다. 문장이 업무상 그럴듯해 보여도 RAG 답변에서는 근거가 없는 추가 정보다.

답변을 점검할 때는 문장 단위로 나누어 보는 편이 낫다.

답변 문장근거 section판정
모니터는 안내 데스크에서 대여 기록 후 사용한다equipment유지
회의실 예약은 사용 시작 1시간 전까지 가능하다reservation유지
모든 장비 대여는 팀장 승인이 필요하다없음제거 또는 확인 필요

이 과정을 거치면 prompt를 고칠지, 답변 후처리를 넣을지, citation 출력을 강제할지 판단이 쉬워진다. 특히 업무 문서 RAG에서는 "모른다"나 "검색 결과에 없다"를 말하는 능력이 답변을 길게 쓰는 능력보다 중요할 때가 많다.

citation은 각주 모양이 아니라 metadata 계약이다

citation을 나중에 붙일 수 있는 장식처럼 생각하면 어렵다. 출처 표시는 document loading 단계에서 metadata를 남기고, chunking 단계에서 metadata를 보존하고, retrieval 단계에서 metadata를 함께 반환해야 가능하다.

답변에 필요한 최소 metadata는 프로젝트마다 다르지만, 학습용 예시에서는 아래 정도를 생각할 수 있다.

# citation을 만들려면 검색 결과에 source, section, chunk id 같은 추적 필드가 남아 있어야 한다.Document(    page_content="회의실 예약은 사용 시작 1시간 전까지 가능합니다.",    metadata={        "source": "sample-office-guide.md",        "section": "reservation",        "chunk_id": "sample-office-guide.md#reservation:001",        "updated_at": "2026-06-01",    },)

이 metadata가 있으면 답변을 아래처럼 만들 수 있다.

# 사용자가 다시 확인할 수 있도록 답변 문장 옆에 근거를 붙인다.발표용 모니터는 안내 데스크에서 대여 기록 후 사용할 수 있습니다. [sample-office-guide.md, equipment]회의실 예약은 사용 시작 1시간 전까지 가능합니다. [sample-office-guide.md, reservation]

citation 형식은 서비스에 맞게 달라질 수 있다. [파일, section]으로 보일 수도 있고, 웹 서비스라면 링크나 문서 제목으로 보일 수도 있다. 어떤 형식을 택하든 답변 문장과 근거 문서가 다시 연결되어야 한다.

출처 귀속은 사용자 경험이기도 하다

source attribution은 내부 평가용 로그만이 아니다. 사용자가 답변을 신뢰할 수 있게 만드는 인터페이스다. 특히 RAG 답변이 정책, 계약, 운영 절차처럼 책임 있는 내용을 다룬다면 "어디에서 온 말인지"를 보여주는 방식이 필요하다.

다만 모든 근거를 그대로 화면에 쏟아내면 읽기 어려워진다. 사용자가 확인해야 하는 수준에 맞춰 나눈다.

표시 수준화면에 보이는 내용적합한 경우
간단 표시문서명과 section사내 도움말, 학습용 챗봇
상세 표시문서명, section, 갱신일, chunk id운영자 검수 화면
링크 표시원문 페이지나 문서 위치웹 문서, 제품 문서

출처가 표시되면 답변 오류를 다루는 방식도 바뀐다. 사용자는 "답변이 이상하다"가 아니라 "이 section은 관련이 없다" 또는 "갱신일이 오래됐다"라고 피드백할 수 있다. 개발자 입장에서는 retriever, metadata, 문서 갱신 문제를 분리해서 볼 수 있다.

적용 예시: 평가 기록이 있어야 튜닝 방향이 보인다

RAG 답변을 고칠 때 가장 답답한 상황은 원인이 한 덩어리로 보이는 경우다. 답변이 틀렸다는 사실만 남으면 retriever를 고쳐야 하는지, chunk를 다시 나눠야 하는지, prompt를 바꿔야 하는지 알기 어렵다. 그래서 5편의 평가 기록은 단순한 로그가 아니라 다음 수정 방향을 고르는 기준이 된다.

주의 사례는 단순하다. 답변이 틀렸다는 이유로 prompt만 고치면, 실제 문제였던 metadata filter나 검색 후보 누락은 그대로 남는다. 반대로 retriever만 바꾸면, context 밖 문장을 추가하는 답변 문제를 놓칠 수 있다.

같은 질문이라도 실패 위치에 따라 손댈 곳이 달라진다.

관찰한 현상먼저 볼 곳가능한 조정
필요한 section이 검색 후보에 없다retriever와 metadata filterquery transformation, filter 완화, multi-query 검토
후보는 있는데 답변이 빠뜨린다prompt context와 answer formatcontext 정렬, 답변 형식, citation 요구 조정
답변이 context 밖 말을 추가한다answer faithfulness근거 없는 문장 제거, "검색 결과에 없음" 응답 허용
출처가 맞지 않거나 사라진다metadata 전달 경로loading, splitting, vector store 저장 필드 확인

이 기록이 쌓이면 개선 작업도 작아진다. 매번 전체 파이프라인을 다시 설계하는 것이 아니라, 이번 실패가 검색 전 조건 분리 문제인지, 후보 선택 문제인지, 답변 생성 후 검증 문제인지 좁혀서 볼 수 있다. 작은 RAG 예제에서는 과해 보일 수 있지만, 문서가 늘어날수록 이런 기록이 없으면 품질 개선이 감으로 흐르기 쉽다.

다음 단계로 넘기기 전에 남길 것

5편까지 오면 RAG 흐름은 단순히 "문서를 넣고 답변을 받는 코드"가 아니다. 문서는 chunk가 되고, chunk는 검색 후보가 되고, 검색 후보는 prompt context가 되고, 답변은 다시 근거와 연결된다.

다음 글에서는 이 흐름을 실제 문서와 운영 환경으로 옮길 때 생기는 경계를 본다. PDF는 텍스트가 생각처럼 깨끗하지 않을 수 있고, 웹 문서는 최신성과 중복을 신경 써야 한다. Markdown은 heading 구조가 안정적일 때 강하지만, 문서 작성 규칙이 흔들리면 splitting 품질도 같이 흔들린다. 또한 token budget과 prompt injection risk는 검색 품질과 운영 안전성에 직접 영향을 준다.

5편에서 가져갈 기준은 이렇다.

  • 답변 전에는 retriever 후보를 평가한다.
  • 답변 후에는 문장이 context 밖으로 나가지 않았는지 본다.
  • citation은 답변 마지막에 붙이는 장식이 아니라 metadata 보존의 결과다.
  • source attribution은 사용자와 개발자가 같은 근거를 보게 만드는 장치다.

자주 묻는 질문

모든 RAG에 평가 프레임워크가 필요한가?

처음부터 거창한 평가 시스템이 필요하지는 않다. 하지만 최소한 질문, 검색 후보, 선택한 context, 제외한 후보, 최종 답변은 함께 남겨야 한다. 이 기록이 없으면 답변 품질이 좋아졌는지 나빠졌는지 감으로만 판단하게 된다.

citation만 붙이면 hallucination 문제가 해결되나?

아니다. citation은 답변과 근거를 연결하는 표시일 뿐이다. 답변 문장이 실제 context를 따르는지 확인하는 과정이 함께 있어야 한다. 출처가 붙어 있어도 문장 자체가 근거를 벗어나면 잘못된 답변이다.

self-query는 언제 먼저 검토할 만한가?

문서에 source, section, date, product, role 같은 metadata가 안정적으로 있고, 사용자가 질문에서 그 조건을 자주 말한다면 검토할 만하다. 반대로 metadata 품질이 낮으면 self-query가 검색 범위를 잘못 좁힐 수 있다.

댓글

GitHub 계정으로 로그인하면 댓글을 남길 수 있습니다. 댓글은 GitHub Discussions를 통해 운영됩니다.

TOP