Qdrant 벡터 검색 도구
Qdrant Vector Search Tool은 Qdrant 벡터 유사성 검색 엔진을 활용하여 CrewAI 에이전트에 시맨틱 검색 기능을 제공합니다. 이 도구를 사용하면 에이전트가 Qdrant 컬렉션에 저장된 문서를 시맨틱 유사성을 기반으로 검색할 수 있습니다.
필수 패키지를 설치하세요:
uv add qdrant-client기본 사용법
섹션 제목: “기본 사용법”아래는 도구를 사용하는 최소한의 예시입니다:
from crewai import Agentfrom crewai_tools import QdrantVectorSearchTool, QdrantConfig
# QdrantConfig로 도구 초기화qdrant_tool = QdrantVectorSearchTool( qdrant_config=QdrantConfig( qdrant_url="your_qdrant_url", qdrant_api_key="your_qdrant_api_key", collection_name="your_collection" ))
# Create an agent that uses the toolagent = Agent( role="Research Assistant", goal="Find relevant information in documents", tools=[qdrant_tool])
# The tool will automatically use OpenAI embeddings# and return the 3 most relevant results with scores > 0.35완전한 작동 예시
섹션 제목: “완전한 작동 예시”아래는 다음과 같은 방법을 보여주는 완전한 예시입니다:
- PDF에서 텍스트 추출
- OpenAI를 사용하여 임베딩 생성
- Qdrant에 저장
- CrewAI agentic RAG 워크플로우로 시맨틱 검색 생성
import osimport uuidimport pdfplumberfrom openai import OpenAIfrom dotenv import load_dotenvfrom crewai import Agent, Task, Crew, Process, LLMfrom crewai_tools import QdrantVectorSearchToolfrom qdrant_client import QdrantClientfrom qdrant_client.models import PointStruct, Distance, VectorParams
# Load environment variablesload_dotenv()
# Initialize OpenAI clientclient = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
# Extract text from PDFdef extract_text_from_pdf(pdf_path): text = [] with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: page_text = page.extract_text() if page_text: text.append(page_text.strip()) return text
# Generate OpenAI embeddingsdef get_openai_embedding(text): response = client.embeddings.create( input=text, model="text-embedding-3-large" ) return response.data[0].embedding
# Store text and embeddings in Qdrantdef load_pdf_to_qdrant(pdf_path, qdrant, collection_name): # Extract text from PDF text_chunks = extract_text_from_pdf(pdf_path)
# Create Qdrant collection if qdrant.collection_exists(collection_name): qdrant.delete_collection(collection_name) qdrant.create_collection( collection_name=collection_name, vectors_config=VectorParams(size=3072, distance=Distance.COSINE) )
# Store embeddings points = [] for chunk in text_chunks: embedding = get_openai_embedding(chunk) points.append(PointStruct( id=str(uuid.uuid4()), vector=embedding, payload={"text": chunk} )) qdrant.upsert(collection_name=collection_name, points=points)
# Initialize Qdrant client and load dataqdrant = QdrantClient( url=os.getenv("QDRANT_URL"), api_key=os.getenv("QDRANT_API_KEY"))collection_name = "example_collection"pdf_path = "path/to/your/document.pdf"load_pdf_to_qdrant(pdf_path, qdrant, collection_name)
# Initialize Qdrant search toolfrom crewai_tools import QdrantConfig
qdrant_tool = QdrantVectorSearchTool( qdrant_config=QdrantConfig( qdrant_url=os.getenv("QDRANT_URL"), qdrant_api_key=os.getenv("QDRANT_API_KEY"), collection_name=collection_name, limit=3, score_threshold=0.35 ))
# Create CrewAI agentssearch_agent = Agent( role="Senior Semantic Search Agent", goal="Find and analyze documents based on semantic search", backstory="""You are an expert research assistant who can find relevant information using semantic search in a Qdrant database.""", tools=[qdrant_tool], verbose=True)
answer_agent = Agent( role="Senior Answer Assistant", goal="Generate answers to questions based on the context provided", backstory="""You are an expert answer assistant who can generate answers to questions based on the context provided.""", tools=[qdrant_tool], verbose=True)
# Define taskssearch_task = Task( description="""Search for relevant documents about the {query}. Your final answer should include: - The relevant information found - The similarity scores of the results - The metadata of the relevant documents""", agent=search_agent)
answer_task = Task( description="""Given the context and metadata of relevant documents, generate a final answer based on the context.""", agent=answer_agent)
# Run CrewAI workflowcrew = Crew( agents=[search_agent, answer_agent], tasks=[search_task, answer_task], process=Process.sequential, verbose=True)
result = crew.kickoff( inputs={"query": "What is the role of X in the document?"})print(result)도구 매개변수
섹션 제목: “도구 매개변수”필수 파라미터
섹션 제목: “필수 파라미터”qdrant_config(QdrantConfig): 모든 Qdrant 설정을 포함하는 구성 객체
QdrantConfig 매개변수
섹션 제목: “QdrantConfig 매개변수”qdrant_url(str): Qdrant 서버의 URLqdrant_api_key(str, 선택 사항): Qdrant 인증을 위한 API 키collection_name(str): 검색할 Qdrant 컬렉션의 이름limit(int): 반환할 최대 결과 수 (기본값: 3)score_threshold(float): 최소 유사도 점수 임계값 (기본값: 0.35)filter(Any, 선택 사항): 고급 필터링을 위한 Qdrant Filter 인스턴스 (기본값: None)
선택적 도구 매개변수
섹션 제목: “선택적 도구 매개변수”custom_embedding_fn(Callable[[str], list[float]]): 텍스트 벡터화를 위한 사용자 지정 함수qdrant_package(str): Qdrant의 기본 패키지 경로 (기본값: “qdrant_client”)client(Any): 사전 초기화된 Qdrant 클라이언트 (선택 사항)
고급 필터링
섹션 제목: “고급 필터링”QdrantVectorSearchTool은 검색 결과를 세밀하게 조정할 수 있는 강력한 필터링 기능을 지원합니다:
동적 필터링
섹션 제목: “동적 필터링”검색 시 filter_by 및 filter_value 매개변수를 사용하여 즉석에서 결과를 필터링할 수 있습니다:
# 에이전트는 도구를 호출할 때 이러한 매개변수를 사용합니다# 도구 스키마는 filter_by 및 filter_value를 허용합니다# 예시: 카테고리 필터를 사용한 검색# 결과는 category == "기술"인 항목으로 필터링됩니다QdrantConfig를 사용한 사전 설정 필터
섹션 제목: “QdrantConfig를 사용한 사전 설정 필터”복잡한 필터링의 경우 구성에서 Qdrant Filter 인스턴스를 사용하세요:
from qdrant_client.http import models as qmodelsfrom crewai_tools import QdrantVectorSearchTool, QdrantConfig
# 특정 조건에 대한 필터 생성preset_filter = qmodels.Filter( must=[ qmodels.FieldCondition( key="category", match=qmodels.MatchValue(value="research") ), qmodels.FieldCondition( key="year", match=qmodels.MatchValue(value=2024) ) ])
# 사전 설정 필터로 도구 초기화qdrant_tool = QdrantVectorSearchTool( qdrant_config=QdrantConfig( qdrant_url="your_url", qdrant_api_key="your_key", collection_name="your_collection", filter=preset_filter # 모든 검색에 적용되는 사전 설정 필터 ))필터 결합
섹션 제목: “필터 결합”도구는 QdrantConfig의 사전 설정 필터와 filter_by 및 filter_value의 동적 필터를 자동으로 결합합니다:
# QdrantConfig에 category="research"에 대한 사전 설정 필터가 있고# 검색에서 filter_by="year", filter_value=2024를 사용하는 경우# 두 필터가 모두 결합됩니다 (AND 논리)검색 매개변수
섹션 제목: “검색 매개변수”이 도구는 스키마에서 다음과 같은 매개변수를 허용합니다:
query(str): 유사한 문서를 찾기 위한 검색 쿼리filter_by(str, 선택 사항): 필터링할 메타데이터 필드filter_value(Any, 선택 사항): 필터 기준 값
반환 형식
섹션 제목: “반환 형식”이 도구는 결과를 JSON 형식으로 반환합니다:
[ { "metadata": { // Any metadata stored with the document }, "context": "The actual text content of the document", "distance": 0.95 // Similarity score }]기본 임베딩
섹션 제목: “기본 임베딩”기본적으로, 이 도구는 벡터화를 위해 OpenAI의 text-embedding-3-large 모델을 사용합니다. 이를 위해서는 다음이 필요합니다:
- 환경변수에 설정된 OpenAI API 키:
OPENAI_API_KEY
커스텀 임베딩
섹션 제목: “커스텀 임베딩”기본 임베딩 모델 대신 다음과 같은 경우에 사용자 정의 임베딩 함수를 사용하고 싶을 수 있습니다:
- 다른 임베딩 모델을 사용하고 싶은 경우 (예: Cohere, HuggingFace, Ollama 모델)
- 오픈소스 임베딩 모델을 사용하여 비용을 절감해야 하는 경우
- 벡터 차원 또는 임베딩 품질에 대한 특정 요구 사항이 있는 경우
- 도메인 특화 임베딩을 사용하고 싶은 경우 (예: 의료 또는 법률 텍스트)
다음은 HuggingFace 모델을 사용하는 예시입니다:
from transformers import AutoTokenizer, AutoModelimport torch
# Load model and tokenizertokenizer = AutoTokenizer.from_pretrained('sentence-transformers/all-MiniLM-L6-v2')model = AutoModel.from_pretrained('sentence-transformers/all-MiniLM-L6-v2')
def custom_embeddings(text: str) -> list[float]: # Tokenize and get model outputs inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True) outputs = model(**inputs)
# Use mean pooling to get text embedding embeddings = outputs.last_hidden_state.mean(dim=1)
# Convert to list of floats and return return embeddings[0].tolist()
# Use custom embeddings with the toolfrom crewai_tools import QdrantConfig
tool = QdrantVectorSearchTool( qdrant_config=QdrantConfig( qdrant_url="your_url", qdrant_api_key="your_key", collection_name="your_collection" ), custom_embedding_fn=custom_embeddings # Pass your custom function)오류 처리
섹션 제목: “오류 처리”이 도구는 다음과 같은 특정 오류를 처리합니다:
qdrant-client가 설치되어 있지 않으면 ImportError를 발생시킵니다 (자동 설치 옵션 제공)QDRANT_URL이 설정되어 있지 않으면 ValueError를 발생시킵니다- 누락된 경우
uv add qdrant-client를 사용하여qdrant-client설치를 안내합니다
환경 변수
섹션 제목: “환경 변수”필수 환경 변수:
export QDRANT_URL="your_qdrant_url" # If not provided in constructorexport QDRANT_API_KEY="your_api_key" # If not provided in constructorexport OPENAI_API_KEY="your_openai_key" # If using default embeddings