# مدیریت مدل‌های Embedding در سیستم Islamic Scholar Agent ## 📋 نمای کلی سیستم مدیریت مدل‌های embedding در پروژه Islamic Scholar Agent به صورت متمرکز و هوشمند طراحی شده است. این سیستم امکان تغییر سریع بین مدل‌های embedding مختلف، مدیریت اتوماتیک collectionها و جلوگیری از مشکلات dimension mismatch را فراهم می‌کند. ## 🎯 چرایی این سیستم ### مشکل رویکرد قدیمی در گذشته، مدل‌های embedding به صورت مستقیم در کد استفاده می‌شدند و collectionها ثابت بودند: ```python # ❌ رویکرد قدیمی - hardcoded embedder = SentenceTransformerEmbedder(id="all-MiniLM-L6-v2") vector_store = Qdrant(collection="islamic_knowledge", embedder=embedder) # ❌ مشکل dimension mismatch # اگر embedder تغییر کند، داده‌های قدیمی با dimensions جدید سازگار نیستند ``` ### راه‌حل جدید ```python # ✅ رویکرد جدید - هوشمند و متمرکز 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 را تعریف می‌کند: ```yaml 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 مناسب را از تنظیمات ایجاد می‌کند: ```python 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: ```python 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` نحوه استفاده در اپلیکیشن اصلی: ```python 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) ``` ## 🚀 نحوه استفاده ### استفاده پایه ```python # ایجاد embedder از تنظیمات پیش‌فرض from src.knowledge.embedding_factory import EmbeddingFactory embed_factory = EmbeddingFactory() embedder = embed_factory.get_embedder() # استفاده از مدل پیش‌فرض (jina_AI) ``` ### تغییر مدل embedding در زمان اجرا ```python # تغییر به OpenAI embeddings embedder = embed_factory.get_embedder('openai_small') # تغییر به local model (اگر تعریف شده) # embedder = embed_factory.get_embedder('minilm_l6') ``` ### تغییر مدل پیش‌فرض ```yaml # config/embeddings.yaml embeddings: default: openai_small # تغییر از jina_AI به openai_small ``` ### افزودن مدل embedding جدید #### مرحله 1: افزودن به YAML ```yaml # config/embeddings.yaml models: cohere_embed: provider: "cohere" id: "embed-multilingual-v3.0" dimensions: 1024 api_key: ${COHERE_API_KEY} ``` #### مرحله 2: افزودن پشتیبانی در factory ```python # 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** ```python # ❌ مشکل قدیمی # 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. **سوئیچ سریع بین مدل‌ها** ```python # تغییر از 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های مختلف** ```yaml models: # API-based providers openai_small: # OpenAI jina_AI: # Jina AI cohere_embed: # Cohere # Local providers (اختیاری) minilm_l6: # HuggingFace local ``` ### 5. **امنیت و مدیریت API Keys** ```bash # تنظیم متغیرهای محیطی 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** ```python # لاگ 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** ```python # 🚨 هشدار مهم # وقتی 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 ```python # قبل از استفاده، بررسی کنید که 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 ```bash # .env # Embedding Configuration JINA_API_KEY=jina_... OPENAI_API_KEY=sk-... # Vector Database COLLECTION_NAME=islamic_knowledge QDRANT_URL=http://localhost:6333 ``` ### تنظیمات Batch Processing ```yaml # config/embeddings.yaml models: minilm_l6: provider: "local" id: "all-MiniLM-L6-v2" dimensions: 384 batch_size: 100 # برای پردازش دسته‌ای ``` ### Caching Embeddings (اختیاری) ```python 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) ``` ## 🧪 تست سیستم ### تست‌های واحد ```python # 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 ```python # 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 نشده‌اند. **راه‌حل**: ```bash # داده‌ها را دوباره ingest کنید python scripts/ingest_data.py --hadiths data/raw/hadiths_data.xlsx ``` #### 2. API Key یافت نشد ``` ValueError: API key for provider 'openai' not found ``` **راه‌حل**: متغیر محیطی را تنظیم کنید: ```bash 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 ```python # فعال کردن debug در factory import logging logging.basicConfig(level=logging.DEBUG) factory = EmbeddingFactory() embedder = factory.get_embedder() # لاگ‌های مفصل نمایش داده می‌شود ``` ## 📊 مانیتورینگ و Metrics ### پیگیری استفاده از Embedderها ```python # در 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 ```python 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 } ``` ## 🔄 مهاجرت از سیستم قدیمی ### قبل از تغییر ```python # کد قدیمی - hardcoded embedder = SentenceTransformerEmbedder(id="all-MiniLM-L6-v2") vector_store = Qdrant(collection="islamic_knowledge", embedder=embedder) ``` ### بعد از تغییر ```python # کد جدید - متمرکز 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 استفاده کنید** ```yaml # ✅ خوب api_key: ${JINA_API_KEY} # ❌ بد api_key: jina_123456789 ``` ### 2. **نام‌گذاری معنادار مدل‌ها** ```yaml models: jina_AI: # ✅ واضح embed_model_1: # ❌ نام‌گذار بی‌معنا ``` ### 3. **Re-ingest را فراموش نکنید** ```bash # چک‌لیست تغییر embedder: # 1. تنظیمات را در YAML تغییر دهید # 2. متغیرهای محیطی را تنظیم کنید # 3. اپلیکیشن را restart کنید # 4. ✅ داده‌ها را دوباره ingest کنید # 5. عملکرد را تست کنید ``` ### 4. **Validation تنظیمات** ```python 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ها** ```bash # قبل از تغییر 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 را آسان می‌سازد.