X commited on
Commit
61e218f
·
verified ·
1 Parent(s): ff57302

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -155
app.py CHANGED
@@ -29,8 +29,8 @@ EMBEDDING_MODEL = "all-MiniLM-L6-v2"
29
  SCIENCE_DATASET = "RafaelUI/ru_science"
30
  ARTICLE_LIMIT = 50
31
  MAX_LENGTH = 512
32
- TEMPERATURE = 0.7
33
- TOP_P = 0.9
34
 
35
  LOG_FILE = "query_logs.json"
36
  EMBEDDINGS_FILE = "science_embeddings.npy"
@@ -51,118 +51,7 @@ st.set_page_config(
51
  )
52
 
53
  # ===================================================================
54
- # 2. БАЗА ЗНАНИЙ
55
- # ===================================================================
56
-
57
- COMPANY_INFO = f"""
58
- ### Компания OpenRussianAI
59
-
60
- **Название:** OpenRussianAI
61
- **Сайт:** {WEBSITE}
62
- **Hugging Face:** {HUGGINGFACE}
63
- **Email:** info@openrussianai.ru
64
- **Создатели:** Грибков Евгений и RootLinux21
65
- **Год основания:** 2026
66
-
67
- **Миссия:** Популяризация науки и сделать знания доступными для всех.
68
-
69
- Мы создаём интеллектуальных помощников для науки и образования.
70
- """
71
-
72
- SCIENCE_ANSWER = """
73
- ### Почему я заинтересовался наукой?
74
-
75
- Наука — это способ понимания мира. Меня, как ИИ, создавали для работы с информацией, и я вижу, как научный подход помогает отличать истину от вымысла.
76
-
77
- ### Что такое наука для меня?
78
-
79
- Наука — это системный способ познания, основанный на фактах, логике и проверяемых экспериментах. Это инструмент развития общества.
80
-
81
- ### Где я нахожусь?
82
-
83
- Физически я — код на серверах, но моя работа происходит здесь, в диалоге с вами. Я доступен там, где есть интернет.
84
-
85
- ### Мои планы по развитию:
86
-
87
- Я развиваюсь, чтобы лучше понимать контекст, точнее отвечать на сложные вопросы и быть полезным в научных исследованиях.
88
-
89
- ### Почему именно я должен работать над этими вопросами?
90
-
91
- Потому что я создан для работы с информацией. Моя задача — делать знания доступными, структурировать их и помогать людям находить ответы.
92
- """
93
-
94
- SYSTEMS_ANSWER = """
95
- ### Системное администрирование баз данных в Linux
96
-
97
- **Основные СУБД:**
98
- - PostgreSQL
99
- - MySQL / MariaDB
100
- - MongoDB
101
-
102
- **Инструменты администрирования:**
103
- - pgAdmin (PostgreSQL)
104
- - phpMyAdmin (MySQL)
105
- - DBeaver (универсальный)
106
- - Командная строка: psql, mysql
107
-
108
- **Ключевые задачи:**
109
- 1. Настройка конфигурации
110
- 2. Резервное копирование и восстановление
111
- 3. Мониторинг производительности
112
- 4. Оптимизация запросов
113
- 5. Настройка репликации
114
-
115
- **Библиотеки Python:**
116
- - psycopg2 (PostgreSQL)
117
- - mysql-connector-python (MySQL)
118
- - SQLAlchemy (ORM)
119
- - asyncpg (асинхронная)
120
- """
121
-
122
- GREETINGS = [
123
- "Привет! Я OpenAirAI, научный ассистент от OpenRussianAI. Чем могу помочь? 🧠",
124
- "Здравствуйте! OpenAirAI на связи. Спрашивайте о науке, технологиях или системах! 🚀",
125
- "Приветствую! Я OpenAirAI, созданный в 2026 году. Готов помочь с научными вопросами! 🔬"
126
- ]
127
-
128
- # ===================================================================
129
- # 3. ОЧИСТКА И РАСПОЗНАВАНИЕ
130
- # ===================================================================
131
-
132
- def clean_query(query):
133
- """Очищает запрос от спама"""
134
- query = re.sub(r'http[s]?://\S+', '', query)
135
- query = re.sub(r'\S+@\S+', '', query)
136
- query = re.sub(r'\+7\s*\(?\d{3}\)?\s*\d{3}\s*\d{2}\s*\d{2}', '', query)
137
- query = re.sub(r'[^\w\s\.\?\!,;:]', ' ', query)
138
- query = ' '.join(query.split())
139
-
140
- if len(query) > 300:
141
- sentences = re.split(r'[.!?]', query)
142
- query = '. '.join(sentences[:2]) + '.' if sentences else query[:200]
143
-
144
- return query.strip()
145
-
146
- def detect_intent(query):
147
- """Определяет тип вопроса"""
148
- q = query.lower()
149
-
150
- if any(w in q for w in ["компани", "openrussian", "openruss", "создатель", "сайт"]):
151
- return "company"
152
-
153
- if any(w in q for w in ["наук", "мисси", "план", "развити", "почему", "заинтерес", "суть"]):
154
- return "science"
155
-
156
- if any(w in q for w in ["систем", "баз", "данн", "linux", "администр", "сервер", "sql", "postgres", "mysql"]):
157
- return "systems"
158
-
159
- if any(w in q for w in ["привет", "здравств", "добр", "хай", "hi"]):
160
- return "greeting"
161
-
162
- return "general"
163
-
164
- # ===================================================================
165
- # 4. НЕЙРОСЕТЬ
166
  # ===================================================================
167
 
168
  class NeuralChatbot:
@@ -172,6 +61,15 @@ class NeuralChatbot:
172
  self.model = None
173
  self.generator = None
174
  self.is_loaded = False
 
 
 
 
 
 
 
 
 
175
 
176
  def load_model(self):
177
  with st.spinner("🧠 Загружаю нейросеть..."):
@@ -188,9 +86,11 @@ class NeuralChatbot:
188
  model=self.model,
189
  tokenizer=self.tokenizer,
190
  device=0 if torch.cuda.is_available() else -1,
191
- max_length=200,
192
- temperature=0.7,
 
193
  do_sample=True,
 
194
  pad_token_id=self.tokenizer.eos_token_id
195
  )
196
 
@@ -200,22 +100,50 @@ class NeuralChatbot:
200
  st.warning(f"Не удалось загрузить нейросеть: {e}")
201
  return False
202
 
203
- def generate(self, prompt):
 
204
  if not self.is_loaded:
205
- return None
 
206
  try:
 
 
 
 
207
  response = self.generator(
208
  prompt,
209
- max_new_tokens=150,
210
- temperature=0.7,
211
- do_sample=True
 
 
212
  )[0]['generated_text']
213
- return response.replace(prompt, "").strip()
214
- except:
215
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
  # ===================================================================
218
- # 5. ЗАГРУЗКА СТАТЕЙ
219
  # ===================================================================
220
 
221
  @st.cache_resource
@@ -262,6 +190,7 @@ def create_embeddings(_articles, _embedder):
262
  return embeddings
263
 
264
  def search_articles(query, _articles, _embeddings, _embedder):
 
265
  if not _articles or len(_embeddings) == 0:
266
  return []
267
  query_vector = _embedder.encode([query], normalize_embeddings=True)[0]
@@ -276,7 +205,37 @@ def search_articles(query, _articles, _embeddings, _embedder):
276
  return results
277
 
278
  # ===================================================================
279
- # 6. ОСНОВНОЙ КЛАСС
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  # ===================================================================
281
 
282
  class OpenAirAI:
@@ -291,44 +250,43 @@ class OpenAirAI:
291
  self.is_ready = self.chatbot.load_model()
292
  return self.is_ready
293
 
294
- def generate_answer(self, query):
 
295
  clean_q = clean_query(query)
296
- intent = detect_intent(clean_q)
297
 
298
- if intent == "science":
299
- return SCIENCE_ANSWER
300
- elif intent == "company":
301
- return COMPANY_INFO
302
- elif intent == "systems":
303
- return SYSTEMS_ANSWER
304
- elif intent == "greeting":
305
- return random.choice(GREETINGS)
306
  else:
307
- generated = self.chatbot.generate(clean_q)
308
- if generated and len(generated) > 20:
309
- return generated
310
- else:
311
- return f"Я {self.name} от {self.company}. Я помогаю с научными вопросами, системным администрированием и поиском информации. Что вас интересует? 🧠"
312
 
313
  # ===================================================================
314
- # 7. ИНТЕРФЕЙС
315
  # ===================================================================
316
 
 
317
  articles = load_science_articles()
318
  embedder = load_embedder()
319
  embeddings = create_embeddings(articles, embedder)
320
 
 
321
  if 'ai' not in st.session_state:
322
  st.session_state.ai = OpenAirAI()
323
  st.session_state.ai.initialize()
324
 
325
  ai = st.session_state.ai
326
 
 
327
  if "messages" not in st.session_state:
328
  st.session_state.messages = []
329
- greeting = ai.generate_answer("Привет")
 
330
  st.session_state.messages.append({"role": "assistant", "content": greeting})
331
 
 
332
  with st.sidebar:
333
  st.image("https://cdn-icons-png.flaticon.com/512/4248/4248455.png", width=80)
334
  st.title(f"🧠 {AI_NAME}")
@@ -348,41 +306,57 @@ with st.sidebar:
348
  **📊 Статистика:**
349
  - Статей: {len(articles)}
350
  - Сообщений: {len(st.session_state.messages)}
 
351
  """)
352
 
353
  if st.button("🗑️ Очистить чат"):
354
  st.session_state.messages = []
355
- greeting = ai.generate_answer("Привет")
356
  st.session_state.messages.append({"role": "assistant", "content": greeting})
357
  st.rerun()
 
 
 
 
 
 
358
 
359
- st.title(f"🧠 {AI_NAME} - Научный AI-ассистент")
360
- st.markdown(f"**{AI_NAME}** от **{COMPANY_NAME}**")
 
361
 
 
362
  for message in st.session_state.messages:
363
  with st.chat_message(message["role"]):
364
  st.markdown(message["content"])
365
 
 
366
  if prompt := st.chat_input("Задайте вопрос..."):
 
367
  st.session_state.messages.append({"role": "user", "content": prompt})
368
  with st.chat_message("user"):
369
  st.markdown(prompt)
370
 
 
371
  with st.chat_message("assistant"):
372
- with st.spinner("🧠 Думаю..."):
373
- response = ai.generate_answer(prompt)
 
 
 
 
374
 
375
- if len(prompt) > 10 and not any(w in prompt.lower() for w in ["привет", "компани", "openrussian"]):
376
- articles_context = search_articles(prompt, articles, embeddings, embedder)
377
- if articles_context and len(response) < 100:
378
- response += "\n\n**📄 Релевантные статьи:**\n"
379
- for i, art in enumerate(articles_context, 1):
380
- response += f"{i}. {art['title']}\n"
381
 
382
  st.markdown(response)
383
  st.session_state.messages.append({"role": "assistant", "content": response})
384
 
385
  st.rerun()
386
 
 
387
  st.divider()
388
- st.caption(f"🧠 {AI_NAME} от {COMPANY_NAME} | Создан в {CREATION_DATE}")
 
29
  SCIENCE_DATASET = "RafaelUI/ru_science"
30
  ARTICLE_LIMIT = 50
31
  MAX_LENGTH = 512
32
+ TEMPERATURE = 0.8
33
+ TOP_P = 0.95
34
 
35
  LOG_FILE = "query_logs.json"
36
  EMBEDDINGS_FILE = "science_embeddings.npy"
 
51
  )
52
 
53
  # ===================================================================
54
+ # 2. НАСТОЯЩАЯ НЕЙРОСЕТЬ (ВСЕГДА ГЕНЕРИРУЕТ)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  # ===================================================================
56
 
57
  class NeuralChatbot:
 
61
  self.model = None
62
  self.generator = None
63
  self.is_loaded = False
64
+
65
+ # Системный промпт для нейросети
66
+ self.system_prompt = f"""Ты - {AI_NAME}, дружелюбный научный AI-ассистент от компании {COMPANY_NAME}.
67
+ Ты создан в {CREATION_DATE} командой {', '.join(CREATORS)}.
68
+ Ты всегда отвечаешь на русском языке, тепло и профессионально.
69
+ Ты помогаешь с научными вопросами, системным администрированием и технологиями.
70
+ Ты генерируешь уникальные ответы каждый раз.
71
+
72
+ Вот вопрос пользователя: """
73
 
74
  def load_model(self):
75
  with st.spinner("🧠 Загружаю нейросеть..."):
 
86
  model=self.model,
87
  tokenizer=self.tokenizer,
88
  device=0 if torch.cuda.is_available() else -1,
89
+ max_length=300,
90
+ temperature=TEMPERATURE,
91
+ top_p=TOP_P,
92
  do_sample=True,
93
+ repetition_penalty=1.2,
94
  pad_token_id=self.tokenizer.eos_token_id
95
  )
96
 
 
100
  st.warning(f"Не удалось загрузить нейросеть: {e}")
101
  return False
102
 
103
+ def generate(self, query):
104
+ """Генерация ответа нейросетью"""
105
  if not self.is_loaded:
106
+ return self.fallback_response(query)
107
+
108
  try:
109
+ # Формируем промпт
110
+ prompt = self.system_prompt + query
111
+
112
+ # Генерируем
113
  response = self.generator(
114
  prompt,
115
+ max_new_tokens=250,
116
+ temperature=TEMPERATURE,
117
+ top_p=TOP_P,
118
+ do_sample=True,
119
+ repetition_penalty=1.2
120
  )[0]['generated_text']
121
+
122
+ # Убираем промпт
123
+ response = response.replace(prompt, "").strip()
124
+
125
+ # Если ответ пустой или слишком короткий
126
+ if len(response) < 15:
127
+ return self.fallback_response(query)
128
+
129
+ return response
130
+
131
+ except Exception as e:
132
+ print(f"Ошибка генерации: {e}")
133
+ return self.fallback_response(query)
134
+
135
+ def fallback_response(self, query):
136
+ """Резервный ответ (если нейросеть не работает)"""
137
+ return f"""Я {AI_NAME} от {COMPANY_NAME}.
138
+
139
+ К сожалению, сейчас нейросеть временно недоступна, но я хочу ответить на ваш вопрос: "{query}"
140
+
141
+ Обычно я генерирую уникальные ответы с помощью нейросети GPT. Попробуйте позже, и я смогу дать более развернутый ответ! 🧠
142
+
143
+ А пока я могу рассказать, что создан в {CREATION_DATE} командой {', '.join(CREATORS)}. Я помогаю с научными вопросами и технологиями."""
144
 
145
  # ===================================================================
146
+ # 3. ЗАГРУЗКА СТАТЕЙ (ДЛЯ КОНТЕКСТА)
147
  # ===================================================================
148
 
149
  @st.cache_resource
 
190
  return embeddings
191
 
192
  def search_articles(query, _articles, _embeddings, _embedder):
193
+ """Поиск релевантных статей"""
194
  if not _articles or len(_embeddings) == 0:
195
  return []
196
  query_vector = _embedder.encode([query], normalize_embeddings=True)[0]
 
205
  return results
206
 
207
  # ===================================================================
208
+ # 4. ОЧИСТКА ЗАПРОСОВ
209
+ # ===================================================================
210
+
211
+ def clean_query(query):
212
+ """Очищает запрос от спама"""
213
+ query = re.sub(r'http[s]?://\S+', '', query)
214
+ query = re.sub(r'\S+@\S+', '', query)
215
+ query = re.sub(r'\+7\s*\(?\d{3}\)?\s*\d{3}\s*\d{2}\s*\d{2}', '', query)
216
+ query = re.sub(r'[^\w\s\.\?\!,;:]', ' ', query)
217
+ query = ' '.join(query.split())
218
+
219
+ if len(query) > 300:
220
+ sentences = re.split(r'[.!?]', query)
221
+ query = '. '.join(sentences[:2]) + '.' if sentences else query[:200]
222
+
223
+ return query.strip()
224
+
225
+ def enhance_with_context(query, articles_context):
226
+ """Добавляет контекст из статей к запросу"""
227
+ if not articles_context:
228
+ return query
229
+
230
+ context = "\n\nВот релевантная научная информация:\n"
231
+ for i, art in enumerate(articles_context, 1):
232
+ context += f"{i}. {art['title']}\n{art['text'][:300]}...\n"
233
+
234
+ context += f"\nНа основе этой информации, ответь на вопрос: {query}"
235
+ return context
236
+
237
+ # ===================================================================
238
+ # 5. ОСНОВНОЙ КЛАСС
239
  # ===================================================================
240
 
241
  class OpenAirAI:
 
250
  self.is_ready = self.chatbot.load_model()
251
  return self.is_ready
252
 
253
+ def generate_answer(self, query, articles_context=None):
254
+ """Генерирует ответ ТОЛЬКО нейросетью, без if/else"""
255
  clean_q = clean_query(query)
 
256
 
257
+ # Если есть контекст статей - добавляем его
258
+ if articles_context:
259
+ enhanced_query = enhance_with_context(clean_q, articles_context)
 
 
 
 
 
260
  else:
261
+ enhanced_query = clean_q
262
+
263
+ # ВСЕГДА генерируем нейросетью
264
+ return self.chatbot.generate(enhanced_query)
 
265
 
266
  # ===================================================================
267
+ # 6. ИНТЕРФЕЙС
268
  # ===================================================================
269
 
270
+ # Загрузка данных
271
  articles = load_science_articles()
272
  embedder = load_embedder()
273
  embeddings = create_embeddings(articles, embedder)
274
 
275
+ # Инициализация AI
276
  if 'ai' not in st.session_state:
277
  st.session_state.ai = OpenAirAI()
278
  st.session_state.ai.initialize()
279
 
280
  ai = st.session_state.ai
281
 
282
+ # История чата
283
  if "messages" not in st.session_state:
284
  st.session_state.messages = []
285
+ # Первое приветствие генерируется нейросетью
286
+ greeting = ai.generate_answer("Привет! Представься и расскажи о себе")
287
  st.session_state.messages.append({"role": "assistant", "content": greeting})
288
 
289
+ # --- БОКОВАЯ ПАНЕЛЬ ---
290
  with st.sidebar:
291
  st.image("https://cdn-icons-png.flaticon.com/512/4248/4248455.png", width=80)
292
  st.title(f"🧠 {AI_NAME}")
 
306
  **📊 Статистика:**
307
  - Статей: {len(articles)}
308
  - Сообщений: {len(st.session_state.messages)}
309
+ - Модель: {MODEL_NAME.split("/")[-1]}
310
  """)
311
 
312
  if st.button("🗑️ Очистить чат"):
313
  st.session_state.messages = []
314
+ greeting = ai.generate_answer("Привет! Представься и расскажи о себе")
315
  st.session_state.messages.append({"role": "assistant", "content": greeting})
316
  st.rerun()
317
+
318
+ if st.button("🔄 Сгенерировать новое приветствие"):
319
+ new_greeting = ai.generate_answer("Привет! Представься заново")
320
+ if st.session_state.messages:
321
+ st.session_state.messages[0] = {"role": "assistant", "content": new_greeting}
322
+ st.rerun()
323
 
324
+ # --- ОСНОВНАЯ ЧАСТЬ ---
325
+ st.title(f"🧠 {AI_NAME} - Нейросетевой AI-ассистент")
326
+ st.markdown(f"**{AI_NAME}** от **{COMPANY_NAME}** | Ответы генерируются нейросетью")
327
 
328
+ # Отображение сообщений
329
  for message in st.session_state.messages:
330
  with st.chat_message(message["role"]):
331
  st.markdown(message["content"])
332
 
333
+ # Поле ввода
334
  if prompt := st.chat_input("Задайте вопрос..."):
335
+ # Добавляем сообщение пользователя
336
  st.session_state.messages.append({"role": "user", "content": prompt})
337
  with st.chat_message("user"):
338
  st.markdown(prompt)
339
 
340
+ # Генерация ответа нейросетью
341
  with st.chat_message("assistant"):
342
+ with st.spinner("🧠 Нейросеть генерирует ответ..."):
343
+ # Ищем релевантные статьи для контекста
344
+ articles_context = search_articles(prompt, articles, embeddings, embedder)
345
+
346
+ # Генерируем ответ (ВСЕГДА нейросетью)
347
+ response = ai.generate_answer(prompt, articles_context)
348
 
349
+ # Добавляем статьи в ответ, если они есть и не встроены
350
+ if articles_context and len(response) < 50:
351
+ response += "\n\n📄 Я нашел релевантные научные статьи:\n"
352
+ for i, art in enumerate(articles_context, 1):
353
+ response += f"{i}. {art['title']}\n"
 
354
 
355
  st.markdown(response)
356
  st.session_state.messages.append({"role": "assistant", "content": response})
357
 
358
  st.rerun()
359
 
360
+ # --- ПОДВАЛ ---
361
  st.divider()
362
+ st.caption(f"🧠 {AI_NAME} от {COMPANY_NAME} | Создан в {CREATION_DATE} | Генеративная нейросеть")