الانتقال إلى المحتوى الرئيسي

نظرة عامة

تتيح أداة البحث المتجهي Qdrant إمكانيات البحث الدلالي في وكلاء CrewAI من خلال الاستفادة من Qdrant، محرك بحث التشابه المتجهي. تسمح هذه الأداة لوكلائك بالبحث في المستندات المخزنة في مجموعة Qdrant باستخدام التشابه الدلالي.

التثبيت

قم بتثبيت الحزم المطلوبة:
uv add qdrant-client

الاستخدام الأساسي

إليك مثال بسيط لكيفية استخدام الأداة:
from crewai import Agent
from crewai_tools import QdrantVectorSearchTool, QdrantConfig

# Initialize the tool with 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 tool
agent = 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

مثال عملي كامل

إليك مثالاً كاملاً يوضح كيفية:
  1. استخراج النص من ملف PDF
  2. توليد التضمينات باستخدام OpenAI
  3. التخزين في Qdrant
  4. إنشاء سير عمل RAG وكيلي باستخدام CrewAI للبحث الدلالي
import os
import uuid
import pdfplumber
from openai import OpenAI
from dotenv import load_dotenv
from crewai import Agent, Task, Crew, Process, LLM
from crewai_tools import QdrantVectorSearchTool
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct, Distance, VectorParams

# Load environment variables
load_dotenv()

# Initialize OpenAI client
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# Extract text from PDF
def 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 embeddings
def 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 Qdrant
def 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 data
qdrant = 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 tool
from 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 agents
search_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 tasks
search_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 workflow
crew = 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

  • qdrant_url (str): عنوان URL لخادم Qdrant الخاص بك
  • qdrant_api_key (str, اختياري): مفتاح API للمصادقة مع Qdrant
  • collection_name (str): اسم مجموعة Qdrant المراد البحث فيها
  • limit (int): الحد الأقصى لعدد النتائج المُرجعة (الافتراضي: 3)
  • score_threshold (float): الحد الأدنى لدرجة التشابه (الافتراضي: 0.35)
  • filter (Any, اختياري): نسخة Filter من Qdrant للتصفية المتقدمة (الافتراضي: None)

المعاملات الاختيارية للأداة

  • 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

للتصفية المعقدة، استخدم نسخ Filter من Qdrant في تكوينك:
from qdrant_client.http import models as qmodels
from crewai_tools import QdrantVectorSearchTool, QdrantConfig

# Create a filter for specific conditions
preset_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 filter
qdrant_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)

معاملات البحث

تقبل الأداة هذه المعاملات في مخططها:
  • 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
  }
]

التضمين الافتراضي

بشكل افتراضي، تستخدم الأداة نموذج text-embedding-3-large من OpenAI للتحويل إلى متجهات. يتطلب ذلك:
  • تعيين مفتاح OpenAI API في البيئة: OPENAI_API_KEY

التضمينات المخصصة

بدلاً من استخدام نموذج التضمين الافتراضي، قد ترغب في استخدام دالة تضمين خاصة بك في الحالات التالية:
  1. تريد استخدام نموذج تضمين مختلف (مثل Cohere أو HuggingFace أو نماذج Ollama)
  2. تحتاج إلى تقليل التكاليف باستخدام نماذج تضمين مفتوحة المصدر
  3. لديك متطلبات محددة لأبعاد المتجهات أو جودة التضمين
  4. تريد استخدام تضمينات خاصة بمجال معين (مثل النصوص الطبية أو القانونية)
إليك مثال باستخدام نموذج HuggingFace:
from transformers import AutoTokenizer, AutoModel
import torch

# Load model and tokenizer
tokenizer = 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 tool
from 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
)

معالجة الأخطاء

تتعامل الأداة مع هذه الأخطاء المحددة:
  • تُثير ImportError إذا لم يكن qdrant-client مثبتاً (مع خيار التثبيت التلقائي)
  • تُثير ValueError إذا لم يتم تعيين QDRANT_URL
  • تطلب تثبيت qdrant-client إذا كان مفقوداً باستخدام uv add qdrant-client

متغيرات البيئة

متغيرات البيئة المطلوبة:
export QDRANT_URL="your_qdrant_url"  # If not provided in constructor
export QDRANT_API_KEY="your_api_key"  # If not provided in constructor
export OPENAI_API_KEY="your_openai_key"  # If using default embeddings