|
import gradio as gr
|
|
import os
|
|
from datetime import datetime
|
|
from retriever import retriever, reload_retriever
|
|
from generator import answer_query
|
|
from langchain_community.document_loaders import PyPDFLoader, TextLoader, CSVLoader, UnstructuredWordDocumentLoader
|
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
|
from langchain_community.embeddings import HuggingFaceEmbeddings
|
|
from langchain_community.vectorstores import FAISS
|
|
|
|
|
|
def process_document(file):
|
|
file_path = file.name
|
|
|
|
|
|
if file_path.endswith(".pdf"):
|
|
loader = PyPDFLoader(file_path)
|
|
elif file_path.endswith(".csv"):
|
|
loader = CSVLoader(file_path)
|
|
elif file_path.endswith(".txt"):
|
|
loader = TextLoader(file_path)
|
|
elif file_path.endswith(".docx") or file_path.endswith(".doc"):
|
|
loader = UnstructuredWordDocumentLoader(file_path)
|
|
else:
|
|
return "Định dạng file không hỗ trợ!"
|
|
|
|
|
|
documents = loader.load()
|
|
|
|
|
|
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
|
|
docs = splitter.split_documents(documents)
|
|
|
|
if not docs:
|
|
return "Không trích xuất được nội dung từ file tải lên."
|
|
|
|
|
|
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
|
db = FAISS.from_documents(docs, embeddings)
|
|
|
|
db.save_local("vectorstore")
|
|
reload_retriever()
|
|
|
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
return f"Đã xử lý và thêm {len(docs)} đoạn tài liệu vào VectorStore lúc {timestamp}"
|
|
|
|
|
|
def query_function(question, model_choice, temperature, include_sources):
|
|
answer, sources = answer_query(question, model=model_choice, temperature=temperature)
|
|
|
|
if include_sources and sources:
|
|
sources_text = "\n\n**Nguồn tài liệu:**\n"
|
|
for i, doc in enumerate(sources):
|
|
sources_text += f"{i+1}. {doc.page_content}\n"
|
|
if hasattr(doc, 'metadata') and doc.metadata:
|
|
sources_text += f" - Nguồn: {doc.metadata.get('source', 'Unknown')}\n"
|
|
sources_text += f" - Trang: {doc.metadata.get('page', 'N/A')}\n"
|
|
result = answer + sources_text
|
|
else:
|
|
result = answer
|
|
result = result.encode('utf-8', errors='ignore').decode('utf-8')
|
|
return result
|
|
|
|
def clear_inputs():
|
|
return "", []
|
|
|
|
|
|
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
with gr.Row():
|
|
with gr.Column(scale=3):
|
|
gr.Markdown(
|
|
"""
|
|
# 🔎 RAGFlow Enterprise Search
|
|
### Công cụ tìm kiếm thông minh dựa trên RAG (Retrieval-Augmented Generation)
|
|
|
|
Hệ thống giúp truy xuất và trả lời câu hỏi từ tài liệu nội bộ doanh nghiệp.
|
|
"""
|
|
)
|
|
|
|
with gr.Tabs():
|
|
|
|
with gr.TabItem("Tìm kiếm 🔍"):
|
|
with gr.Row():
|
|
with gr.Column(scale=3):
|
|
question = gr.Textbox(
|
|
label="Nhập câu hỏi của bạn:",
|
|
placeholder="Ví dụ: Quy trình xin nghỉ phép nội bộ là gì?",
|
|
lines=2
|
|
)
|
|
with gr.Column(scale=1):
|
|
model_choice = gr.Dropdown(
|
|
label="Mô hình AI",
|
|
choices=["Gemini Pro", "GPT-3.5", "GPT-4", "Claude"],
|
|
value="Gemini Pro"
|
|
)
|
|
temperature = gr.Slider(
|
|
label="Temperature",
|
|
minimum=0.0,
|
|
maximum=1.0,
|
|
value=0.2,
|
|
step=0.1
|
|
)
|
|
include_sources = gr.Checkbox(
|
|
label="Hiển thị nguồn tài liệu",
|
|
value=True
|
|
)
|
|
|
|
search_button = gr.Button("🔍 Tìm kiếm", variant="primary")
|
|
clear_button = gr.Button("🗑️ Xóa")
|
|
output = gr.Textbox(
|
|
label="Kết quả tìm kiếm:",
|
|
lines=15,
|
|
interactive=False
|
|
)
|
|
|
|
search_button.click(
|
|
query_function,
|
|
inputs=[question, model_choice, temperature, include_sources],
|
|
outputs=output
|
|
)
|
|
question.submit(
|
|
query_function,
|
|
inputs=[question, model_choice, temperature, include_sources],
|
|
outputs=output
|
|
)
|
|
clear_button.click(clear_inputs, outputs=[question, output])
|
|
|
|
|
|
with gr.TabItem("📚 Quản lý tài liệu"):
|
|
with gr.Row():
|
|
with gr.Column():
|
|
upload_file = gr.File(
|
|
label="Tải lên tài liệu mới (PDF, Word, CSV, TXT)",
|
|
file_types=[".pdf", ".docx", ".doc", ".csv", ".txt"]
|
|
)
|
|
upload_button = gr.Button("📤 Tải lên và xử lý", variant="primary")
|
|
|
|
with gr.Column():
|
|
upload_status = gr.Textbox(
|
|
label="📄 Trạng thái:",
|
|
lines=3,
|
|
interactive=False
|
|
)
|
|
|
|
gr.Markdown("### 📊 Danh sách tài liệu đã xử lý")
|
|
upload_button.click(
|
|
process_document,
|
|
inputs=upload_file,
|
|
outputs=upload_status
|
|
)
|
|
|
|
|
|
with gr.TabItem("⚙️ Cài đặt hệ thống"):
|
|
gr.Markdown("### ⚙️ Cấu hình Vector Store & Embedding")
|
|
with gr.Row():
|
|
with gr.Column():
|
|
vector_store = gr.Dropdown(
|
|
label="Vector Store",
|
|
choices=["FAISS", "Pinecone", "Milvus"],
|
|
value="FAISS"
|
|
)
|
|
embedding_model = gr.Dropdown(
|
|
label="Embedding Model",
|
|
choices=["Sentence-Transformers", "OpenAI Embeddings", "Cohere Embeddings"],
|
|
value="Sentence-Transformers"
|
|
)
|
|
with gr.Column():
|
|
chunk_size = gr.Slider(
|
|
label="Chunk size (độ dài văn bản mỗi đoạn)",
|
|
minimum=100,
|
|
maximum=1000,
|
|
value=500,
|
|
step=50
|
|
)
|
|
chunk_overlap = gr.Slider(
|
|
label="Chunk overlap (chồng lấp giữa các đoạn)",
|
|
minimum=0,
|
|
maximum=200,
|
|
value=50,
|
|
step=10
|
|
)
|
|
|
|
save_settings = gr.Button("💾 Lưu cài đặt", variant="primary")
|
|
settings_status = gr.Textbox(
|
|
label="🗂️ Trạng thái:",
|
|
interactive=False
|
|
)
|
|
|
|
def save_system_settings(vector_store, embedding_model, chunk_size, chunk_overlap):
|
|
return f"✅ Đã lưu: VectorStore={vector_store}, Embedding={embedding_model}, ChunkSize={chunk_size}, Overlap={chunk_size}"
|
|
|
|
save_settings.click(
|
|
save_system_settings,
|
|
inputs=[vector_store, embedding_model, chunk_size, chunk_overlap],
|
|
outputs=settings_status
|
|
)
|
|
|
|
|
|
demo.launch()
|
|
|