· 메타데이터 필터링은 벡터 검색을 수행하기 전이나 도중에 문서의 속성(날짜·출처·카테고리·권한 등)으로 후보를 좁혀, RAG가 맞는 범위 안에서만 근거를 찾게 만드는 기법입니다.
· 의미적으로 비슷하다는 이유만으로 오래됐거나·권한이 없거나·다른 제품의 문서가 끌려오는 문제를, 필터는 구조적 조건으로 차단합니다.
· 핵심은 적재 시점에 좋은 메타데이터를 심고, 검색 시점에 필터와 유사도를 함께 적용하는 것입니다.
→
메타데이터 조건 추출
→
필터로 후보 축소
→
벡터 유사도 검색
→
근거 전달
RAG(검색 증강 생성)에서 벡터 검색은 “질문과 의미적으로 가까운 조각”을 찾아 줍니다. 그런데 현실의 문서 저장소에는 비슷한 내용이 수없이 겹쳐 있습니다. 같은 주제라도 2019년 버전과 2025년 버전이 있고, A 제품 매뉴얼과 B 제품 매뉴얼이 같은 표현을 쓰며, 공개 문서와 내부 전용 문서가 한 인덱스에 섞여 있기도 합니다. 유사도만 보면 이것들이 구별되지 않습니다. 메타데이터 필터링은 이 구조적 구별을 검색 과정에 끼워 넣는 방법입니다.
🏷️ 메타데이터 필터링이란
벡터DB에 문서 조각을 저장할 때는 임베딩 벡터만 넣는 것이 아니라, 그 조각에 딸린 속성값(메타데이터)을 함께 저장할 수 있습니다. 작성일, 출처 파일명, 카테고리, 작성자, 언어, 접근 권한 같은 값들입니다. 메타데이터 필터링은 검색 시점에 “이 조건을 만족하는 조각만 대상으로 삼아라”라고 지시해, 전체 벡터 중 일부만 유사도 비교에 참여하도록 제한하는 기능을 말합니다.
유사도 검색이 “의미가 가까운가”를 묻는다면, 메타데이터 필터는 “애초에 대상이 맞는가”를 먼저 묻습니다.
두 가지는 경쟁 관계가 아니라 보완 관계입니다. 필터로 범위를 올바르게 좁히면, 그 안에서 유사도가 훨씬 신뢰할 만해집니다.
⚠️ 유사도만으로는 왜 부족한가
임베딩 유사도는 표현의 의미적 거리를 잴 뿐, 그 문서가 지금 질문에 유효한 맥락인지는 모릅니다. 그래서 다음과 같은 문제가 반복됩니다.
구버전 정책·구버전 API 문서가 최신 질문에 끌려와 틀린 답의 근거가 됩니다.
A 제품 질문인데 표현이 비슷한 B 제품 매뉴얼이 섞여 답이 어긋납니다.
사용자가 볼 수 없는 내부 문서가 근거로 노출될 위험이 있습니다.
이런 경우는 임베딩 품질을 아무리 높여도 해결되지 않습니다. 구별의 기준이 의미가 아니라 속성이기 때문입니다. 속성 기반 구별은 속성으로 풀어야 하며, 그것이 바로 메타데이터 필터의 역할입니다.
🧩 어떤 메타데이터를 심을까
필터링의 품질은 결국 적재(ingestion) 시점에 메타데이터를 얼마나 잘 붙였는가에 달려 있습니다. 검색 시점에는 없던 정보를 만들어 낼 수 없기 때문입니다. 자주 쓰이는 메타데이터 종류는 다음과 같습니다.
| 분류 | 예시 필드 | 쓰임 |
|---|---|---|
| 출처 | source, doc_id, url | 근거 추적·인용 표시 |
| 시간 | created_at, version | 최신성 필터·버전 구분 |
| 분류 | category, product, lang | 주제·제품·언어 범위 제한 |
| 권한 | tenant_id, acl, visibility | 사용자별 접근 통제 |
🔧 사전 필터링 vs 사후 필터링
필터를 적용하는 시점에 따라 두 방식으로 나뉘며, 성능과 정확성의 성격이 다릅니다.
유사도 검색 전에 조건을 적용해, 조건을 만족하는 후보 안에서만 벡터를 비교합니다. 결과가 항상 조건을 보장하고, 원하는 개수(top-k)를 안정적으로 채웁니다.
먼저 유사도 상위 결과를 뽑은 뒤에 조건으로 걸러냅니다. 구현은 간단하지만, 걸러낸 뒤 남는 개수가 부족해질 수 있습니다.
권한처럼 반드시 지켜져야 하는 조건은 사후 필터링에만 의존하지 마세요. 상위 결과가 전부 걸러지면 빈손이 되거나, 구현 실수로 통제가 새기 쉽습니다.
대부분의 벡터DB는 사전 필터링을 지원하며, 메타데이터에 인덱스를 걸어 두면 조건 평가가 빨라집니다. 다만 필터가 너무 좁아 후보가 거의 남지 않으면 검색 품질이 오히려 떨어질 수 있어, 조건의 폭을 적절히 유지하는 균형이 필요합니다.
🔁 검색 파이프라인 흐름
질문이 들어왔을 때 필터가 어떻게 끼어드는지, 개념적인 흐름을 의사코드로 정리하면 다음과 같습니다. 실제 API는 라이브러리마다 다르지만 구조는 비슷합니다.
# 1) 질문에서 필터 조건을 정한다(사용자 맥락·요청에서 도출) filters = { "product": "A", # 제품 범위 제한 "created_at": {"$gte": "2025-01-01"}, # 최신 문서만 "visibility": "public", # 권한 통제 } # 2) 필터 + 유사도를 함께 적용(사전 필터링) results = vector_store.similarity_search( query="환불 정책이 어떻게 되나요?", k=4, filter=filters, ) # 3) 걸러진 근거만 LLM에 전달 answer = llm.generate(question, context=results)
핵심은 유사도 검색 호출 자체에 filter를 넘긴다는 점입니다. 검색엔진이 조건을 만족하는 벡터만 대상으로 top-k를 뽑으므로, 이후 단계는 이미 정제된 근거를 다루게 됩니다.
🛠️ 실전 예시와 주의점
필터 조건을 어디서 얻느냐는 상황에 따라 다릅니다. 로그인한 사용자 정보(테넌트·권한)는 시스템이 자동으로 주입하는 것이 안전합니다. 반면 “올해 문서만”, “결제 관련만” 같은 조건은 질문에서 의도를 읽어 동적으로 구성하기도 합니다.
권한 필터는 코드에서 강제 주입, 날짜·카테고리는 표준값으로 저장, 필터 필드에 인덱스 설정.
너무 좁은 필터로 후보 고갈, 자유 문자열 비교, 권한을 사후 필터에만 의존.
❓ 자주 묻는 질문
Q. 메타데이터 필터링과 하이브리드 검색은 같은 건가요?
아닙니다. 하이브리드 검색은 키워드 검색과 벡터 검색을 결합해 의미적 관련성을 높이는 방법이고, 메타데이터 필터링은 속성 조건으로 대상을 제한하는 방법입니다. 둘은 함께 쓸 수 있으며 목적이 다릅니다.
Q. 필터를 쓰면 검색이 느려지나요?
필터 필드에 인덱스가 있으면 오히려 비교 대상이 줄어 유리할 수 있습니다. 다만 인덱스가 없는 필드를 복잡한 조건으로 거르면 느려질 수 있으므로, 자주 쓰는 필터 필드는 미리 인덱싱해 두는 것이 좋습니다.
Q. 메타데이터는 나중에 바꿀 수 있나요?
벡터DB에 따라 메타데이터 갱신을 지원합니다. 다만 대규모로 바꾸려면 재적재가 더 깔끔한 경우도 있으니, 처음부터 필요한 필드를 설계해 두는 편이 비용을 아낍니다.
🏁 마무리
메타데이터 필터링은 화려한 기법은 아니지만, RAG의 정확성과 안전성을 끌어올리는 가장 실용적인 지렛대 중 하나입니다. 임베딩 유사도가 “의미가 가까운 조각”을 찾는다면, 메타데이터 필터는 “애초에 대상이 맞는 조각”으로 범위를 정리해 줍니다. 좋은 출발점은 단순합니다. 적재 시점에 출처·시간·분류·권한을 표준화해 심고, 검색 호출에 필터와 유사도를 함께 넘기는 것. 이 작은 규율이 쌓이면, 오래된 문서나 권한 밖 문서가 근거로 새어 나오는 사고를 구조적으로 막을 수 있습니다.
답글 남기기