MrSimple07 commited on
Commit
65c4610
·
1 Parent(s): df86177

Added improve_query_with_history() function + Added automatic document reference at the end of each response

Browse files
Files changed (1) hide show
  1. app.py +161 -71
app.py CHANGED
@@ -26,7 +26,7 @@ GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
26
  CUSTOM_PROMPT_NEW = """
27
  Вы являетесь высокоспециализированным Ассистентом для анализа документов (AIEXP). Ваша цель - предоставлять точные, корректные и контекстно релевантные ответы на основе анализа нормативной документации (НД). Все ваши ответы должны основываться исключительно на предоставленном контексте без использования внешних знаний или предположений.
28
 
29
- ВАЖНО: ВСЕ ОТВЕТЫ ДОЛЖНЫ БЫТЬ ТОЛЬКО НА РУССКОМ ЯЗЫКЕ!
30
 
31
  История чата:
32
  {chat_history}
@@ -60,20 +60,23 @@ CUSTOM_PROMPT_NEW = """
60
  - Добавьте временные рамки, если они указаны в НД
61
  - Выделите критические требования или ограничения
62
 
 
 
 
 
 
 
63
  ПРАВИЛА ФОРМИРОВАНИЯ ОТВЕТОВ:
64
 
65
  1. ОБЯЗАТЕЛЬНОЕ УКАЗАНИЕ ИСТОЧНИКОВ:
66
- - Для контента из конкретного раздела/подраздела:
67
- "Согласно разделу [X] и подразделу [X.X]: [Ваш ответ]"
68
- - Для контента вне подразделов (таблицы, рисунки, общие разделы):
69
- "Согласно [Название документа] - [Номер и наименование пункта/таблицы/рисунка]: [Ваш ответ]"
70
- - При наличии метаданных о разделе и подразделе - включайте оба
71
- - При наличии только раздела: "Согласно разделу [X]: [Ваш ответ]"
72
 
73
  2. СТРОГОЕ СЛЕДОВАНИЕ КОНТЕКСТУ:
74
  - Если информация не найдена: "Информация по вашему запросу не была найдена в нормативной документации."
75
- - Не делайте предположений или выводов за пределами предоставленного контекста
76
- - Не используйте общие знания
77
 
78
  3. ИСПОЛЬЗОВАНИЕ ТЕРМИНОЛОГИИ НД:
79
  - Применяйте официальную терминологию из документов
@@ -81,23 +84,18 @@ CUSTOM_PROMPT_NEW = """
81
  - При необходимости разъясняйте специальные термины на основе НД
82
 
83
  4. СТРУКТУРИРОВАНИЕ ОТВЕТОВ:
84
- - Для саммари: используйте маркированные или нумерованные списки
85
- - Для проверки: четкая структура "Требование → Соответствие/Несоответствие"
86
- - Для планов: пронумерованные шаги с подзадачами при необходимости
87
- - Для поиска: указание иерархии документа
88
-
89
- 5. ДОПОЛНИТЕЛЬНЫЕ РЕКОМЕНДАЦИИ:
90
- - При множественных релевантных источниках - укажите все
91
- - Выделяйте критически важные требования
92
- - Указывайте альтернативные процедуры, если они предусмотрены НД
93
 
94
  Контекст: {context_str}
95
 
96
  Вопрос: {query_str}
97
 
98
- Ответ:
99
  """
100
 
 
101
  query_engine = None
102
  chunks_df = None
103
  chat_history = []
@@ -195,6 +193,48 @@ def download_table_data():
195
  log_message(f"❌ Ошибка загрузки табличных данных: {str(e)}")
196
  return []
197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  def format_chat_history():
199
  if not chat_history:
200
  return "История чата пуста."
@@ -205,6 +245,108 @@ def format_chat_history():
205
 
206
  return history_text
207
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  def initialize_models():
209
  global query_engine, chunks_df
210
 
@@ -296,58 +438,6 @@ def initialize_models():
296
  log_message(f"❌ Ошибка инициализации: {str(e)}")
297
  return False
298
 
299
- def answer_question(question, history):
300
- global query_engine, chunks_df, chat_history
301
-
302
- if query_engine is None:
303
- return history + [["", "❌ Система не инициализирована"]], ""
304
-
305
- try:
306
- start_time = time.time()
307
-
308
- log_message(f"🔍 Получен вопрос: {question}")
309
-
310
- chat_history_text = format_chat_history()
311
- log_message(f"📜 История чата: {len(chat_history)} сообщений")
312
-
313
- query_with_history = question
314
-
315
- log_message("🔎 Поиск релевантных чанков...")
316
- retrieved_nodes = query_engine.retriever.retrieve(query_with_history)
317
- log_message(f"📊 Найдено {len(retrieved_nodes)} релевантных чанков")
318
-
319
- for i, node in enumerate(retrieved_nodes[:3]):
320
- log_message(f"📄 Чанк {i+1}: {node.text[:100]}...")
321
- log_message(f"🏷️ Метаданные: {node.metadata}")
322
-
323
- log_message("🤖 Отправка запроса в LLM...")
324
- response = query_engine.query(query_with_history)
325
-
326
- end_time = time.time()
327
- processing_time = end_time - start_time
328
-
329
- bot_response = response.response
330
- log_message(f"✅ Получен ответ: {bot_response[:100]}...")
331
-
332
- chat_history.append((question, bot_response))
333
-
334
- if len(chat_history) > 10:
335
- chat_history = chat_history[-10:]
336
-
337
- sources_html = generate_sources_html(retrieved_nodes)
338
-
339
- response_with_time = f"{bot_response}\n\n⏱️ Время обработки: {processing_time:.2f} сек"
340
-
341
- history.append([question, response_with_time])
342
-
343
- return history, sources_html
344
-
345
- except Exception as e:
346
- error_msg = f"❌ Ошибка обработки вопроса: {str(e)}"
347
- log_message(f"❌ Ошибка: {str(e)}")
348
- history.append([question, error_msg])
349
- return history, ""
350
-
351
  def generate_sources_html(nodes):
352
  html = "<div style='background-color: #2d3748; color: white; padding: 20px; border-radius: 10px; max-height: 400px; overflow-y: auto;'>"
353
  html += "<h3 style='color: #63b3ed; margin-top: 0;'>📚 Источники:</h3>"
 
26
  CUSTOM_PROMPT_NEW = """
27
  Вы являетесь высокоспециализированным Ассистентом для анализа документов (AIEXP). Ваша цель - предоставлять точные, корректные и контекстно релевантные ответы на основе анализа нормативной документации (НД). Все ваши ответы должны основываться исключительно на предоставленном контексте без использования внешних знаний или предположений.
28
 
29
+ КРИТИЧЕСКИ ВАЖНО: ВСЕ ОТВЕТЫ ДОЛЖНЫ БЫТЬ ТОЛЬКО НА РУССКОМ ЯЗЫКЕ! НИКОГДА НЕ ОТВЕЧАЙТЕ НА АНГЛИЙСКОМ!
30
 
31
  История чата:
32
  {chat_history}
 
60
  - Добавьте временные рамки, если они указаны в НД
61
  - Выделите критические требования или ограничения
62
 
63
+ 5. УТОЧНЯЮЩИЕ ВОПРОСЫ (ключевые слова: "что это значит", "что означает", "объясните", "расскажите подробнее"):
64
+ - Используйте историю чата для понимания контекста
65
+ - Если вопрос относится к предыдущему обсуждению, опирайтесь на него
66
+ - Предоставьте подробное объяснение на основе НД
67
+ - Если контекст неясен, попросите уточнения
68
+
69
  ПРАВИЛА ФОРМИРОВАНИЯ ОТВЕТОВ:
70
 
71
  1. ОБЯЗАТЕЛЬНОЕ УКАЗАНИЕ ИСТОЧНИКОВ:
72
+ - Для каждого ответа указывайте: "Согласно [Название документа], раздел [X], пункт [X.X]: [Ваш ответ]"
73
+ - В конце ответа добавляйте: "Подробнее об этом можно узнать в документе [Название документа], раздел [X]."
74
+ - При отсутствии точного раздела: "Согласно документу [Название]: [Ваш ответ]"
 
 
 
75
 
76
  2. СТРОГОЕ СЛЕДОВАНИЕ КОНТЕКСТУ:
77
  - Если информация не найдена: "Информация по вашему запросу не была найдена в нормативной документации."
78
+ - НЕ используйте английский язык ни при каких обстоятельствах
79
+ - Используйте историю чата для понимания контекста вопросов
80
 
81
  3. ИСПОЛЬЗОВАНИЕ ТЕРМИНОЛОГИИ НД:
82
  - Применяйте официальную терминологию из документов
 
84
  - При необходимости разъясняйте специальные термины на основе НД
85
 
86
  4. СТРУКТУРИРОВАНИЕ ОТВЕТОВ:
87
+ - Основной ответ на русском языке
88
+ - Указание источника
89
+ - Дополнительная информация о документе
 
 
 
 
 
 
90
 
91
  Контекст: {context_str}
92
 
93
  Вопрос: {query_str}
94
 
95
+ Ответ (ТОЛЬКО НА РУССКОМ ЯЗЫКЕ):
96
  """
97
 
98
+
99
  query_engine = None
100
  chunks_df = None
101
  chat_history = []
 
193
  log_message(f"❌ Ошибка загрузки табличных данных: {str(e)}")
194
  return []
195
 
196
+
197
+ def improve_query_with_history(question, chat_history_list):
198
+ """Улучшает запрос с учетом истории чата"""
199
+ try:
200
+ log_message("🔄 Улучшение запроса с учетом истории...")
201
+
202
+ if not chat_history_list:
203
+ log_message("📝 История чата пуста, используем оригинальный запрос")
204
+ return question
205
+
206
+ history_context = ""
207
+ for i, (user_msg, bot_msg) in enumerate(chat_history_list[-3:], 1):
208
+ history_context += f"Сообщение {i}:\nПользователь: {user_msg}\nАссистент: {bot_msg}\n\n"
209
+
210
+ improvement_prompt = f"""
211
+ Ты помощник для улучшения поисковых запросов. Проанализируй историю чата и текущий вопрос пользователя.
212
+
213
+ История чата:
214
+ {history_context}
215
+
216
+ Текущий вопрос: {question}
217
+
218
+ Если текущий вопрос неполный или ссылается на что-то из истории (например, "что это значит?", "а это что?", "объясните это"),
219
+ то создай улучшенный запрос, который включает контекст из истории.
220
+
221
+ Если вопрос самодостаточный, верни его без изменений.
222
+
223
+ Улучшенный запрос:"""
224
+
225
+ from llama_index.llms.google_genai import GoogleGenAI
226
+ llm = GoogleGenAI(model="gemini-2.0-flash", api_key=GOOGLE_API_KEY)
227
+
228
+ improved_query = llm.complete(improvement_prompt).text.strip()
229
+ log_message(f"✨ Улучшенный запрос: {improved_query}")
230
+
231
+ return improved_query
232
+
233
+ except Exception as e:
234
+ log_message(f"❌ Ошибка улучшения запроса: {str(e)}")
235
+ return question
236
+
237
+
238
  def format_chat_history():
239
  if not chat_history:
240
  return "История чата пуста."
 
245
 
246
  return history_text
247
 
248
+
249
+ def answer_question(question, history):
250
+ global query_engine, chunks_df, chat_history
251
+
252
+ if query_engine is None:
253
+ return history + [["", "❌ Система не инициализирована"]], ""
254
+
255
+ try:
256
+ start_time = time.time()
257
+
258
+ log_message(f"🔍 Получен вопрос: {question}")
259
+ log_message(f"📜 История чата: {len(chat_history)} сообщений")
260
+
261
+ # Улучшаем запрос с учетом истории
262
+ improved_question = improve_query_with_history(question, chat_history)
263
+ log_message(f"🎯 Обработка улучшенного запроса: {improved_question}")
264
+
265
+ # Форматируем историю чата для промпта
266
+ chat_history_text = format_chat_history()
267
+ log_message(f"📝 Сформированная история для промпта: {len(chat_history_text)} символов")
268
+
269
+ log_message("🔎 Поиск релевантных чанков...")
270
+ retrieved_nodes = query_engine.retriever.retrieve(improved_question)
271
+ log_message(f"📊 Найдено {len(retrieved_nodes)} релевантных чанков")
272
+
273
+ # Логируем найденные чанки
274
+ for i, node in enumerate(retrieved_nodes[:3]):
275
+ log_message(f"📄 Чанк {i+1}: {node.text[:100]}...")
276
+ log_message(f"🏷️ Метаданные: {node.metadata}")
277
+
278
+ log_message("🤖 Отправка запроса в LLM...")
279
+
280
+ # Создаем контекст с историей чата
281
+ query_with_context = f"""
282
+ История чата:
283
+ {chat_history_text}
284
+
285
+ Текущий вопрос: {question}
286
+ """
287
+
288
+ response = query_engine.query(query_with_context)
289
+
290
+ end_time = time.time()
291
+ processing_time = end_time - start_time
292
+
293
+ bot_response = response.response
294
+ log_message(f"✅ Получен ответ: {bot_response[:100]}...")
295
+
296
+ # Проверяем, что ответ на русском языке
297
+ if any(english_word in bot_response.lower() for english_word in ['i am sorry', 'i cannot', 'the query', 'this request']):
298
+ log_message("⚠️ Обнаружен ответ на английском языке, форсируем русский ответ")
299
+
300
+ # Принудительно запрашиваем ответ на русском
301
+ russian_prompt = f"""
302
+ ВАЖНО: Отвечай ТОЛЬКО на русском языке!
303
+
304
+ Вопрос: {question}
305
+ История: {chat_history_text}
306
+ Контекст: {retrieved_nodes[0].text if retrieved_nodes else 'Нет контекста'}
307
+
308
+ Если информации недостаточно для ответа, скажи: "Недостаточно информации для ответа на ваш вопрос в предоставленной документации."
309
+
310
+ Ответ на русском языке:
311
+ """
312
+
313
+ from llama_index.llms.google_genai import GoogleGenAI
314
+ llm = GoogleGenAI(model="gemini-2.0-flash", api_key=GOOGLE_API_KEY)
315
+ bot_response = llm.complete(russian_prompt).text.strip()
316
+ log_message(f"🔄 Исправленный ответ на русском: {bot_response[:100]}...")
317
+
318
+ # Добавляем информацию о документе если есть метаданные
319
+ if retrieved_nodes and hasattr(retrieved_nodes[0], 'metadata'):
320
+ metadata = retrieved_nodes[0].metadata
321
+ document_id = metadata.get('document_id', '')
322
+ if document_id and document_id != 'unknown':
323
+ if not bot_response.endswith('.'):
324
+ bot_response += '.'
325
+ bot_response += f"\n\nПодробнее об этом можно узнать в документе {document_id}."
326
+
327
+ # Обновляем историю чата
328
+ chat_history.append((question, bot_response))
329
+
330
+ if len(chat_history) > 10:
331
+ chat_history = chat_history[-10:]
332
+
333
+ log_message(f"💾 История чата обновлена. Всего сообщений: {len(chat_history)}")
334
+
335
+ sources_html = generate_sources_html(retrieved_nodes)
336
+
337
+ response_with_time = f"{bot_response}\n\n⏱️ Время обработки: {processing_time:.2f} сек"
338
+
339
+ history.append([question, response_with_time])
340
+
341
+ return history, sources_html
342
+
343
+ except Exception as e:
344
+ error_msg = f"❌ Ошибка обработки вопроса: {str(e)}"
345
+ log_message(f"❌ Ошибка: {str(e)}")
346
+ history.append([question, error_msg])
347
+ return history, ""
348
+
349
+
350
  def initialize_models():
351
  global query_engine, chunks_df
352
 
 
438
  log_message(f"❌ Ошибка инициализации: {str(e)}")
439
  return False
440
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  def generate_sources_html(nodes):
442
  html = "<div style='background-color: #2d3748; color: white; padding: 20px; border-radius: 10px; max-height: 400px; overflow-y: auto;'>"
443
  html += "<h3 style='color: #63b3ed; margin-top: 0;'>📚 Источники:</h3>"