tahamajs's picture
download
raw
21.5 kB
#!/usr/bin/env python3
"""
سیستم RAG کامل با Gemini API
Complete RAG System with Gemini API
"""
import os
import json
import logging
from typing import List, Dict, Any, Optional, Tuple
from pathlib import Path
import pickle
# LangChain imports
from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings
from langchain_community.document_loaders import (
TextLoader,
PyPDFLoader,
DirectoryLoader,
CSVLoader,
JSONLoader,
WebBaseLoader,
)
from langchain_community.vectorstores import Chroma, FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter, TokenTextSplitter
from langchain.chains import RetrievalQA, ConversationalRetrievalChain
from langchain.memory import ConversationBufferWindowMemory, ConversationSummaryMemory
from langchain_core.prompts import PromptTemplate
from langchain_core.callbacks import StreamingStdOutCallbackHandler
from langchain_core.runnables import RunnablePassthrough
# Gemini configuration
from gemini_config import get_gemini_config
# تنظیم لاگ
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class DocumentProcessor:
"""پردازشگر اسناد"""
def __init__(self, chunk_size: int = 1000, chunk_overlap: int = 200):
"""مقداردهی اولیه"""
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap
# ایجاد text splitter
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
separators=["\n\n", "\n", " ", ""],
)
# ایجاد token splitter برای متن‌های طولانی
self.token_splitter = TokenTextSplitter(
chunk_size=chunk_size, chunk_overlap=chunk_overlap
)
def load_documents(self, paths: List[str]) -> List[Any]:
"""بارگذاری اسناد از مسیرهای مختلف"""
documents = []
for path in paths:
try:
if os.path.isfile(path):
# بارگذاری فایل تکی
doc = self._load_single_file(path)
if doc:
documents.extend(doc)
elif os.path.isdir(path):
# بارگذاری پوشه
docs = self._load_directory(path)
documents.extend(docs)
else:
logger.warning(f"مسیر یافت نشد: {path}")
except Exception as e:
logger.error(f"خطا در بارگذاری {path}: {e}")
logger.info(f"📄 {len(documents)} سند بارگذاری شد")
return documents
def _load_single_file(self, path: str) -> Optional[List[Any]]:
"""بارگذاری فایل تکی"""
try:
if path.endswith(".pdf"):
loader = PyPDFLoader(path)
elif path.endswith(".csv"):
loader = CSVLoader(path, encoding="utf-8")
elif path.endswith(".json"):
loader = JSONLoader(path, jq_schema=".", text_content=False)
elif path.endswith(".txt"):
loader = TextLoader(path, encoding="utf-8")
else:
logger.warning(f"فرمت فایل پشتیبانی نمی‌شود: {path}")
return None
return loader.load()
except Exception as e:
logger.error(f"خطا در بارگذاری فایل {path}: {e}")
return None
def _load_directory(self, directory: str) -> List[Any]:
"""بارگذاری پوشه"""
try:
loader = DirectoryLoader(
directory,
glob="**/*.txt",
loader_cls=TextLoader,
loader_kwargs={"encoding": "utf-8"},
)
return loader.load()
except Exception as e:
logger.error(f"خطا در بارگذاری پوشه {directory}: {e}")
return []
def split_documents(
self, documents: List[Any], use_token_splitter: bool = False
) -> List[Any]:
"""تقسیم اسناد به بخش‌های کوچک‌تر"""
try:
if use_token_splitter:
splits = self.token_splitter.split_documents(documents)
else:
splits = self.text_splitter.split_documents(documents)
logger.info(f"📝 {len(splits)} بخش ایجاد شد")
return splits
except Exception as e:
logger.error(f"خطا در تقسیم اسناد: {e}")
return documents
class VectorStoreManager:
"""مدیر vector store"""
def __init__(self, embeddings, persist_directory: str = "./vector_store"):
"""مقداردهی اولیه"""
self.embeddings = embeddings
self.persist_directory = persist_directory
self.vector_store = None
def create_vector_store(
self, documents: List[Any], store_type: str = "chroma"
) -> Any:
"""ایجاد vector store"""
try:
if store_type.lower() == "chroma":
self.vector_store = Chroma.from_documents(
documents=documents,
embedding=self.embeddings,
persist_directory=self.persist_directory,
)
elif store_type.lower() == "faiss":
self.vector_store = FAISS.from_documents(
documents=documents, embedding=self.embeddings
)
# ذخیره FAISS
self.vector_store.save_local(self.persist_directory)
else:
raise ValueError(f"نوع vector store پشتیبانی نمی‌شود: {store_type}")
logger.info(f"✅ Vector store از نوع {store_type} ایجاد شد")
return self.vector_store
except Exception as e:
logger.error(f"خطا در ایجاد vector store: {e}")
raise
def load_vector_store(self, store_type: str = "chroma") -> Any:
"""بارگذاری vector store موجود"""
try:
if store_type.lower() == "chroma":
self.vector_store = Chroma(
persist_directory=self.persist_directory,
embedding_function=self.embeddings,
)
elif store_type.lower() == "faiss":
self.vector_store = FAISS.load_local(
self.persist_directory,
self.embeddings,
allow_dangerous_deserialization=True,
)
else:
raise ValueError(f"نوع vector store پشتیبانی نمی‌شود: {store_type}")
logger.info(f"✅ Vector store از نوع {store_type} بارگذاری شد")
return self.vector_store
except Exception as e:
logger.error(f"خطا در بارگذاری vector store: {e}")
raise
def get_retriever(
self, search_type: str = "similarity", k: int = 4, **kwargs
) -> Any:
"""دریافت retriever"""
if self.vector_store is None:
raise ValueError("Vector store ایجاد نشده است")
return self.vector_store.as_retriever(
search_type=search_type, search_kwargs={"k": k, **kwargs}
)
class RAGSystem:
"""سیستم RAG کامل"""
def __init__(self, model_name: str = "gemini-1.5-pro", temperature: float = 0.7):
"""مقداردهی اولیه"""
self.model_name = model_name
self.temperature = temperature
self.config = get_gemini_config()
# ایجاد مدل‌ها
self.llm = ChatGoogleGenerativeAI(
model=self.model_name,
temperature=self.temperature,
google_api_key=self.config.api_key,
convert_system_message_to_human=True,
streaming=True,
)
self.embeddings = GoogleGenerativeAIEmbeddings(
model="models/embedding-001", google_api_key=self.config.api_key
)
# ایجاد پردازشگر اسناد
self.doc_processor = DocumentProcessor()
# ایجاد مدیر vector store
self.vector_manager = VectorStoreManager(self.embeddings)
# حافظه مکالمه
self.memory = ConversationBufferWindowMemory(
k=5, memory_key="chat_history", return_messages=True
)
# chain های مختلف
self.qa_chain = None
self.conversational_chain = None
logger.info("✅ سیستم RAG ایجاد شد")
def setup_documents(
self,
document_paths: List[str],
store_type: str = "chroma",
recreate: bool = False,
) -> None:
"""تنظیم اسناد برای RAG"""
try:
# بررسی وجود vector store
if not recreate and os.path.exists(self.vector_manager.persist_directory):
logger.info("بارگذاری vector store موجود...")
self.vector_manager.load_vector_store(store_type)
else:
logger.info("ایجاد vector store جدید...")
# بارگذاری اسناد
documents = self.doc_processor.load_documents(document_paths)
# تقسیم اسناد
splits = self.doc_processor.split_documents(documents)
# ایجاد vector store
self.vector_manager.create_vector_store(splits, store_type)
# ایجاد retriever
self.retriever = self.vector_manager.get_retriever(
search_type="mmr", k=6, fetch_k=20, lambda_mult=0.7
)
logger.info("✅ اسناد برای RAG تنظیم شدند")
except Exception as e:
logger.error(f"خطا در تنظیم اسناد: {e}")
raise
def create_qa_chain(self, custom_prompt: Optional[str] = None) -> RetrievalQA:
"""ایجاد chain برای Q&A"""
try:
# Prompt پیش‌فرض
if custom_prompt is None:
custom_prompt = """
شما یک دستیار هوشمند هستید که بر اساس اسناد ارائه شده پاسخ می‌دهید.
متن اسناد:
{context}
سوال: {question}
دستورالعمل‌ها:
1. فقط از اطلاعات موجود در اسناد پاسخ دهید
2. اگر پاسخ در اسناد نیست، بگویید "اطلاعات کافی در اسناد موجود نیست"
3. پاسخ را به فارسی و واضح ارائه دهید
4. در صورت نیاز، منابع را ذکر کنید
پاسخ:
"""
prompt = PromptTemplate(
template=custom_prompt, input_variables=["context", "question"]
)
# ایجاد chain
self.qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
chain_type="stuff",
retriever=self.retriever,
chain_type_kwargs={"prompt": prompt},
return_source_documents=True,
)
logger.info("✅ Chain Q&A ایجاد شد")
return self.qa_chain
except Exception as e:
logger.error(f"خطا در ایجاد chain Q&A: {e}")
raise
def create_conversational_chain(self) -> ConversationalRetrievalChain:
"""ایجاد chain مکالمه‌ای"""
try:
self.conversational_chain = ConversationalRetrievalChain.from_llm(
llm=self.llm,
retriever=self.retriever,
memory=self.memory,
return_source_documents=True,
verbose=True,
max_tokens_limit=4000,
)
logger.info("✅ Chain مکالمه‌ای ایجاد شد")
return self.conversational_chain
except Exception as e:
logger.error(f"خطا در ایجاد chain مکالمه‌ای: {e}")
raise
def ask_question(
self, question: str, use_conversation: bool = False
) -> Dict[str, Any]:
"""پرسیدن سوال از سیستم RAG"""
try:
if use_conversation and self.conversational_chain:
result = self.conversational_chain.invoke({"question": question})
elif self.qa_chain:
result = self.qa_chain.invoke({"query": question})
else:
raise ValueError("هیچ chain فعال نیست")
return {
"question": question,
"answer": result["result"],
"sources": [
doc.page_content[:200] + "..."
for doc in result.get("source_documents", [])
],
"metadata": [
doc.metadata for doc in result.get("source_documents", [])
],
}
except Exception as e:
logger.error(f"خطا در پاسخ‌دهی: {e}")
return {
"question": question,
"answer": f"خطا: {str(e)}",
"sources": [],
"metadata": [],
}
def batch_questions(
self, questions: List[str], use_conversation: bool = False
) -> List[Dict[str, Any]]:
"""پرسیدن چندین سوال"""
results = []
for question in questions:
logger.info(f"پردازش سوال: {question}")
result = self.ask_question(question, use_conversation)
results.append(result)
return results
def evaluate_system(
self, test_questions: List[str], expected_answers: List[str] = None
) -> Dict[str, Any]:
"""ارزیابی سیستم RAG"""
try:
results = self.batch_questions(test_questions)
evaluation = {
"total_questions": len(test_questions),
"successful_answers": len(
[r for r in results if "خطا" not in r["answer"]]
),
"failed_answers": len([r for r in results if "خطا" in r["answer"]]),
"avg_sources": sum(len(r["sources"]) for r in results) / len(results),
"results": results,
}
evaluation["success_rate"] = (
evaluation["successful_answers"] / evaluation["total_questions"]
)
logger.info(f"📊 ارزیابی سیستم: {evaluation['success_rate']:.2%} موفقیت")
return evaluation
except Exception as e:
logger.error(f"خطا در ارزیابی سیستم: {e}")
return {"error": str(e)}
def save_system_state(self, filepath: str) -> None:
"""ذخیره وضعیت سیستم"""
try:
state = {
"model_name": self.model_name,
"temperature": self.temperature,
"persist_directory": self.vector_manager.persist_directory,
"memory_state": self.memory.save_context({"input": ""}, {"output": ""}),
}
with open(filepath, "w", encoding="utf-8") as f:
json.dump(state, f, ensure_ascii=False, indent=2)
logger.info(f"✅ وضعیت سیستم در {filepath} ذخیره شد")
except Exception as e:
logger.error(f"خطا در ذخیره وضعیت: {e}")
def load_system_state(self, filepath: str) -> None:
"""بارگذاری وضعیت سیستم"""
try:
with open(filepath, "r", encoding="utf-8") as f:
state = json.load(f)
self.model_name = state["model_name"]
self.temperature = state["temperature"]
self.vector_manager.persist_directory = state["persist_directory"]
logger.info(f"✅ وضعیت سیستم از {filepath} بارگذاری شد")
except Exception as e:
logger.error(f"خطا در بارگذاری وضعیت: {e}")
# توابع کمکی
def create_sample_documents() -> List[str]:
"""ایجاد اسناد نمونه"""
sample_docs = [
"""
هوش مصنوعی و یادگیری ماشین
هوش مصنوعی (AI) شاخه‌ای از علوم کامپیوتر است که به ایجاد سیستم‌های هوشمند می‌پردازد.
یادگیری ماشین (ML) زیرمجموعه‌ای از هوش مصنوعی است که به ماشین‌ها امکان یادگیری از داده‌ها را می‌دهد.
انواع یادگیری ماشین:
1. یادگیری نظارت شده (Supervised Learning)
2. یادگیری بدون نظارت (Unsupervised Learning)
3. یادگیری تقویتی (Reinforcement Learning)
کاربردهای هوش مصنوعی:
- تشخیص تصویر و پردازش زبان طبیعی
- سیستم‌های توصیه‌گر
- خودروهای خودران
- پزشکی و تشخیص بیماری‌ها
""",
"""
پردازش زبان طبیعی (NLP)
پردازش زبان طبیعی شاخه‌ای از هوش مصنوعی است که با تعامل بین کامپیوتر و زبان انسان سروکار دارد.
وظایف اصلی NLP:
1. تحلیل احساسات (Sentiment Analysis)
2. ترجمه ماشینی (Machine Translation)
3. خلاصه‌سازی متن (Text Summarization)
4. تولید متن (Text Generation)
5. پاسخ‌دهی به سوالات (Question Answering)
مدل‌های زبانی بزرگ (LLMs) مانند GPT، BERT، و Gemini انقلابی در حوزه NLP ایجاد کرده‌اند.
این مدل‌ها قادر به درک و تولید متن با کیفیت بالا هستند.
""",
"""
سیستم‌های RAG (Retrieval-Augmented Generation)
RAG ترکیبی از بازیابی اطلاعات و تولید متن است که دقت و صحت پاسخ‌های مدل‌های زبانی را بهبود می‌بخشد.
اجزای اصلی RAG:
1. بازیاب (Retriever): یافتن اطلاعات مرتبط از پایگاه دانش
2. تولیدکننده (Generator): تولید پاسخ بر اساس اطلاعات بازیابی شده
3. پایگاه دانش (Knowledge Base): مجموعه اسناد و اطلاعات
مزایای RAG:
- کاهش توهمات مدل
- دسترسی به اطلاعات به‌روز
- قابلیت استناد به منابع
- بهبود دقت پاسخ‌ها
کاربردهای RAG در سیستم‌های پشتیبانی، تحقیق، و تحلیل اسناد است.
""",
]
# ذخیره اسناد نمونه
docs_dir = Path("sample_documents")
docs_dir.mkdir(exist_ok=True)
for i, doc in enumerate(sample_docs, 1):
with open(docs_dir / f"document_{i}.txt", "w", encoding="utf-8") as f:
f.write(doc.strip())
return [str(docs_dir / f"document_{i}.txt") for i in range(1, len(sample_docs) + 1)]
def demo_rag_system():
"""نمایش سیستم RAG"""
try:
# ایجاد اسناد نمونه
sample_docs = create_sample_documents()
# ایجاد سیستم RAG
rag = RAGSystem()
# تنظیم اسناد
rag.setup_documents(sample_docs, recreate=True)
# ایجاد chain ها
rag.create_qa_chain()
rag.create_conversational_chain()
# سوالات تست
test_questions = [
"هوش مصنوعی چیست؟",
"انواع یادگیری ماشین کدامند؟",
"RAG چه مزایایی دارد؟",
"کاربردهای NLP چیست؟",
]
print("🤖 سیستم RAG آماده است!")
print("=" * 50)
# تست سوالات
for question in test_questions:
print(f"\n❓ سوال: {question}")
result = rag.ask_question(question)
print(f"✅ پاسخ: {result['answer']}")
print(f"📚 منابع: {len(result['sources'])} منبع")
# ارزیابی سیستم
print("\n📊 ارزیابی سیستم:")
evaluation = rag.evaluate_system(test_questions)
print(f"نرخ موفقیت: {evaluation['success_rate']:.2%}")
print(f"میانگین منابع: {evaluation['avg_sources']:.1f}")
logger.info("✅ نمایش سیستم RAG با موفقیت انجام شد")
except Exception as e:
logger.error(f"❌ خطا در نمایش سیستم RAG: {e}")
if __name__ == "__main__":
demo_rag_system()

Xet Storage Details

Size:
21.5 kB
·
Xet hash:
81c4713f281d02986258b6e79ac13d581e4b3155ca1737c58f90546b35e7dfa5

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.