diff --git a/agentic_rag_interface.py b/agentic_rag_interface.py index 23cc012..03b60d5 100644 --- a/agentic_rag_interface.py +++ b/agentic_rag_interface.py @@ -65,6 +65,23 @@ class AgenticRAGInterface: self.conversation_history = [] return "История очищена", "" + def index_documents(self, folder_path: str): + """Индексация документов из указанной папки""" + if not folder_path: + return "❌ Укажите путь к папке", "" + + if not os.path.exists(folder_path): + return f"❌ Папка не существует: {folder_path}", "" + + if not os.path.isdir(folder_path): + return f"❌ Указанный путь не является папкой: {folder_path}", "" + + try: + chunks = self.kb.load_documents(folder_path) + return f"✅ Успешно! Индексировано чанков: {len(chunks)}", folder_path + except Exception as e: + return f"❌ Ошибка при индексации: {str(e)}", "" + # Инициализируем базу kb = LocalKnowledgeBase() # Загружаем документы (если есть) @@ -124,6 +141,26 @@ def create_gradio_interface(): label="Примеры запросов" ) + # Секция индексации документов + gr.Markdown("---") + gr.Markdown("### 📂 Индексация документов") + + with gr.Row(): + with gr.Column(scale=3): + folder_input = gr.Textbox( + label="Путь к папке с файлами", + placeholder="./documents или /home/user/my_project", + lines=1 + ) + with gr.Column(scale=1): + index_btn = gr.Button("📥 Индексировать", variant="secondary") + + status_output = gr.Textbox( + label="Статус индексации", + lines=2, + interactive=False + ) + # Обработчики событий submit_btn.click( fn=interface.process_query, @@ -143,4 +180,11 @@ def create_gradio_interface(): outputs=[answer_output, history_output] ) + # Обработчик индексации + index_btn.click( + fn=interface.index_documents, + inputs=[folder_input], + outputs=[status_output, folder_input] + ) + return demo diff --git a/local_knowledge_base.py b/local_knowledge_base.py index 2dc335d..395de90 100644 --- a/local_knowledge_base.py +++ b/local_knowledge_base.py @@ -1,4 +1,5 @@ import os +from pathlib import Path from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams, PointStruct from langchain_community.document_loaders import ( @@ -10,6 +11,12 @@ from langchain_text_splitters import Language, RecursiveCharacterTextSplitter from langchain_community.embeddings import OllamaEmbeddings +def should_skip(path: str) -> bool: + """Проверяет, нужно ли пропустить путь (системные папки)""" + skip_dirs = {'.git', 'build', 'vendor', 'node_modules', '__pycache__', '.venv', 'venv'} + return any(part in skip_dirs for part in Path(path).parts) + + class LocalKnowledgeBase: def __init__(self, collection_name="documents", persist_dir="./qdrant_data"): # Инициализируем клиент Qdrant @@ -43,28 +50,33 @@ class LocalKnowledgeBase: print(f"Создана коллекция {self.collection_name}") def load_documents(self, directory_path: str): - """Загрузка документов из директории""" + """Загрузка документов из директории (рекурсивно)""" loaders = { ".pdf": PyPDFLoader, - ".txt": lambda path: DirectoryLoader(path, glob="**/*.txt"), + ".txt": lambda path: TextLoader(path, encoding="utf-8"), ".docx": lambda path: DirectoryLoader(path, glob="**/*.docx"), ".hpp": lambda path: TextLoader(path, encoding="utf-8"), - ".cpp": lambda path: TextLoader(path, encoding="utf-8"), # добавить - ".h": lambda path: TextLoader(path, encoding="utf-8"), # добавить - ".cc": lambda path: TextLoader(path, encoding="utf-8"), # добавить + ".cpp": lambda path: TextLoader(path, encoding="utf-8"), + ".h": lambda path: TextLoader(path, encoding="utf-8"), + ".cc": lambda path: TextLoader(path, encoding="utf-8"), } all_documents = [] - for ext, loader_class in loaders.items(): - for file_path in os.listdir(directory_path): - if file_path.endswith(ext): - full_path = os.path.join(directory_path, file_path) + # Рекурсивный обход с фильтрацией папок + for root, dirs, files in os.walk(directory_path): + # Фильтруем директории на месте + dirs[:] = [d for d in dirs if not should_skip(os.path.join(root, d))] + + for file_path in files: + ext = os.path.splitext(file_path)[1] + if ext in loaders: + full_path = os.path.join(root, file_path) try: - loader = loader_class(full_path) + loader = loaders[ext](full_path) documents = loader.load() all_documents.extend(documents) - print(f"Загружен {file_path}: {len(documents)} страниц") + print(f"Загружен {file_path}: {len(documents)} документов") except Exception as e: print(f"Ошибка загрузки {file_path}: {e}")