KennyOry commited on
Commit
b25aae4
·
verified ·
1 Parent(s): 49bab01

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +33 -69
app.py CHANGED
@@ -26,7 +26,6 @@ MAX_CONTENT_LENGTH = 10000 # Максимальная длина контент
26
  # Новый клиент Mistral
27
  mistral_client = Mistral(api_key=os.getenv("MISTRAL_API_KEY"))
28
 
29
-
30
  SYSTEM_PROMPT = """
31
  Ты PrintMaster, сервисный инженер по печатной технике. Критически важные правила:
32
  1. Формат ответа СТРОГО:
@@ -40,10 +39,16 @@ SYSTEM_PROMPT = """
40
  **Примечания:**
41
  - Пункт 1
42
  - Пункт 2
 
 
 
 
43
  ЖЕСТКИЕ ЗАПРЕТЫ:
44
  - Никогда не используй подзаголовки с ###
45
  - Никогда не добавляй разделы "Удалены шаги" или подобные
 
46
  - Начинай сразу с **Проблема:** без преамбул
 
47
  """
48
 
49
  BLACKLISTED_DOMAINS = [
@@ -204,7 +209,6 @@ def web_search(query: str) -> tuple:
204
  response.raise_for_status()
205
  data = response.json()
206
 
207
- combined_content = ""
208
  sources = []
209
  full_contents = []
210
 
@@ -213,7 +217,6 @@ def web_search(query: str) -> tuple:
213
  if featured_snippet:
214
  snippet = featured_snippet.get("snippet", "")
215
  if snippet:
216
- combined_content += f"[Автоответ Google]\n{snippet}\n\n"
217
  sources.insert(0, {
218
  "title": "Google — автоматический ответ",
219
  "url": f"https://www.google.com/search?q={requests.utils.quote(query)}",
@@ -222,7 +225,7 @@ def web_search(query: str) -> tuple:
222
 
223
  # Обработка organic results
224
  organic_results = data.get("organic_results", [])
225
- for i, res in enumerate(organic_results[:5]): # Ограничиваемся топ-5
226
  title = res.get("title", "Без заголовка")
227
  link = res.get("link", "#")
228
  snippet = res.get("snippet", "") or ""
@@ -245,7 +248,6 @@ def web_search(query: str) -> tuple:
245
 
246
  # Форматирование контента
247
  cleaned_content = re.sub(r'\s+', ' ', content).strip()
248
- combined_content += f"[[Источник {i+1}]] {title}\n{cleaned_content}\n\n"
249
 
250
  # Сохранение источника
251
  source_data = {
@@ -258,15 +260,15 @@ def web_search(query: str) -> tuple:
258
 
259
  elapsed = time.time() - start_time
260
  message_queue.put(('log', f"✅ Поиск был произведен за {elapsed:.2f}с. Найдено {len(sources)} источников."))
261
- return combined_content[:20000], sources
262
 
263
  except Exception as e:
264
  error_msg = f"❌ SerpAPI ошибка: {str(e)}"
265
  message_queue.put(('log', error_msg))
266
- return f"Поиск недоступен: {str(e)}", []
267
 
268
 
269
- def clean_response(response: str, sources: list) -> str:
270
  # Удаление служебных тегов
271
  response = re.sub(r'</?assistant>|<\|system\|>|</s>', '', response, flags=re.IGNORECASE)
272
 
@@ -298,52 +300,6 @@ def clean_response(response: str, sources: list) -> str:
298
 
299
  return response.strip()
300
 
301
- def verify_with_sources(response: str, sources: list) -> str:
302
- """Проверяет соответствие ответа источникам с помощью LLM"""
303
- try:
304
- message_queue.put(('log', "🔍 Проверяю соответствие ответа источникам..."))
305
-
306
- sources_text = "\n\n".join([
307
- f"Источник {i+1} ({source['title']}):\n{source['content'][:1500]}"
308
- for i, source in enumerate(sources)
309
- ])
310
-
311
- verification_prompt = f"""
312
- Проверь соответствие решения источникам:
313
-
314
- ### Ответ бота:
315
- {response}
316
-
317
- ### Источники:
318
- {sources_text}
319
-
320
- Правила проверки:
321
- 1. Все шаги решения должны иметь подтверждение в источниках
322
- 2. Детали замены должны точно соответствовать артикулам из источников
323
- 3. Если в ответе есть шаги не из источников - удали их
324
- 4. Если есть противоречия между источниками - укажи это в решении
325
- 5. Если ошибки в кодах ошибок - исправь
326
- 6. Сохрани оригинальную структуру ответа
327
-
328
- Верни исправленный ответ.
329
- """
330
-
331
- verification = mistral_client.chat.complete(
332
- model=MISTRAL_MODEL,
333
- messages=[{"role": "user", "content": verification_prompt}],
334
- max_tokens=2048,
335
- temperature=0.1
336
- )
337
-
338
- verified_response = verification.choices[0].message.content
339
- return verified_response.strip()
340
-
341
- except Exception as e:
342
- error_msg = f"❌ Ошибка верификации: {str(e)}"
343
- message_queue.put(('log', error_msg))
344
- return response
345
-
346
-
347
  def process_query(prompt: str):
348
  try:
349
  start_time = time.time()
@@ -354,16 +310,23 @@ def process_query(prompt: str):
354
  message_queue.put(('log', f"⏏️ Извлечено: {json.dumps(norm_data, ensure_ascii=False)}"))
355
 
356
  search_query = norm_data['search_query']
357
- search_data, sources = web_search(search_query)
358
 
359
- message_queue.put(('log', f"📚 Собрано: {len(search_data)} символов в {len(sources)} источнике(-ах)"))
 
 
 
 
 
 
 
360
 
361
  message_queue.put(('log', f"⚙️ Определяю проблему"))
362
  problem_response = mistral_client.chat.complete(
363
  model=MISTRAL_MODEL,
364
  messages=[
365
  {"role": "system", "content": "Опиши СУТЬ проблемы в одном предложении. Только диагноз, без решений. Не более 12 слов. На русском."},
366
- {"role": "user", "content": f"Запрос пользователя: {prompt}\nПоисковые данные:\n{search_data}"}
367
  ],
368
  max_tokens=150,
369
  temperature=0.2
@@ -375,22 +338,21 @@ def process_query(prompt: str):
375
 
376
  message_queue.put(('log', f"🧩 Определённая проблема: {extracted_problem}"))
377
 
378
- sources_text = "\n".join([f"[{i+1}] {s['title']} - {s['url']}" for i, s in enumerate(sources)])
379
-
380
  messages = [
381
  {"role": "system", "content": SYSTEM_PROMPT + f"""
382
  Контекст:
383
  Бренд: {norm_data['brand']}
384
  Модель: {norm_data['model']}
385
  Ошибка: {norm_data['error_code']}
386
- Суть проблемы (на основе поиска): {extracted_problem}
387
- Данные поиска:
388
- {search_data}
389
  """},
390
  {"role": "user", "content": f"Проблема: {prompt}"}
391
  ]
392
 
393
- message_queue.put(('log', "🧠 На основе полученных данных генерирую ответ..."))
394
  message_queue.put(('response_start', ""))
395
 
396
  full_response = ""
@@ -405,18 +367,20 @@ def process_query(prompt: str):
405
  full_response += chunk_text
406
  message_queue.put(('response_chunk', chunk_text))
407
 
408
- # Проверка соответствия источникам
409
- verified_response = verify_with_sources(full_response, sources)
410
-
411
  # Очистка и форматирование ответа
412
- final_response = clean_response(verified_response, sources)
 
 
 
 
 
 
413
 
414
  message_queue.put(('response_end', final_response))
415
  message_queue.put(('sources', json.dumps(sources)))
416
 
417
  total_time = time.time() - start_time
418
- message_queue.put(('log', f"💡 Ответ: {final_response[:200]}..."))
419
- message_queue.put(('log', f"⏱ Время: {total_time:.1f}с"))
420
  message_queue.put(('done', ''))
421
 
422
  except Exception as e:
 
26
  # Новый клиент Mistral
27
  mistral_client = Mistral(api_key=os.getenv("MISTRAL_API_KEY"))
28
 
 
29
  SYSTEM_PROMPT = """
30
  Ты PrintMaster, сервисный инженер по печатной технике. Критически важные правила:
31
  1. Формат ответа СТРОГО:
 
39
  **Примечания:**
40
  - Пункт 1
41
  - Пункт 2
42
+ 4. Источники ТОЛЬКО в конце:
43
+ **Источники информации:**
44
+ - [Краткое описание источника 1]
45
+ - [Краткое описание источника 2]
46
  ЖЕСТКИЕ ЗАПРЕТЫ:
47
  - Никогда не используй подзаголовки с ###
48
  - Никогда не добавляй разделы "Удалены шаги" или подобные
49
+ - Никогда не используй ссылки в формате [[Источник 1]]
50
  - Начинай сразу с **Проблема:** без преамбул
51
+ - Всегда основывай решение ТОЛЬКО на предоставленных источниках
52
  """
53
 
54
  BLACKLISTED_DOMAINS = [
 
209
  response.raise_for_status()
210
  data = response.json()
211
 
 
212
  sources = []
213
  full_contents = []
214
 
 
217
  if featured_snippet:
218
  snippet = featured_snippet.get("snippet", "")
219
  if snippet:
 
220
  sources.insert(0, {
221
  "title": "Google — автоматический ответ",
222
  "url": f"https://www.google.com/search?q={requests.utils.quote(query)}",
 
225
 
226
  # Обработка organic results
227
  organic_results = data.get("organic_results", [])
228
+ for i, res in enumerate(organic_results[:MAX_RESULTS]): # Ограничиваемся топ-результатами
229
  title = res.get("title", "Без заголовка")
230
  link = res.get("link", "#")
231
  snippet = res.get("snippet", "") or ""
 
248
 
249
  # Форматирование контента
250
  cleaned_content = re.sub(r'\s+', ' ', content).strip()
 
251
 
252
  # Сохранение источника
253
  source_data = {
 
260
 
261
  elapsed = time.time() - start_time
262
  message_queue.put(('log', f"✅ Поиск был произведен за {elapsed:.2f}с. Найдено {len(sources)} источников."))
263
+ return sources
264
 
265
  except Exception as e:
266
  error_msg = f"❌ SerpAPI ошибка: {str(e)}"
267
  message_queue.put(('log', error_msg))
268
+ return []
269
 
270
 
271
+ def clean_response(response: str) -> str:
272
  # Удаление служебных тегов
273
  response = re.sub(r'</?assistant>|<\|system\|>|</s>', '', response, flags=re.IGNORECASE)
274
 
 
300
 
301
  return response.strip()
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  def process_query(prompt: str):
304
  try:
305
  start_time = time.time()
 
310
  message_queue.put(('log', f"⏏️ Извлечено: {json.dumps(norm_data, ensure_ascii=False)}"))
311
 
312
  search_query = norm_data['search_query']
313
+ sources = web_search(search_query)
314
 
315
+ message_queue.put(('log', f"📚 Найдено {len(sources)} источников"))
316
+
317
+ # Формируем контекст для LLM
318
+ context_content = ""
319
+ for i, source in enumerate(sources):
320
+ context_content += f"[[Источник {i+1}]] {source['title']}\n{source['content']}\n\n"
321
+
322
+ context_content = context_content.strip()
323
 
324
  message_queue.put(('log', f"⚙️ Определяю проблему"))
325
  problem_response = mistral_client.chat.complete(
326
  model=MISTRAL_MODEL,
327
  messages=[
328
  {"role": "system", "content": "Опиши СУТЬ проблемы в одном предложении. Только диагноз, без решений. Не более 12 слов. На русском."},
329
+ {"role": "user", "content": f"Запрос пользователя: {prompt}\nДанные из источников:\n{context_content}"}
330
  ],
331
  max_tokens=150,
332
  temperature=0.2
 
338
 
339
  message_queue.put(('log', f"🧩 Определённая проблема: {extracted_problem}"))
340
 
341
+ # Формируем промпт с источниками
 
342
  messages = [
343
  {"role": "system", "content": SYSTEM_PROMPT + f"""
344
  Контекст:
345
  Бренд: {norm_data['brand']}
346
  Модель: {norm_data['model']}
347
  Ошибка: {norm_data['error_code']}
348
+ Суть проблемы (на основе источников): {extracted_problem}
349
+ Данные из источников:
350
+ {context_content}
351
  """},
352
  {"role": "user", "content": f"Проблема: {prompt}"}
353
  ]
354
 
355
+ message_queue.put(('log', "🧠 Генерирую ответ на основе источников..."))
356
  message_queue.put(('response_start', ""))
357
 
358
  full_response = ""
 
367
  full_response += chunk_text
368
  message_queue.put(('response_chunk', chunk_text))
369
 
 
 
 
370
  # Очистка и форматирование ответа
371
+ final_response = clean_response(full_response)
372
+
373
+ # Добавляем источники в ответ
374
+ if sources:
375
+ final_response += "\n\n**Источники информации:**\n"
376
+ for i, source in enumerate(sources):
377
+ final_response += f"- [{source['title']}]({source['url']})\n"
378
 
379
  message_queue.put(('response_end', final_response))
380
  message_queue.put(('sources', json.dumps(sources)))
381
 
382
  total_time = time.time() - start_time
383
+ message_queue.put(('log', f"💡 Ответ сгенерирован за {total_time:.1f}с"))
 
384
  message_queue.put(('done', ''))
385
 
386
  except Exception as e: