vydrking commited on
Commit
33a43a3
·
verified ·
1 Parent(s): 8740c0e

Upload 17 files

Browse files
Files changed (6) hide show
  1. .dockerignore +31 -0
  2. Dockerfile +22 -0
  3. README.md +59 -32
  4. app.py +278 -179
  5. docker-compose.yml +12 -0
  6. requirements.txt +1 -1
.dockerignore ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .Python
6
+ env
7
+ pip-log.txt
8
+ pip-delete-this-directory.txt
9
+ .tox
10
+ .coverage
11
+ .coverage.*
12
+ .cache
13
+ nosetests.xml
14
+ coverage.xml
15
+ *.cover
16
+ *.log
17
+ .git
18
+ .mypy_cache
19
+ .pytest_cache
20
+ .hypothesis
21
+ .DS_Store
22
+ .env
23
+ .venv
24
+ venv/
25
+ ENV/
26
+ env/
27
+ .idea/
28
+ .vscode/
29
+ *.swp
30
+ *.swo
31
+ *~
Dockerfile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ # Устанавливаем рабочую директорию
4
+ WORKDIR /app
5
+
6
+ # Копируем requirements.txt
7
+ COPY requirements.txt .
8
+
9
+ # Устанавливаем зависимости
10
+ RUN pip install --no-cache-dir -r requirements.txt
11
+
12
+ # Копируем код приложения
13
+ COPY . .
14
+
15
+ # Создаем директории для данных
16
+ RUN mkdir -p data/processed
17
+
18
+ # Открываем порт
19
+ EXPOSE 5000
20
+
21
+ # Запускаем приложение
22
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -9,7 +9,7 @@ app_file: app.py
9
  pinned: false
10
  ---
11
 
12
- # 🤖 ITMO Магистратура - Чат-бот
13
 
14
  Минимально работающий прототип чат-бота для абитуриентов магистратур ITMO с парсингом данных, диалоговой системой и персонализированными рекомендациями.
15
 
@@ -35,58 +35,63 @@ pinned: false
35
 
36
  ## 🚀 Быстрый старт
37
 
38
- ### Локальный запуск
39
-
40
- ```bash
41
- # Установка зависимостей
42
- pip install -r requirements.txt
43
-
44
- # Запуск приложения
45
- python app.py
46
- ```
47
-
48
  ### Деплой на Hugging Face Spaces
49
 
50
- 1. Создайте новый Space на [Hugging Face](https://huggingface.co/spaces)
51
- 2. Выберите **Gradio SDK**
52
- 3. Загрузите файлы:
53
  - `app.py` → `app.py`
54
  - `parser.py` → `parser.py`
55
  - `data_layer.py` → `data_layer.py`
56
  - `llm.py` → `llm.py`
57
  - `requirements.txt` → `requirements.txt`
 
58
  - `README.md` → `README.md`
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  ## 📁 Структура проекта
61
 
62
  ```
63
- ├── app.py # Основное приложение Gradio
64
  ├── parser.py # Парсинг данных с сайтов ITMO
65
  ├── data_layer.py # Работа с данными и рекомендации
66
  ├── llm.py # LLM система
67
- ├── requirements.txt # Зависимости
68
- ├── README.md # Документация
69
- └── data/ # Данные (автоматически создается)
70
- └── processed/ # Обработанные данные
71
- └── courses.json
72
  ```
73
 
74
  ## 🎯 Что работает
75
 
76
- ✅ **Парсинг данных** - автоматический сбор с сайтов ITMO
77
- ✅ **Диалоговая система** - LLM-powered чат с контекстным поиском
78
- ✅ **Рекомендации** - персонализированные по профилю студента
79
- ✅ **12 fallback курсов** - полные учебные планы ИИ и AI Product
80
- ✅ **Строгая релевантность** - отвечает только на вопросы об ITMO
81
- ✅ **Fallback режим** - работает без LLM
82
- **Модульная архитектура** - разделение на компоненты
 
83
 
84
  ## 🔧 Технологии
85
 
86
- - **Gradio** - веб-интерфейс
87
  - **Transformers** - LLM модель (RuT5-base-multitask)
88
  - **BeautifulSoup** - парсинг HTML страниц
89
  - **Requests** - HTTP запросы к сайтам ITMO
 
90
 
91
  ## 📊 Данные
92
 
@@ -97,6 +102,7 @@ python app.py
97
  ### Курсы (12 fallback курсов)
98
  - **Семестры 1-4** с полной информацией
99
  - **Теги** для поиска и рекомендаций (ml, dl, nlp, cv, product, business, etc.)
 
100
  - **Кредиты и часы** обучения
101
  - **Типы курсов** - required/elective
102
 
@@ -119,7 +125,7 @@ python app.py
119
 
120
  ## 🔄 Обновление данных
121
 
122
- Кнопка "🔄 Обновить данные" выполняет:
123
  - **Парсинг страниц** программ с сайта ITMO
124
  - **Поиск PDF файлов** с учебными планами
125
  - **Обновление курсов** и метаданных
@@ -161,6 +167,27 @@ python app.py
161
  - id, program_id, semester, name, credits, type, short_desc, tags
162
  - Используются при пустом/недоступном парсинге
163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  ## 📝 Лицензия
165
 
166
  MIT License - свободное использование и модификация.
@@ -168,8 +195,8 @@ MIT License - свободное использование и модифика
168
  ## 🤝 Поддержка
169
 
170
  При возникновении проблем:
171
- 1. Проверьте логи в консоли
172
- 2. Убедитесь в наличии всех зависимостей
173
  3. Проверьте доступность сайта ITMO
174
  4. Используйте fallback режим при проблемах с LLM
175
 
@@ -177,4 +204,4 @@ MIT License - свободное использование и модифика
177
 
178
  **Создано для абитуриентов магистратур ITMO** 🎓
179
 
180
- *Минимально работающий прототип: парсинг + диалог + рекомендации*
 
9
  pinned: false
10
  ---
11
 
12
+ # 🤖 ITMO Магистратура - Чат-бот (HF Spaces)
13
 
14
  Минимально работающий прототип чат-бота для абитуриентов магистратур ITMO с парсингом данных, диалоговой системой и персонализированными рекомендациями.
15
 
 
35
 
36
  ## 🚀 Быстрый старт
37
 
 
 
 
 
 
 
 
 
 
 
38
  ### Деплой на Hugging Face Spaces
39
 
40
+ 1. **Создайте новый Space** на [Hugging Face](https://huggingface.co/spaces)
41
+ 2. **Выберите SDK**: `Docker`
42
+ 3. **Загрузите файлы** в репозиторий:
43
  - `app.py` → `app.py`
44
  - `parser.py` → `parser.py`
45
  - `data_layer.py` → `data_layer.py`
46
  - `llm.py` → `llm.py`
47
  - `requirements.txt` → `requirements.txt`
48
+ - `Dockerfile` → `Dockerfile`
49
  - `README.md` → `README.md`
50
 
51
+ 4. **Space автоматически соберет и запустит** приложение
52
+
53
+ ### Локальный запуск
54
+
55
+ ```bash
56
+ # Устанавливаем зависимости
57
+ pip install -r requirements.txt
58
+
59
+ # Запускаем приложение
60
+ python app.py
61
+ ```
62
+
63
+ Приложение будет доступно по адресу: http://localhost:7860
64
+
65
  ## 📁 Структура проекта
66
 
67
  ```
68
+ ├── app.py # Flask приложение с встроенным HTML
69
  ├── parser.py # Парсинг данных с сайтов ITMO
70
  ├── data_layer.py # Работа с данными и рекомендации
71
  ├── llm.py # LLM система
72
+ ├── Dockerfile # Docker конфигурация
73
+ ├── requirements.txt # Зависимости
74
+ └── README.md # Документация
 
 
75
  ```
76
 
77
  ## 🎯 Что работает
78
 
79
+ ✅ **Парсинг данных** - автоматический сбор с сайтов ITMO
80
+ ✅ **Диалоговая система** - LLM-powered чат с контекстным поиском
81
+ ✅ **Рекомендации** - персонализированные по профилю студента
82
+ ✅ **12 fallback курсов** - полные учебные планы ИИ и AI Product
83
+ ✅ **Строгая релевантность** - отвечает только на вопросы об ITMO
84
+ ✅ **Fallback режим** - работает без LLM
85
+ **HF Spaces совместимость** - работает на Hugging Face Spaces
86
+ ✅ **REST API** - JSON API для интеграции
87
 
88
  ## 🔧 Технологии
89
 
90
+ - **Flask** - веб-фреймворк
91
  - **Transformers** - LLM модель (RuT5-base-multitask)
92
  - **BeautifulSoup** - парсинг HTML страниц
93
  - **Requests** - HTTP запросы к сайтам ITMO
94
+ - **Docker** - контейнеризация
95
 
96
  ## 📊 Данные
97
 
 
102
  ### Курсы (12 fallback курсов)
103
  - **Семестры 1-4** с полной информацией
104
  - **Теги** для поиска и рекомендаций (ml, dl, nlp, cv, product, business, etc.)
105
+ - **Сложность** - beginner/intermediate/advanced
106
  - **Кредиты и часы** обучения
107
  - **Типы курсов** - required/elective
108
 
 
125
 
126
  ## 🔄 Обновление данных
127
 
128
+ Кнопка "Обновить данные" выполняет:
129
  - **Парсинг страниц** программ с сайта ITMO
130
  - **Поиск PDF файлов** с учебными планами
131
  - **Обновление курсов** и метаданных
 
167
  - id, program_id, semester, name, credits, type, short_desc, tags
168
  - Используются при пустом/недоступном парсинге
169
 
170
+ ## 🐳 Docker деплой
171
+
172
+ ### На любом сервере с Docker:
173
+
174
+ ```bash
175
+ # Клонируем репозиторий
176
+ git clone <your-repo-url>
177
+ cd <your-repo-name>
178
+
179
+ # Запускаем
180
+ docker build -t itmo-chatbot .
181
+ docker run -p 7860:7860 itmo-chatbot
182
+ ```
183
+
184
+ ### На Hugging Face Spaces:
185
+
186
+ 1. Создайте новый Space
187
+ 2. Выберите **Docker SDK**
188
+ 3. Загрузите все файлы в репозиторий
189
+ 4. Space автоматически соберет и запустит Docker контейнер
190
+
191
  ## 📝 Лицензия
192
 
193
  MIT License - свободное использование и модификация.
 
195
  ## 🤝 Поддержка
196
 
197
  При возникновении проблем:
198
+ 1. Проверьте логи в консоли HF Spaces
199
+ 2. Убедитесь в наличии всех файлов
200
  3. Проверьте доступность сайта ITMO
201
  4. Используйте fallback режим при проблемах с LLM
202
 
 
204
 
205
  **Создано для абитуриентов магистратур ITMO** 🎓
206
 
207
+ *Минимально работающий прототип: парсинг + диалог + рекомендации + HF Spaces*
app.py CHANGED
@@ -1,179 +1,278 @@
1
- import gradio as gr
2
- import os
3
-
4
- # Импорты модулей
5
- from parser import parse_all
6
- from data_layer import load_courses, filter_courses, recommend_courses, is_relevant
7
- from llm import answer, generate_recommendations
8
-
9
- # Инициализация данных
10
- courses = load_courses()
11
- print(f'Загружено курсов: {len(courses)}')
12
-
13
- def handle_message(message, history):
14
- """Обработчик сообщений чата"""
15
- if not message.strip():
16
- return history, ''
17
-
18
- # Проверяем релевантность
19
- if not is_relevant(message):
20
- response = '''Похоже, вопрос не относится к магистратурам ITMO и их учебным планам.
21
-
22
- Попробуйте спросить, например:
23
- • "Какие дисциплины по NLP в 1 семестре программы ИИ?"
24
- "Расскажи о программе AI Product"
25
- "Какие курсы по машинному обучению есть в программе ИИ?"
26
- "Сколько кредитов за дисциплину 'Глубокое обучение'?"
27
- "Какие курсы подходят для моего профиля?"'''
28
- return history + [[message, response]], ''
29
-
30
- # Определяем программу из сообщения
31
- program_id = None
32
- message_lower = message.lower()
33
- if any(word in message_lower for word in ['ai product', 'продукт', 'менеджмент', 'аналитика']):
34
- program_id = 'ai_product'
35
- elif any(word in message_lower for word in ['ии', 'ai', 'машинное обучение', 'глубокое обучение', 'nlp', 'cv']):
36
- program_id = 'ai'
37
-
38
- # Извлекаем семестр если указан
39
- semester = None
40
- for i in range(1, 5):
41
- if f'{i} семестр' in message_lower or f'{i} семестре' in message_lower:
42
- semester = i
43
- break
44
-
45
- # Фильтруем курсы
46
- context = filter_courses(message, program_id, semester)
47
-
48
- if not context:
49
- response = 'К сожалению, не нашел релевантной информации в учебных планах ITMO. Попробуйте переформулировать вопрос.'
50
- else:
51
- # Генерируем ответ с помощью LLM
52
- response = answer(message, context)
53
-
54
- # Ограничиваем историю до 3 последних сообщений
55
- new_history = history + [[message, response]]
56
- if len(new_history) > 3:
57
- new_history = new_history[-3:]
58
-
59
- return new_history, ''
60
-
61
- def get_recommendations(programming_exp, math_level, interests, semester):
62
- """Получение рекомендаций по курсам"""
63
- if not semester:
64
- return 'Пожалуйста, укажите семестр для получения рекомендаций.'
65
-
66
- try:
67
- semester_int = int(semester)
68
- except ValueError:
69
- return 'Пожалуйста, выберите корректный семестр.'
70
-
71
- # Формируем профиль
72
- profile = {
73
- 'programming_experience': programming_exp,
74
- 'math_level': math_level,
75
- 'interests': interests,
76
- 'semester': semester_int
77
- }
78
-
79
- # Получаем рекомендации
80
- recommended_courses = recommend_courses(profile)
81
-
82
- if not recommended_courses:
83
- return f'К сожалению, не найдено подходящих курсов для {semester} семестра.'
84
-
85
- # Генерируем ответ с помощью LLM
86
- return generate_recommendations(recommended_courses, profile)
87
-
88
- def update_data():
89
- """Обновление данных"""
90
- try:
91
- success = parse_all()
92
- if success:
93
- # Перезагружаем курсы
94
- global courses
95
- courses = load_courses()
96
- return f'Данные успешно обновлены! Загружено {len(courses)} курсов.'
97
- else:
98
- return 'Ошибка при обновлении данных. Используются базовые курсы.'
99
- except Exception as e:
100
- return f'Ошибка обновления данных: {e}'
101
-
102
- # Создание интерфейса
103
- with gr.Blocks(title='ITMO Магистратура - Чат-бот', theme=gr.themes.Soft()) as demo:
104
- gr.Markdown('# 🤖 Чат-бот для абитуриентов магистратур ITMO')
105
- gr.Markdown('Задавайте вопросы о программах ИИ и AI Product, получайте персональные рекомендации по курсам.')
106
-
107
- gr.Markdown(f'📊 **Загружено курсов**: {len(courses)}')
108
-
109
- with gr.Row():
110
- with gr.Column(scale=2):
111
- # Чат
112
- chatbot = gr.Chatbot(
113
- label='💬 Чат с ботом',
114
- height=400,
115
- show_label=True
116
- )
117
- msg = gr.Textbox(
118
- label='Введите сообщение',
119
- placeholder='Спрашивайте о дисциплинах, программах, учебных планах...',
120
- lines=2
121
- )
122
- clear = gr.Button('Очистить чат')
123
-
124
- # Обработчики
125
- msg.submit(handle_message, [msg, chatbot], [chatbot, msg])
126
- clear.click(lambda: [], None, chatbot, queue=False)
127
-
128
- with gr.Column(scale=1):
129
- gr.Markdown('### 👤 Профиль для рекомендаций')
130
-
131
- programming_exp = gr.Slider(
132
- minimum=0, maximum=5, value=2, step=1,
133
- label='Опыт программирования (0-5)',
134
- info='0 - нет опыта, 5 - эксперт'
135
- )
136
-
137
- math_level = gr.Slider(
138
- minimum=0, maximum=4, value=2, step=1,
139
- label='Уровень математики (0-4)',
140
- info='0 - базовый, 4 - продвинутый'
141
- )
142
-
143
- interests = gr.CheckboxGroup(
144
- choices=['ml', 'dl', 'nlp', 'cv', 'product', 'business', 'research', 'data', 'systems', 'python', 'math'],
145
- value=['ml'],
146
- label='Интересы',
147
- info='Выберите интересующие направления'
148
- )
149
-
150
- semester = gr.Dropdown(
151
- choices=['1', '2', '3', '4'],
152
- label='Целевой семестр',
153
- info='Для получения рекомендаций'
154
- )
155
-
156
- recommend_btn = gr.Button('🎯 Получить рекомендации', variant='primary')
157
- recommendations_output = gr.Textbox(
158
- label='Рекомендации',
159
- lines=12,
160
- interactive=False
161
- )
162
-
163
- recommend_btn.click(
164
- get_recommendations,
165
- inputs=[programming_exp, math_level, interests, semester],
166
- outputs=recommendations_output
167
- )
168
-
169
- with gr.Row():
170
- update_btn = gr.Button('🔄 Обновить данные', variant='secondary')
171
- update_status = gr.Textbox(
172
- label='Статус обновления',
173
- interactive=False
174
- )
175
-
176
- update_btn.click(update_data, outputs=update_status)
177
-
178
- if __name__ == '__main__':
179
- demo.launch(server_name='0.0.0.0', server_port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template_string, request, jsonify
2
+ import os
3
+
4
+ # Импорты модулей
5
+ from parser import parse_all
6
+ from data_layer import load_courses, filter_courses, recommend_courses, is_relevant
7
+ from llm import answer, generate_recommendations
8
+
9
+ app = Flask(__name__)
10
+
11
+ # Инициализация данных
12
+ courses = load_courses()
13
+ print(f'Загружено курсов: {len(courses)}')
14
+
15
+ # Простой HTML шаблон
16
+ HTML_TEMPLATE = '''
17
+ <!DOCTYPE html>
18
+ <html lang="ru">
19
+ <head>
20
+ <meta charset="UTF-8">
21
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
22
+ <title>ITMO Магистратура - Чат-бот</title>
23
+ <style>
24
+ body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
25
+ .chat-container { height: 300px; overflow-y: auto; border: 1px solid #ccc; padding: 10px; margin: 10px 0; }
26
+ .message { margin: 5px 0; padding: 8px; border-radius: 5px; }
27
+ .user { background: #e3f2fd; text-align: right; }
28
+ .bot { background: #f5f5f5; }
29
+ .input-group { display: flex; gap: 10px; margin: 10px 0; }
30
+ input, button, select { padding: 8px; }
31
+ button { background: #007bff; color: white; border: none; cursor: pointer; }
32
+ .recommendations { margin-top: 20px; }
33
+ textarea { width: 100%; height: 100px; }
34
+ </style>
35
+ </head>
36
+ <body>
37
+ <h1>🤖 ITMO Магистратура - Чат-бот</h1>
38
+ <p>Загружено курсов: {{ courses_count }}</p>
39
+
40
+ <div class="chat-container" id="chatContainer">
41
+ <div class="message bot">Привет! Задавай вопросы о магистерских программах ITMO.</div>
42
+ </div>
43
+
44
+ <div class="input-group">
45
+ <input type="text" id="messageInput" placeholder="Введите сообщение..." style="flex: 1;">
46
+ <button onclick="sendMessage()">Отправить</button>
47
+ <button onclick="clearChat()">Очистить</button>
48
+ </div>
49
+
50
+ <div class="recommendations">
51
+ <h3>Рекомендации курсов</h3>
52
+ <div class="input-group">
53
+ <select id="semester">
54
+ <option value="">Выберите семестр</option>
55
+ <option value="1">1 семестр</option>
56
+ <option value="2">2 семестр</option>
57
+ <option value="3">3 семестр</option>
58
+ <option value="4">4 семестр</option>
59
+ </select>
60
+ <button onclick="getRecommendations()">Получить рекомендации</button>
61
+ <button onclick="updateData()">Обновить данные</button>
62
+ </div>
63
+ <textarea id="recommendationsOutput" placeholder="Здесь появятся рекомендации..." readonly></textarea>
64
+ </div>
65
+
66
+ <script>
67
+ function addMessage(message, isUser = false) {
68
+ const container = document.getElementById('chatContainer');
69
+ const div = document.createElement('div');
70
+ div.className = `message ${isUser ? 'user' : 'bot'}`;
71
+ div.textContent = isUser ? 'Вы: ' + message : 'Бот: ' + message;
72
+ container.appendChild(div);
73
+ container.scrollTop = container.scrollHeight;
74
+ }
75
+
76
+ async function sendMessage() {
77
+ const input = document.getElementById('messageInput');
78
+ const message = input.value.trim();
79
+ if (!message) return;
80
+
81
+ addMessage(message, true);
82
+ input.value = '';
83
+
84
+ try {
85
+ const response = await fetch('/api/chat', {
86
+ method: 'POST',
87
+ headers: {'Content-Type': 'application/json'},
88
+ body: JSON.stringify({message: message})
89
+ });
90
+ const data = await response.json();
91
+ addMessage(response.ok ? data.response : 'Ошибка: ' + data.error);
92
+ } catch (error) {
93
+ addMessage('Ошибка соединения: ' + error.message);
94
+ }
95
+ }
96
+
97
+ async function getRecommendations() {
98
+ const semester = document.getElementById('semester').value;
99
+ if (!semester) {
100
+ alert('Выберите семестр');
101
+ return;
102
+ }
103
+
104
+ const output = document.getElementById('recommendationsOutput');
105
+ output.value = 'Генерируем рекомендации...';
106
+
107
+ try {
108
+ const response = await fetch('/api/recommendations', {
109
+ method: 'POST',
110
+ headers: {'Content-Type': 'application/json'},
111
+ body: JSON.stringify({
112
+ programming_exp: 2,
113
+ math_level: 2,
114
+ interests: ['ml'],
115
+ semester: semester
116
+ })
117
+ });
118
+ const data = await response.json();
119
+ output.value = response.ok ? data.response : 'Ошибка: ' + data.error;
120
+ } catch (error) {
121
+ output.value = 'Ошибка соединения: ' + error.message;
122
+ }
123
+ }
124
+
125
+ async function updateData() {
126
+ const output = document.getElementById('recommendationsOutput');
127
+ output.value = 'Обновляем данные...';
128
+
129
+ try {
130
+ const response = await fetch('/api/update', {
131
+ method: 'POST',
132
+ headers: {'Content-Type': 'application/json'}
133
+ });
134
+ const data = await response.json();
135
+ output.value = response.ok ? data.message : 'Ошибка: ' + data.error;
136
+ if (response.ok) location.reload();
137
+ } catch (error) {
138
+ output.value = 'Ошибка соединения: ' + error.message;
139
+ }
140
+ }
141
+
142
+ function clearChat() {
143
+ document.getElementById('chatContainer').innerHTML =
144
+ '<div class="message bot">Привет! Задавай вопросы о магистерских программах ITMO.</div>';
145
+ }
146
+
147
+ document.getElementById('messageInput').addEventListener('keypress', function(e) {
148
+ if (e.key === 'Enter') sendMessage();
149
+ });
150
+ </script>
151
+ </body>
152
+ </html>
153
+ '''
154
+
155
+ @app.route('/')
156
+ def index():
157
+ """Главная страница"""
158
+ return render_template_string(HTML_TEMPLATE, courses_count=len(courses))
159
+
160
+ @app.route('/api/chat', methods=['POST'])
161
+ def chat():
162
+ """API для чата"""
163
+ try:
164
+ data = request.get_json()
165
+ message = data.get('message', '').strip()
166
+
167
+ if not message:
168
+ return jsonify({'error': 'Пустое сообщение'}), 400
169
+
170
+ # Проверяем релевантность
171
+ if not is_relevant(message):
172
+ response = '''Похоже, вопрос не относится к магистратурам ITMO и их учебным планам.
173
+
174
+ Попробуйте спросить, например:
175
+ • "Какие дисциплины по NLP в 1 семестре программы ИИ?"
176
+ "Расскажи о программе AI Product"
177
+ • "Какие курсы по машинному обучению есть в программе ИИ?"
178
+ "Сколько кредитов за дисциплину 'Глубокое обучение'?"
179
+ • "Какие курсы подходят для моего профиля?"'''
180
+ return jsonify({'response': response})
181
+
182
+ # Определяем программу из сообщения
183
+ program_id = None
184
+ message_lower = message.lower()
185
+ if any(word in message_lower for word in ['ai product', 'продукт', 'менеджмент', 'аналитика']):
186
+ program_id = 'ai_product'
187
+ elif any(word in message_lower for word in ['ии', 'ai', 'машинное обучение', 'глубокое обучение', 'nlp', 'cv']):
188
+ program_id = 'ai'
189
+
190
+ # Извлекаем семестр если указан
191
+ semester = None
192
+ for i in range(1, 5):
193
+ if f'{i} семестр' in message_lower or f'{i} семестре' in message_lower:
194
+ semester = i
195
+ break
196
+
197
+ # Фильтруем курсы
198
+ context = filter_courses(message, program_id, semester)
199
+
200
+ if not context:
201
+ response = 'К сожалению, не нашел релевантной информации в учебных планах ITMO. Попробуйте переформулировать вопрос.'
202
+ else:
203
+ # Генерируем ответ с помощью LLM
204
+ response = answer(message, context)
205
+
206
+ return jsonify({'response': response})
207
+
208
+ except Exception as e:
209
+ return jsonify({'error': f'Ошибка обработки: {str(e)}'}), 500
210
+
211
+ @app.route('/api/recommendations', methods=['POST'])
212
+ def get_recommendations():
213
+ """API для рекомендаций"""
214
+ try:
215
+ data = request.get_json()
216
+ programming_exp = data.get('programming_exp', 2)
217
+ math_level = data.get('math_level', 2)
218
+ interests = data.get('interests', [])
219
+ semester = data.get('semester', '')
220
+
221
+ if not semester:
222
+ return jsonify({'error': 'Пожалуйста, укажите семестр для получения рекомендаций.'}), 400
223
+
224
+ try:
225
+ semester_int = int(semester)
226
+ except ValueError:
227
+ return jsonify({'error': 'Пожалуйста, выберите корректный семестр.'}), 400
228
+
229
+ # Формируем профиль
230
+ profile = {
231
+ 'programming_experience': programming_exp,
232
+ 'math_level': math_level,
233
+ 'interests': interests,
234
+ 'semester': semester_int
235
+ }
236
+
237
+ # Получаем рекомендации
238
+ recommended_courses = recommend_courses(profile)
239
+
240
+ if not recommended_courses:
241
+ return jsonify({'error': f'К сожалению, не найдено подходящих курсов для {semester} семестра.'}), 404
242
+
243
+ # Генерируем ответ с помощью LLM
244
+ response = generate_recommendations(recommended_courses, profile)
245
+
246
+ return jsonify({'response': response})
247
+
248
+ except Exception as e:
249
+ return jsonify({'error': f'Ошибка получения рекомендаций: {str(e)}'}), 500
250
+
251
+ @app.route('/api/update', methods=['POST'])
252
+ def update_data():
253
+ """API для обновления данных"""
254
+ try:
255
+ success = parse_all()
256
+ if success:
257
+ # Перезагружаем курсы
258
+ global courses
259
+ courses = load_courses()
260
+ return jsonify({'message': f'Данные успешно обновлены! Загружено {len(courses)} курсов.'})
261
+ else:
262
+ return jsonify({'error': 'Ошибка при обновлении данных. Используются базовые курсы.'}), 500
263
+ except Exception as e:
264
+ return jsonify({'error': f'Ошибка обновления данных: {str(e)}'}), 500
265
+
266
+ @app.route('/api/status')
267
+ def status():
268
+ """API для статуса системы"""
269
+ return jsonify({
270
+ 'status': 'ok',
271
+ 'courses_count': len(courses),
272
+ 'llm_available': True
273
+ })
274
+
275
+ # Для HF Spaces
276
+ if __name__ == '__main__':
277
+ port = int(os.environ.get('PORT', 7860))
278
+ app.run(host='0.0.0.0', port=port, debug=False)
docker-compose.yml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ itmo-chatbot:
5
+ build: .
6
+ ports:
7
+ - "5000:5000"
8
+ volumes:
9
+ - ./data:/app/data
10
+ environment:
11
+ - FLASK_ENV=production
12
+ restart: unless-stopped
requirements.txt CHANGED
@@ -1,4 +1,4 @@
1
- gradio==4.44.0
2
  transformers==4.36.2
3
  torch==2.1.0
4
  requests==2.31.0
 
1
+ flask==2.3.3
2
  transformers==4.36.2
3
  torch==2.1.0
4
  requests==2.31.0