File size: 6,570 Bytes
2fc8dc5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
import re
import json
from typing import List, Dict, Tuple
from knowledge_base import KnowledgeBase
from retriever import Retriever
class ITMOChatbot:
def __init__(self):
self.kb = KnowledgeBase()
self.retriever = Retriever()
self.max_history_turns = 3
self.max_context_tokens = 1200
self.relevance_threshold = 0.38
try:
from transformers import pipeline
self.generator = pipeline('text2text-generation', model='cointegrated/rut5-base-multitask')
except Exception as e:
print(f'Генеративная модель не загружена: {e}')
self.generator = None
def chat(self, message: str, history: list) -> Tuple[str, float]:
if not message.strip():
return 'Пожалуйста, задайте вопрос.', 0.0
if not self.kb.is_itmo_query(message):
return self._get_irrelevant_response(), 0.0
context = self._get_context(message)
if not context:
return 'К сожалению, не нашел релевантной информации в учебных планах ITMO.', 0.0
response = self._generate_response(message, history, context)
relevance_score = self._calculate_relevance_score(message, context)
return response, relevance_score
def recommend_courses(self, profile: dict) -> str:
if not profile.get('semester'):
return 'Пожалуйста, укажите целевой семестр для получения рекомендаций.'
recommendations = self.kb.recommend(profile)
if not recommendations:
return 'К сожалению, не удалось найти подходящие курсы для вашего профиля.'
result = '🎯 Рекомендуемые курсы (из официальных учебных планов ITMO):\n\n'
for i, rec in enumerate(recommendations[:7], 1):
result += f'{i}. {rec["name"]} ({rec["semester"]} семестр, {rec["credits"]} кредитов)\n'
result += f' {rec["why"]}\n\n'
return result
def _get_context(self, message: str) -> List[Dict]:
try:
results = self.retriever.retrieve(message, k=6, threshold=0.35)
# Преобразуем результаты в нужный формат
formatted_results = []
for result in results:
course_id = result.get('course_id')
if course_id:
course = self.kb.get_course_by_id(course_id)
if course:
course['score'] = result.get('score', 0.0)
formatted_results.append(course)
return formatted_results
except Exception as e:
print(f'Ошибка при получении контекста: {e}')
return []
def _generate_response(self, message: str, history: list, context: List[Dict]) -> str:
if not context:
return 'В предоставленных данных об этом не сказано.'
prompt = self._build_prompt(message, history, context)
if self.generator:
try:
response = self.generator(
prompt,
max_new_tokens=180,
temperature=0.4,
do_sample=True
)[0]['generated_text']
return response.strip()
except Exception as e:
print(f'Ошибка генерации: {e}')
return self._fallback_response(context)
def _build_prompt(self, message: str, history: list, context: List[Dict]) -> str:
system_prompt = 'Отвечай только по контексту (ниже). Если недостаточно данных — прямо скажи: "в предоставленных данных об этом не сказано". Отвечай кратко и по делу.'
history_text = ''
if history:
recent_history = history[-self.max_history_turns:]
for turn in recent_history:
history_text += f'Пользователь: {turn[0]}\nБот: {turn[1]}\n'
context_text = 'Контекст:\n'
for item in context:
context_text += f'- {item["name"]} ({item["semester"]} семестр, {item["credits"]} кредитов): {item["short_desc"]}\n'
prompt = f'{system_prompt}\n\n{history_text}Контекст:\n{context_text}\nВопрос: {message}'
if len(prompt) > self.max_context_tokens * 4:
prompt = prompt[:self.max_context_tokens * 4]
return prompt
def _fallback_response(self, context: List[Dict]) -> str:
if not context:
return 'В предоставленных данных об этом не сказано.'
courses = []
for item in context[:3]:
courses.append(f'{item["name"]} ({item["semester"]} семестр, {item["credits"]} кредитов)')
return f'Найденные курсы: {", ".join(courses)}. Для более подробной информации обратитесь к официальным учебным планам ITMO.'
def _calculate_relevance_score(self, message: str, context: List[Dict]) -> float:
if not context:
return 0.0
scores = [item.get('score', 0.0) for item in context]
return sum(scores) / len(scores) if scores else 0.0
def _get_irrelevant_response(self) -> str:
return '''Похоже, вопрос не относится к магистратурам ITMO и их учебным планам.
Попробуйте спросить, например:
• "Какие дисциплины по NLP в 1 семестре программы ИИ?"
• "Расскажи о программе AI Product"
• "Какие курсы по машинному обучению есть в программе ИИ?"
• "Сколько кредитов за дисциплину 'Глубокое обучение'?"'''
|