Добавил возможность индексации директории, указываемой из интерфейса

This commit is contained in:
ki.sagidullin
2026-04-01 09:04:19 +05:00
parent b31e1377b3
commit 4816950382
2 changed files with 67 additions and 11 deletions

View File

@@ -65,6 +65,23 @@ class AgenticRAGInterface:
self.conversation_history = [] self.conversation_history = []
return "История очищена", "" 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() kb = LocalKnowledgeBase()
# Загружаем документы (если есть) # Загружаем документы (если есть)
@@ -124,6 +141,26 @@ def create_gradio_interface():
label="Примеры запросов" 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( submit_btn.click(
fn=interface.process_query, fn=interface.process_query,
@@ -143,4 +180,11 @@ def create_gradio_interface():
outputs=[answer_output, history_output] outputs=[answer_output, history_output]
) )
# Обработчик индексации
index_btn.click(
fn=interface.index_documents,
inputs=[folder_input],
outputs=[status_output, folder_input]
)
return demo return demo

View File

@@ -1,4 +1,5 @@
import os import os
from pathlib import Path
from qdrant_client import QdrantClient from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct from qdrant_client.models import Distance, VectorParams, PointStruct
from langchain_community.document_loaders import ( from langchain_community.document_loaders import (
@@ -10,6 +11,12 @@ from langchain_text_splitters import Language, RecursiveCharacterTextSplitter
from langchain_community.embeddings import OllamaEmbeddings 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: class LocalKnowledgeBase:
def __init__(self, collection_name="documents", persist_dir="./qdrant_data"): def __init__(self, collection_name="documents", persist_dir="./qdrant_data"):
# Инициализируем клиент Qdrant # Инициализируем клиент Qdrant
@@ -43,28 +50,33 @@ class LocalKnowledgeBase:
print(f"Создана коллекция {self.collection_name}") print(f"Создана коллекция {self.collection_name}")
def load_documents(self, directory_path: str): def load_documents(self, directory_path: str):
"""Загрузка документов из директории""" """Загрузка документов из директории (рекурсивно)"""
loaders = { loaders = {
".pdf": PyPDFLoader, ".pdf": PyPDFLoader,
".txt": lambda path: DirectoryLoader(path, glob="**/*.txt"), ".txt": lambda path: TextLoader(path, encoding="utf-8"),
".docx": lambda path: DirectoryLoader(path, glob="**/*.docx"), ".docx": lambda path: DirectoryLoader(path, glob="**/*.docx"),
".hpp": lambda path: TextLoader(path, encoding="utf-8"), ".hpp": lambda path: TextLoader(path, encoding="utf-8"),
".cpp": lambda path: TextLoader(path, encoding="utf-8"), # добавить ".cpp": lambda path: TextLoader(path, encoding="utf-8"),
".h": lambda path: TextLoader(path, encoding="utf-8"), # добавить ".h": lambda path: TextLoader(path, encoding="utf-8"),
".cc": lambda path: TextLoader(path, encoding="utf-8"), # добавить ".cc": lambda path: TextLoader(path, encoding="utf-8"),
} }
all_documents = [] all_documents = []
for ext, loader_class in loaders.items(): # Рекурсивный обход с фильтрацией папок
for file_path in os.listdir(directory_path): for root, dirs, files in os.walk(directory_path):
if file_path.endswith(ext): # Фильтруем директории на месте
full_path = os.path.join(directory_path, file_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: try:
loader = loader_class(full_path) loader = loaders[ext](full_path)
documents = loader.load() documents = loader.load()
all_documents.extend(documents) all_documents.extend(documents)
print(f"Загружен {file_path}: {len(documents)} страниц") print(f"Загружен {file_path}: {len(documents)} документов")
except Exception as e: except Exception as e:
print(f"Ошибка загрузки {file_path}: {e}") print(f"Ошибка загрузки {file_path}: {e}")