LangChain으로 SelfQueryRetriever를 사용하여 벡터 데이터베이스 쿼리하기
LangChain은 대규모 언어 모델(LLM)과의 상호작용을 오케스트레이션하는 것으로 잘 알려져 있습니다. 최근 LangChain은 self-querying을 수행하는 방법을 도입하여, 사용자 입력 쿼리를 사용해 “chain” 또는 모듈 자체를 쿼리할 수 있게 했습니다. 이 게시물에서는 세계에서 가장 인기 있는 vector database인 Milvus에서 self-query를 수행하는 방법을 보여드립니다. 사용자 쿼리를 해석함으로써 시스템은 vector store 내 검색을 정교화하여 검색 결과를 향상시킬 수 있습니다. 이 예제의 코드는 CoLab here에서 찾을 수 있습니다.
LangChain으로 Milvus에서 self-querying을 설정해 보겠습니다. 이 프로세스는 네 가지 논리적 단계로 구성됩니다.
LangChain과 Milvus의 기본 사항을 설정합니다.
필요한 데이터를 얻거나 생성합니다.
예상 데이터 형식에 대해 모델에 알립니다.
self-querying을 시연하고 작동 방식을 설명합니다.
Self-Querying 소개
Self-querying은 자연어 처리(NLP)와 정보 검색에서 사용되는 강력한 기법입니다. 이를 통해 시스템은 사용자의 입력 쿼리를 사용하여 자기 자신을 쿼리할 수 있으며, 더 정확하고 관련성 높은 결과를 제공할 수 있습니다. LangChain의 맥락에서 self-querying은 문서나 데이터 포인트를 나타내는 벡터를 저장하는 데이터베이스인 vector store에서 정보를 검색하는 데 사용됩니다. self-query retriever는 LangChain 프레임워크의 핵심 구성 요소로, 자연어를 사용하여 복잡한 쿼리와 검색을 생성할 수 있게 합니다.
Self-querying을 활용함으로써 LangChain은 사용자의 쿼리를 vector store가 이해할 수 있는 구조화된 형식으로 분해할 수 있습니다. 이 프로세스는 검색된 문서가 사용자의 쿼리와 매우 관련성이 높도록 보장하여 필요한 정보를 더 쉽게 찾을 수 있게 합니다. 대화형 AI 모델을 구축하든 검색 엔진을 구축하든, self-querying은 결과의 정확성과 관련성을 크게 향상시킬 수 있습니다.
LangChain과 Milvus 설정하기
첫 번째 단계는 필요한 라이브러리를 설정하는 것입니다. pip install openai langchain milvus python-dotenv를 사용하여 필요한 라이브러리를 설치할 수 있습니다. 제 이전 튜토리얼을 따라오셨다면, 환경 변수를 처리할 때 제가 선호하는 라이브러리인 python-dotenv에 이미 익숙하실 것입니다. 우리는 LangChain과 함께 OpenAI 라이브러리를 사용하여 GPT에 액세스하고, Milvus를 vector store로 활용합니다.
OpenAI API 키에 연결한 후 필요한 LangChain 모듈을 가져옵니다. 다음 여섯 개 모듈이 필요합니다.
Document: 데이터 저장을 위한 LangChain 데이터 유형.OpenAI및OpenAIEmbeddings: OpenAI와 그 임베딩에 액세스하기 위한 두 가지 기능.Milvus: LangChain에서 Milvus에 액세스하기 위한 모듈.SelfQueryRetriever: retriever 모듈.AttributeInfo: 우리의 데이터 구조를 LangChain에 정의하는 모듈.
모듈을 가져온 후, 변수 embeddings를 OpenAI Embeddings의 기본 함수로 설정합니다. 설정 프로세스의 마지막 단계는 milvus 라이브러리를 사용하여 노트북에서 Milvus Lite 인스턴스를 실행하는 것입니다.
이제 데이터를 살펴보겠습니다.
import os
from dotenv import load_dotenv
load_dotenv()
import openai
openai.api_key = os.getenv("OPENAI_API_KEY")
from langchain.schema import Document
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Milvus
from langchain.llms import OpenAI
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain.chains.query_constructor.base import AttributeInfo
embeddings = OpenAIEmbeddings()
from milvus import default_server
default_server.start()
데이터를 좀 수집해 봅시다. 데이터를 스크래핑하거나, 제공된 데이터를 그대로 사용하거나, 이를 템플릿으로 사용해 데이터를 만들 수 있습니다. 이 예제를 위해 Jurassic Park, Toy Story, Finding Nemo, The Unbearable Weight of Massive Talent, Lord of War, Ghost Rider를 포함한 영화 관련 문서 목록을 준비했습니다. 데이터에 제목을 포함하지는 않지만, 각각이 어떤 영화를 가리키는지 알아두셨으면 합니다.
저는 이 영화들을 각각 LangChain Document 객체로 저장합니다. 여기에는 문자열에 해당하는 page_content 키가 포함되며, 이 경우에는 영화 설명입니다. 또한 연도, 평점, 영화 장르와 같은 metadata도 포함됩니다.
docs = [
# jurassic park
Document(page_content="A bunch of scientists bring back dinosaurs and mayhem breaks loose",
metadata={"year": 1993, "rating": 7.7, "genre": "action"}),
# toy story
Document(page_content="Toys come alive and have a blast doing so",
metadata={"year": 1995, "genre": "animated", "rating": 9.3 }),
# finding nemo
Document(page_content="A dad teams up with a mentally disabled partner to break into a dentist\'s office to save his son.",
metadata={"year": 2003, "genre": "animated", "rating": 8.2 }),
# unbearable weight of massive talent
Document(page_content="Nicholas Cage plays Nicholas Cage in this movie about Nicholas Cage.",
metadata={"year": 2022, "genre": "comedy", "rating": 7.0 }),
# lord of war
Document(page_content="Nicholas Cage sells guns until he has enough money to marry his favorite model. Then he sells more guns.",
metadata={"year": 2005, "genre": "comedy", "rating": 7.6 }),
# ghost rider
Document(page_content="Nicholas Cage loses his skin and sets his skull on fire. Then he rides a motorcycle.",
metadata={"year": 2007, "genre": "action", "rating": 5.3 }),
]
LangChain 및 Milvus를 위한 self-query metadata와 self query retriever 정의하기
이 퍼즐의 처음 두 단계를 완료했으니, 이제 세 번째 단계로 넘어갈 시간입니다.
먼저, 수집을 위한 벡터 데이터베이스를 설정해 봅시다. LangChain Milvus 구현을 사용하여 문서를 수집하고 기존 문서를 기반으로 벡터 데이터베이스를 생성할 수 있습니다. 이 단계에서는 앞서 만든 embeddings 변수를 사용하여 embedding 함수도 활용됩니다. 비교 문은 데이터 검색 프로세스를 안내하는 필터 조건을 만드는 데 필요한 특정 형식과 가이드라인을 고려합니다.
connection_args 매개변수는 Milvus에 연결하기 위한 유일한 필수 매개변수입니다. 이 튜토리얼에서는 데이터를 저장할 컬렉션에 이름을 지정하기 위해 collection_name 매개변수도 사용합니다. Milvus의 각 컬렉션에는 이름이 있어야 합니다. 기본적으로 LangChain은 LangChainCollection을 사용합니다.
vector_store = Milvus.from_documents(
docs,
embedding=embeddings,
connection_args={"host": "localhost", "port": default_server.listen_port},
collection_name="movies"
)
다음으로, LangChain이 무엇을 기대해야 하는지 알 수 있도록 AttributeInfo 기능을 사용해 metadata 정보를 정의해 봅시다. 이 섹션에서는 데이터에 대한 attribute 정보 목록을 생성합니다. 각 attribute의 이름, 설명, 데이터 유형을 지정할 것입니다. 여기서 논리 조건문은 쿼리 구성 프로세스 내에서 비교 및 논리 연산을 공식화하는 데 필수적인 요소가 됩니다.
metadata_field_info = [
AttributeInfo(
name="genre",
description="The genre of the movie",
type="string",
),
AttributeInfo(
name="year",
description="The year the movie was released",
type="integer",
),
AttributeInfo(
name="rating",
description="A 1-10 rating for the movie",
type="float"
),
]
마지막 몇 부분은 문서를 설명하고, LLM을 초기화하며, Self-Query Retriever를 정의합니다. self-query retriever는 ‘from_llm’ 메서드를 호출하여 정의합니다. 이 메서드를 사용해 LLM, vector store, 문서 콘텐츠 설명, metadata field information을 연결해야 합니다. 이 예제에서는 이 self-query retriever에 대한 자세한 출력을 활성화하기 위해 ‘verbose = True’도 설정합니다. 쿼리 문자열에는 문서 내용과 일치하는 관련 텍스트만 포함해야 하며, 필터의 모든 조건은 명확히 분리되어 쿼리 문자열 자체에는 포함되지 않도록 해야 합니다.
document_content_description = "Brief summary of a movie"
llm = OpenAI(temperature=0)
retriever = SelfQueryRetriever.from_llm(
llm, vector_store, document_content_description, metadata_field_info, verbose=True
)
Self-querying 결과
self-query retriever 설정을 마쳤습니다. 이제 실제로 어떻게 작동하는지 살펴보겠습니다. 이 예제에서는 Nicholas Cage와 관련된 영화 설명 세 개를 사용했습니다. 그래서 우리가 묻는 질문은 다음과 같습니다: Nicholas Cage에 관한 영화에는 어떤 것들이 있나요?
# This example only specifies a relevant query
retriever.get_relevant_documents("What are some movies about Nicholas Cage?")
아래와 같은 출력을 얻을 수 있습니다.
langchain-query-vector-database.png
verbose=True로 설정하면 query, filter, limit을 확인할 수 있습니다. LLM은 우리의 쿼리 “What are some movies about Nicholas Cage?”를 “Nicholas Cage.”로 변환합니다. 결과를 보면 상위 세 개의 결과가 모두 Nicholas Cage가 출연한 영화임을 확인할 수 있습니다. 하지만 네 번째 결과인 Finding Nemo는 네 개의 결과를 검색하도록 설정된 매개변수 때문에 관련이 없습니다.
이 코드 walkthrough가 vector database에서의 self-querying 개념을 명확히 이해하는 데 도움이 되었기를 바랍니다. LangChain 웹사이트에서 LangChain은 self-querying을 LLM이 underlying vector store를 사용해 스스로 쿼리하는 방법이라고 설명합니다. 다시 말해, LangChain은 self-querying 기능을 포함하는 retrieval augmented generation (RAG) 앱을 CVP framework에서 간단히 개발하고 있습니다.
이 튜토리얼에서는 Milvus를 underlying vector store로 사용하여 LangChain의 self-query 기능을 살펴보았습니다. Self-querying을 사용하면 LLM과 vector database를 결합해 간단한 RAG 앱을 만들 수 있습니다. LLM은 자연어 쿼리를 문자열로 분해한 다음, 쿼리를 위해 벡터화합니다.
우리 예제에서는 영화와 관련된 샘플 데이터를 생성했습니다. 이 예제를 확장하는 한 가지 방법은 여러분의 데이터를 수집하는 것입니다. self-query retriever를 정의할 때는 vector store와 metadata 모두에 대한 설명을 제공하는 것을 잊지 마세요.
LangChain의 self-query 기능을 시작하고 실험해 보려면 colab notebook을 참고하세요.
논리 연산문으로 Vector Store 쿼리하기
vector store를 쿼리할 때 logical operation statements는 문서 필터링 조건을 지정하는 데 사용됩니다. logical operation statement는 op(statement1, statement2, …) 형식을 취하며, 여기서 op는 AND, OR, NOT과 같은 논리 연산자입니다. 각 statement는 비교문일 수 있으며, comp(attr, val) 형식을 취합니다. 여기서 comp는 EQ, LT, GT와 같은 비교자이고, attr과 val은 각각 비교되는 attribute와 value입니다.
예를 들어, logical operation statement는 다음과 같을 수 있습니다: AND(EQ(language, “English”), GT(rating, 4)). 이 statement는 language attribute가 “English”와 같고 rating attribute가 4보다 큰 문서를 필터링합니다. logical operation statements를 사용하면 여러 조건을 결합하는 복잡한 쿼리를 만들 수 있어 vector store의 문서를 더 정밀하게 필터링할 수 있습니다.
사용자의 쿼리 처리하기
사용자가 쿼리를 입력하면, self-query retriever는 쿼리 생성자를 사용하여 구조화된 쿼리를 생성합니다. 그런 다음 구조화된 쿼리는 벡터 스토어 쿼리로 변환되고, 관련 문서를 검색하기 위해 벡터 스토어에서 실행됩니다. 쿼리 생성자는 프롬프트와 출력 파서를 사용하여 구조화된 쿼리를 생성하며, 이는 사용자가 지정한 필터를 포착합니다.
예를 들어, 사용자가 “평점이 4보다 크고 실행 시간이 2시간 미만인 영화를 찾아줘”라는 쿼리를 입력하면, 쿼리 생성자는 다음과 같은 구조화된 쿼리를 생성할 수 있습니다: AND(GT(rating, 4), LT(runtime, 120)). 그런 다음 이 구조화된 쿼리는 벡터 스토어 쿼리로 변환되고, 관련 문서를 검색하기 위해 벡터 스토어에서 실행됩니다. 사용자의 쿼리를 이러한 방식으로 처리함으로써, self-query retriever는 결과가 사용자의 특정 요구 사항에 맞게 조정되도록 보장합니다.
모범 사례 및 결론
self-query retriever를 사용할 때는 정확하고 관련성 높은 결과를 보장하기 위해 모범 사례를 따르는 것이 중요합니다. 다음은 몇 가지 팁입니다:
쿼리를 입력할 때 구체적이고 간결한 언어를 사용하세요.
문서를 필터링하기 위한 조건을 지정하려면 논리 연산문을 사용하세요.
속성과 값을 비교하려면 비교문을 사용하세요.
정확히 일치하는 항목을 지정하려면 EQ 비교자를 사용하세요.
범위 쿼리를 지정하려면 LT 및 GT 비교자를 사용하세요.
조건을 결합하려면 AND 및 OR 논리 연산자를 사용하세요.
결론적으로, self-query retriever는 다양한 소스에서 정보를 검색하고 처리할 수 있는 대화형 AI 모델을 구축하기 위한 강력한 도구입니다. 논리 연산문과 비교문을 사용함으로써, 사용자는 복잡한 쿼리를 지정하고 벡터 스토어에서 관련 문서를 검색할 수 있습니다. 모범 사례를 따르고 self-query retriever를 효과적으로 사용함으로써, 개발자는 광범위한 사용자 쿼리를 처리할 수 있는 더 정확하고 관련성 높은 AI 모델을 구축할 수 있습니다.
계속 읽기

What Is a Vector Lakebase?
A Vector Lakebase is a unified, lake-native data architecture for AI that combines vector-database-grade serving with open lake storage, reusable lake-level indexes, and a shared semantic layer.

8 Latest RAG Advancements Every Developer Should Know
Explore eight advanced RAG variants that can solve real problems you might be facing: slow retrieval, poor context understanding, multimodal data handling, and resource optimization.

Building RAG Pipelines for Real-Time Data with Cloudera and Milvus
explore how Cloudera can be integrated with Milvus to effectively implement some of the key functionalities of RAG pipelines.



