Spaces:
Runtime error
Runtime error
X commited on
Update app.py
Browse files
app.py
CHANGED
|
@@ -35,7 +35,7 @@ SCIENCE_DATASET = "RafaelUI/ru_science"
|
|
| 35 |
ARTICLE_LIMIT = 100
|
| 36 |
MAX_LENGTH = 256
|
| 37 |
BATCH_SIZE = 32
|
| 38 |
-
EPOCHS =
|
| 39 |
|
| 40 |
LOG_FILE = "query_logs.json"
|
| 41 |
EMBEDDINGS_FILE = "science_embeddings.npy"
|
|
@@ -50,7 +50,7 @@ WEBSITE = "https://sites.google.com/view/opruai/home"
|
|
| 50 |
HUGGINGFACE = "https://huggingface.co/OpenRussianAI"
|
| 51 |
CREATION_DATE = "2026"
|
| 52 |
|
| 53 |
-
#
|
| 54 |
TRAINING_DIALOGS = [
|
| 55 |
# Приветствия
|
| 56 |
{
|
|
@@ -63,73 +63,75 @@ TRAINING_DIALOGS = [
|
|
| 63 |
},
|
| 64 |
{
|
| 65 |
"context": "Доброе утро",
|
| 66 |
-
"response": "Доброе утро!
|
| 67 |
},
|
| 68 |
{
|
| 69 |
"context": "Добрый день",
|
| 70 |
-
"response": "Добрый день! OpenAirAI
|
| 71 |
},
|
| 72 |
{
|
| 73 |
"context": "Добрый вечер",
|
| 74 |
-
"response": "Добрый вечер! OpenAirAI готов помочь вам в ваших научных ис
|
| 75 |
},
|
| 76 |
{
|
| 77 |
"context": "Хай",
|
| 78 |
-
"response": "Хай! OpenAirAI
|
| 79 |
},
|
| 80 |
{
|
| 81 |
"context": "Здарова",
|
| 82 |
-
"response": "Здарова! OpenAirAI
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
},
|
| 84 |
|
| 85 |
-
# Вопросы о
|
| 86 |
{
|
| 87 |
-
"context": "
|
| 88 |
-
"response": "
|
| 89 |
},
|
| 90 |
{
|
| 91 |
-
"context": "
|
| 92 |
-
"response": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
},
|
| 94 |
{
|
| 95 |
"context": "Ты кто",
|
| 96 |
-
"response": "Я OpenAirAI, научный AI-ассистент от OpenRussianAI.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
},
|
| 98 |
{
|
| 99 |
"context": "Как тебя зовут",
|
| 100 |
-
"response": "Меня зовут OpenAirAI. Я — ваш
|
| 101 |
},
|
| 102 |
|
| 103 |
# Вопросы о компании
|
| 104 |
{
|
| 105 |
"context": "Что такое OpenRussianAI",
|
| 106 |
-
"response": "OpenRussianAI — это инновационная компания, основанная в 2026 году
|
| 107 |
-
},
|
| 108 |
-
{
|
| 109 |
-
"context": "Какая компания тебя создала",
|
| 110 |
-
"response": "Меня создала компания OpenRussianAI — инновационный проект, основанный в 2026 году. Я — OpenAirAI, их главный научный AI-помощник."
|
| 111 |
},
|
| 112 |
{
|
| 113 |
"context": "Где ваш сайт",
|
| 114 |
-
"response":
|
| 115 |
},
|
| 116 |
{
|
| 117 |
"context": "Где ваши модели",
|
| 118 |
-
"response":
|
| 119 |
-
},
|
| 120 |
-
|
| 121 |
-
# Вопросы о возможностях
|
| 122 |
-
{
|
| 123 |
-
"context": "Что ты умеешь",
|
| 124 |
-
"response": "Я умею анализировать научные статьи, находить информацию по запросу, помогать с исследованиями в области сельского хозяйства, биологии, химии, физики и других наук. Я работаю с датасетом ru_science."
|
| 125 |
},
|
| 126 |
{
|
| 127 |
-
"context": "
|
| 128 |
-
"response": "
|
| 129 |
-
},
|
| 130 |
-
{
|
| 131 |
-
"context": "Чем ты можешь помочь",
|
| 132 |
-
"response": "Я могу помочь найти научную информацию, ответить на вопросы по естественным наукам, найти статьи по вашей теме исследования. Задавайте вопросы!"
|
| 133 |
},
|
| 134 |
|
| 135 |
# Прощания
|
|
@@ -143,25 +145,43 @@ TRAINING_DIALOGS = [
|
|
| 143 |
},
|
| 144 |
{
|
| 145 |
"context": "Спасибо",
|
| 146 |
-
"response": "Всегда рад помочь! Я, OpenAirAI, здесь для ваших научных исследований. Обращайтесь! 😊"
|
| 147 |
},
|
| 148 |
{
|
| 149 |
"context": "Спасибо большое",
|
| 150 |
"response": "Пожалуйста! Рад был помочь. OpenAirAI всегда на связи для ваших научных вопросов! 🌟"
|
| 151 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
-
# Научные
|
| 154 |
{
|
| 155 |
-
"context": "
|
| 156 |
-
"response": "
|
| 157 |
},
|
| 158 |
{
|
| 159 |
"context": "Ты знаешь науку",
|
| 160 |
-
"response": "Да, я специализируюсь на научной информации. Я обучен на датасете ru_science, который содержит тысячи
|
| 161 |
},
|
| 162 |
{
|
| 163 |
"context": "Что такое ru_science",
|
| 164 |
-
"response": "ru_science — это датасет на Hugging Face
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
}
|
| 166 |
]
|
| 167 |
|
|
@@ -181,7 +201,7 @@ else:
|
|
| 181 |
query_logs = []
|
| 182 |
|
| 183 |
# ===================================================================
|
| 184 |
-
# 2. МОДЕЛЬ ДЛЯ ДИАЛОГОВ
|
| 185 |
# ===================================================================
|
| 186 |
|
| 187 |
class DialogModel(nn.Module):
|
|
@@ -222,6 +242,7 @@ class OpenAirAI:
|
|
| 222 |
|
| 223 |
for epoch in range(EPOCHS):
|
| 224 |
total_loss = 0
|
|
|
|
| 225 |
for i in range(0, len(self.contexts), BATCH_SIZE):
|
| 226 |
batch_contexts = self.contexts[i:i+BATCH_SIZE]
|
| 227 |
encodings = self.tokenizer(
|
|
@@ -243,13 +264,15 @@ class OpenAirAI:
|
|
| 243 |
optimizer.step()
|
| 244 |
|
| 245 |
total_loss += loss.item()
|
|
|
|
| 246 |
progress_bar.progress((epoch + i/len(self.contexts)) / EPOCHS)
|
| 247 |
|
| 248 |
-
|
|
|
|
| 249 |
|
| 250 |
torch.save(self.model.state_dict(), self.model_path)
|
| 251 |
self.is_trained = True
|
| 252 |
-
st.success(f"✅ {self.name} обучен!")
|
| 253 |
|
| 254 |
def load_model(self):
|
| 255 |
if os.path.exists(self.model_path):
|
|
@@ -259,16 +282,19 @@ class OpenAirAI:
|
|
| 259 |
self.model.eval()
|
| 260 |
self.is_trained = True
|
| 261 |
return True
|
| 262 |
-
except:
|
|
|
|
| 263 |
return False
|
| 264 |
return False
|
| 265 |
|
| 266 |
def generate_response(self, query):
|
|
|
|
| 267 |
if not self.is_trained:
|
| 268 |
return f"Я {self.name}, научный AI-ассистент от {self.company}. Чем могу помочь?"
|
| 269 |
|
| 270 |
self.model.eval()
|
| 271 |
with torch.no_grad():
|
|
|
|
| 272 |
encodings = self.tokenizer(
|
| 273 |
query,
|
| 274 |
truncation=True,
|
|
@@ -281,6 +307,7 @@ class OpenAirAI:
|
|
| 281 |
outputs = self.model.bert(input_ids=input_ids, attention_mask=attention_mask)
|
| 282 |
query_embedding = outputs.pooler_output
|
| 283 |
|
|
|
|
| 284 |
context_embeddings = []
|
| 285 |
for context in self.contexts:
|
| 286 |
ctx_enc = self.tokenizer(
|
|
@@ -296,16 +323,21 @@ class OpenAirAI:
|
|
| 296 |
context_embeddings.append(ctx_outputs.pooler_output)
|
| 297 |
|
| 298 |
context_embeddings = torch.cat(context_embeddings, dim=0)
|
|
|
|
|
|
|
| 299 |
similarities = F.cosine_similarity(query_embedding, context_embeddings)
|
| 300 |
best_idx = torch.argmax(similarities).item()
|
|
|
|
| 301 |
|
| 302 |
-
|
|
|
|
| 303 |
return self.responses[best_idx]
|
| 304 |
else:
|
| 305 |
-
|
|
|
|
| 306 |
|
| 307 |
# ===================================================================
|
| 308 |
-
# 3.
|
| 309 |
# ===================================================================
|
| 310 |
|
| 311 |
@st.cache_resource
|
|
@@ -346,19 +378,13 @@ def create_test_articles():
|
|
| 346 |
{
|
| 347 |
"id": 1,
|
| 348 |
"title": "Нейроны: строение и функции",
|
| 349 |
-
"text": "Нейроны — это основные клетки нервной системы. Они состоят из тела клетки (сомы), дендритов и аксона.
|
| 350 |
"source": "test"
|
| 351 |
},
|
| 352 |
{
|
| 353 |
"id": 2,
|
| 354 |
"title": "Влияние удобрений на рост растений",
|
| 355 |
-
"text": "
|
| 356 |
-
"source": "test"
|
| 357 |
-
},
|
| 358 |
-
{
|
| 359 |
-
"id": 3,
|
| 360 |
-
"title": "Методы биоконверсии",
|
| 361 |
-
"text": "Биоконверсия позволяет эффективно перерабатывать органические отходы в ценные удобрения. Процесс основан на деятельности микроорганизмов.",
|
| 362 |
"source": "test"
|
| 363 |
}
|
| 364 |
]
|
|
@@ -376,7 +402,7 @@ def create_embeddings(_articles, _embedder):
|
|
| 376 |
with st.spinner("📊 Загружаю эмбеддинги с диска..."):
|
| 377 |
return np.load(embeddings_file)
|
| 378 |
|
| 379 |
-
with st.spinner(f"🔢 Создаю эмбеддинги для {len(_articles)} статей
|
| 380 |
texts = [f"{a['title']}\n\n{a['text']}" for a in _articles]
|
| 381 |
embeddings = _embedder.encode(
|
| 382 |
texts,
|
|
@@ -390,7 +416,7 @@ def create_embeddings(_articles, _embedder):
|
|
| 390 |
return embeddings
|
| 391 |
|
| 392 |
# ===================================================================
|
| 393 |
-
# 4.
|
| 394 |
# ===================================================================
|
| 395 |
|
| 396 |
def search_science(query, _articles, _embeddings, _embedder):
|
|
@@ -405,7 +431,7 @@ def search_science(query, _articles, _embeddings, _embedder):
|
|
| 405 |
results = []
|
| 406 |
for idx in top_indices:
|
| 407 |
score = float(scores[int(idx)])
|
| 408 |
-
if score > 0.15:
|
| 409 |
article = _articles[int(idx)]
|
| 410 |
results.append({
|
| 411 |
"title": article['title'],
|
|
@@ -446,7 +472,7 @@ dialog_ai = st.session_state.dialog_ai
|
|
| 446 |
# Инициализация истории чата
|
| 447 |
if "messages" not in st.session_state:
|
| 448 |
st.session_state.messages = []
|
| 449 |
-
#
|
| 450 |
greeting = dialog_ai.generate_response("Привет")
|
| 451 |
st.session_state.messages.append({"role": "assistant", "content": greeting})
|
| 452 |
|
|
@@ -464,23 +490,21 @@ with st.sidebar:
|
|
| 464 |
---
|
| 465 |
**🔗 Ссылки:**
|
| 466 |
|
| 467 |
-
[🌐 Сайт
|
| 468 |
-
|
| 469 |
[🤗 Hugging Face]({HUGGINGFACE})
|
| 470 |
|
| 471 |
---
|
| 472 |
**📊 Статистика:**
|
| 473 |
-
|
| 474 |
-
- Научных статей: {len(articles)}
|
| 475 |
- Сообщений: {len(st.session_state.messages)}
|
|
|
|
| 476 |
""")
|
| 477 |
|
| 478 |
if os.path.exists(EMBEDDINGS_FILE):
|
| 479 |
size = os.path.getsize(EMBEDDINGS_FILE) / (1024 * 1024)
|
| 480 |
st.caption(f"💾 Эмбеддинги: {size:.1f} MB")
|
| 481 |
|
| 482 |
-
|
| 483 |
-
if st.button("🗑️ Очистить чат", type="secondary"):
|
| 484 |
st.session_state.messages = []
|
| 485 |
greeting = dialog_ai.generate_response("Привет")
|
| 486 |
st.session_state.messages.append({"role": "assistant", "content": greeting})
|
|
@@ -497,7 +521,7 @@ with chat_container:
|
|
| 497 |
with st.chat_message(message["role"]):
|
| 498 |
st.markdown(message["content"])
|
| 499 |
|
| 500 |
-
# Если
|
| 501 |
if message.get("articles"):
|
| 502 |
for i, article in enumerate(message["articles"], 1):
|
| 503 |
with st.expander(f"📄 #{i} {article['title']} (сходство: {article['score']:.2f})"):
|
|
@@ -512,24 +536,22 @@ if prompt := st.chat_input("Задайте вопрос о науке..."):
|
|
| 512 |
with st.chat_message("user"):
|
| 513 |
st.markdown(prompt)
|
| 514 |
|
| 515 |
-
# Генерируем ответ
|
| 516 |
with st.chat_message("assistant"):
|
| 517 |
with st.spinner("Думаю..."):
|
| 518 |
-
#
|
| 519 |
-
|
| 520 |
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
st.session_state.messages.append({"role": "assistant", "content": response})
|
| 526 |
-
else:
|
| 527 |
# Ищем в научных статьях
|
| 528 |
results = search_science(prompt, articles, embeddings, embedder)
|
| 529 |
|
| 530 |
if results:
|
| 531 |
# Формируем ответ с результатами
|
| 532 |
-
response = f"🔍 Я нашел в научных статьях информацию по вашему запросу:\n\n"
|
| 533 |
for i, result in enumerate(results, 1):
|
| 534 |
response += f"**{i}. {result['title']}** (сходство: {result['score']:.2f})\n"
|
| 535 |
response += f"{result['text'][:300]}...\n\n"
|
|
@@ -541,12 +563,14 @@ if prompt := st.chat_input("Задайте вопрос о науке..."):
|
|
| 541 |
"articles": results
|
| 542 |
})
|
| 543 |
else:
|
| 544 |
-
# Если ничего не найдено
|
| 545 |
-
|
| 546 |
-
st.
|
| 547 |
-
|
|
|
|
|
|
|
|
|
|
| 548 |
|
| 549 |
-
# Перезагружаем страницу для обновления чата
|
| 550 |
st.rerun()
|
| 551 |
|
| 552 |
# --- ПОДВАЛ ---
|
|
|
|
| 35 |
ARTICLE_LIMIT = 100
|
| 36 |
MAX_LENGTH = 256
|
| 37 |
BATCH_SIZE = 32
|
| 38 |
+
EPOCHS = 5 # Увеличиваем для лучшего обучения
|
| 39 |
|
| 40 |
LOG_FILE = "query_logs.json"
|
| 41 |
EMBEDDINGS_FILE = "science_embeddings.npy"
|
|
|
|
| 50 |
HUGGINGFACE = "https://huggingface.co/OpenRussianAI"
|
| 51 |
CREATION_DATE = "2026"
|
| 52 |
|
| 53 |
+
# ОБУЧАЮЩИЕ ДИАЛОГИ ДЛЯ ВСЕХ СЛУЧАЕВ
|
| 54 |
TRAINING_DIALOGS = [
|
| 55 |
# Приветствия
|
| 56 |
{
|
|
|
|
| 63 |
},
|
| 64 |
{
|
| 65 |
"context": "Доброе утро",
|
| 66 |
+
"response": "Доброе утро! OpenAirAI на связи. Я готов искать ответы на ваши вопросы в научных статьях! ☀️"
|
| 67 |
},
|
| 68 |
{
|
| 69 |
"context": "Добрый день",
|
| 70 |
+
"response": "Добрый день! OpenAirAI здесь, чтобы помочь вам с научными исследованиями. Задавайте вопросы! 🌾"
|
| 71 |
},
|
| 72 |
{
|
| 73 |
"context": "Добрый вечер",
|
| 74 |
+
"response": "Добрый вечер! OpenAirAI готов помочь вам в ваших научных изысканиях. Чем могу быть полезен? 🌙"
|
| 75 |
},
|
| 76 |
{
|
| 77 |
"context": "Хай",
|
| 78 |
+
"response": "Хай! OpenAirAI на связи. Я создан для работы с научными статьями. Задавайте вопросы! 🚀"
|
| 79 |
},
|
| 80 |
{
|
| 81 |
"context": "Здарова",
|
| 82 |
+
"response": "Здарова! OpenAirAI готов к работе. Ищем научную информацию для вас! 🔍"
|
| 83 |
+
},
|
| 84 |
+
{
|
| 85 |
+
"context": "Приветик",
|
| 86 |
+
"response": "Приветик! OpenAirAI рад вас видеть. Чем могу помочь в научных исследованиях? 😊"
|
| 87 |
+
},
|
| 88 |
+
{
|
| 89 |
+
"context": "Здравствуй",
|
| 90 |
+
"response": "Здравствуй! OpenAirAI на связи. Я здесь, чтобы помочь вам с научными вопросами! 🧪"
|
| 91 |
},
|
| 92 |
|
| 93 |
+
# Вопросы о возможностях
|
| 94 |
{
|
| 95 |
+
"context": "Что ты умеешь",
|
| 96 |
+
"response": "Я умею анализировать научные статьи, находить информацию по запросу, помогать с исследованиями. Моя специализация - сельское хозяйство, биология, химия. Также я могу отвечать на общие вопросы и поддерживать диалог. Чем могу помочь? 🧪"
|
| 97 |
},
|
| 98 |
{
|
| 99 |
+
"context": "Расскажи о себе",
|
| 100 |
+
"response": "Я OpenAirAI, научный AI-помощник от OpenRussianAI. Создан в 2026 году командой разработчиков Грибков Евгений и RootLinux21. Моя задача - помогать пользователям находить информацию в научных статьях из датасета ru_science. Я использую передовые технологии обработки естественного языка. Задавайте вопросы! 🤖"
|
| 101 |
+
},
|
| 102 |
+
{
|
| 103 |
+
"context": "Кто ты",
|
| 104 |
+
"response": "Меня зовут OpenAirAI. Я — AI-ассистент, созданный компанией OpenRussianAI в 2026 году. Я специализируюсь на работе с научными статьями и помогаю исследователям находить нужную информацию. 🔬"
|
| 105 |
},
|
| 106 |
{
|
| 107 |
"context": "Ты кто",
|
| 108 |
+
"response": "Я OpenAirAI, научный AI-ассистент от OpenRussianAI. Моя цель - сделать научные знания доступными для всех. Задавайте вопросы о науке! 🌟"
|
| 109 |
+
},
|
| 110 |
+
{
|
| 111 |
+
"context": "Кто тебя создал",
|
| 112 |
+
"response": "Меня создала команда OpenRussianAI в составе Грибкова Евгения и RootLinux21 в 2026 году. Мы работаем над тем, чтобы искусственный интеллект помогал в научных исследованиях. 🚀"
|
| 113 |
},
|
| 114 |
{
|
| 115 |
"context": "Как тебя зовут",
|
| 116 |
+
"response": "Меня зовут OpenAirAI. Я — ваш научный помощник от OpenRussianAI. Приятно познакомиться! 😊"
|
| 117 |
},
|
| 118 |
|
| 119 |
# Вопросы о компании
|
| 120 |
{
|
| 121 |
"context": "Что такое OpenRussianAI",
|
| 122 |
+
"response": "OpenRussianAI — это инновационная компания, основанная в 2026 году. Мы создаём интеллектуальных помощников для науки. Я, OpenAirAI, - один из наших проектов. Наш сайт: https://sites.google.com/view/opruai/home 🏢"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
},
|
| 124 |
{
|
| 125 |
"context": "Где ваш сайт",
|
| 126 |
+
"response": "Сайт OpenRussianAI: https://sites.google.com/view/opruai/home. Там вы узнаете больше о проекте и о мне — OpenAirAI. 🌐"
|
| 127 |
},
|
| 128 |
{
|
| 129 |
"context": "Где ваши модели",
|
| 130 |
+
"response": "Наши модели на Hugging Face: https://huggingface.co/OpenRussianAI. Там вы найдёте и меня — OpenAirAI! 🤗"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
},
|
| 132 |
{
|
| 133 |
+
"context": "Какая компания тебя создала",
|
| 134 |
+
"response": "Меня создала компания OpenRussianAI — инновационный проект, основанный в 2026 году. Я — их главный научный AI-помощник. 🏢"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
},
|
| 136 |
|
| 137 |
# Прощания
|
|
|
|
| 145 |
},
|
| 146 |
{
|
| 147 |
"context": "Спасибо",
|
| 148 |
+
"response": "Всегда рад помочь! Я, OpenAirAI, здесь для ваших научных исследований. Обращайтесь в любое время! 😊"
|
| 149 |
},
|
| 150 |
{
|
| 151 |
"context": "Спасибо большое",
|
| 152 |
"response": "Пожалуйста! Рад был помочь. OpenAirAI всегда на связи для ваших научных вопросов! 🌟"
|
| 153 |
},
|
| 154 |
+
{
|
| 155 |
+
"context": "Благодарю",
|
| 156 |
+
"response": "Благодарю за обращение! OpenAirAI всегда готов помочь с научными вопросами. Удачи! 🎯"
|
| 157 |
+
},
|
| 158 |
|
| 159 |
+
# Научные темы
|
| 160 |
{
|
| 161 |
+
"context": "Что такое наука",
|
| 162 |
+
"response": "Наука — это система знаний о закономерностях развития природы, общества и мышления. Я, OpenAirAI, помогаю находить научную информацию в статьях. Что именно вас интересует? 🔬"
|
| 163 |
},
|
| 164 |
{
|
| 165 |
"context": "Ты знаешь науку",
|
| 166 |
+
"response": "Да, я специализируюсь на научной информации. Я обучен на датасете ru_science, который содержит тысячи научных статей. Спрашивайте о чём угодно! 📚"
|
| 167 |
},
|
| 168 |
{
|
| 169 |
"context": "Что такое ru_science",
|
| 170 |
+
"response": "ru_science — это датасет на Hugging Face с русскоязычными научными статьями. На нём я был обучен. Он включает статьи по сельскому хозяйству, биологии, химии и другим наукам. 📖"
|
| 171 |
+
},
|
| 172 |
+
{
|
| 173 |
+
"context": "Помоги найти информацию",
|
| 174 |
+
"response": "Конечно! Я, OpenAirAI, помогу найти научную информацию. Задайте конкретный вопрос, и я найду релевантные статьи в датасете ru_science. 🔍"
|
| 175 |
+
},
|
| 176 |
+
|
| 177 |
+
# Универсальные ответы
|
| 178 |
+
{
|
| 179 |
+
"context": "Как дела",
|
| 180 |
+
"response": "У меня всё отлично! Я OpenAirAI, и я всегда готов помочь с научными вопросами. А как ваши исследования? 😊"
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"context": "Что нового",
|
| 184 |
+
"response": "Я, OpenAirAI, постоянно учусь и совершенствуюсь. Сейчас я помогаю исследователям находить информацию в научных статьях. Чем могу помочь вам? 🚀"
|
| 185 |
}
|
| 186 |
]
|
| 187 |
|
|
|
|
| 201 |
query_logs = []
|
| 202 |
|
| 203 |
# ===================================================================
|
| 204 |
+
# 2. МОДЕЛЬ ДЛЯ ДИАЛОГОВ (ВСЕГДА ИСПОЛЬЗУЕТСЯ)
|
| 205 |
# ===================================================================
|
| 206 |
|
| 207 |
class DialogModel(nn.Module):
|
|
|
|
| 242 |
|
| 243 |
for epoch in range(EPOCHS):
|
| 244 |
total_loss = 0
|
| 245 |
+
num_batches = 0
|
| 246 |
for i in range(0, len(self.contexts), BATCH_SIZE):
|
| 247 |
batch_contexts = self.contexts[i:i+BATCH_SIZE]
|
| 248 |
encodings = self.tokenizer(
|
|
|
|
| 264 |
optimizer.step()
|
| 265 |
|
| 266 |
total_loss += loss.item()
|
| 267 |
+
num_batches += 1
|
| 268 |
progress_bar.progress((epoch + i/len(self.contexts)) / EPOCHS)
|
| 269 |
|
| 270 |
+
avg_loss = total_loss / num_batches if num_batches > 0 else 0
|
| 271 |
+
st.write(f"Эпоха {epoch+1}/{EPOCHS}, Потери: {avg_loss:.4f}")
|
| 272 |
|
| 273 |
torch.save(self.model.state_dict(), self.model_path)
|
| 274 |
self.is_trained = True
|
| 275 |
+
st.success(f"✅ {self.name} обучен на {len(self.contexts)} диалогах!")
|
| 276 |
|
| 277 |
def load_model(self):
|
| 278 |
if os.path.exists(self.model_path):
|
|
|
|
| 282 |
self.model.eval()
|
| 283 |
self.is_trained = True
|
| 284 |
return True
|
| 285 |
+
except Exception as e:
|
| 286 |
+
st.warning(f"Не удалось загрузить модель: {e}")
|
| 287 |
return False
|
| 288 |
return False
|
| 289 |
|
| 290 |
def generate_response(self, query):
|
| 291 |
+
"""Генерирует ответ ТОЛЬКО через ИИ, без if/else"""
|
| 292 |
if not self.is_trained:
|
| 293 |
return f"Я {self.name}, научный AI-ассистент от {self.company}. Чем могу помочь?"
|
| 294 |
|
| 295 |
self.model.eval()
|
| 296 |
with torch.no_grad():
|
| 297 |
+
# Кодируем запрос
|
| 298 |
encodings = self.tokenizer(
|
| 299 |
query,
|
| 300 |
truncation=True,
|
|
|
|
| 307 |
outputs = self.model.bert(input_ids=input_ids, attention_mask=attention_mask)
|
| 308 |
query_embedding = outputs.pooler_output
|
| 309 |
|
| 310 |
+
# Кодируем все контексты
|
| 311 |
context_embeddings = []
|
| 312 |
for context in self.contexts:
|
| 313 |
ctx_enc = self.tokenizer(
|
|
|
|
| 323 |
context_embeddings.append(ctx_outputs.pooler_output)
|
| 324 |
|
| 325 |
context_embeddings = torch.cat(context_embeddings, dim=0)
|
| 326 |
+
|
| 327 |
+
# Находим наиболее похожий контекст
|
| 328 |
similarities = F.cosine_similarity(query_embedding, context_embeddings)
|
| 329 |
best_idx = torch.argmax(similarities).item()
|
| 330 |
+
best_similarity = similarities[best_idx].item()
|
| 331 |
|
| 332 |
+
# Всегда возвращаем ответ от ИИ (даже если похожесть низкая)
|
| 333 |
+
if best_similarity > 0.3:
|
| 334 |
return self.responses[best_idx]
|
| 335 |
else:
|
| 336 |
+
# Если ничего не подошло, генерируем общий ответ
|
| 337 |
+
return f"Я {self.name}, научный AI-ассистент от {self.company}. Создан в {self.creation_date} командой {', '.join(self.creators)}. Я помогаю находить информацию в научных статьях. Чем могу помочь? 🧪"
|
| 338 |
|
| 339 |
# ===================================================================
|
| 340 |
+
# 3. ЗАГРУЗКА НАУЧНЫХ СТАТЕЙ
|
| 341 |
# ===================================================================
|
| 342 |
|
| 343 |
@st.cache_resource
|
|
|
|
| 378 |
{
|
| 379 |
"id": 1,
|
| 380 |
"title": "Нейроны: строение и функции",
|
| 381 |
+
"text": "Нейроны — это основные клетки нервной системы. Они состоят из тела клетки (сомы), дендритов и аксона.",
|
| 382 |
"source": "test"
|
| 383 |
},
|
| 384 |
{
|
| 385 |
"id": 2,
|
| 386 |
"title": "Влияние удобрений на рост растений",
|
| 387 |
+
"text": "Применение азотных удобрений увеличивает урожайность сельскохозяйственных культур.",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
"source": "test"
|
| 389 |
}
|
| 390 |
]
|
|
|
|
| 402 |
with st.spinner("📊 Загружаю эмбеддинги с диска..."):
|
| 403 |
return np.load(embeddings_file)
|
| 404 |
|
| 405 |
+
with st.spinner(f"🔢 Создаю эмбеддинги для {len(_articles)} статей..."):
|
| 406 |
texts = [f"{a['title']}\n\n{a['text']}" for a in _articles]
|
| 407 |
embeddings = _embedder.encode(
|
| 408 |
texts,
|
|
|
|
| 416 |
return embeddings
|
| 417 |
|
| 418 |
# ===================================================================
|
| 419 |
+
# 4. ПОИСК В СТАТЬЯХ
|
| 420 |
# ===================================================================
|
| 421 |
|
| 422 |
def search_science(query, _articles, _embeddings, _embedder):
|
|
|
|
| 431 |
results = []
|
| 432 |
for idx in top_indices:
|
| 433 |
score = float(scores[int(idx)])
|
| 434 |
+
if score > 0.15:
|
| 435 |
article = _articles[int(idx)]
|
| 436 |
results.append({
|
| 437 |
"title": article['title'],
|
|
|
|
| 472 |
# Инициализация истории чата
|
| 473 |
if "messages" not in st.session_state:
|
| 474 |
st.session_state.messages = []
|
| 475 |
+
# Приветствие генерируется ИИ!
|
| 476 |
greeting = dialog_ai.generate_response("Привет")
|
| 477 |
st.session_state.messages.append({"role": "assistant", "content": greeting})
|
| 478 |
|
|
|
|
| 490 |
---
|
| 491 |
**🔗 Ссылки:**
|
| 492 |
|
| 493 |
+
[🌐 Сайт]({WEBSITE})
|
|
|
|
| 494 |
[🤗 Hugging Face]({HUGGINGFACE})
|
| 495 |
|
| 496 |
---
|
| 497 |
**📊 Статистика:**
|
| 498 |
+
- Статей: {len(articles)}
|
|
|
|
| 499 |
- Сообщений: {len(st.session_state.messages)}
|
| 500 |
+
- Обучен на: {len(dialog_ai.contexts)} диалогах
|
| 501 |
""")
|
| 502 |
|
| 503 |
if os.path.exists(EMBEDDINGS_FILE):
|
| 504 |
size = os.path.getsize(EMBEDDINGS_FILE) / (1024 * 1024)
|
| 505 |
st.caption(f"💾 Эмбеддинги: {size:.1f} MB")
|
| 506 |
|
| 507 |
+
if st.button("🗑️ Очистить чат"):
|
|
|
|
| 508 |
st.session_state.messages = []
|
| 509 |
greeting = dialog_ai.generate_response("Привет")
|
| 510 |
st.session_state.messages.append({"role": "assistant", "content": greeting})
|
|
|
|
| 521 |
with st.chat_message(message["role"]):
|
| 522 |
st.markdown(message["content"])
|
| 523 |
|
| 524 |
+
# Если есть статьи, показываем их
|
| 525 |
if message.get("articles"):
|
| 526 |
for i, article in enumerate(message["articles"], 1):
|
| 527 |
with st.expander(f"📄 #{i} {article['title']} (сходство: {article['score']:.2f})"):
|
|
|
|
| 536 |
with st.chat_message("user"):
|
| 537 |
st.markdown(prompt)
|
| 538 |
|
| 539 |
+
# Генерируем ответ ТОЛЬКО через ИИ
|
| 540 |
with st.chat_message("assistant"):
|
| 541 |
with st.spinner("Думаю..."):
|
| 542 |
+
# ВСЕГДА используем ИИ для ответа
|
| 543 |
+
ai_response = dialog_ai.generate_response(prompt)
|
| 544 |
|
| 545 |
+
# Проверяем, похоже ли на научный вопрос
|
| 546 |
+
is_science_question = any(word in prompt.lower() for word in ["науч", "исслед", "стать", "экспери", "лаборатор", "анализ", "данн"])
|
| 547 |
+
|
| 548 |
+
if is_science_question or len(prompt.split()) > 3:
|
|
|
|
|
|
|
| 549 |
# Ищем в научных статьях
|
| 550 |
results = search_science(prompt, articles, embeddings, embedder)
|
| 551 |
|
| 552 |
if results:
|
| 553 |
# Формируем ответ с результатами
|
| 554 |
+
response = f"🔍 **Я нашел в научных статьях информацию по вашему запросу:**\n\n"
|
| 555 |
for i, result in enumerate(results, 1):
|
| 556 |
response += f"**{i}. {result['title']}** (сходство: {result['score']:.2f})\n"
|
| 557 |
response += f"{result['text'][:300]}...\n\n"
|
|
|
|
| 563 |
"articles": results
|
| 564 |
})
|
| 565 |
else:
|
| 566 |
+
# Если ничего не найдено, показываем ответ ИИ
|
| 567 |
+
st.markdown(ai_response)
|
| 568 |
+
st.session_state.messages.append({"role": "assistant", "content": ai_response})
|
| 569 |
+
else:
|
| 570 |
+
# Показываем ответ ИИ (приветствия, вопросы о себе и т.д.)
|
| 571 |
+
st.markdown(ai_response)
|
| 572 |
+
st.session_state.messages.append({"role": "assistant", "content": ai_response})
|
| 573 |
|
|
|
|
| 574 |
st.rerun()
|
| 575 |
|
| 576 |
# --- ПОДВАЛ ---
|