KennyOry commited on
Commit
07f4322
·
verified ·
1 Parent(s): e43d141

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -41
app.py CHANGED
@@ -1,4 +1,4 @@
1
- from flask import Flask, render_template, request, Response, jsonify
2
  from mistralai import Mistral
3
  import logging
4
  import time
@@ -11,9 +11,11 @@ import os
11
  import trafilatura
12
  from bs4 import BeautifulSoup
13
  import random
 
14
 
15
  app = Flask(__name__)
16
  app.secret_key = 'super_secret_key'
 
17
 
18
  message_queue = queue.Queue()
19
 
@@ -44,7 +46,6 @@ SYSTEM_PROMPT = """
44
  **Источники информации:**
45
  - [Краткое описание источника 1]
46
  - [Краткое описание источника 2]
47
-
48
  ЖЕСТКИЕ ЗАПРЕТЫ:
49
  - Никогда не используй подзаголовки с ###
50
  - Никогда не добавляй разделы "Удалены шаги" или подобные
@@ -52,6 +53,14 @@ SYSTEM_PROMPT = """
52
  - Начинай сразу с **Проблема:** без преамбул
53
  """
54
 
 
 
 
 
 
 
 
 
55
  BLACKLISTED_DOMAINS = [
56
  'reddit.com',
57
  'stackoverflow.com',
@@ -349,54 +358,120 @@ def verify_with_sources(response: str, sources: list) -> str:
349
  message_queue.put(('log', error_msg))
350
  return response
351
 
352
-
353
- def process_query(prompt: str):
354
  try:
355
- start_time = time.time()
356
- message_queue.put(('log', f"👤 Запрос: {prompt}"))
357
- message_queue.put(('log', f"⚙️ Извлекаю параметры из входящего запроса"))
358
-
359
- norm_data = generate_search_query(prompt)
360
- message_queue.put(('log', f"⏏️ Извлечено: {json.dumps(norm_data, ensure_ascii=False)}"))
361
 
362
- search_query = norm_data['search_query']
363
- search_data, sources = web_search(search_query)
364
-
365
- message_queue.put(('log', f"📚 Собрано: {len(search_data)} символов в {len(sources)} источнике(-ах)"))
366
-
367
- message_queue.put(('log', f"⚙️ Определяю проблему"))
368
- problem_response = mistral_client.chat.complete(
369
- model=MISTRAL_MODEL,
 
 
 
 
 
 
370
  messages=[
371
- {"role": "system", "content": "Опиши СУТЬ проблемы в одном предложении. Только диагноз, без решений. Не более 12 слов. На русском."},
372
- {"role": "user", "content": f"Запрос пользователя: {prompt}\nПоисковые данные:\n{search_data}"}
373
  ],
374
- max_tokens=150,
375
- temperature=0.2
376
  )
377
- extracted_problem = problem_response.choices[0].message.content.strip()
378
-
379
- if not extracted_problem or len(extracted_problem) < 5:
380
- extracted_problem = f"Неисправность {norm_data['brand']} {norm_data['model']}"
 
 
 
381
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382
  message_queue.put(('log', f"🧩 Определённая проблема: {extracted_problem}"))
383
 
384
- sources_text = "\n".join([f"[{i+1}] {s['title']} - {s['url']}" for i, s in enumerate(sources)])
385
-
386
  messages = [
387
- {"role": "system", "content": SYSTEM_PROMPT + f"""
388
- Контекст:
389
- Бренд: {norm_data['brand']}
390
- Модель: {norm_data['model']}
391
- Ошибка: {norm_data['error_code']}
392
- Суть проблемы (на основе поиска): {extracted_problem}
393
- Данные поиска:
394
- {search_data}
395
- """},
396
- {"role": "user", "content": f"Проблема: {prompt}"}
 
397
  ]
398
 
399
- message_queue.put(('log', "🧠 На основе полученных данных генерирую ответ..."))
400
  message_queue.put(('response_start', ""))
401
 
402
  full_response = ""
@@ -412,6 +487,7 @@ def process_query(prompt: str):
412
  message_queue.put(('response_chunk', chunk_text))
413
 
414
  # Проверка соответствия источникам
 
415
  verified_response = verify_with_sources(full_response, sources)
416
 
417
  # Очистка и форматирование ответа
@@ -420,6 +496,12 @@ def process_query(prompt: str):
420
  message_queue.put(('response_end', final_response))
421
  message_queue.put(('sources', json.dumps(sources)))
422
 
 
 
 
 
 
 
423
  total_time = time.time() - start_time
424
  message_queue.put(('log', f"💡 Ответ: {final_response[:200]}..."))
425
  message_queue.put(('log', f"⏱ Время: {total_time:.1f}с"))
@@ -434,17 +516,55 @@ def process_query(prompt: str):
434
 
435
  @app.route('/')
436
  def index():
 
 
 
 
 
 
 
 
 
 
 
437
  return render_template('index.html')
438
 
439
-
440
  @app.route('/ask', methods=['POST'])
441
  def ask():
442
  user_input = request.form['message']
443
- thread = threading.Thread(target=process_query, args=(user_input,))
 
444
  thread.daemon = True
445
  thread.start()
446
  return jsonify({'status': 'processing'})
447
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
 
449
  @app.route('/stream')
450
  def stream():
 
1
+ from flask import Flask, render_template, request, Response, jsonify, session
2
  from mistralai import Mistral
3
  import logging
4
  import time
 
11
  import trafilatura
12
  from bs4 import BeautifulSoup
13
  import random
14
+ import uuid
15
 
16
  app = Flask(__name__)
17
  app.secret_key = 'super_secret_key'
18
+ app.config['SESSION_TYPE'] = 'filesystem'
19
 
20
  message_queue = queue.Queue()
21
 
 
46
  **Источники информации:**
47
  - [Краткое описание источника 1]
48
  - [Краткое описание источника 2]
 
49
  ЖЕСТКИЕ ЗАПРЕТЫ:
50
  - Никогда не используй подзаголовки с ###
51
  - Никогда не добавляй разделы "Удалены шаги" или подобные
 
53
  - Начинай сразу с **Проблема:** без преамбул
54
  """
55
 
56
+ CONTEXT_PROMPT = """
57
+ Ты PrintMaster, сервисный инженер по печатной технике. Отвечай на вопросы пользователя в контексте текущего диалога.
58
+ Правила:
59
+ 1. Если вопрос связан с текущей проблемой - отвечай на основе предыдущих данных
60
+ 2. Если нужны новые технические данные - попроси пользователя уточнить
61
+ 3. Если вопрос не связан с текущей проблемой - уточни у пользователя, хочет ли он начать новую тему
62
+ """
63
+
64
  BLACKLISTED_DOMAINS = [
65
  'reddit.com',
66
  'stackoverflow.com',
 
358
  message_queue.put(('log', error_msg))
359
  return response
360
 
361
+ def needs_web_search(user_input: str, context: list) -> bool:
362
+ """Определяет, нужен ли новый поиск для текущего запроса"""
363
  try:
364
+ prompt = f"""
365
+ Определи, нужен ли новый поиск в интернете для ответа на последний запрос пользователя.
 
 
 
 
366
 
367
+ История диалога:
368
+ {json.dumps(context[-5:], ensure_ascii=False)}
369
+
370
+ Последний запрос пользователя: {user_input}
371
+
372
+ Правила:
373
+ 1. Если запрос требует новой информации (технические детали, коды ошибок, инструкции) - верни True
374
+ 2. Если запрос относится к текущей проблеме и не требует новых данных - верни False
375
+ 3. Если пользователь просит уточнить или переформулировать ответ - верни False
376
+ 4. Если пользователь запрашивает новые примеры или альтернативные решения - верни True
377
+ """
378
+
379
+ response = mistral_client.chat.complete(
380
+ model="mistral-medium",
381
  messages=[
382
+ {"role": "system", "content": "Ты эксперт по анализу запросов. Отвечай только TRUE или FALSE."},
383
+ {"role": "user", "content": prompt}
384
  ],
385
+ max_tokens=10,
386
+ temperature=0.0
387
  )
388
+
389
+ decision = response.choices[0].message.content.strip().lower()
390
+ return "true" in decision or "да" in decision or "1" in decision
391
+
392
+ except Exception as e:
393
+ logging.error(f"Ошибка определения необходимости поиска: {str(e)}")
394
+ return True
395
 
396
+ def process_query(prompt: str, session_id: str):
397
+ try:
398
+ start_time = time.time()
399
+ message_queue.put(('log', f"👤 Запрос: {prompt}"))
400
+
401
+ # Инициализация сессии
402
+ if 'context' not in session:
403
+ session['context'] = {
404
+ 'history': [],
405
+ 'sources': [],
406
+ 'problem': '',
407
+ 'norm_data': {},
408
+ 'last_query': '',
409
+ 'last_response': ''
410
+ }
411
+
412
+ context = session['context']
413
+ context['history'].append({"role": "user", "content": prompt})
414
+ context['last_query'] = prompt
415
+
416
+ # Определяем, нужен ли новый поиск
417
+ needs_search = True
418
+ if context['sources'] and context['problem']:
419
+ message_queue.put(('status', "🔍 Анализирую необходимость нового поиска..."))
420
+ needs_search = needs_web_search(prompt, context['history'])
421
+
422
+ search_data = ""
423
+ sources = []
424
+ norm_data = {}
425
+
426
+ if needs_search or not context['sources']:
427
+ message_queue.put(('status', "⚙️ Извлекаю параметры из входящего запроса"))
428
+ norm_data = generate_search_query(prompt)
429
+ message_queue.put(('log', f"⏏️ Извлечено: {json.dumps(norm_data, ensure_ascii=False)}"))
430
+
431
+ search_query = norm_data['search_query']
432
+ search_data, sources = web_search(search_query)
433
+ context['sources'] = sources
434
+ context['norm_data'] = norm_data
435
+ message_queue.put(('log', f"📚 Собрано: {len(search_data)} символов в {len(sources)} источнике(-ах)"))
436
+ else:
437
+ message_queue.put(('status', "⚙️ Использую данные из предыдущего поиска"))
438
+ sources = context['sources']
439
+ norm_data = context['norm_data']
440
+ search_data = "\n\n".join([f"[[Источник {i+1}]] {s['title']}\n{s['content']}" for i, s in enumerate(sources)])
441
+
442
+ if not context['problem'] or needs_search:
443
+ message_queue.put(('status', "🧩 Определяю проблему"))
444
+ problem_response = mistral_client.chat.complete(
445
+ model=MISTRAL_MODEL,
446
+ messages=[
447
+ {"role": "system", "content": "Опиши СУТЬ проблемы в одном предложении. Только диагноз, без решений. Не более 12 слов. На русском."},
448
+ {"role": "user", "content": f"Запрос пользователя: {prompt}\nПоисковые данные:\n{search_data}"}
449
+ ],
450
+ max_tokens=150,
451
+ temperature=0.2
452
+ )
453
+ extracted_problem = problem_response.choices[0].message.content.strip()
454
+ context['problem'] = extracted_problem
455
+ else:
456
+ extracted_problem = context['problem']
457
+
458
  message_queue.put(('log', f"🧩 Определённая проблема: {extracted_problem}"))
459
 
 
 
460
  messages = [
461
+ {"role": "system", "content": CONTEXT_PROMPT if context['history'] else SYSTEM_PROMPT},
462
+ *context['history'],
463
+ {"role": "system", "content": f"""
464
+ Контекст:
465
+ Бренд: {norm_data.get('brand', '')}
466
+ Модель: {norm_data.get('model', '')}
467
+ Ошибка: {norm_data.get('error_code', '')}
468
+ Суть проблемы (на основе поиска): {extracted_problem}
469
+ Данные поиска:
470
+ {search_data}
471
+ """}
472
  ]
473
 
474
+ message_queue.put(('status', "🧠 Генерирую ответ..."))
475
  message_queue.put(('response_start', ""))
476
 
477
  full_response = ""
 
487
  message_queue.put(('response_chunk', chunk_text))
488
 
489
  # Проверка соответствия источникам
490
+ message_queue.put(('status', "🔍 Проверяю ответ по источникам..."))
491
  verified_response = verify_with_sources(full_response, sources)
492
 
493
  # Очистка и форматирование ответа
 
496
  message_queue.put(('response_end', final_response))
497
  message_queue.put(('sources', json.dumps(sources)))
498
 
499
+ # Сохраняем ответ в контексте
500
+ context['history'].append({"role": "assistant", "content": final_response})
501
+ context['last_response'] = final_response
502
+ session['context'] = context
503
+ session.modified = True
504
+
505
  total_time = time.time() - start_time
506
  message_queue.put(('log', f"💡 Ответ: {final_response[:200]}..."))
507
  message_queue.put(('log', f"⏱ Время: {total_time:.1f}с"))
 
516
 
517
  @app.route('/')
518
  def index():
519
+ # Инициализируем сессию при первом посещении
520
+ if 'session_id' not in session:
521
+ session['session_id'] = str(uuid.uuid4())
522
+ session['context'] = {
523
+ 'history': [],
524
+ 'sources': [],
525
+ 'problem': '',
526
+ 'norm_data': {},
527
+ 'last_query': '',
528
+ 'last_response': ''
529
+ }
530
  return render_template('index.html')
531
 
 
532
  @app.route('/ask', methods=['POST'])
533
  def ask():
534
  user_input = request.form['message']
535
+ session_id = session.get('session_id', str(uuid.uuid4()))
536
+ thread = threading.Thread(target=process_query, args=(user_input, session_id))
537
  thread.daemon = True
538
  thread.start()
539
  return jsonify({'status': 'processing'})
540
 
541
+ @app.route('/repeat', methods=['POST'])
542
+ def repeat():
543
+ if 'context' in session:
544
+ context = session['context']
545
+ last_query = context.get('last_query', '')
546
+ if last_query:
547
+ session_id = session.get('session_id', str(uuid.uuid4()))
548
+ thread = threading.Thread(target=process_query, args=(last_query, session_id))
549
+ thread.daemon = True
550
+ thread.start()
551
+ return jsonify({'status': 'repeating'})
552
+ return jsonify({'status': 'no_query'})
553
+
554
+ @app.route('/new_session', methods=['POST'])
555
+ def new_session():
556
+ session.clear()
557
+ session['session_id'] = str(uuid.uuid4())
558
+ session['context'] = {
559
+ 'history': [],
560
+ 'sources': [],
561
+ 'problem': '',
562
+ 'norm_data': {},
563
+ 'last_query': '',
564
+ 'last_response': ''
565
+ }
566
+ return jsonify({'status': 'new_session'})
567
+
568
 
569
  @app.route('/stream')
570
  def stream():