HeavensHackDev commited on
Commit
26471fd
·
verified ·
1 Parent(s): 899870f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +165 -120
app.py CHANGED
@@ -1,142 +1,187 @@
1
  # app.py
 
2
  import gradio as gr
3
  from transformers import pipeline, set_seed
4
  import random
5
- import torch # Может понадобиться для указания устройства
6
-
7
- # --- Настройки ---
8
- # Выбираем русскую модель
9
- # Попробуем 'sberbank-ai/rugpt3small_based_on_gpt2'.
10
- # Если Space будет падать из-за нехватки памяти, возможно, придется вернуться к distilgpt2
11
- # или искать еще более легкую модель.
12
- MODEL_NAME = 'sberbank-ai/rugpt3small_based_on_gpt2'
13
- # MODEL_NAME = 'distilgpt2' # Запасной вариант
14
-
15
- # Параметры генерации
16
- MAX_NEW_TOKENS = 100 # Увеличим максимальную длину *нового* текста
17
- TEMPERATURE = 0.8 # Температура генерации (чуть больше случайности, >1 = больше бреда)
18
- TOP_P = 0.9 # Ядерная выборка (nucleus sampling)
19
- # --- Конец Настроек ---
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
- print(f"Загрузка модели: {MODEL_NAME}...")
 
23
  try:
24
- # Попробуем указать device_map='auto' для лучшего распределения на ресурсах Space
25
- # или torch_dtype=torch.float16 для экономии памяти (если поддерживается)
26
- generator = pipeline(
27
- 'text-generation',
28
- model=MODEL_NAME,
29
- # Если есть GPU и CUDA: device=0
30
- # Для CPU или автоматического определения: device=-1 (по умолчанию)
31
- # Попробуем автоматическое распределение:
32
- # device_map="auto", # Может помочь с памятью на много-GPU инстансах (редко в free Spaces)
33
- # torch_dtype=torch.float16, # Если модель поддерживает и есть CUDA, ускорит и сэкономит память
34
- )
35
- print("Модель успешно загружена!")
36
  except Exception as e:
37
- print(f"Ошибка при загрузке модели: {e}")
38
- # Завершаем работу, если модель не загрузилась
39
- raise SystemExit(f"Не удалось загрузить модель {MODEL_NAME}. Ошибка: {e}")
 
 
 
 
 
 
 
 
 
40
 
 
41
 
42
- # Функция, которая будет обрабатывать ввод пользователя и историю чата
43
  def respond(message, chat_history):
44
  """
45
- Принимает сообщение пользователя и историю чата, возвращает ответ бота.
46
  """
47
  print(f"Получено сообщение: {message}")
48
- print(f"История чата (до): {chat_history}")
49
-
50
- # --- Улучшенный Промпт ---
51
- # Добавляем инструкцию/роль для бота (можно сделать сложнее)
52
- system_prompt = "Ты — дружелюбный и полезный ИИ-ассистент. Отвечай на вопросы пользователя."
53
-
54
- # Формируем историю для промпта (последние N обменов)
55
- history_limit = 3 # Сколько последних пар "вопрос-ответ" учитывать
56
- history_for_prompt = []
57
- for user_msg, bot_msg in chat_history[-history_limit:]:
58
- history_for_prompt.append(f"Пользователь: {user_msg}")
59
- history_for_prompt.append(f"Ассистент: {bot_msg}")
60
-
61
- history_str = "\n".join(history_for_prompt)
62
-
63
- # Собираем финальный промпт
64
- prompt = f"{system_prompt}\n\n{history_str}\nПользователь: {message}\nАссистент:"
65
- print(f"--- Промпт для модели --- \n{prompt}\n------------------------")
66
-
67
- try:
68
- # Генерируем ответ
69
- # Используем max_new_tokens для контроля длины именно ответа
70
- generated_outputs = generator(
71
- prompt,
72
- max_new_tokens=MAX_NEW_TOKENS,
73
- num_return_sequences=1,
74
- # truncation=True, # Трункация уже не так нужна, если мы контролируем длину истории
75
- do_sample=True, # Включаем сэмплирование для более "живых" ответов
76
- temperature=TEMPERATURE,
77
- top_p=TOP_P,
78
- # pad_token_id=generator.tokenizer.eos_token_id # Указываем токен для паддинга
79
- )
80
- bot_response = generated_outputs[0]['generated_text']
81
-
82
- # --- Очистка ответа ---
83
- # Убираем весь промпт из сгенерированного текста
84
- if bot_response.startswith(prompt):
85
- bot_response = bot_response[len(prompt):].strip()
86
- else:
87
- # Иногда модель может начать ответ сразу, без повторения промпта,
88
- # но может повторить последнюю часть (например, "Ассистент:")
89
- # Это требует более сложной очистки, пока оставим базовую.
90
- # Можно попробовать убирать только последнюю строку промпта:
91
- last_prompt_line = "Ассистент:"
92
- if bot_response.strip().startswith(last_prompt_line):
93
- bot_response = bot_response.strip()[len(last_prompt_line):].strip()
94
-
95
-
96
- # Дополнительная очистка от незавершенных предложений или артефактов
97
- # (можно добавить обрезку по последнему знаку препинания .?!)
98
- last_punctuation = max(bot_response.rfind('.'), bot_response.rfind('!'), bot_response.rfind('?'))
99
- if last_punctuation != -1:
100
- bot_response = bot_response[:last_punctuation+1]
101
-
102
-
103
- # Проверка на пустой ответ
104
- if not bot_response or len(bot_response.strip()) == 0:
105
- bot_response = "... (модель не сгенерировала содержательный ответ)"
106
-
107
- print(f"Сгенерированный ответ (очищенный): {bot_response}")
108
-
109
- except Exception as e:
110
- print(f"Ошибка при генерации: {e}")
111
- # Попр��буем дать более специфичную информацию, если возможно
112
- if "out of memory" in str(e).lower():
113
- bot_response = "Ой, кажется, мне не хватило памяти для генерации ответа. Попробуйте более короткий запрос или администратор может попробовать модель поменьше."
114
- else:
115
- bot_response = f"Ой, произошла ошибка при генерации ответа: {e}"
116
-
117
- # Добавляем пару (сообщение пользователя, ответ бота) в историю
118
- chat_history.append((message, bot_response))
119
- print(f"История чата (после): {chat_history}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- # Возвращаем пустую строку для очистки поля ввода и обновленную историю
 
122
  return "", chat_history
123
 
124
- # --- Создание интерфейса Gradio (остается почти без изменений) ---
125
- with gr.Blocks() as demo:
126
- gr.Markdown("# Улучшенный Чат-Бот на Hugging Face")
127
- gr.Markdown(f"Используемая модель: `{MODEL_NAME}`. Введите ваше сообщение.")
128
 
129
- chatbot = gr.Chatbot(label="Диалог", height=500) # Увеличим высоту окна чата
130
- msg = gr.Textbox(label="Ваше сообщение", placeholder="Спроси меня что-нибудь...")
131
- clear = gr.Button("Очистить диалог")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
 
 
133
  msg.submit(respond, [msg, chatbot], [msg, chatbot])
134
- clear.click(lambda: (None, []), None, [msg, chatbot], queue=False) # Очищаем историю на пустой список []
 
135
 
 
 
136
 
137
- # Запускаем демо
138
- print("Запуск Gradio интерфейса...")
139
- # Используйте share=True тол��ко если запускаете локально и хотите временную публичную ссылку
140
- # Для HF Spaces это не нужно.
141
- demo.launch(debug=True)
142
- # demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  # app.py
2
+
3
  import gradio as gr
4
  from transformers import pipeline, set_seed
5
  import random
6
+ import re # Для регулярных выражений (очистка текста)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
+ # --- Загрузка моделей ---
9
+ # 1. Генератор текста (для общего ответа и диалога)
10
+ # Замените 'distilgpt2' на нужную модель, если используете другую
11
+ # Попробуйте 'microsoft/DialoGPT-medium' - специально для диалогов (английский)
12
+ # Или русские аналоги, если найдете подходящие по размеру для CPU
13
+ try:
14
+ # generator = pipeline('text-generation', model='distilgpt2', max_length=100, num_return_sequences=1, truncation=True, pad_token_id=50256) # Указываем pad_token_id явно
15
+ # generator = pipeline('conversational', model='microsoft/DialoGPT-medium') # Специальная диалоговая модель (англ)
16
+ generator = pipeline('text-generation', model='sberbank-ai/rugpt3small_based_on_gpt2', max_length=100, num_return_sequences=1, truncation=True, pad_token_id=50256) # Попробуем русскую поменьше
17
+ print("Генератор текста загружен.")
18
+ GENERATOR_LOADED = True
19
+ except Exception as e:
20
+ print(f"ОШИБКА: Не удалось загрузить генератор текста! {e}")
21
+ generator = None
22
+ GENERATOR_LOADED = False
23
 
24
+ # 2. Модель для ответов на вопросы (Question Answering)
25
+ # Используем небольшую мультиязычную модель
26
  try:
27
+ qa_pipeline = pipeline('question-answering', model='bert-large-uncased-whole-word-masking-finetuned-squad') # Пример англоязычной
28
+ # qa_pipeline = pipeline('question-answering', model='timpal0l/mdeberta-v3-base-squad2') # Пример мультиязычной (может быть лучше для RU)
29
+ print("QA модель загружена.")
30
+ QA_LOADED = True
 
 
 
 
 
 
 
 
31
  except Exception as e:
32
+ print(f"ОШИБКА: Не удалось загрузить QA модель! {e}")
33
+ qa_pipeline = None
34
+ QA_LOADED = False
35
+
36
+ # --- "Встроенные" знания и команды ---
37
+ knowledge_base = {
38
+ "кто ты": "Я Nova, модель Alpha 0.95, работающая на платформе Hugging Face Spaces. Я использую нейросетевые модели для генерации ответов и ответов на вопросы.",
39
+ "что ты умеешь": "Я могу пытаться поддерживать диалог, отвечать на вопросы по предоставленному контексту (если удастся его определить), �� генерировать текст на основе предыдущих сообщений. Также я знаю некоторые встроенные команды.",
40
+ "как дела": "Как у программы, у меня все по плану! Готова обрабатывать ваши запросы.",
41
+ "помощь": "Спросите меня о чем-нибудь, или дайте команду. Я попробую ответить. Примеры команд: 'кто ты', 'что ты умеешь'.",
42
+ # Можно добавить больше простых ответов
43
+ }
44
 
45
+ # --- Функция обработки запроса ---
46
 
 
47
  def respond(message, chat_history):
48
  """
49
+ Главная функция обработки. Определяет тип запроса и выбирает стратегию ответа.
50
  """
51
  print(f"Получено сообщение: {message}")
52
+ user_message_lower = message.lower().strip().replace("?","").replace(".","").replace("!","") # Очищенный ввод для команд
53
+
54
+ # 1. Проверка на встроенные команды/знания
55
+ if user_message_lower in knowledge_base:
56
+ bot_response = knowledge_base[user_message_lower]
57
+ print(f"Ответ из базы знаний: {bot_response}")
58
+ chat_history.append((message, bot_response))
59
+ return "", chat_history
60
+
61
+ # 2. Попытка ответа на вопрос с помощью QA модели
62
+ # Очень упрощенная логика: если есть "?" и QA модель загружена
63
+ if "?" in message and QA_LOADED and qa_pipeline:
64
+ # Используем историю чата как контекст (последние сообщения)
65
+ context = ""
66
+ if chat_history:
67
+ # Собираем текст из последних N обменов (N=2)
68
+ context_parts = []
69
+ for user_msg, bot_msg in chat_history[-2:]:
70
+ context_parts.append(f"Пользователь: {user_msg}")
71
+ context_parts.append(f"Бот: {bot_msg}")
72
+ context = "\n".join(context_parts)
73
+
74
+ if len(context) > 50: # Нужен минимальный контекст для QA
75
+ print("Попытка ответа на вопрос через QA модель...")
76
+ print(f"Контекст: {context[:200]}...") # Логируем начало контекста
77
+ print(f"Вопрос: {message}")
78
+ try:
79
+ qa_result = qa_pipeline(question=message, context=context)
80
+ print(f"Результат QA: {qa_result}")
81
+ # Если модель уверена в ответе (score > порога)
82
+ if qa_result and qa_result['score'] > 0.1: # Порог уверенности (нужно подбирать)
83
+ bot_response = qa_result['answer']
84
+ chat_history.append((message, bot_response))
85
+ print(f"Ответ от QA модели: {bot_response}")
86
+ return "", chat_history
87
+ else:
88
+ print("QA модель не уверена в ответе.")
89
+ except Exception as e:
90
+ print(f"Ошибка при использовании QA модели: {e}")
91
+ # Если QA не сработала, переходим к генерации
92
+
93
+ # 3. Генерация ответа с помощью основной модели (если команды/QA не сработали)
94
+ if GENERATOR_LOADED and generator:
95
+ print("Генерация ответа с помощью основной модели...")
96
+ # Формируем промпт с историей
97
+ prompt_history = []
98
+ for user_msg, bot_msg in chat_history[-3:]: # Берем последние 3 обмена
99
+ prompt_history.append(f"Пользователь: {user_msg}")
100
+ prompt_history.append(f"Бот: {bot_msg}")
101
+ prompt_history.append(f"Пользователь: {message}")
102
+ prompt_history.append("Бот:") # Приглашение для генерации ответа
103
+
104
+ full_prompt = "\n".join(prompt_history)
105
+ print(f"Промпт для генератора: {full_prompt[-500:]}") # Логируем конец промпта
106
+
107
+ try:
108
+ # Уменьшаем max_length, если промпт уже длинный
109
+ # prompt_tokens = len(generator.tokenizer.encode(full_prompt)) # Подсчет токенов может быть медленным
110
+ # available_tokens = generator.model.config.max_length - prompt_tokens - 10 # Запас
111
+ # current_max_length = max(10, min(generator.model.config.max_length, prompt_tokens + 50)) # Генерация ~50 токенов
112
+ # print(f"Макс. длина для генерации: {current_max_length}")
113
+
114
+ generated_output = generator(full_prompt)[0]['generated_text']
115
+
116
+ # Очистка ответа
117
+ # Убираем промпт из начала
118
+ bot_response = generated_output
119
+ if generated_output.startswith(full_prompt):
120
+ bot_response = generated_output[len(full_prompt):].strip()
121
+ else:
122
+ # Если промпт не найден в начале (некоторые модели ведут себя иначе)
123
+ # Попробуем убрать последнее сообщение пользователя
124
+ last_user_line = f"Пользователь: {message}\nБот:"
125
+ if bot_response.strip().startswith(last_user_line.strip()): # Убираем и саму строку "Бот:"
126
+ bot_response = bot_response.strip()[len(last_user_line.strip()):].strip()
127
+ # Очень грубый способ убрать возможное эхо промпта
128
+ elif len(bot_response) > len(message) and message.lower() in bot_response[:len(message)*2].lower():
129
+ parts = bot_response.split('\n')
130
+ if len(parts)>1:
131
+ bot_response = '\n'.join(parts[1:]).strip()
132
+
133
+
134
+ # Дополнительная очистка (убираем возможные "Пользователь:", "Бот:")
135
+ bot_response = re.sub(r'^\s*(пользователь|user|бот|bot)\s*[:\-]?\s*', '', bot_response, flags=re.IGNORECASE).strip()
136
+
137
+ if not bot_response or len(bot_response) < 3: # Проверка на совсем короткий/пустой ответ
138
+ bot_response = "Хм, не знаю, что на это ответить..."
139
+ print("Сгенерирован слишком короткий ответ, используется заглушка.")
140
+
141
+ except Exception as e:
142
+ print(f"Ошибка при генерации: {e}")
143
+ bot_response = f"Упс, произошла ошибка при генерации: {e}"
144
+
145
+ else:
146
+ # Если ни одна модель не загружена
147
+ bot_response = "К сожалению, мои основные модули сейчас недоступны."
148
+ print("Ошибка: Генератор текста не загружен.")
149
 
150
+ chat_history.append((message, bot_response))
151
+ print(f"Финальный ответ: {bot_response}")
152
  return "", chat_history
153
 
 
 
 
 
154
 
155
+ # --- Создание интерфейса Gradio ---
156
+ with gr.Blocks(css=".gradio-container {background-color: #f9f9f9}") as demo: # Добавим немного CSS
157
+ gr.Markdown(
158
+ """
159
+ # Nova Alpha 0.95 🚀
160
+ Простой чат-бот с базовыми знаниями и попыткой ответа на вопросы.
161
+ Работает на CPU, ответы могут быть медленными.
162
+ """
163
+ )
164
+
165
+ chatbot = gr.Chatbot(label="Диалог", height=500) # Увеличим высоту
166
+ with gr.Row(): # Поместим текстбокс и кнопки в ряд
167
+ msg = gr.Textbox(
168
+ label="Ваше сообщение",
169
+ placeholder="Спросите 'кто ты' или задайте вопрос...",
170
+ scale=4 # Текстбокс займет больше места
171
+ )
172
+ submit_btn = gr.Button("Отправить", variant="primary", scale=1) # Явная кнопка отправки
173
+ clear_btn = gr.Button("Очистить", scale=1)
174
+
175
 
176
+ # Связываем обработчики:
177
+ # Отправка по Enter в Textbox
178
  msg.submit(respond, [msg, chatbot], [msg, chatbot])
179
+ # Отправка по клику на кнопку
180
+ submit_btn.click(respond, [msg, chatbot], [msg, chatbot])
181
 
182
+ # Очистка по клику на кнопку
183
+ clear_btn.click(lambda: (None, []), None, [msg, chatbot], queue=False) # Возвращаем пустой список для истории
184
 
185
+ # Запуск (важно для Spaces!)
186
+ demo.queue() # Используем очередь для обработки запросов по одному (лучше для CPU)
187
+ demo.launch(debug=True) # debug=True покажет логи и ошибки в интерфейсе