안녕하세요 데이터사이언티스트 먼지입니다.
한달간 회사에서 AI Agent개발을 했습니다. 정말 아무것도 모르는 상태에서 시작했고, 시행착오도 많았던 것 같습니다. 사용해본 결과, 결국 selfquery retriever는 필터링이 자연어 질문에 따라 얼마나 정확하게 걸리냐의 문제였습니다. 셀프쿼리 리트리버를 사용하는 이유와 필터링이 걸리지 않았을 때 해결했던 경험을 아래 적어보도록 하겠습니다.
SelfQuery Retriever란?
자연어 질문에서 필터링을 걸어 VectorDB 속 데이터를 가져오는 검색기입니다.
예를 들어, "2025년에 출시된 화장품 중 스킨케어 제품을 알려줘 " 라고 질문이 들어왔을때, 셀프쿼리 리트리버를 거친다면,
- 필터링1. 년 = 2025
- 필터링2. 카테고리 = "스킨케어"
로 필터링이 걸리게 되고, VectorDB에서 2025년, 상품 카테고리가 "스킨케어"인 데이터만 가져올 수 있습니다.
따라서 위 질문에 대해
# 화장품 상품설명 docs객체들 중 2025년, 스킨케어인 데이터
Document(
page_content="수분 가득한 히알루론산 세럼으로 피부 속 깊은 곳까지 수분을 공급합니다.",
metadata={"year": 2025, "category": "스킨케어", "user_rating": 4.7},
),
Document(
page_content="24시간 지속되는 매트한 피니시의 스킨케어, 모공을 커버하고 자연스러운 피부 표현이 가능합니다.",
metadata={"year": 2025, "category": "스킨케어", "user_rating": 4.3},
)
필터링 조건에 해당하는 Document 객체 데이터들을 가져올 수 있게 됩니다. 블로그 작성 상 2개의 Document객체로 표현했지만, 실제 프로젝트에선 수천개의 Document객체 중에서 불러와야 합니다. 더 자세한 기본 내용은 테디노트님 문서를 참조해주시길 바랍니다.
테디노트님 렉쳐노트: https://wikidocs.net/234475
08. 셀프 쿼리 검색기(SelfQueryRetriever)
.custom { background-color: #008d8d; color: white; padding: 0.25em 0.5…
wikidocs.net
셀프쿼리 리트리버 사용 이유?
효율적인 데이터 탐색이 가능하기 때문입니다.
SelfQuery retriever를 사용하지 않았을 시, 자연어 질문에 대해 단순 유사도로 모든 vectorstore의 데이터를 찾게 됩니다.
하지만 SelfQuery retriever 사용시 질문 속에 있는 필터링 조건을 걸어서, 필요한 문서만 가져오게 합니다.
즉, 필터링 기반 벡터스토어 데이터 검색 vs llm 벡터유사도로만 벡터스토어 데이터 검색의 차이라고 보시면 됩니다.
당연히, retriever를 사용해 데이터를 가져오는 방식이 훨씬 빠르겠죠?
사용 후 느낀점: 필터링 결과 확인 필수!
사용시 항상 필터링이 걸리는지를 눈으로 확인해야 합니다.
처음엔 막연히 알아서 잘 해주네? 라고 했지만, 답변을 못하는 경우를 보면, 항상 제대로 필터가 걸리지 않아서 였습니다. 필터링이 걸리지 않을 경우, 너무 많은 데이터를 llm이 처리하다보니 느려지기도 하고, max_token에러를 발생시키는 경우가 많았습니다.
따라서 코드로 구조화된 쿼리를 항상 확인해야 합니다. (gpt에 상황에 맞게 검색하면 됩니다!)
# 구조화된 쿼리 직접 확인
# StructuredQueryOutputParser를 따로 사용해서 파싱된 쿼리를 추출
output_parser = StructuredQueryOutputParser.from_components(metadata_field_info)
structured_query = output_parser.parse(llm.predict(query))
# 구조화된 쿼리 출력
print("🔍 구조화된 쿼리:")
print(structured_query)
위 결과를 출력시 아래와 같이 필터링 결과가 나옵니다.
🔍 구조화된 쿼리:
query: ''
filter: Comparison(
comparator=EQUALS,
attribute='year',
value=2025
) AND Comparison(
comparator=EQUALS,
attribute='category',
value='skin_care'
)
llm에 따라 어떨 때는 필터링을 걸어줬다가, 어떨때는 필터링이 안 걸릴 수도 있기에 반드시 필터링이 걸렸는지 확인해야 합니다.
필터링이 잘 안걸린다면?
(1) 첫번째로, 메타데이터 Info 를 확인해야 합니다.
정말 꼭 필터링이 걸려야 할 항목이라면 한줄 이상으로 상세히 적어야 합니다. (gpt에 물어보면 잘 알려줍니다!) 저 같은 경우 year, month같은 숫자 데이터가 자꾸 문자열로 필터링이 걸리는 문제가 있었는데, 메타데이터를 상세히 적어주는 것으로 해결했습니다.
# 필터링이 안된다면 아래 메타데이터 필드 정보를 자세히 적어야 합니다
from langchain.chains.query_constructor.base import AttributeInfo
metadata_field_info = [
AttributeInfo(
name="category",
description="The category of the cosmetic product. One of ['스킨케어', '메이크업', '클렌징', '선케어']",
type="string",
),
AttributeInfo(
name="year",
description="The year the cosmetic product was released",
type="integer",
),
]
(2) 두번째로, llm 성능의 문제일 수 있습니다.
메타데이터 길이가 너무 길어질 경우 llm이 잘 인식 못할 수 있습니다. 저의 경우 "_"가 포함된 칼럼명을 잘 인식하지 못했었는데요. embedding_model을 바꿔준 이후로 잘 인식하게 되었습니다.
실제로 프로젝트를 하면서 gpt 4o, claude sonnet 4 모델 2가지 모두 사용해봤었는데, 무거운 모델이 확실히 세부적인 데이터까지 인지하는 결과를 보여줬습니다.
마무리 하며..
이번 프로젝트를 하며 진짜 데이터분석가의 시대는 끝났나? 라는 생각이 들었습니다. gpt, 특히 claude 분석 결과가 정말 뛰어났습니다. 따라서 앞으로는 llm에게 데이터를 속도, 안정성 측면에서 어떻게 잘 전달해줄 것이냐가 중요한 문제이지 않을까 싶습니다.
감사합니다.
'AI agent' 카테고리의 다른 글
정형데이터에서 VectorDB를 사용하는 이유 (5) | 2025.08.03 |
---|---|
[월간 데이터 노트] 무료 llm Ollama 로컬 환경 셋팅하기 (4) | 2025.07.28 |