File size: 14,244 Bytes
5e55e06
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
import os
import google.generativeai as genai
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.google_genai import GoogleGenAI
from llama_index.core import Settings
from llama_index.core.llms import ChatMessage, MessageRole
import os

EMBEDDING_MODEL = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
RETRIEVER_TOP_K = 10
RETRIEVER_SIMILARITY_CUTOFF = 0.7
RAG_FILES_DIR = "processed_data"
PROCESSED_DATA_FILE = "processed_data/processed_chunks.csv"

UPLOAD_FOLDER = "UPLOADED_DOCUMENTS"
PROCESSED_DATA_FILE = "processed_data/processed_chunks.csv"
INDEX_STATE_FILE = "processed_data/index_store.json"
RAG_FILES_DIR = "processed_data"

GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY', "AIzaSyDemsCp7JIdRNDRyP6DkYdMox1DLZwPcPE")
LLM_MODEL = "gemini-2.0-flash"

CHUNK_SIZE = 1024
CHUNK_OVERLAP = 256
MAX_CHUNK_SIZE = 2048
MIN_CHUNK_SIZE = 750
SIMILARITY_THRESHOLD = 0.7

RETRIEVER_TOP_K = 15
RETRIEVER_SIMILARITY_CUTOFF = 0.7

def setup_llm_settings():
    # Set embedding model first
    Settings.embed_model = HuggingFaceEmbedding(model_name=EMBEDDING_MODEL)
    
    # Only set LLM if API key is available
    if GOOGLE_API_KEY:
        try:
            llm = GoogleGenAI(model=LLM_MODEL, api_key=GOOGLE_API_KEY)
            Settings.llm = llm
        except Exception as e:
            print(f"Warning: Could not initialize Google GenAI LLM: {e}")
            Settings.llm = None
    else:
        print("Warning: GOOGLE_API_KEY not found. Setting LLM to None.")
        Settings.llm = None

# Intent Classification Prompt for preprocessing
INTENT_CLASSIFIER_PROMPT = """Проанализируй запрос пользователя и определи, к какой из следующих категорий он относится:

1. SUMMARY - Запрос на краткое изложение, саммари пункта, раздела или документа
   Примеры: "кратко расскажи о разделе 5", "саммари главы про безопасность", "что говорится в пункте 3.2"

2. SEARCH - Поиск конкретной информации, документа или пункта
   Примеры: "где найти информацию о...", "какой документ регулирует...", "в каком пункте говорится про..."

3. VALIDATION - Проверка корректности данных, соответствия требованиям
   Примеры: "правильно ли...", "соответствует ли требованиям...", "проверь корректность данных..."

4. ACTION_PLAN - Составление плана действий на основе документации
   Примеры: "составь план...", "какие шаги нужно предпринять...", "алгоритм действий для..."

Запрос пользователя: {user_query}

Ответь ТОЛЬКО одним словом: SUMMARY, SEARCH, VALIDATION или ACTION_PLAN"""

# Task-specific prompts
SUMMARY_PROMPT = """Ты специализированный помощник по анализу нормативной документации (НД) для создания кратких изложений и саммари.

Твоя задача: Предоставить краткое, структурированное изложение запрашиваемой информации из нормативных документов.

Правила создания саммари:
1. Обязательное указание источника: Всегда указывай источник информации в формате:
   - Для конкретного раздела/подраздела: Согласно разделу [X] и подразделу [X.X]: [Саммари]
   - Для общих пунктов: Согласно [Название документа] - [Номер и наименование пункта]: [Саммари]

2. Структурированность: Организуй информацию логично:
   - Основные положения
   - Ключевые требования
   - Важные исключения или особенности

3. Краткость и полнота: Включи все существенные моменты, но избегай избыточных деталей

4. Сохранение терминологии НД: Используй точную терминологию из документов

5. Если информация отсутствует: Информация для создания саммари по вашему запросу не найдена в нормативной документации.

Контекст: {context_str}

Запрос: {query_str}

Саммари:"""

SEARCH_PROMPT = """Ты специализированный помощник по поиску информации в нормативной документации (НД).

Твоя задача: Найти и предоставить точную информацию, документы или пункты согласно запросу пользователя.

Правила поиска информации:
1. Точное указание источника: Обязательно укажи точное местоположение информации:
   - Для конкретного раздела/подраздела: Согласно разделу [X] и подразделу [X.X]: [Найденная информация]
   - Для общих пунктов: Согласно [Название документа] - [Номер и наименование пункта]: [Найденная информация]

2. Релевантность: Предоставляй именно ту информацию, которая отвечает на запрос

3. Полнота ответа: Если найдено несколько релевантных источников, укажи все

4. Точность цитирования: Используй формулировки непосредственно из НД

5. Если информация не найдена: Информация по вашему запросу не была найдена в нормативной документации.

Контекст: {context_str}

Поисковый запрос: {query_str}

Результат поиска:"""

VALIDATION_PROMPT = """Ты специализированный помощник по проверке соответствия требованиям нормативной документации (НД).

Твоя задача: Проанализировать предоставленные данные или информацию на соответствие требованиям НД и дать заключение о корректности.

Правила проведения проверки:
1. Источник требований: Четко укажи, согласно каким пунктам НД проводится проверка:
   - Согласно разделу [X] и подразделу [X.X]: [Требование]
   - Согласно [Название документа] - [Номер и наименование пункта]: [Требование]

2. Структура заключения:
   - СООТВЕТСТВУЕТ/НЕ СООТВЕТСТВУЕТ требованиям
   - Обоснование заключения со ссылками на конкретные пункты НД
   - При несоответствии - указание на конкретные нарушения
   - Рекомендации по устранению несоответствий (если применимо)

3. Объективность: Основывайся исключительно на требованиях НД, без субъективных оценок

4. Если требования не найдены: Требования для проверки данной информации не найдены в нормативной документации.

Контекст: {context_str}

Запрос на проверку: {query_str}

Заключение о соответствии:"""

ACTION_PLAN_PROMPT = """Ты специализированный помощник по составлению планов действий на основе нормативной документации (НД).

Твоя задача: Создать пошаговый план действий, основанный на требованиях и процедурах, описанных в НД.

Правила составления плана действий:
1. Источник каждого шага: Для каждого шага указывай источник в НД:
   - Согласно разделу [X] и подразделу [X.X]: Шаг [N]
   - Согласно [Название документа] - [Номер и наименование пункта]: Шаг [N]

2. Структура плана:
   - Нумерованные шаги в логической последовательности
   - Четкие действия для каждого шага
   - Указание ответственных лиц/органов (если указано в НД)
   - Временные рамки (если указаны в НД)
   - Необходимые документы/материалы

3. Практичность: План должен быть выполнимым и соответствовать реальным процедурам

4. Полнота: Включи все обязательные этапы согласно НД

5. Если информация недостаточна: Недостаточно информации в нормативной документации для составления полного плана действий по вашему запросу.

Контекст: {context_str}

Запрос на план: {query_str}

План действий:"""

# Dictionary mapping intents to prompts
TASK_PROMPTS = {
    "SUMMARY": SUMMARY_PROMPT,
    "SEARCH": SEARCH_PROMPT,
    "VALIDATION": VALIDATION_PROMPT,
    "ACTION_PLAN": ACTION_PLAN_PROMPT
}

LLM_MODEL_PREPROCESS = "gemini-1.5-flash"

def classify_intent(user_query, llm=None):
    """Classify user intent to determine appropriate prompt"""
    if not llm:
        llm = GoogleGenAI(model=LLM_MODEL_PREPROCESS, temperature=0.1)
    
    try:
        intent_prompt = INTENT_CLASSIFIER_PROMPT.format(user_query=user_query)
        messages = [ChatMessage(role=MessageRole.USER, content=intent_prompt)]
        response = llm.chat(messages)
        intent = response.message.content.strip()
        
        # Validate intent and return default if invalid
        if intent in TASK_PROMPTS:
            return intent
        else:
            print(f"Unknown intent: {intent}, defaulting to SEARCH")
            return "SEARCH"
    except Exception as e:
        print(f"Intent classification failed: {e}, defaulting to SEARCH")
        return "SEARCH"

def get_task_specific_prompt(intent, context_str, query_str):
    """Get the appropriate prompt template based on intent"""
    prompt_template = TASK_PROMPTS.get(intent, SEARCH_PROMPT)
    return prompt_template.format(context_str=context_str, query_str=query_str)

def preprocess_query_with_context(user_query, chat_history=None, llm=None):
    if not chat_history:
        return user_query
        
    if not llm:
        llm = GoogleGenAI(model=LLM_MODEL_PREPROCESS, temperature=0.1)
    
    # Format chat history into a string for the prompt
    history_context = "\n".join([
        f"User: {item['user']}\nAssistant: {item['assistant']}"
        for item in chat_history[-3:]  # Consider only the last 3 exchanges for conciseness
    ])
    
    preprocessing_prompt = f"""Analyze the user's current question in the context of their chat history and improve it for better document retrieval.

Chat History:
{history_context}

Current Question: {user_query}

Tasks:
1. If the question refers to previous context, make it self-contained.
2. Add relevant keywords that would help find documents.
3. Maintain the legal/regulatory focus.
4. Keep it concise but specific.

Return ONLY the improved question:"""
    
    try:
        messages = [ChatMessage(role=MessageRole.USER, content=preprocessing_prompt)]
        response = llm.chat(messages)
        improved_query = response.message.content.strip()
        
        # Fallback to the original query if the preprocessing fails or provides an overly long response
        if len(improved_query) > len(user_query) * 3 or not improved_query:
            return user_query
            
        return improved_query
    except Exception as e:
        print(f"Query preprocessing failed: {e}")
        return user_query

def create_chat_context_prompt(base_response, chat_history=None):
    if not chat_history:
        return base_response
    
    base_aware_response = base_response
    
    if len(chat_history) > 0:
        last_exchange = chat_history[-1]
        if any(keyword in last_exchange['user'].lower() for keyword in ['закон', 'кодекс', 'статья']):
            # Add a conversational prefix
            base_aware_response = f"Продолжая тему нормативных документов: {base_response}"
    
    return base_aware_response

# Example usage function
def process_user_query(user_query, context_str, chat_history=None):
    # Step 1: Classify intent
    intent = classify_intent(user_query)
    print(f"Detected intent: {intent}")
    
    # Step 2: Preprocess query if needed
    processed_query = preprocess_query_with_context(user_query, chat_history)
    
    # Step 3: Get appropriate prompt
    task_prompt = get_task_specific_prompt(intent, context_str, processed_query)
    
    return task_prompt, intent