지식베이스를 왜 정기 전량 재구축하면 안 되나
← 전체 목차 · 이전: 지식베이스 갱신 시 인덱스는 어떻게 동기화하나 · 다음: Claude Code는 왜 코드 검색에 RAG를 쓰지 않는가
표준 답은 "너무 느려서"가 아니다 — 그것은 성능만 답한 것이고 일관성을 답하지 않았다.
프로덕션급 RAG는 반드시 증분 색인을 해야 하며, 버전 + 상태 머신으로 다음 여섯 대상이 협동해 갱신되도록 보장해야 한다.
동기화되지 않을 때의 결과: 답변에 신구 자료가 뒤섞인다.
| 비교 항목 | 데모 수준 RAG | 기업급 지식베이스 |
|---|
| 데이터 출처 | 일회성 PDF 임포트 | 매일 누군가 신규/수정/삭제/권한 변경 |
| 절차 | 청킹 → Embedding → 적재, 끝 | 지속적 증분 갱신 링크 필요 |
| 링크가 불안정할 때의 결과 | — | 이미 폐지된 제도를 인용하고, 방금 발행된 중요 변경을 보지 못한다 |
핵심 답변: 증분 색인 + 버전 + 상태 머신
문서 하나가 업로드부터 "검색 가능"까지 가는 완전한 상태 사슬:
업로드 → 파싱 → 청킹 → 벡터화 → 요약 생성 → 권한 동기화 → 색인 커밋 → 가시성 전환 → 검색 가능
철칙: 어느 한 단계가 실패해도 반제품이 프로덕션 리콜에 들어가서는 안 된다.
0층: 무엇이 바뀌었는지 어떻게 아는가 — 지문(Fingerprint) 확권
버전 + 상태 머신을 돌리려면 그 이전에 "이 문서가 바뀌었는가"를 내용을 다시 읽지 않고 판정할 수 있어야 한다. 수정 시각이나 파일명으로 판정하는 것은 가짜 증분이다 — 시각은 안 바뀌었는데 내용이 바뀌거나, 내용은 안 바뀌었는데 시각만 갱신되는 경우를 둘 다 오판한다.
문서·조각마다 MD5 지문을 생성해 「지문 대조 기준표」를 둔다
지문 불변 → 건너뜀(재계산 없음)
지문 변경 → 증분 처리 진입
신규: 지문이 존재하지 않음 → 청킹 → 벡터화 → 입고
수정: 지문이 일치하지 않음 → 신구 내용 자동 대조 → 변동 조각만 특정 → 국소 갱신
삭제: 지문이 원본 파일과 매칭 안 됨(원본 소실) → 대응 벡터 자동 정리
지문의 역할은 "파일을 표시하는 것"이 아니라 **"믿을 만한 변경 경계를 세우는 것"**이다 — 이 경계가 있어야 위 1~5층의 버전·삭제 전파·조각 안정성이 "무엇이 바뀌었는지"라는 전제 위에서 정확히 작동한다. 지문은 반드시 문서 단위와 조각(chunk) 단위 두 레벨로 둔다. 문서 단위만 두면 긴 문서 한 군데만 고쳐도 전체를 재계산해야 하고, 조각 단위까지 있어야 갱신 입도가 진짜로 조각급으로 내려간다.
판정은 반드시 청킹·벡터화 이전에 이루어져야 한다. 지문 대조를 뒤에 두면 GPU와 Embedding 호출 비용이 이미 소모된 뒤이므로, "증분처럼 보이지만 사실은 전량 재계산"이 되어 버린다.
1층: 문서 버전 관리
- 수정할 때마다 반드시 새
document_version을 생성한다(v1.0 → v2.0).
- 조각 데이터도 반드시
version을 지녀야 한다(chunk_v1 / chunk_v2).
- 옛 벡터를 직접 덮어쓰는 것을 엄금한다.
- 그렇게 하지 않을 때의 결과: 답변 중인 요청이 절반은 구 조각, 절반은 신 조각을 리콜할 수 있다.
- 권장 방식: 새 버전 색인 구축이 완료된 뒤에 가시 버전을 전환하고, 구 버전은 리플레이와 책임 규명을 위해 일정 기간 보존한다.
2층: 삭제와 권한 변경의 전파
삭제는 원본 파일을 지우면 끝이 아니며, 아래 다섯 곳을 모두 처리해야 한다.
- 벡터 DB
- 요약 캐시
- 문답 캐시
- 인용 기록
- 평가 샘플
권한 변경은 더 민감하다. 직원 퇴사, 부서 권한 회수 시 검색 필터가 즉시 발효되어야 한다.
가점 요소: "갱신 시간 필드를 하나 추가한다"만 답하지 말고 삭제 전파와 권한 우선순위를 보탤 것.
3층: 실패 복구(예외 큐)
세 가지 전형적 실패: Embedding 서비스 타임아웃 / OCR 실패 / 일부 페이지 파싱 불가.
- 처리 방식: 전부 예외 큐(Dead Letter Queue)로 보내고 조용히 건너뛰는 것을 엄금한다.
- 시스템이 운영에게 알려줄 수 있어야 할 세 가지:
- 어느 문서가 현재 검색 불가인가
- 원인이 무엇인가
- 사람의 처리가 필요한가
- 하지 않을 때의 결과: 사용자가 답을 찾지 못할 때 지식 결손인지 색인 실패인지조차 구분할 수 없다.
4층: 조각 안정성(장·절 단위 앵커)
- 리스크: 문서에서 한 단락만 고쳤는데 재청킹이 발동해 조각이 완전히 달라져 다른 chunk가 생성된다.
- 실효 범위: 과거 인용, 캐시, 평가 샘플.
- 핵심 전략: 되도록 장·절 단위 앵커를 유지해 각 조각에 고유하고 안정적인 신원 좌표를 만든다.
| 앵커 | 영문 |
|---|
| 제목 경로 | TITLE PATH |
| 페이지 번호 | PAGE NUMBER |
| 단락 번호 | PARAGRAPH ID |
| 표 ID | TABLE ID |
효과: 재청킹 전후로 chunk의 ID가 일치하게 유지된다(예: 구 조각 #Sec-2.1 → 신 조각 #Sec-2.1). 다시 벡터화하더라도 신구 조각의 대응 관계를 알 수 있다.
5층: 관측 가능성(진행 대시보드)
진행 대시보드가 보여줘야 할 다섯 지표:
| 파싱 대기 | 벡터화 대기 | 발행 대기 | 실패 재시도 | 권한 지연 |
|---|
상담원 원인 규명 3문: 문서가 이미 적재되었는가? 파싱에 실패했는가? 권한 때문에 보이지 않는가?
- 상담원이 "지식베이스에 없습니다"라고만 말해서는 안 된다.
- 이 능력이 RAG를 운영할 수 있는지 없는지를 직접 결정한다.
멀티테넌트 격리와 긴급 갱신 채널
멀티테넌트 지식베이스는 앞의 5층 파이프라인 위에 두 가지를 더 얹어야 한다.
| 격리 층 | 방식 |
|---|
| 논리 격리 | 지식을 테넌트 ID에 바인딩, 검색 시 테넌트 필터를 강제 적용 |
| 자원 격리 | 대형 테넌트는 독립 샤드, 소형 테넌트는 클러스터 공유 |
| 작업 격리 | 갱신 작업 큐를 테넌트별로 분리해 서로 자원을 선점하지 않게 함 |
긴급 정정 채널: 전량/증분 배치 주기를 기다릴 수 없는 사고성 오류에는 단건 지식 핫 갱신 채널을 별도로 연다 — 실시간 파싱 → 벡터화 → 색인 기록 → 통상 링크와 동일한 검증 규칙을 동기 트리거 → 분 단위 발효. 급행 차선이라고 검증을 건너뛰면 안 된다. 검증을 생략한 긴급 채널 자체가 새로운 더러운 데이터의 원천이 된다.
쓰기 실패 강등: 벡터 DB 쓰기가 실패하면 동일 링크를 재시도하는 것만으로는 부족하다 — 로컬 키워드 검색으로 이종 강등하고 쓰기는 비동기로 재시도한다. 동종 재시도는 의존 전체가 장애일 때 그대로 무력하지만, 이종 강등이라야 주 링크가 완전히 불능일 때도 기본 가용성을 지킨다.
일관성 모델의 답변 기조
| 수용 가능 | 수용 불가 |
|---|
| 짧은 지연 | 설명 불가능한 오류 |
- 좋은 제품 표현: 새 문서 업로드 후 **"색인 중이며 몇 분 후 문답 가능"**을 표시하는 편이, 사용자가 이미 발효되었다고 생각하는데 검색되지 않는 것보다 낫다.
- 중요 정책 갱신: 동기 발행이나 사람 확인을 거쳐 신구 버전 전환에 명확한 시점이 있도록 보장한다.
프로덕션 함정: 내용 변화만 감시해서는 부족하다
- 증분 색인은 파일 내용 외에도 권한, 제목, 목차, 태그, 업무 상태를 감시해야 한다.
- 전형적 사고 시나리오: 문서 내용은 바뀌지 않았는데 권한이 "전원 열람 가능"에서 "부서 열람 가능"으로 바뀌었다. 벡터 색인의 metadata가 동기화되지 않으면 검색층이 여전히 옛 권한의 조각을 리콜할 수 있다.
- 위험한 점: 에러를 내지 않는다(침묵 실패). 그러나 곧바로 보안 사고가 된다.
표준 답안
저는 지식베이스 색인을 증분 데이터 파이프라인으로 봅니다.
- 문서 버전, 조각 버전, 구축 상태, 원자적 전환으로 일관성을 보장하고
- 신규 / 수정 / 삭제 / 권한 변경이 모두 같은 이벤트 스트림을 타게 하며
- 실패는 재시도 가능한 큐로 보내고 구 버전을 리플레이용으로 보존합니다.
핵심 통찰
- "너무 느리다"는 성능 답변이고 "신구 혼용"이 정확성 답변이다 — "왜 X를 하면 안 되나"를 물을 때는 먼저 효율 제약인지 정확성 제약인지 구분할 것. 정확성 제약이야말로 시스템 설계 문제의 진짜 쟁점이다.
- RAG 색인의 본질은 일회성 ETL이 아니라 하나의 데이터 파이프라인이다 — "지속 운행하는 파이프라인"으로 모델링하는 순간 버전, 상태 머신, 재시도 큐, 관측 가능성 같은 고전적 데이터 엔지니어링 수단을 전부 그대로 재사용할 수 있다.
- 구축과 발행은 반드시 디커플링되어야 한다(원자적 전환) — "새 버전 색인 구축이 완료된 뒤에 가시 버전을 전환한다"는 것은 색인층에서의 블루-그린 배포다. 구축하면서 덮어쓰면 반은 새것 반은 옛것인 리콜이 필연적으로 발생한다.
- 삭제가 가장 놓치기 쉬운 쓰기 확산이다 — 문서 한 편이 시스템 안에 6개 이상의 사본 형태(벡터, 요약 캐시, 문답 캐시, 인용 기록, 평가 샘플)로 존재하므로 삭제 조작은 전부로 팬아웃되어야 한다. 원본 파일만 지우는 것이 전형적인 "다 지웠나?" 함정이다.
- 권한은 RAG의 일급 시민이지 필터 위의 패치가 아니다 — 권한 변경이 동기화되지 않으면 에러가 나지 않지만 곧바로 데이터 유출이다. 권한을 내용과 동급의 변경 소스로 취급해 같은 이벤트 스트림에 편입시켜야 한다.
- 안정적인 조각 ID가 RAG 시스템의 "기본 키"다 — 안정적 앵커가 없으면 과거 인용, 평가셋, 캐시가 전부 재청킹과 함께 실효되어 시스템이 회귀 가능성을 잃는다. 앵커는 문서 구조(제목 경로 / 페이지 / 단락 번호 / 표 ID)에서 와야지 바이트 오프셋에서 오면 안 된다.
- 설명 가능한 지연이 설명 불가능한 정확함보다 낫다 — "색인 중이며 몇 분 후 문답 가능" 같은 상태 노출은 시스템 일관성 문제를 제품 체감 문제로 전환하는 표준 수법이다.
- 가장 위험한 버그는 에러를 내지 않는 버그다 — 파싱 실패를 조용히 건너뛰는 것과 metadata 지연은 에러 로그에 남지 않으므로 능동적인 진행 대시보드와 원인 규명 절차로만 드러낼 수 있다.
- 긴급 채널은 검증을 생략하는 지름길이 아니라 발효 시점만 앞당기는 것이다 — "빨리 고쳐야 한다"는 압박이 "검증을 건너뛰어도 된다"는 뜻은 아니다. 동일한 검증 규칙을 동기로 태우면서 주기만 분 단위로 줄이는 것이 핫 갱신 채널의 유일하게 안전한 설계이며, 그렇지 않으면 긴급 수정 자체가 새로운 사고의 진원지가 된다.
- 수정 시각·파일명 기반 증분은 "증분을 흉내 낸 전량"이다 — 지문 대조 없이 시간이나 이름으로 변경을 판정하면 오판(안 바뀌었는데 갱신됨 / 바뀌었는데 감지 못함)이 구조적으로 발생하고, 결국 변경 안 된 내용까지 반복 재처리하게 되어 전량 재구축의 대가를 매 동기화마다 나눠서 치르는 꼴이 된다. 버전 + 상태 머신이라는 "무엇을 할지"의 답 앞에, 지문 대조라는 "무엇이 바뀌었는지"의 답이 먼저 있어야 한다.
엔지니어링 실전 Tips
- 변경 소스를 빠짐없이: 감시 목록 = 파일 내용 + 권한 + 제목 + 목차 + 태그 + 업무 상태. 내용 변화의 webhook만 거는 것이 가장 흔한 누락이다.
- metadata와 벡터를 따로 갱신할 때 일관성 검사를 둘 것: 벡터 자체는 변하지 않았는데 권한 식별자가 바뀐 시나리오에는 독립된 metadata 동기 경로가 필요하며, "metadata 버전 < 문서 버전"인 조각에 경보를 걸 것.
- 운영을 위한 원인 규명 화면을 준비할 것: "문서가 이미 적재되었는가 / 파싱에 실패했는가 / 권한 때문에 보이지 않는가"를 상담원이 셀프로 조회할 수 있게 만들면 "지식베이스에 없습니다" 류의 무효 티켓이 뚜렷이 줄어든다.
- 평가셋은 chunk 내용이 아니라 앵커에 바인딩할 것: 그렇지 않으면 재청킹 때마다 평가셋을 다시 만들어야 해서 회귀 능력이 그대로 0이 된다.
- 상태 노출이 곧 제품이다: 색인 상태(색인 중 / 예상 몇 분)를 최종 사용자에게 노출하는 것이 일관성 문제를 수용 가능한 체감으로 바꾸는 저비용 방법이다.
- 중요 정책 계열 문서에는 동기 발행 / 사람 확인의 급행 차선을 열어 줄 것: 명확한 전환 시점으로 최종 일관성을 대체한다.
- 대량 배치 임포트에는 별도의 서킷브레이커를 걸어 벡터 DB를 트래픽 서지에서 분리할 것: 수천 편 단위의 일괄 갱신이 색인 구축과 동시에 온라인 검색 서비스까지 흔들면 안 된다. 임포트 작업 자체에 레이트 리밋을 걸고, 검색 서비스의 서킷브레이커 입도를 벡터 DB 레벨까지 세밀화해 "구축 부하"와 "조회 부하"가 서로를 잠식하지 않게 할 것.
- 방안을 "가용 / 재최적화 필요 / 고위험" 3단계로 등급을 매겨 점검할 것: 실시간성 요구가 낮아 전량 재구축으로도 버티면 가용, 보상 메커니즘은 있지만 갱신 창 안에 여전히 오염 데이터가 생기면 재최적화 필요, 아무 방어 장치 없이 고동시성에서 벡터 DB가 그대로 노출되어 있으면 고위험이다. 무삭제 메커니즘·무권한 통제·무강등 전략 중 하나라도 걸리면 고위험으로 분류하고 그대로 서비스 개시를 금지할 것.
- 멀티테넌트는 논리·자원·작업 세 층을 함께 격리할 것: 테넌트 필터만 걸고 작업 큐를 공유하면, 한 테넌트의 대량 갱신이 다른 테넌트의 검색 지연을 끌어올린다.
- 벡터 DB 쓰기 실패의 강등은 동종이 아니라 이종으로 설계할 것: 같은 벡터 DB로의 재시도는 의존 자체가 죽었을 때 무의미하다. 상시 유지되는 로컬 키워드 인덱스로의 강등만이 주 링크 전면 장애 시 기본 가용성을 지킨다.
- 지문은 문서·조각 두 레벨로 둘 것: 문서 지문만으로는 "긴 문서 한 곳만 고쳐도 전체 재계산"을 피할 수 없다. 조각 지문까지 있어야 국소 갱신이 실제로 조각급 입도에 도달한다.
- 삭제 경로는 지문 매칭 실패로 독립 분기할 것: 신규·수정은 업무 담당자가 능동적으로 알려 주지만, 삭제(원본 소실)는 조용히 일어난다. "지문이 원본과 매칭되지 않음"을 별도 분기로 명시해 두지 않으면 더러운 데이터가 가장 잘 들어오는 경로가 된다.
- 지문 대조는 청킹·벡터화보다 앞에 있어야 한다: 대조를 뒤로 미루면 GPU·Embedding 호출 비용이 이미 소모된 뒤이므로, 절감 효과 없는 "이름만 증분"이 된다.
- 더 파고들 방향: 조각 안정성의 구체적 구현(Markdown / DOCX 개요 같은 구조화 파싱으로 안정적 앵커를 산출하는 법), 권한 모델과 벡터 DB의 결합 방식(metadata 필터링 vs 분리 인덱스 vs 조회 시 2차 인증), 증분 색인의 일관성 검증 수단(색인과 원본 라이브러리의 대사 작업, 드리프트 탐지), 평가셋의 문서 버전 진화에 따른 관리 방법.