File size: 5,670 Bytes
ac1079c
8edd424
cd75fd1
babb405
 
fecdf12
a8d757c
363cb8e
9088634
859a3f4
cd75fd1
2e4bd35
cd75fd1
2e4bd35
cd75fd1
 
ac1079c
2e4bd35
9088634
 
 
 
 
 
cd75fd1
2e4bd35
 
 
5d5a7f1
2e4bd35
a8d757c
2e4bd35
7eb9717
 
 
 
 
 
ac1079c
a8d757c
ac1079c
 
 
 
 
 
 
 
 
 
cd75fd1
 
7eb9717
 
 
 
 
 
 
cd75fd1
fdcff15
8edd424
2e4bd35
 
8edd424
03f014f
cd75fd1
 
 
 
 
073c315
 
cd75fd1
 
 
 
2e4bd35
 
cd75fd1
 
 
 
 
e3c118a
c0ffbeb
 
 
e3c118a
2e4bd35
babb405
 
16919b8
022791c
99a8b0b
ac1079c
 
2e4bd35
 
c0ffbeb
 
439c92a
c0ffbeb
 
 
 
 
 
ac1079c
 
 
 
 
 
 
 
 
 
 
 
2e4bd35
ac1079c
 
2e4bd35
 
ac1079c
2e4bd35
99a8b0b
 
 
2e4bd35
ac1079c
 
 
 
 
 
 
 
022791c
99a8b0b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import gradio as gr
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.documents import Document
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma
from langchain.vectorstores import FAISS
from langchain_community.llms import HuggingFaceHub
from langchain.prompts import ChatPromptTemplate
from dotenv import load_dotenv
import os
import shutil

# Загрузка переменных окружения
load_dotenv()

CHROMA_PATH = "/tmp/chroma"
DATA_PATH = ""  # Укажите путь к вашим данным, например "data", если файл не в корне
PROMPT_TEMPLATE = """
Ответь на вопрос, используя только следующий контекст:
{context}
---
Ответь на вопрос на основе приведенного контекста: {question}
"""

# Глобальная переменная для статуса
status_message = "Инициализация..."

def initialize_chroma():
    global status_message
    status_message = "Инициализация векторной базы..."
    else:
        status_message = "База данных Chroma уже существует. Пересоздаем базу данных..."
        shutil.rmtree(CHROMA_PATH)
        
    generate_data_store()
    status_message = "База данных Chroma создана и подготовлена."
    
    embeddings = HuggingFaceEmbeddings(
        model_name="sentence-transformers/paraphrase-multilingual-mpnet-base-v2",
        cache_folder="/tmp/model_cache",
        model_kwargs={'device': 'cpu'},
        encode_kwargs={'normalize_embeddings': True}
    )
    db = Chroma(
        persist_directory=CHROMA_PATH,
        embedding_function=embeddings
    )
    return db

def generate_data_store():
    documents = load_documents()
    if not documents:
        raise Exception("Документы не загружены!")
    chunks = split_text(documents)
    status_message = f"Первые 50 символов первого чанка: {chunks[0].page_content[:50]}"
    save_to_chroma(chunks)
    
    
def load_documents():
    file_path = os.path.join(DATA_PATH, "pl250320252.md")
    if not os.path.exists(file_path):
        global status_message
        status_message = f"Ошибка: Файл {file_path} не найден."
        return []
    loader = UnstructuredMarkdownLoader(file_path)
    documents = loader.load()
    return documents

def split_text(documents: list[Document]):
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=900,
        chunk_overlap=300,
        length_function=len,
        add_start_index=True,
    )
    chunks = text_splitter.split_documents(documents)
    global status_message
    status_message += f"\nРазделено {len(documents)} документов на {len(chunks)} частей."
    return chunks

def save_to_chroma(chunks: list[Document]):
    if os.path.exists(CHROMA_PATH):
        shutil.rmtree(CHROMA_PATH)
    embeddings = HuggingFaceEmbeddings(
        model_name="sentence-transformers/paraphrase-multilingual-mpnet-base-v2",
        model_kwargs={'device': 'cpu', 'trust_remote_code': True},
        encode_kwargs={'normalize_embeddings': False}  # Экспериментируйте с этим
    )
    Chroma.from_documents(
        chunks,
        embeddings,
        persist_directory=CHROMA_PATH
    )

def process_query(query_text: str, db):
    results = db.similarity_search_with_relevance_scores(query_text, k=3)
    global status_message
    status_message += f"\nНайдено {len(results)} результатов с релевантностью: {[round(score, 2) for _, score in results]}"
    
    if not results:
        return "Не найдено подходящих результатов.", []
    
    if results[0][1] < 0.5:
        response = "Нет высококачественных совпадений. Возможные варианты:\n"
        response += "\n".join([f"- {doc.page_content[:50]}..." for doc, _ in results])
        return response, [doc.metadata.get("source") for doc, _ in results]
    
    context_text = "\n\n---\n\n".join([doc.page_content for doc, _ in results])
    prompt_template = ChatPromptTemplate.from_template(PROMPT_TEMPLATE)
    prompt = prompt_template.format(context=context_text, question=query_text)
    model = HuggingFaceHub(
        repo_id="google/flan-t5-small",
        model_kwargs={"temperature": 0.5, "max_length": 512}
    )
    response_text = model.predict(prompt)
    sources = [doc.metadata.get("source", None) for doc, _ in results]
    return response_text, sources

def chat_interface(query_text):
    global status_message
    db = initialize_chroma()
    response, sources = process_query(query_text, db)
    full_response = f"{status_message}\n\nОтвет: {response}\n\nИсточники: {', '.join(sources) if sources else 'Нет источников'}"
    return full_response

# Создание папок
os.makedirs("/tmp/model_cache", exist_ok=True)
os.makedirs("/tmp/chroma", exist_ok=True)

# Интерфейс Gradio
interface = gr.Interface(
    fn=chat_interface,
    inputs=gr.Textbox(lines=2, placeholder="Введите ваш вопрос здесь..."),
    outputs="text",
    title="Чат с документами",
    description="Задайте вопрос, и я отвечу на основе загруженных документов."
)

if __name__ == "__main__":
    interface.launch()