You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

18 KiB

مدیریت مدل‌های Embedding در سیستم Islamic Scholar Agent

📋 نمای کلی

سیستم مدیریت مدل‌های embedding در پروژه Islamic Scholar Agent به صورت متمرکز و هوشمند طراحی شده است. این سیستم امکان تغییر سریع بین مدل‌های embedding مختلف، مدیریت اتوماتیک collectionها و جلوگیری از مشکلات dimension mismatch را فراهم می‌کند.

🎯 چرایی این سیستم

مشکل رویکرد قدیمی

در گذشته، مدل‌های embedding به صورت مستقیم در کد استفاده می‌شدند و collectionها ثابت بودند:

# ❌ رویکرد قدیمی - hardcoded
embedder = SentenceTransformerEmbedder(id="all-MiniLM-L6-v2")
vector_store = Qdrant(collection="islamic_knowledge", embedder=embedder)

# ❌ مشکل dimension mismatch
# اگر embedder تغییر کند، داده‌های قدیمی با dimensions جدید سازگار نیستند

راه‌حل جدید

# ✅ رویکرد جدید - هوشمند و متمرکز
from src.knowledge.embedding_factory import EmbeddingFactory
from src.knowledge.vector_store import get_qdrant_store

embed_factory = EmbeddingFactory()
embedder = embed_factory.get_embedder()  # مدل پیش‌فرض از config

# collection اتوماتیک تغییر می‌کند!
vector_store = get_qdrant_store(embedder=embedder)

🏗️ معماری سیستم

ساختار فایل‌ها

config/
├── embeddings.yaml          # تنظیمات متمرکز embeddingها

src/knowledge/
├── embedding_factory.py     # کارخانه embeddingها
├── vector_store.py          # منطق هوشمند collection
├── rag_pipeline.py          # استفاده از سیستم

فایل‌های کلیدی

1. config/embeddings.yaml

فایل تنظیمات مرکزی که همه مدل‌های embedding را تعریف می‌کند:

embeddings:
  # سوئیچ اصلی - تغییر این مقدار = تغییر embedding کل سیستم
  default: jina_AI

  models:
    # OpenAI Embeddings (API-based)
    openai_small:
      provider: "openai"
      id: "text-embedding-3-small"
      dimensions: 1536
      api_key: ${OPENAI_API_KEY}

    # Jina AI Embeddings (API-based)
    jina_AI:
      provider: "jinaai"
      id: "jina-embeddings-v4"
      dimensions: 1024
      api_key: ${JINA_API_KEY}

    # Local HuggingFace (اختیاری - کامنت شده)
    # minilm_l6:
    #   provider: "local"
    #   id: "all-MiniLM-L6-v2"
    #   dimensions: 384
    #   batch_size: 100

2. src/knowledge/embedding_factory.py

کارخانه اصلی که embedding مناسب را از تنظیمات ایجاد می‌کند:

from agno.knowledge.embedder.openai import OpenAIEmbedder
from agno.knowledge.embedder.jina import JinaEmbedder

class EmbeddingFactory:
    def __init__(self, config_path: str = "config/embeddings.yaml"):
        with open(config_path) as f:
            # Resolve environment variables
            content = f.read()
            for key, val in os.environ.items():
                content = content.replace(f"${{{key}}}", val)
            self.config = yaml.safe_load(content)

    def get_embedder(self, model_name: Optional[str] = None):
        # استفاده از مدل پیش‌فرض
        if model_name is None:
            model_name = self.config['embeddings']['default']

        models_config = self.config['embeddings']['models']
        if model_name not in models_config:
            raise ValueError(f"Embedding model '{model_name}' not found in config.")

        config = models_config[model_name]
        provider = config['provider']

        # Resolve API key
        api_key_env = config.get('api_key')
        if api_key_env and api_key_env.startswith("${"):
            api_key = os.getenv(api_key_env[2:-1])
        else:
            api_key = api_key_env

        # ایجاد embedder بر اساس provider
        if provider == "openai":
            return OpenAIEmbedder(
                id=config['id'],
                dimensions=config['dimensions'],
                api_key=api_key
            )
        elif provider == "jinaai":
            return JinaEmbedder(
                id=config['id'],
                dimensions=config['dimensions'],
                api_key=api_key
            )

        raise ValueError(f"Unknown provider type: {provider}")

3. src/knowledge/vector_store.py

سیستم هوشمند مدیریت collection:

def get_qdrant_store(collection_name=None, url=None, embedder=None):
    """Get configured Qdrant vector store with automatic collection naming"""

    # استفاده از BASE_COLLECTION_NAME از environment
    collection = collection_name or os.getenv("COLLECTION_NAME")

    # 🚀 ویژگی کلیدی: collection اتوماتیک بر اساس embedder تغییر می‌کند
    collection = f"{collection}_{embedder.id}"

    qdrant_url = url or os.getenv("QDRANT_URL")

    # اطمینان از وجود embedder
    if embedder is None:
        raise ValueError("You must provide an 'embedder' instance to get_qdrant_store!")

    return Qdrant(
        collection=collection,
        url=qdrant_url,
        embedder=embedder,
        timeout=10.0
    )

4. src/main.py

نحوه استفاده در اپلیکیشن اصلی:

def create_app():
    # ایجاد embedding factory
    embed_factory = EmbeddingFactory()
    current_embedder = embed_factory.get_embedder()
    print(f"Current Embedder: {current_embedder.id}")

    # ارسال embedder به سیستم دانش
    knowledge_base = create_knowledge_base(
        embedder=current_embedder,
        vector_store_type="qdrant"
    )

    # ایجاد agent با knowledge base
    agent = IslamicScholarAgent(model.get_model(), knowledge_base)

🚀 نحوه استفاده

استفاده پایه

# ایجاد embedder از تنظیمات پیش‌فرض
from src.knowledge.embedding_factory import EmbeddingFactory

embed_factory = EmbeddingFactory()
embedder = embed_factory.get_embedder()  # استفاده از مدل پیش‌فرض (jina_AI)

تغییر مدل embedding در زمان اجرا

# تغییر به OpenAI embeddings
embedder = embed_factory.get_embedder('openai_small')

# تغییر به local model (اگر تعریف شده)
# embedder = embed_factory.get_embedder('minilm_l6')

تغییر مدل پیش‌فرض

# config/embeddings.yaml
embeddings:
  default: openai_small  # تغییر از jina_AI به openai_small

افزودن مدل embedding جدید

مرحله 1: افزودن به YAML

# config/embeddings.yaml
models:
  cohere_embed:
    provider: "cohere"
    id: "embed-multilingual-v3.0"
    dimensions: 1024
    api_key: ${COHERE_API_KEY}

مرحله 2: افزودن پشتیبانی در factory

# src/knowledge/embedding_factory.py
from agno.knowledge.embedder.cohere import CohereEmbedder

# در بخش provider logic
elif provider == "cohere":
    return CohereEmbedder(
        id=config['id'],
        dimensions=config['dimensions'],
        api_key=api_key
    )

مزایای این سیستم

1. جلوگیری از Dimension Mismatch

# ❌ مشکل قدیمی
# Collection: "islamic_knowledge"
# Embedder 1: all-MiniLM-L6-v2 (dimensions: 384)
# Embedder 2: text-embedding-3-small (dimensions: 1536)
# ❌ داده‌های قدیمی با embedder جدید سازگار نیستند!

# ✅ راه‌حل جدید
# Collection: "islamic_knowledge_all-MiniLM-L6-v2"
# Collection: "islamic_knowledge_text-embedding-3-small"
# ✅ هر embedder collection جداگانه خودش را دارد

2. سوئیچ سریع بین مدل‌ها

# تغییر از Jina به OpenAI فقط با یک خط تغییر در YAML
# config/embeddings.yaml
default: openai_small  # تغییر از jina_AI

# سیستم اتوماتیک:
# - Collection جدید ایجاد می‌کند: islamic_knowledge_text-embedding-3-small
# - از داده‌های قدیمی استفاده نمی‌کند
# - نیاز به re-ingest دارد

3. مدیریت متمرکز تنظیمات

  • همه تنظیمات embedding در یک فایل YAML
  • تغییر تنظیمات بدون تغییر کد
  • امکان مقایسه عملکرد مدل‌های مختلف

4. پشتیبانی از Providerهای مختلف

models:
  # API-based providers
  openai_small:     # OpenAI
  jina_AI:         # Jina AI
  cohere_embed:    # Cohere

  # Local providers (اختیاری)
  minilm_l6:       # HuggingFace local

5. امنیت و مدیریت API Keys

# تنظیم متغیرهای محیطی
export OPENAI_API_KEY="sk-..."
export JINA_API_KEY="jina_..."
export COHERE_API_KEY="..."

# استفاده در YAML
api_key: ${OPENAI_API_KEY}

6. قابلیت توسعه‌پذیری

  • افزودن provider جدید بدون تغییر کد موجود
  • پشتیبانی از تنظیمات خاص هر provider
  • امکان customization برای نیازهای خاص

7. شفافیت و Debugging

# لاگ embedder فعلی
print(f"Current Embedder: {current_embedder.id}")
# Current Embedder: jina-embeddings-v4

# collection اتوماتیک نام‌گذاری می‌شود
print(f"Collection: {collection}_{embedder.id}")
# Collection: islamic_knowledge_jina-embeddings-v4

⚠️ نکات مهم

ضرورت Re-ingest هنگام تغییر Embedder

# 🚨 هشدار مهم
# وقتی embedder تغییر می‌کند، collection جدید ایجاد می‌شود
# داده‌های قدیمی در collection قدیمی باقی می‌مانند

# مثال:
# تغییر از jina_AI به openai_small:
# Collection قدیمی: islamic_knowledge_jina-embeddings-v4 (داده‌ها موجود)
# Collection جدید: islamic_knowledge_text-embedding-3-small (خالی!)

# ✅ راه‌حل: حتماً داده‌ها را دوباره ingest کنید
python scripts/ingest_data.py --hadiths data/raw/hadiths_data.xlsx

بررسی وجود داده در Collection

# قبل از استفاده، بررسی کنید که collection داده دارد
def check_collection_data(embedder):
    store = get_qdrant_store(embedder=embedder)
    count = store.client.count(store.collection_name)
    if count.count == 0:
        print(f"⚠️  Collection {store.collection_name} is empty!")
        print("Please run data ingestion first.")
    return count.count > 0

🔧 تنظیمات پیشرفته

تنظیمات Environment Variables

# .env
# Embedding Configuration
JINA_API_KEY=jina_...
OPENAI_API_KEY=sk-...

# Vector Database
COLLECTION_NAME=islamic_knowledge
QDRANT_URL=http://localhost:6333

تنظیمات Batch Processing

# config/embeddings.yaml
models:
  minilm_l6:
    provider: "local"
    id: "all-MiniLM-L6-v2"
    dimensions: 384
    batch_size: 100  # برای پردازش دسته‌ای

Caching Embeddings (اختیاری)

from functools import lru_cache

@lru_cache(maxsize=1000)
def get_cached_embedding(text: str, embedder_id: str):
    """Cache embeddings برای جلوگیری از recomputation"""
    embedder = EmbeddingFactory().get_embedder(embedder_id)
    return embedder.get_embedding(text)

🧪 تست سیستم

تست‌های واحد

# tests/test_embeddings.py
def test_embedding_factory_default():
    factory = EmbeddingFactory()
    embedder = factory.get_embedder()
    assert embedder.id == "jina-embeddings-v4"
    assert embedder.dimensions == 1024

def test_embedding_factory_specific():
    factory = EmbeddingFactory()
    embedder = factory.get_embedder('openai_small')
    assert embedder.id == "text-embedding-3-small"
    assert embedder.dimensions == 1536

تست‌های integration

# tests/test_vector_store.py
def test_collection_naming():
    factory = EmbeddingFactory()

    # تست تغییر اتوماتیک collection
    embedder1 = factory.get_embedder('jina_AI')
    store1 = get_qdrant_store(embedder=embedder1)
    assert "jina-embeddings-v4" in store1.collection

    embedder2 = factory.get_embedder('openai_small')
    store2 = get_qdrant_store(embedder=embedder2)
    assert "text-embedding-3-small" in store2.collection

    # collectionها باید متفاوت باشند
    assert store1.collection != store2.collection

🚨 عیب‌یابی

مشکلات رایج

1. Collection خالی است

⚠️ Collection islamic_knowledge_jina-embeddings-v4 is empty!

علت: embedder تغییر کرده اما داده‌ها re-ingest نشده‌اند.

راه‌حل:

# داده‌ها را دوباره ingest کنید
python scripts/ingest_data.py --hadiths data/raw/hadiths_data.xlsx

2. API Key یافت نشد

ValueError: API key for provider 'openai' not found

راه‌حل: متغیر محیطی را تنظیم کنید:

export OPENAI_API_KEY="your_key_here"

3. Embedder یافت نشد

ValueError: Embedding model 'unknown_model' not found in config

راه‌حل: مدل را در config/embeddings.yaml تعریف کنید.

4. Dimension mismatch (اگر سیستم قدیمی استفاده شود)

Error: Embedding dimensions do not match stored vectors

راه‌حل: از سیستم جدید استفاده کنید که اتوماتیک collection را تغییر می‌دهد.

Debug Mode

# فعال کردن debug در factory
import logging
logging.basicConfig(level=logging.DEBUG)

factory = EmbeddingFactory()
embedder = factory.get_embedder()  # لاگ‌های مفصل نمایش داده می‌شود

📊 مانیتورینگ و Metrics

پیگیری استفاده از Embedderها

# در main.py
logger.info(f"Knowledge base initialized with: {current_embedder.id}")
logger.info(f"Embedding dimensions: {current_embedder.dimensions}")
logger.info(f"Collection: {collection}_{embedder.id}")

مقایسه Performance

import time

def benchmark_embedder(embedder_name):
    factory = EmbeddingFactory()
    embedder = factory.get_embedder(embedder_name)

    start_time = time.time()
    # تست embedding چند متن
    embeddings = embedder.get_embedding_batch(["text1", "text2", "text3"])
    duration = time.time() - start_time

    return {
        "embedder": embedder_name,
        "duration": duration,
        "dimensions": embedder.dimensions
    }

🔄 مهاجرت از سیستم قدیمی

قبل از تغییر

# کد قدیمی - hardcoded
embedder = SentenceTransformerEmbedder(id="all-MiniLM-L6-v2")
vector_store = Qdrant(collection="islamic_knowledge", embedder=embedder)

بعد از تغییر

# کد جدید - متمرکز
from src.knowledge.embedding_factory import EmbeddingFactory

embed_factory = EmbeddingFactory()
embedder = embed_factory.get_embedder()  # از config
vector_store = get_qdrant_store(embedder=embedder)  # collection اتوماتیک

مهاجرت تدریجی

  1. مرحله ۱: تنظیمات را در config/embeddings.yaml تعریف کنید
  2. مرحله ۲: EmbeddingFactory را ایجاد کنید
  3. مرحله ۳: کد را به استفاده از factory تغییر دهید
  4. مرحله ۴: داده‌ها را با embedder جدید ingest کنید
  5. مرحله ۵: سیستم قدیمی را حذف کنید

📚 بهترین تجربیات (Best Practices)

1. همیشه از Environment Variables استفاده کنید

# ✅ خوب
api_key: ${JINA_API_KEY}

# ❌ بد
api_key: jina_123456789

2. نام‌گذاری معنادار مدل‌ها

models:
  jina_AI:        # ✅ واضح
  embed_model_1:  # ❌ نام‌گذار بی‌معنا

3. Re-ingest را فراموش نکنید

# چک‌لیست تغییر embedder:
# 1. تنظیمات را در YAML تغییر دهید
# 2. متغیرهای محیطی را تنظیم کنید
# 3. اپلیکیشن را restart کنید
# 4. ✅ داده‌ها را دوباره ingest کنید
# 5. عملکرد را تست کنید

4. Validation تنظیمات

def _validate_config(self):
    """Validate embedding configuration"""
    required_keys = ['embeddings', 'models', 'default']
    for key in required_keys:
        if key not in self.config:
            raise ValueError(f"Missing required config key: {key}")

    # بررسی dimensions
    for model_name, model_config in self.config['embeddings']['models'].items():
        if 'dimensions' not in model_config:
            raise ValueError(f"Model {model_name} missing dimensions")

5. Backup Collectionها

# قبل از تغییر embedder، backup بگیرید
qdrant_backup --collection islamic_knowledge_old_embedder

🎯 نتیجه‌گیری

این سیستم مدیریت embedding مزایای زیر را فراهم می‌کند:

  1. جلوگیری از Dimension Mismatch: collection اتوماتیک بر اساس embedder تغییر می‌کند
  2. انعطاف‌پذیری بالا: تغییر embedder با یک خط تغییر در YAML
  3. مدیریت متمرکز: همه تنظیمات در یک مکان
  4. ایمنی: API keys در environment variables
  5. قابل توسعه: افزودن provider جدید ساده
  6. شفافیت: logging و debugging کامل
  7. کارایی: جلوگیری از recomputation غیرضروری

این رویکرد نه تنها مشکلات سیستم قدیمی را حل می‌کند، بلکه پایه‌ای محکم برای توسعه آینده سیستم فراهم می‌کند و امکان مقایسه و انتخاب بهترین مدل embedding را آسان می‌سازد.