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 اتوماتیک
مهاجرت تدریجی
- مرحله ۱: تنظیمات را در
config/embeddings.yamlتعریف کنید - مرحله ۲:
EmbeddingFactoryرا ایجاد کنید - مرحله ۳: کد را به استفاده از factory تغییر دهید
- مرحله ۴: دادهها را با embedder جدید ingest کنید
- مرحله ۵: سیستم قدیمی را حذف کنید
📚 بهترین تجربیات (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 مزایای زیر را فراهم میکند:
- جلوگیری از Dimension Mismatch: collection اتوماتیک بر اساس embedder تغییر میکند
- انعطافپذیری بالا: تغییر embedder با یک خط تغییر در YAML
- مدیریت متمرکز: همه تنظیمات در یک مکان
- ایمنی: API keys در environment variables
- قابل توسعه: افزودن provider جدید ساده
- شفافیت: logging و debugging کامل
- کارایی: جلوگیری از recomputation غیرضروری
این رویکرد نه تنها مشکلات سیستم قدیمی را حل میکند، بلکه پایهای محکم برای توسعه آینده سیستم فراهم میکند و امکان مقایسه و انتخاب بهترین مدل embedding را آسان میسازد.