rag 데모는 PDF 몇 개를 청킹하고 벡터 DB에 넣으면 금방 동작한다. 문제는 문서가 수정·삭제·폐기되고, 임베딩 모델이 바뀌고, 사용자가 “왜 이 답이 나왔나”를 묻기 시작하는 순간이다. 프로덕션 RAG는 검색 정확도만큼 인덱스 운영과 관찰성이 중요하다.
1. 문서 레지스트리를 별도로 둔다
벡터 DB는 보통 “문서 1개가 어떤 청크 ID들로 분해됐는지”를 관계형 DB처럼 관리해주지 않는다. 문서가 업데이트됐을 때 기존 청크 15개를 정확히 삭제하고 새 청크 17개를 넣으려면 별도 레지스트리가 필요하다.
필수 메타데이터:
doc_idchunk_vector_idcontent_hashversionindexed_atstatus:active,deleted,superseded
이 구조가 없으면 삭제된 정책 문서나 구버전 매뉴얼이 검색 결과에 계속 섞인다. RAG의 환각처럼 보이지만 실제 원인은 인덱스 정합성 문제다.
2. 인덱스 교체를 배포처럼 다룬다
대규모 재색인 중간에 작업이 실패하면 일부 문서는 v1, 일부 문서는 v2인 혼합 상태가 된다. 안전한 패턴은 Elasticsearch식 alias 배포다.
rag_index_2026_05_17 # 새로 빌드하고 검증한 인덱스
rag_index_current # 운영 쿼리가 바라보는 alias새 인덱스를 완성하고 벤치마크 질의로 검증한 뒤 alias를 원자적으로 바꾼다. 즉시 반영이 필요한 시스템은 valid_from 같은 가시성 타임스탬프를 메타데이터에 두고 쿼리 시점 필터링으로 부분 업데이트를 통제한다.
3. 검색 추적을 LLM 추적과 분리하지 않는다
프로덕션에서 잘못된 답변은 두 종류다.
- 검색이 잘못된 청크를 가져왔다
- 검색은 맞았지만 LLM이 컨텍스트를 잘못 사용했다
둘을 구분하려면 요청마다 다음 span을 남겨야 한다.
rag_request
embedding.query
retrieval.vector_search
retrieval.rerank
prompt.assembly
llm.generate특히 chunk_retrieved 이벤트에는 chunk ID, source document, score, rerank score, index version을 남긴다. 그러면 “모델이 환각했다”가 아니라 “deprecated v1 정책 문서가 top-3에 들어왔다”처럼 바로 조치 가능한 원인을 찾을 수 있다.
실무 체크리스트
- 청킹은 고정 길이보다 문단·문장·AST·섹션 구조를 우선한다
- chunk마다 source, section, page, timestamp, content hash를 저장한다
- 임베딩 모델명과 버전을 chunk metadata에 저장한다
- 임베딩 모델 변경은 전체 재색인과 shadow index 검증으로 처리한다
- hybrid search(vector + BM25)와 cross-encoder reranking을 별도 단계로 둔다
- 답변 품질뿐 아니라 검색 품질을 trace ID 기준으로 평가한다
관련 문서
- rag — 검색 증강 생성 기본 구조
- rag-tips-long-context — 장문 컨텍스트 RAG 기법
- gemini-file-search — 관리형 파일 검색 기반 RAG
- llm-observability-tips-tools — LLM 관찰성 도구
참고 자료
- What Matters in Production RAG — Arpit Bhayani (2026-05)