지금까지의 모든 편이 "한 번 인덱스를 만드는" 이야기였다면, 프로덕션 팀이 실제로 피를 흘리는 곳은 그다음이다: 문서가 수정되고, 삭제되고, 권한이 바뀔 때 인덱스가 따라가는가. upsert 경로·삭제 경로·권한 경로 세 가지를 첫날부터 설계하라.
upsert 경로 — 해시 기반 변경 감지
프레임워크 기본기가 됐다: LangChain Indexing API는 문서 해시를 RecordManager에 기록해 변경분만 재임베딩하고 4가지 cleanup 모드(incremental/full/scoped_full)로 "수정된 문서의 옛 청크가 남는" 문제를 처리한다. LlamaIndex IngestionPipeline은 docstore 전략(UPSERTS_AND_DELETE가 소스에서 사라진 문서까지 삭제) + 변환 결과 캐시. 전제조건이자 실무 함정: 문서 ID를 파일 경로 같은 안정된 키로 고정할 것. RDB가 원천이면 크롤 재실행이 아니라 CDC가 정석 — Debezium 3.x는 변경 이벤트를 파이프라인 안에서 바로 임베딩해 벡터스토어에 밀어넣고 원본 DELETE도 전파한다(Kafka 없이 Debezium Server만으로 시작 가능). 더 급진적으로는 pgai Vectorizer처럼 임베딩을 "선언적 파생물"로 만들어 동기화 문제 자체를 없애는 방향(⑤편의 "잘못된 추상화" 논거의 해법).
삭제 경로 — "지웠는데 검색에 나온다"의 원리
벡터 DB의 DELETE는 대부분 논리 삭제다: Milvus는 bloom filter+bitset으로 검색 시 걸러내고 물리 삭제는 compaction 때만(일관성 수준 Strong=즉시 비가시화 vs Bounded=수 초 랙). ES는 refresh_interval(기본 1초, 벌크 색인 시 30초+)이 신선도를 정한다. Pinecone 서버리스는 최종 일관성 — 쓰기 응답의 LSN 헤더와 쿼리의 max-indexed-LSN을 비교하는 read-after-write 검증 코드를 짤 수 있다. 요점: 신선도 SLA를 감이 아니라 일관성 수준으로 명시하고, 관리형(Bedrock KB 등)도 수 분 랙이 현실 기준선임을 알 것.
임베딩 모델 교체 — blue-green이 정답
제자리 재임베딩이 아니라: 새 컬렉션을 백그라운드에서 병렬 빌드 → alias 원자 스위치 → 구 컬렉션 폐기(Qdrant/ES/Milvus 모두 alias 지원, 롤백은 alias 되돌리기). 비용 공포는 과장이다 — Modal 실측: 영문 위키피디아 전체 임베딩이 A10G 50개 병렬로 15분, ~$15. 진짜 관건은 비용이 아니라 재색인 오케스트레이션이다.
권한 경로 — 재임베딩 없는 ACL 갱신
Azure 시큐리티 트리밍 패턴(모든 벡터 DB에 이식 가능): 문서마다 group_ids 필드를 색인하고 쿼리 시 사용자 그룹 필터를 항상(옵션이 아니라 게이트웨이 계층에서) 주입. 권한 변경은 메타데이터 부분 갱신만으로 — 임베딩 호출 0회, 권한 회수는 즉시 적용. 사내 문서 RAG에서 법적 리스크가 걸리는 부분이라 첫날부터 스키마에 넣어야 한다.
한 줄 요약: 안정된 문서 ID + 해시 변경 감지 + 논리 삭제의 시차 인지 + blue-green 재색인 + 메타데이터 ACL — 색인을 늙지 않게 하는 다섯 가지 설계다. (참고: 이 주제를 정면으로 다룬 한국어 자료는 거의 없다 — 이 글이 그 공백을 채우는 위치에 있다.)
— RAG 실전 시리즈 ⑨ / 다음 글: 코드베이스 RAG — 코드는 문서와 규칙이 다르다
댓글 없음:
댓글 쓰기
국정원의 댓글 공작을 지탄합니다.