Qdrant Vector Search Tool
Qdrant Vector Search Tool 通过利用 Qdrant 这一向量相似度搜索引擎,为你的 CrewAI agents 提供语义搜索能力。这个工具允许你的 agents 通过语义相似度在存储于 Qdrant collection 中的文档之间进行搜索。
安装所需包:
uv add qdrant-client下面是一个使用该工具的最小示例:
from crewai import Agentfrom crewai_tools import QdrantVectorSearchTool, QdrantConfig
# Initialize the tool with QdrantConfigqdrant_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完整可运行示例
Section titled “完整可运行示例”下面的完整示例展示了如何:
- 从 PDF 中提取文本
- 使用 OpenAI 生成 embeddings
- 存储到 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 参数
Section titled “QdrantConfig 参数”qdrant_url(str):你的 Qdrant 服务器 URLqdrant_api_key(str, optional):Qdrant 认证 API keycollection_name(str):要搜索的 Qdrant collection 名称limit(int):返回结果的最大数量(默认:3)score_threshold(float):最小相似度阈值(默认:0.35)filter(Any, optional):用于高级过滤的 Qdrant Filter 实例(默认:None)
可选工具参数
Section titled “可选工具参数”custom_embedding_fn(Callable[[str], list[float]]):自定义文本向量化函数qdrant_package(str):Qdrant 的基础包路径(默认:“qdrant_client”)client(Any):预初始化的 Qdrant 客户端(可选)
QdrantVectorSearchTool 支持强大的过滤能力,可进一步细化搜索结果:
在搜索时使用 filter_by 和 filter_value 参数,可动态过滤结果:
# Agent will use these parameters when calling the tool# The tool schema accepts filter_by and filter_value# Example: search with category filter# Results will be filtered where category == "technology"使用 QdrantConfig 预设过滤器
Section titled “使用 QdrantConfig 预设过滤器”对于复杂过滤,可以在配置中使用 Qdrant Filter 实例:
from qdrant_client.http import models as qmodelsfrom crewai_tools import QdrantVectorSearchTool, QdrantConfig
# Create a filter for specific conditionspreset_filter = qmodels.Filter( must=[ qmodels.FieldCondition( key="category", match=qmodels.MatchValue(value="research") ), qmodels.FieldCondition( key="year", match=qmodels.MatchValue(value=2024) ) ])
# Initialize tool with preset filterqdrant_tool = QdrantVectorSearchTool( qdrant_config=QdrantConfig( qdrant_url="your_url", qdrant_api_key="your_key", collection_name="your_collection", filter=preset_filter # Preset filter applied to all searches ))该工具会自动将 QdrantConfig 中的预设过滤器与 filter_by、filter_value 中的动态过滤器组合起来:
# If QdrantConfig has a preset filter for category="research"# And the search uses filter_by="year", filter_value=2024# Both filters will be combined (AND logic)工具在其 schema 中接受以下参数:
query(str):用于查找相似文档的搜索查询filter_by(str, optional):要过滤的元数据字段filter_value(Any, optional):用于过滤的值
工具以 JSON 格式返回结果:
[ { "metadata": { // Any metadata stored with the document }, "context": "The actual text content of the document", "distance": 0.95 // Similarity score }]默认 Embedding
Section titled “默认 Embedding”默认情况下,该工具使用 OpenAI 的 text-embedding-3-large 模型进行向量化。这需要:
- 在环境变量中设置 OpenAI API key:
OPENAI_API_KEY
自定义 Embeddings
Section titled “自定义 Embeddings”如果你想使用自己的 embedding 函数,而不是默认模型,通常适用于以下场景:
- 想使用不同的 embedding 模型(例如 Cohere、HuggingFace、Ollama models)
- 想使用开源 embedding 模型以降低成本
- 对向量维度或 embedding 质量有特定要求
- 想使用领域特定 embeddings(例如医疗或法律文本)
下面是一个使用 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 - 如果缺少
qdrant-client,会提示通过uv add 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