Spaces:
Sleeping
Sleeping
Commit
·
65c4610
1
Parent(s):
df86177
Added improve_query_with_history() function + Added automatic document reference at the end of each response
Browse files
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 |
-
|
| 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>"
|