tahamajs's picture
download
raw
21 kB
#!/usr/bin/env python3
"""
پردازشگر پیشرفته اسناد با Gemini API
Advanced Document Processor with Gemini API
"""
import os
import json
import logging
from typing import List, Dict, Any, Optional, Tuple
from pathlib import Path
import hashlib
from datetime import datetime
# LangChain imports
from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings
from langchain_community.document_loaders import (
TextLoader, PyPDFLoader, DirectoryLoader,
CSVLoader, JSONLoader, WebBaseLoader,
UnstructuredFileLoader, UnstructuredMarkdownLoader
)
from langchain_community.vectorstores import Chroma, FAISS
from langchain.text_splitter import (
RecursiveCharacterTextSplitter,
TokenTextSplitter,
SpacyTextSplitter,
NLTKTextSplitter
)
from langchain_core.documents import Document
from langchain_core.prompts import PromptTemplate
from langchain.chains import RetrievalQA, MapReduceDocumentsChain, ReduceDocumentsChain
from langchain.chains.combine_documents.stuff import StuffDocumentsChain
# Gemini configuration
from gemini_config import get_gemini_config
# تنظیم لاگ
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class DocumentProcessor:
"""پردازشگر پیشرفته اسناد"""
def __init__(self, model_name: str = "gemini-1.5-pro", temperature: float = 0.3):
"""مقداردهی اولیه"""
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
)
self.embeddings = GoogleGenerativeAIEmbeddings(
model="models/embedding-001",
google_api_key=self.config.api_key
)
# تنظیمات پردازش
self.processing_settings = {
"chunk_size": 1000,
"chunk_overlap": 200,
"max_chunks": 1000,
"min_chunk_size": 100,
"enable_metadata": True,
"enable_summarization": True
}
# تاریخچه پردازش
self.processing_history = []
logger.info("✅ پردازشگر اسناد ایجاد شد")
def update_settings(self, **kwargs):
"""به‌روزرسانی تنظیمات پردازش"""
for key, value in kwargs.items():
if key in self.processing_settings:
self.processing_settings[key] = value
logger.info("✅ تنظیمات پردازش به‌روزرسانی شد")
def load_document(self, file_path: str) -> List[Document]:
"""بارگذاری سند تکی"""
try:
file_path = Path(file_path)
if not file_path.exists():
raise FileNotFoundError(f"فایل یافت نشد: {file_path}")
# انتخاب loader مناسب
loader = self._get_loader(file_path)
if loader is None:
raise ValueError(f"فرمت فایل پشتیبانی نمی‌شود: {file_path.suffix}")
documents = loader.load()
# اضافه کردن metadata
for doc in documents:
doc.metadata.update({
"source": str(file_path),
"file_type": file_path.suffix,
"file_size": file_path.stat().st_size,
"processed_at": datetime.now().isoformat(),
"file_hash": self._calculate_file_hash(file_path)
})
logger.info(f"📄 {len(documents)} سند از {file_path} بارگذاری شد")
return documents
except Exception as e:
logger.error(f"❌ خطا در بارگذاری {file_path}: {e}")
raise
def load_directory(self, directory_path: str, glob_pattern: str = "**/*") -> List[Document]:
"""بارگذاری پوشه اسناد"""
try:
directory_path = Path(directory_path)
if not directory_path.exists():
raise FileNotFoundError(f"پوشه یافت نشد: {directory_path}")
documents = []
file_count = 0
for file_path in directory_path.glob(glob_pattern):
if file_path.is_file():
try:
docs = self.load_document(str(file_path))
documents.extend(docs)
file_count += 1
except Exception as e:
logger.warning(f"خطا در بارگذاری {file_path}: {e}")
continue
logger.info(f"📁 {file_count} فایل از {directory_path} بارگذاری شد")
return documents
except Exception as e:
logger.error(f"❌ خطا در بارگذاری پوشه {directory_path}: {e}")
raise
def _get_loader(self, file_path: Path):
"""انتخاب loader مناسب"""
suffix = file_path.suffix.lower()
if suffix == '.pdf':
return PyPDFLoader(str(file_path))
elif suffix == '.txt':
return TextLoader(str(file_path), encoding='utf-8')
elif suffix == '.csv':
return CSVLoader(str(file_path), encoding='utf-8')
elif suffix == '.json':
return JSONLoader(str(file_path), jq_schema='.', text_content=False)
elif suffix == '.md':
return UnstructuredMarkdownLoader(str(file_path))
else:
# تلاش برای استفاده از UnstructuredFileLoader
try:
return UnstructuredFileLoader(str(file_path))
except:
return None
def _calculate_file_hash(self, file_path: Path) -> str:
"""محاسبه hash فایل"""
try:
with open(file_path, 'rb') as f:
content = f.read()
return hashlib.md5(content).hexdigest()
except:
return "unknown"
def split_documents(self, documents: List[Document], method: str = "recursive") -> List[Document]:
"""تقسیم اسناد به بخش‌های کوچک‌تر"""
try:
if method == "recursive":
splitter = RecursiveCharacterTextSplitter(
chunk_size=self.processing_settings["chunk_size"],
chunk_overlap=self.processing_settings["chunk_overlap"],
length_function=len,
separators=["\n\n", "\n", " ", ""]
)
elif method == "token":
splitter = TokenTextSplitter(
chunk_size=self.processing_settings["chunk_size"],
chunk_overlap=self.processing_settings["chunk_overlap"]
)
else:
raise ValueError(f"روش تقسیم نامعتبر: {method}")
splits = splitter.split_documents(documents)
# محدود کردن تعداد chunks
if len(splits) > self.processing_settings["max_chunks"]:
splits = splits[:self.processing_settings["max_chunks"]]
logger.warning(f"تعداد chunks به {self.processing_settings['max_chunks']} محدود شد")
# فیلتر کردن chunks کوچک
min_size = self.processing_settings["min_chunk_size"]
splits = [doc for doc in splits if len(doc.page_content) >= min_size]
logger.info(f"📝 {len(splits)} بخش از {len(documents)} سند ایجاد شد")
return splits
except Exception as e:
logger.error(f"❌ خطا در تقسیم اسناد: {e}")
raise
def summarize_documents(self, documents: List[Document]) -> Dict[str, str]:
"""خلاصه‌سازی اسناد"""
try:
summaries = {}
for i, doc in enumerate(documents):
if len(doc.page_content) > 500: # فقط اسناد طولانی
prompt = f"""
خلاصه‌ای از متن زیر ارائه دهید:
{doc.page_content[:2000]}...
خلاصه باید شامل:
1. موضوع اصلی
2. نکات کلیدی
3. نتیجه‌گیری
خلاصه:
"""
summary = self.llm.invoke(prompt)
summaries[f"document_{i}"] = summary.content
# محدود کردن تعداد خلاصه‌ها
if len(summaries) >= 10:
break
logger.info(f"📋 {len(summaries)} خلاصه ایجاد شد")
return summaries
except Exception as e:
logger.error(f"❌ خطا در خلاصه‌سازی: {e}")
return {}
def extract_keywords(self, documents: List[Document], top_k: int = 20) -> List[str]:
"""استخراج کلمات کلیدی"""
try:
# ترکیب متن همه اسناد
combined_text = " ".join([doc.page_content for doc in documents[:10]]) # محدود کردن
prompt = f"""
کلمات کلیدی مهم متن زیر را استخراج کنید:
{combined_text[:3000]}...
لطفاً {top_k} کلمه کلیدی مهم را لیست کنید:
"""
response = self.llm.invoke(prompt)
# پردازش پاسخ
keywords = []
for line in response.content.split('\n'):
line = line.strip()
if line and not line.startswith('#'):
# حذف شماره‌ها و علائم
keyword = ''.join(c for c in line if c.isalnum() or c.isspace()).strip()
if keyword:
keywords.append(keyword)
keywords = keywords[:top_k]
logger.info(f"🔑 {len(keywords)} کلمه کلیدی استخراج شد")
return keywords
except Exception as e:
logger.error(f"❌ خطا در استخراج کلمات کلیدی: {e}")
return []
def analyze_sentiment(self, documents: List[Document]) -> Dict[str, Any]:
"""تحلیل احساسات اسناد"""
try:
sentiments = []
for doc in documents[:5]: # محدود کردن
prompt = f"""
تحلیل احساسات متن زیر را انجام دهید:
{doc.page_content[:1000]}
لطفاً تحلیل را در قالب JSON ارائه دهید:
{{
"sentiment": "positive/negative/neutral",
"confidence": 0.0-1.0,
"emotions": ["list", "of", "emotions"]
}}
"""
try:
response = self.llm.invoke(prompt)
sentiment_data = json.loads(response.content)
sentiments.append(sentiment_data)
except:
continue
if sentiments:
avg_confidence = sum(s['confidence'] for s in sentiments) / len(sentiments)
dominant_sentiment = max(set(s['sentiment'] for s in sentiments),
key=[s['sentiment'] for s in sentiments].count)
result = {
"dominant_sentiment": dominant_sentiment,
"average_confidence": avg_confidence,
"total_documents": len(sentiments),
"sentiments": sentiments
}
logger.info(f"😊 تحلیل احساسات انجام شد: {dominant_sentiment}")
return result
return {"error": "هیچ تحلیل احساساتی انجام نشد"}
except Exception as e:
logger.error(f"❌ خطا در تحلیل احساسات: {e}")
return {"error": str(e)}
def create_vector_store(self, documents: List[Document], store_type: str = "chroma",
persist_directory: str = "./vector_store") -> Any:
"""ایجاد vector store"""
try:
if store_type.lower() == "chroma":
vector_store = Chroma.from_documents(
documents=documents,
embedding=self.embeddings,
persist_directory=persist_directory
)
elif store_type.lower() == "faiss":
vector_store = FAISS.from_documents(
documents=documents,
embedding=self.embeddings
)
vector_store.save_local(persist_directory)
else:
raise ValueError(f"نوع vector store پشتیبانی نمی‌شود: {store_type}")
logger.info(f"✅ Vector store از نوع {store_type} ایجاد شد")
return vector_store
except Exception as e:
logger.error(f"❌ خطا در ایجاد vector store: {e}")
raise
def process_documents(self, input_path: str, output_dir: str = "./processed_documents") -> Dict[str, Any]:
"""پردازش کامل اسناد"""
try:
input_path = Path(input_path)
output_dir = Path(output_dir)
output_dir.mkdir(exist_ok=True)
# بارگذاری اسناد
if input_path.is_file():
documents = self.load_document(str(input_path))
else:
documents = self.load_directory(str(input_path))
if not documents:
return {"error": "هیچ سندی یافت نشد"}
# تقسیم اسناد
chunks = self.split_documents(documents)
# خلاصه‌سازی (اگر فعال باشد)
summaries = {}
if self.processing_settings["enable_summarization"]:
summaries = self.summarize_documents(documents)
# استخراج کلمات کلیدی
keywords = self.extract_keywords(chunks)
# تحلیل احساسات
sentiment_analysis = self.analyze_sentiment(documents)
# ایجاد vector store
vector_store = self.create_vector_store(chunks, persist_directory=str(output_dir / "vector_store"))
# ذخیره نتایج
results = {
"processing_info": {
"input_path": str(input_path),
"output_dir": str(output_dir),
"processed_at": datetime.now().isoformat(),
"total_documents": len(documents),
"total_chunks": len(chunks),
"settings": self.processing_settings
},
"keywords": keywords,
"summaries": summaries,
"sentiment_analysis": sentiment_analysis,
"chunks_preview": [chunk.page_content[:200] + "..." for chunk in chunks[:5]]
}
# ذخیره در فایل
with open(output_dir / "processing_results.json", 'w', encoding='utf-8') as f:
json.dump(results, f, ensure_ascii=False, indent=2)
# ذخیره chunks
chunks_data = []
for i, chunk in enumerate(chunks):
chunks_data.append({
"id": i,
"content": chunk.page_content,
"metadata": chunk.metadata
})
with open(output_dir / "chunks.json", 'w', encoding='utf-8') as f:
json.dump(chunks_data, f, ensure_ascii=False, indent=2)
logger.info(f"✅ پردازش کامل انجام شد. نتایج در {output_dir} ذخیره شد")
# اضافه کردن به تاریخچه
self.processing_history.append({
"timestamp": datetime.now().isoformat(),
"input_path": str(input_path),
"output_dir": str(output_dir),
"documents_count": len(documents),
"chunks_count": len(chunks)
})
return results
except Exception as e:
logger.error(f"❌ خطا در پردازش اسناد: {e}")
return {"error": str(e)}
def get_processing_history(self) -> List[Dict[str, Any]]:
"""دریافت تاریخچه پردازش"""
return self.processing_history
def export_processing_report(self, filepath: str) -> None:
"""صادرات گزارش پردازش"""
try:
report = {
"processor_info": {
"model": self.model_name,
"temperature": self.temperature,
"settings": self.processing_settings
},
"processing_history": self.processing_history,
"export_time": datetime.now().isoformat()
}
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
logger.info(f"✅ گزارش پردازش در {filepath} ذخیره شد")
except Exception as e:
logger.error(f"❌ خطا در صادرات گزارش: {e}")
def demo_document_processor():
"""نمایش پردازشگر اسناد"""
print("📄 نمایش پردازشگر اسناد")
print("=" * 40)
try:
# ایجاد پردازشگر
processor = DocumentProcessor()
# ایجاد اسناد نمونه
sample_dir = Path("sample_docs")
sample_dir.mkdir(exist_ok=True)
sample_texts = [
"هوش مصنوعی شاخه‌ای از علوم کامپیوتر است که به ایجاد سیستم‌های هوشمند می‌پردازد.",
"یادگیری ماشین زیرمجموعه‌ای از هوش مصنوعی است که به ماشین‌ها امکان یادگیری می‌دهد.",
"پردازش زبان طبیعی یکی از کاربردهای مهم هوش مصنوعی در دنیای امروز است."
]
for i, text in enumerate(sample_texts, 1):
with open(sample_dir / f"doc_{i}.txt", 'w', encoding='utf-8') as f:
f.write(text)
print(f"✅ {len(sample_texts)} سند نمونه ایجاد شد")
# پردازش اسناد
results = processor.process_documents(str(sample_dir))
if "error" not in results:
print(f"📊 نتایج پردازش:")
print(f" • اسناد پردازش شده: {results['processing_info']['total_documents']}")
print(f" • بخش‌های ایجاد شده: {results['processing_info']['total_chunks']}")
print(f" • کلمات کلیدی: {len(results['keywords'])}")
print(f" • خلاصه‌ها: {len(results['summaries'])}")
print(f"\n🔑 کلمات کلیدی:")
for keyword in results['keywords'][:5]:
print(f" • {keyword}")
if results['summaries']:
print(f"\n📋 نمونه خلاصه:")
first_summary = list(results['summaries'].values())[0]
print(f" {first_summary[:200]}...")
logger.info("✅ نمایش پردازشگر اسناد با موفقیت انجام شد")
except Exception as e:
logger.error(f"❌ خطا در نمایش پردازشگر: {e}")
if __name__ == "__main__":
demo_document_processor()

Xet Storage Details

Size:
21 kB
·
Xet hash:
6062e793cdf7e8221f11fe269a24b89ebd06b3219e133ea53d4f6c87d7ef66fd

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