|
import logging |
|
|
|
|
|
_generator = None |
|
|
|
def load_model(): |
|
"""Ленивая загрузка модели""" |
|
global _generator |
|
if _generator is None: |
|
try: |
|
from transformers import pipeline |
|
print('Загрузка LLM модели...') |
|
_generator = pipeline('text2text-generation', model='cointegrated/rut5-base-multitask') |
|
print('LLM модель загружена') |
|
except Exception as e: |
|
print(f'Ошибка загрузки LLM: {e}') |
|
_generator = None |
|
return _generator |
|
|
|
def answer(question, context, system_prompt=None): |
|
"""Генерирует ответ с помощью LLM""" |
|
generator = load_model() |
|
|
|
if not generator or not context: |
|
return fallback_answer(context) |
|
|
|
try: |
|
|
|
context_text = 'Доступные курсы:\n' |
|
for i, course in enumerate(context[:6], 1): |
|
context_text += f'{i}. {course["name"]} ({course["semester"]} семестр, {course["credits"]} кредитов)\n' |
|
context_text += f' Описание: {course["short_desc"]}\n' |
|
context_text += f' Теги: {", ".join(course["tags"])}\n\n' |
|
|
|
|
|
if system_prompt is None: |
|
system_prompt = '''Ты помощник для абитуриентов магистратуры ITMO. Отвечай только по предоставленному контексту. |
|
Если в контексте нет нужной информации — ответь: "в предоставленных данных об этом не сказано." |
|
Отвечай кратко и по делу. |
|
Не выдумывай факты и не давай общих ответов без ссылок на элементы контекста.''' |
|
|
|
|
|
prompt = f'''{system_prompt} |
|
|
|
{context_text} |
|
|
|
Вопрос: {question}''' |
|
|
|
|
|
response = generator( |
|
prompt, |
|
max_new_tokens=180, |
|
temperature=0.3, |
|
do_sample=True |
|
)[0]['generated_text'] |
|
|
|
return response.strip() |
|
|
|
except Exception as e: |
|
print(f'Ошибка генерации LLM: {e}') |
|
return fallback_answer(context) |
|
|
|
def fallback_answer(context): |
|
"""Fallback ответ без LLM""" |
|
if not context: |
|
return 'В предоставленных данных об этом не сказано.' |
|
|
|
courses = [] |
|
for item in context[:3]: |
|
courses.append(f'{item["name"]} ({item["semester"]} семестр, {item["credits"]} кредитов)') |
|
|
|
return f'Найденные курсы: {", ".join(courses)}.' |
|
|
|
def generate_recommendations(courses, profile): |
|
"""Генерирует рекомендации с помощью LLM""" |
|
generator = load_model() |
|
|
|
if not generator or not courses: |
|
return fallback_recommendations(courses, profile) |
|
|
|
try: |
|
|
|
courses_text = 'Доступные курсы:\n' |
|
for i, course in enumerate(courses[:7], 1): |
|
courses_text += f'{i}. {course["name"]} ({course["credits"]} кредитов)\n' |
|
courses_text += f' Сложность: {course.get("difficulty", "не указана")}, Теги: {", ".join(course["tags"])}\n' |
|
courses_text += f' Описание: {course["short_desc"]}\n\n' |
|
|
|
|
|
programming_exp = profile.get('programming_experience', 2) |
|
math_level = profile.get('math_level', 2) |
|
interests = profile.get('interests', []) |
|
semester = profile.get('semester', 'не указан') |
|
|
|
prompt = f'''Ты эксперт по выбору курсов. Дай персонализированные рекомендации студенту. |
|
|
|
Профиль студента: |
|
- Опыт программирования: {programming_exp}/5 |
|
- Уровень математики: {math_level}/4 |
|
- Интересы: {", ".join(interests)} |
|
- Целевой семестр: {semester} |
|
|
|
{courses_text} |
|
|
|
Дай 5-7 лучших рекомендаций с объяснением почему они подходят. Учитывай уровень сложности и интересы. Отвечай кратко, по делу.''' |
|
|
|
response = generator( |
|
prompt, |
|
max_new_tokens=300, |
|
temperature=0.4, |
|
do_sample=True |
|
)[0]['generated_text'] |
|
|
|
return response.strip() |
|
|
|
except Exception as e: |
|
print(f'Ошибка генерации рекомендаций: {e}') |
|
return fallback_recommendations(courses, profile) |
|
|
|
def fallback_recommendations(courses, profile): |
|
"""Fallback рекомендации без LLM""" |
|
if not courses: |
|
semester = profile.get('semester', 'не указан') |
|
return f'Нет курсов для {semester} семестра.' |
|
|
|
programming_exp = profile.get('programming_experience', 2) |
|
math_level = profile.get('math_level', 2) |
|
interests = profile.get('interests', []) |
|
semester = profile.get('semester', 'не указан') |
|
|
|
result = f'🎯 Рекомендации для {semester} семестра:\n\n' |
|
|
|
for i, course in enumerate(courses[:7], 1): |
|
result += f'{i}. {course["name"]} ({course["credits"]} кредитов)\n' |
|
|
|
|
|
reasons = [] |
|
matching_tags = [tag for tag in interests if tag in course.get('tags', [])] |
|
if matching_tags: |
|
reasons.append(f'подходит по интересам: {", ".join(matching_tags)}') |
|
|
|
if programming_exp <= 2 and 'python' in course.get('tags', []): |
|
reasons.append('подходит для начинающих программистов') |
|
elif programming_exp >= 4 and 'dl' in course.get('tags', []): |
|
reasons.append('подходит для опытных программистов') |
|
|
|
if math_level >= 2 and 'math' in course.get('tags', []): |
|
reasons.append('требует хорошую математическую подготовку') |
|
|
|
if reasons: |
|
result += f' Почему подходит: {"; ".join(reasons)}\n' |
|
|
|
result += '\n' |
|
|
|
return result |
|
|