AInfo 챗봇 구조 #4 - Vector 검색 구조 분석
AInfo 프로젝트에서 벡터 검색을 담당하는 VectorRetriever 클래스의 전체 구조와 검색 흐름, 멀티 컬렉션 구성 방식에 대해 정리합니다.
AInfo 챗봇 구조 #4 - Vector 검색 구조 분석
벡터 검색이 중요한 이유
AInfo는 사용자 질문에 대해 RAG 기반의 정보 검색을 수행합니다. 이때 핵심이 되는 건 얼마나 적절한 문서를 빠르게 유사도 기반으로 찾을 수 있는가입니다. 이를 위해 다음 구조의 검색기를 구성했습니다:
- LangChain + ChromaDB를 활용한 고속 벡터 검색
- 여러 컬렉션을 동시에 검색 (멀티 DB)
- 메타데이터 필터링 기반 사용자 조건 매칭 지원
- Markdown 형식으로 결과 포맷팅
VectorRetriever 구조
1
2
3
class VectorRetriever:
def search(self, query, k=5, filters=None, collection_names=None): ...
def format_docs(self, docs): ...
VectorRetriever
는 AInfo의 RAG 검색을 담당하는 싱글톤 클래스입니다. 주요 특징은 다음과 같습니다:
구성 요소 | 설명 |
---|---|
__new__ | 싱글톤으로 클래스 생성 제한 |
_initialize | 임베딩 모델 및 DB 디렉토리 초기화 |
_register_collections | Chroma 컬렉션 등록 (5개) |
search() | 유사도 검색 실행 + 메타데이터 필터링 |
format_docs() | Markdown 형식으로 결과 포맷 |
1. 컬렉션 등록 방식
1
2
3
4
collection_names = [
"gov24_service_list", "gov24_service_detail",
"youth_policy_list", "employment_programs", "pdf_sections",
]
- Chroma의 각 컬렉션은 공공서비스 출처별로 분리되어 있음
- 초기 실행 시 한 번만 등록되어 메모리에서 재사용됨
1
2
3
4
5
6
7
8
return {
name: Chroma(
collection_name=name,
embedding_function=self.embedding_model,
persist_directory=self.DB_DIR,
)
for name in collection_names
}
한 번 로딩한 컬렉션은 서버 실행 중 계속 재사용 가능함으로 빠름
2. 멀티 컬렉션 검색 흐름
1
2
def search(self, query, k=5, filters=None, collection_names=None):
...
- 기본적으로 모든 등록된 컬렉션에 대해 검색
collection_names
를 넘기면 원하는 DB만 검색 가능- 각 컬렉션에서 최대
k
개의 문서를 가져옴 _metadata_match()
함수로 필터 조건이 있는 경우 체크
예시:
1
2
3
4
5
results = retriever.search(
query="서울 청년 지원금",
k=5,
filters={"지역": "서울", "나이": "29"},
)
컬렉션마다 비슷한 정책이 있을 수 있으므로 다수 컬렉션 병렬 검색 구조가 효율적입니다.
3. 메타데이터 기반 필터링
1
2
3
4
def _metadata_match(self, metadata, filters):
for key, value in filters.items():
if key not in metadata or value not in str(metadata[key]):
return False
- 검색 결과 문서가 조건에 부합하지 않으면 제거
- 예:
{"지역": "서울"}
→metadata["지역"]
에 “서울”이 포함되지 않으면 제외
결과 품질이 올라가는 핵심 로직이며, RAG에 들어가는 문서 수를 줄여 비용도 절감됩니다.
4. 결과 포맷팅: Markdown 변환
검색된 결과는 그대로 LLM에 넘기기보다, 사용자에게 보여줄 수 있는 형태로 변환합니다.
1
2
def format_docs(self, docs):
# [(컬렉션 이름, Document)] → Markdown string
예시 출력:
1
2
3
4
5
6
7
8
**[서울 청년수당]**
- 내용: 만 19~34세 청년에게 월 50만원을 6개월 지원합니다.
- 링크: [바로가기](https://youth.seoul.go.kr)
**[취업날개 서비스]**
- 내용: 서울시 청년 대상 면접 정장 무료 대여
- 링크: 해당 서비스는 URL이 제공되지 않습니다.
- 제목은 다양한 필드에서 자동 추출 (
서비스명
,title
,plcyNm
등) - URL이 없는 경우도 자연스럽게 안내
5. 활용 예시: CrewAI Tool에서도 사용
VectorRetriever
는 LangChain 체인뿐 아니라, CrewAI Tool에서도 직접 사용됩니다.
1
2
3
4
5
class RagSearchTool(BaseTool):
def _run(...):
retriever = VectorRetriever()
docs = retriever.search(...)
return retriever.format_docs(docs)
- Tool 내부에서
search → format_docs
호출 - 크루 에이전트가 그대로 사용자에게 결과를 설명하는 데 사용됨
마무리
AInfo 프로젝트에서는 다양한 출처의 문서를 검색해야 하기에, 싱글톤 + 멀티 컬렉션 + 메타 필터링 + 포맷 변환까지 책임지는 VectorRetriever
구조는 핵심 모듈입니다.
This post is licensed under CC BY 4.0 by the author.