practice-ai-backend / ai_backend.py
vadimser's picture
Upload 4 files
5e9fd8b verified
"""
AI Backend Simplified - без spacy, совместим с Python 3.14
Использует: transformers, scikit-learn, nltk, re, numpy
"""
import re
import datetime
import numpy as np
from typing import List, Dict, Optional, Tuple, Any
from dataclasses import dataclass
from enum import Enum
import hashlib
import json
from collections import Counter
import warnings
# ML Libraries (с обработкой ошибок импорта)
try:
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import joblib
import pandas as pd
ML_AVAILABLE = True
except ImportError:
ML_AVAILABLE = False
warnings.warn("scikit-learn not available, using fallback methods")
try:
import torch
from transformers import (
pipeline,
AutoTokenizer,
AutoModelForSequenceClassification,
AutoModelForSeq2SeqLM
)
from sentence_transformers import SentenceTransformer
TRANSFORMERS_AVAILABLE = True
except ImportError:
TRANSFORMERS_AVAILABLE = False
warnings.warn("transformers not available")
try:
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.stem import SnowballStemmer
NLTK_AVAILABLE = True
except ImportError:
NLTK_AVAILABLE = False
warnings.warn("nltk not available")
# Конфигурация
@dataclass
class MLConfig:
"""Конфигурация ML моделей"""
plagiarism_threshold: float = 0.85
min_text_length: int = 30
embedding_model: str = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
classification_model: str = "cointegrated/rubert-tiny-cased-nli-threeway"
paraphraser_model: str = "IlyaGusev/rut5_base_headline_gen_telegram"
use_gpu: bool = torch.cuda.is_available() if 'torch' in globals() else False
cache_dir: str = "./ml_cache"
batch_size: int = 16
language: str = "russian"
class Specialization(Enum):
"""Поддерживаемые специальности"""
IT = "it"
MEDICINE = "medicine"
CONSTRUCTION = "construction"
DESIGN = "design"
MARKETING = "marketing"
LAW = "law"
FINANCE = "finance"
EDUCATION = "education"
ENGINEERING = "engineering"
SCIENCE = "science"
@dataclass
class TextAnalysis:
"""Результат анализа текста"""
trust_score: float
is_original: bool
plagiarism_sources: List[str]
readability_score: float
keyword_matches: Dict[str, float]
sentiment: float # -1 to 1
complexity: float # 0 to 1
class PracticeAIBackendSimple:
"""
Упрощенный бэкенд без spacy, совместимый с Python 3.14
"""
def __init__(self, config: Optional[MLConfig] = None):
self.config = config or MLConfig()
self.knowledge_base = self._load_knowledge_base()
self.models = {}
self.vectorizer = None
self.embeddings_cache = {}
# Инициализация ML моделей
self._initialize_models()
# Загрузка стоп-слов и других ресурсов
self._load_nlp_resources()
# Кэш для быстрого доступа
self.text_cache = {}
def _initialize_models(self):
"""Инициализация ML моделей"""
if TRANSFORMERS_AVAILABLE:
try:
# Модель для эмбеддингов (векторизации текста) - используем более легкую модель
print("Загрузка модели для эмбеддингов...")
self.models['embedding'] = SentenceTransformer(
'paraphrase-multilingual-MiniLM-L12-v2',
device='cuda' if self.config.use_gpu and torch.cuda.is_available() else 'cpu'
)
# Модель для классификации специальностей
print("Загрузка модели классификации...")
try:
self.models['classifier'] = pipeline(
"zero-shot-classification",
model="facebook/bart-large-mnli",
device=0 if self.config.use_gpu and torch.cuda.is_available() else -1
)
except:
# Fallback модель
self.models['classifier'] = None
# Модель для перефразирования (улучшения текста)
print("Загрузка модели перефразирования...")
try:
self.models['paraphraser'] = pipeline(
"text2text-generation",
model="google/flan-t5-small",
device=0 if self.config.use_gpu and torch.cuda.is_available() else -1
)
except:
self.models['paraphraser'] = None
print("ML модели успешно загружены")
except Exception as e:
print(f"Ошибка загрузки моделей: {e}")
self.models = {}
if ML_AVAILABLE:
# Инициализация TF-IDF векторизатора
self.vectorizer = TfidfVectorizer(
max_features=2000,
ngram_range=(1, 2),
stop_words=self._get_stop_words() if self._get_stop_words() else None
)
def _load_nlp_resources(self):
"""Загрузка NLP ресурсов"""
self.stop_words = self._get_stop_words()
# Инициализация стеммера для русского языка
self.stemmer = None
if NLTK_AVAILABLE:
try:
# Загружаем необходимые ресурсы nltk
nltk.download('punkt', quiet=True)
nltk.download('stopwords', quiet=True)
self.stemmer = SnowballStemmer("russian")
except:
self.stemmer = None
# Простой токенизатор на случай отсутствия nltk
self.simple_tokenizer = lambda text: re.findall(r'\b\w+\b', text.lower())
def _get_stop_words(self) -> List[str]:
"""Получение стоп-слов"""
stop_words = []
if NLTK_AVAILABLE:
try:
stop_words = stopwords.words('russian')
except:
pass
# Дополнительные стоп-слова
additional_stop_words = [
'это', 'вот', 'ну', 'так', 'как', 'что', 'который',
'весь', 'свой', 'наш', 'ваш', 'их', 'мой', 'твой',
'тот', 'сам', 'очень', 'более', 'менее', 'просто'
]
stop_words.extend(additional_stop_words)
return list(set(stop_words)) # Удаляем дубликаты
def _load_knowledge_base(self) -> Dict:
"""Загрузка расширенной базы знаний"""
return {
'it': {
'tech': ['Python', 'Django', 'React', 'Docker', 'PostgreSQL',
'FastAPI', 'Git', 'Kubernetes', 'AWS', 'TensorFlow',
'PyTorch', 'Redis', 'MongoDB', 'Node.js', 'TypeScript',
'JavaScript', 'HTML', 'CSS', 'SQL', 'NoSQL', 'Linux'],
'tasks': [
'разработал API с использованием REST/GraphQL',
'оптимизировал SQL запросы и добавил индексы',
'настроил CI/CD пайплайн с автоматическим тестированием',
'реализовал микросервисную архитектуру',
'провел рефакторинг legacy кода',
'настроил мониторинг и алертинг',
'разработал систему кэширования',
'внедрил практики DevOps',
'создал пользовательский интерфейс',
'написал unit-тесты и интеграционные тесты'
],
'results': [
'увеличил производительность на 40%',
'снизил время отклика API с 200мс до 50мс',
'уменьшил потребление памяти на 25%',
'повысил тестовое покрытие до 85%',
'автоматизировал 80% рутинных операций',
'улучшил пользовательский опыт',
'сократил время загрузки страницы',
'повысил безопасность приложения'
],
'keywords': ['код', 'программа', 'алгоритм', 'база данных',
'сервер', 'клиент', 'интерфейс', 'фреймворк',
'разработка', 'тестирование', 'деплой', 'оптимизация']
},
'medicine': {
'tech': ['ЭКГ', 'УЗИ', 'КТ', 'МРТ', 'лабораторная диагностика',
'эндоскопия', 'реанимационное оборудование'],
'tasks': [
'провел осмотр пациента',
'выполнил диагностическую процедуру',
'назначил курс лечения',
'провел оперативное вмешательство',
'анализировал результаты исследований'
],
'results': [
'установил точный диагноз',
'улучшил состояние пациента',
'провел успешную операцию',
'подобрал эффективное лечение',
'предотвратил осложнения'
],
'keywords': ['пациент', 'диагноз', 'лечение', 'болезнь', 'симптом',
'анализ', 'исследование', 'процедура', 'операция']
},
'design': {
'tech': ['Figma', 'Adobe Photoshop', 'Illustrator', 'Sketch',
'InDesign', 'After Effects', 'Blender'],
'tasks': [
'создал дизайн макета',
'разработал логотип и айдентику',
'подготовил UI/UX дизайн',
'создал иллюстрации и графику',
'разработал анимацию'
],
'results': [
'улучшил визуальное восприятие',
'повысил удобство использования',
'создал запоминающийся бренд',
'увеличил конверсию',
'улучшил пользовательский опыт'
],
'keywords': ['дизайн', 'макет', 'интерфейс', 'графика', 'цвет',
'композиция', 'типографика', 'брендинг']
},
'marketing': {
'tech': ['Google Analytics', 'Яндекс.Метрика', 'CRM',
'SMM инструменты', 'SEO инструменты'],
'tasks': [
'провел анализ рынка',
'разработал маркетинговую стратегию',
'создал контент-план',
'запустил рекламную кампанию',
'анализировал эффективность каналов'
],
'results': [
'увеличил трафик на 40%',
'повысил конверсию',
'снизил стоимость привлечения клиента',
'увеличил узнаваемость бренда',
'привлек новых клиентов'
],
'keywords': ['маркетинг', 'реклама', 'аналитика', 'конверсия',
'трафик', 'стратегия', 'кампания', 'таргетинг']
}
}
def analyze_honesty(self, text: str, history: List[str]) -> Dict:
"""
Анализ честности текста
"""
if not text or len(text) < self.config.min_text_length:
return {
'trust_score': 0,
'status': 'too_short',
'is_original': False,
'plagiarism_sources': [],
'readability': 0,
'sentiment': 0
}
# Нормализация текста
clean_text = self._normalize_text(text)
# Проверка на плагиат
plagiarism_results = self._detect_plagiarism(clean_text, history)
# Анализ читабельности
readability = self._calculate_readability(text)
# Анализ тональности
sentiment = self._analyze_sentiment(text)
# Итоговая оценка доверия
trust_score = self._calculate_trust_score_simple(
plagiarism_results['similarity'],
readability,
sentiment
)
return {
'trust_score': round(trust_score, 2),
'is_original': plagiarism_results['similarity'] < self.config.plagiarism_threshold,
'plagiarism_sources': plagiarism_results['sources'][:3],
'readability': readability,
'sentiment': sentiment,
'status': 'original' if plagiarism_results['similarity'] < self.config.plagiarism_threshold else 'suspicious'
}
def _detect_plagiarism(self, text: str, history: List[str]) -> Dict:
"""
Обнаружение плагиата
"""
if not text or not history:
return {'similarity': 0.0, 'sources': []}
# Используем эмбеддинги если доступны
if TRANSFORMERS_AVAILABLE and 'embedding' in self.models:
try:
return self._detect_plagiarism_with_embeddings(text, history)
except:
pass
# Fallback на простой метод
return self._detect_plagiarism_simple(text, history)
def _detect_plagiarism_with_embeddings(self, text: str, history: List[str]) -> Dict:
"""Детекция плагиата с использованием эмбеддингов"""
text_embedding = self.models['embedding'].encode([text], convert_to_tensor=True)
max_similarity = 0.0
sources = []
for i, entry in enumerate(history[:50]): # Ограничиваем для производительности
if len(entry) < 30:
continue
# Кэширование
entry_hash = hashlib.md5(entry.encode()).hexdigest()[:16]
if entry_hash in self.embeddings_cache:
entry_embedding = self.embeddings_cache[entry_hash]
else:
entry_embedding = self.models['embedding'].encode([entry], convert_to_tensor=True)
self.embeddings_cache[entry_hash] = entry_embedding
# Расчет схожести
similarity = cosine_similarity(
text_embedding.cpu().numpy(),
entry_embedding.cpu().numpy()
)[0][0]
if similarity > max_similarity:
max_similarity = similarity
if similarity > 0.7:
sources.append(f"Источник #{i+1}: {similarity:.2f}")
return {
'similarity': float(max_similarity),
'sources': sources
}
def _detect_plagiarism_simple(self, text: str, history: List[str]) -> Dict:
"""Простая детекция плагиата"""
# Токенизация
text_tokens = set(self._tokenize(text))
if not text_tokens:
return {'similarity': 0.0, 'sources': []}
max_similarity = 0.0
sources = []
for i, entry in enumerate(history):
if len(entry) < 30:
continue
entry_tokens = set(self._tokenize(entry))
if not entry_tokens:
continue
# Схожесть Жаккара
intersection = len(text_tokens.intersection(entry_tokens))
union = len(text_tokens.union(entry_tokens))
similarity = intersection / union if union > 0 else 0
if similarity > max_similarity:
max_similarity = similarity
if similarity > 0.5:
sources.append(f"Запись #{i+1}: {similarity:.2f}")
return {
'similarity': float(max_similarity),
'sources': sources[:5]
}
def improve_text(self, text: str, specialization: str = 'it') -> str:
"""
Улучшение текста
"""
if not text or len(text) < 10:
return text
# Используем ML модель если доступна
if TRANSFORMERS_AVAILABLE and self.models.get('paraphraser'):
try:
# Подготовка промпта
prompt = f"Улучши текст, сделай его более профессиональным: {text}"
# Генерация
result = self.models['paraphraser'](
prompt,
max_length=200,
min_length=30,
do_sample=True,
temperature=0.7,
top_p=0.9
)
if result and len(result) > 0:
improved = result[0]['generated_text']
# Удаляем промпт если он остался
improved = improved.replace(prompt, "").strip()
return self._postprocess_text(improved, specialization)
except Exception as e:
print(f"Ошибка улучшения текста ML: {e}")
# Rule-based улучшение
return self._improve_text_rule_based(text, specialization)
def _improve_text_rule_based(self, text: str, specialization: str) -> str:
"""Rule-based улучшение текста"""
spec_data = self.knowledge_base.get(specialization, self.knowledge_base['it'])
# Разбиваем на предложения
sentences = re.split(r'[.!?]+', text)
sentences = [s.strip() for s in sentences if s.strip()]
if not sentences:
return text
improved_sentences = []
for sentence in sentences:
if not sentence:
continue
# Капитализация
sentence = sentence[0].upper() + sentence[1:] if sentence else ""
# Проверяем, содержит ли предложение профессиональные термины
has_tech = any(tech.lower() in sentence.lower() for tech in spec_data['tech'])
has_task = any(task.lower() in sentence.lower() for task in spec_data['tasks'])
# Добавляем профессиональные термины если их нет
if not has_tech and len(sentence.split()) < 10:
tech = np.random.choice(spec_data['tech'])
sentence = f"{sentence}. Использовал {tech}."
if not has_task and len(sentence.split()) < 8:
task = np.random.choice(spec_data['tasks'])
result = np.random.choice(spec_data['results'])
sentence = f"{sentence}. {task}, что позволило {result}."
improved_sentences.append(sentence)
# Собираем обратно
improved = '. '.join(improved_sentences)
# Добавляем точку в конце если нет
if improved and not improved.endswith(('.', '!', '?')):
improved += '.'
return improved
def detect_specialization(self, text: str) -> Tuple[str, Dict[str, float]]:
"""
Определение специальности по тексту
"""
if not text or len(text) < 20:
return 'it', {'it': 1.0}
# Используем ML классификатор если доступен
if TRANSFORMERS_AVAILABLE and self.models.get('classifier'):
try:
candidate_labels = list(self.knowledge_base.keys())
result = self.models['classifier'](
text,
candidate_labels=candidate_labels,
multi_label=False
)
scores = {}
for label, score in zip(result['labels'][:3], result['scores'][:3]):
scores[label] = float(score)
return result['labels'][0], scores
except:
pass
# Rule-based классификация
return self._detect_specialization_rule_based(text)
def _detect_specialization_rule_based(self, text: str) -> Tuple[str, Dict[str, float]]:
"""Rule-based определение специальности"""
text_lower = text.lower()
scores = {}
for spec, data in self.knowledge_base.items():
score = 0
# Ключевые слова
for keyword in data.get('keywords', []):
if keyword.lower() in text_lower:
score += 2
# Технологии
for tech in data.get('tech', []):
if tech.lower() in text_lower:
score += 1.5
# Задачи
for task in data.get('tasks', []):
if task.lower() in text_lower:
score += 1.2
scores[spec] = score
if not scores or max(scores.values()) == 0:
return 'it', {'it': 1.0}
# Нормализация
max_score = max(scores.values())
normalized_scores = {k: v/max_score for k, v in scores.items()}
# Выбираем лучшую
best_spec = max(scores.items(), key=lambda x: x[1])[0]
return best_spec, normalized_scores
def calculate_reputation(self, entries: List[Dict]) -> Dict[str, Any]:
"""
Расчет репутации пользователя
"""
if not entries:
return {
'score': 0,
'level': 'beginner',
'breakdown': {},
'metrics': {
'total_entries': 0,
'approved_entries': 0,
'approval_rate': 0
}
}
# Фильтруем одобренные записи
approved_entries = [e for e in entries if e.get('status') == 'approved']
# Базовые метрики
total_entries = len(entries)
approved_count = len(approved_entries)
approval_rate = approved_count / total_entries if total_entries > 0 else 0
# Расчет баллов
score = 0
breakdown = {
'activity': 0,
'quality': 0,
'consistency': 0,
'diversity': 0,
'proofs': 0
}
# 1. Активность (до 30 баллов)
activity_score = min(30, approved_count * 3)
breakdown['activity'] = activity_score
score += activity_score
# 2. Качество (до 25 баллов)
if approved_entries:
total_length = sum(len(e.get('description', '')) for e in approved_entries)
avg_length = total_length / approved_count
if avg_length > 400:
quality_score = 25
elif avg_length > 250:
quality_score = 20
elif avg_length > 150:
quality_score = 15
elif avg_length > 80:
quality_score = 10
else:
quality_score = 5
breakdown['quality'] = quality_score
score += quality_score
# 3. Доказательства (до 20 баллов)
proofs_score = 0
for entry in approved_entries:
if entry.get('image'):
proofs_score += 2
if entry.get('code_snippet'):
proofs_score += 3
if entry.get('location'):
proofs_score += 1
proofs_score = min(20, proofs_score)
breakdown['proofs'] = proofs_score
score += proofs_score
# 4. Разнообразие (до 15 баллов)
if approved_entries and ML_AVAILABLE:
try:
texts = [e.get('description', '') for e in approved_entries]
unique_words = set()
for text in texts:
tokens = self._tokenize(text)
unique_words.update(tokens)
diversity_score = min(15, len(unique_words) // 10)
breakdown['diversity'] = diversity_score
score += diversity_score
except:
pass
# 5. Консистентность (до 10 баллов)
if approved_count >= 5:
# Проверяем, есть ли записи за последнюю неделю
recent_entries = 0
week_ago = datetime.datetime.now() - datetime.timedelta(days=7)
for entry in approved_entries:
if 'date' in entry:
try:
entry_date = datetime.datetime.fromisoformat(entry['date'].replace('Z', '+00:00'))
if entry_date > week_ago:
recent_entries += 1
except:
pass
if recent_entries >= 3:
consistency_score = 10
elif recent_entries >= 1:
consistency_score = 5
else:
consistency_score = 0
breakdown['consistency'] = consistency_score
score += consistency_score
# Ограничиваем 0-100
score = min(100, max(0, score))
# Определяем уровень
if score >= 85:
level = 'expert'
elif score >= 70:
level = 'advanced'
elif score >= 50:
level = 'intermediate'
elif score >= 25:
level = 'beginner'
else:
level = 'newbie'
return {
'score': int(score),
'level': level,
'breakdown': breakdown,
'metrics': {
'total_entries': total_entries,
'approved_entries': approved_count,
'approval_rate': round(approval_rate * 100, 1)
}
}
def generate_thought(self, specialization: str = 'it', context: Optional[str] = None) -> str:
"""
Генерация текста записи
"""
spec_data = self.knowledge_base.get(specialization, self.knowledge_base['it'])
templates = [
"В рамках практики {task}. Применение {tech} позволило {result}.",
"Основное внимание уделил {task}. Использовал {tech}, что привело к {result}.",
"Сегодня работал над {task}. С помощью {tech} удалось {result}.",
"Задачи дня включали {task}. Применение {tech} способствовало {result}.",
"Сфокусировался на {task}. Технология {tech} помогла {result}."
]
template = np.random.choice(templates)
task = np.random.choice(spec_data['tasks'])
tech = np.random.choice(spec_data['tech'])
result = np.random.choice(spec_data['results'])
thought = template.format(task=task, tech=tech, result=result)
# Добавляем контекст если есть
if context:
# Находим общие слова с контекстом
context_words = set(self._tokenize(context.lower()))
thought_words = set(self._tokenize(thought.lower()))
common_words = context_words.intersection(thought_words)
if common_words:
thought = f"Продолжая работу, {thought.lower()}"
return self._correct_text(thought)
def analyze_skill_gap(self, user_texts: List[str], target_specialization: str) -> Dict:
"""
Анализ пробелов в навыках
"""
spec_data = self.knowledge_base.get(target_specialization, self.knowledge_base['it'])
# Объединяем все тексты
all_text = ' '.join(user_texts).lower()
# Анализируем упомянутые навыки
mentioned_skills = {}
for tech in spec_data['tech']:
tech_lower = tech.lower()
if tech_lower in all_text:
mentioned_skills[tech] = all_text.count(tech_lower)
# Находим отсутствующие навыки
missing_skills = [tech for tech in spec_data['tech'] if tech.lower() not in all_text]
# Анализируем задачи
mentioned_tasks = [task for task in spec_data['tasks'] if task.lower() in all_text]
missing_tasks = [task for task in spec_data['tasks'] if task.lower() not in all_text]
# Рекомендации
recommendations = []
if missing_skills:
top_missing = missing_skills[:3]
recommendations.append(f"Рекомендуется изучить: {', '.join(top_missing)}")
if missing_tasks:
top_tasks = missing_tasks[:2]
recommendations.append(f"Попрактиковаться в: {', '.join(top_tasks)}")
if len(mentioned_skills) < 3:
recommendations.append("Расширьте спектр используемых технологий")
# Покрытие навыков
coverage = len(mentioned_skills) / len(spec_data['tech']) * 100 if spec_data['tech'] else 0
return {
'skill_coverage': round(coverage, 1),
'mentioned_skills': mentioned_skills,
'missing_skills': missing_skills[:5],
'mentioned_tasks': mentioned_tasks,
'missing_tasks': missing_tasks[:3],
'recommendations': recommendations,
'proficiency_level': self._calculate_proficiency_level(coverage)
}
def _calculate_proficiency_level(self, coverage: float) -> str:
"""Определение уровня владения"""
if coverage >= 80:
return "эксперт"
elif coverage >= 60:
return "продвинутый"
elif coverage >= 40:
return "средний"
elif coverage >= 20:
return "начинающий"
else:
return "новичок"
# Вспомогательные методы
def _normalize_text(self, text: str) -> str:
"""Нормализация текста"""
if not text:
return ""
# Приведение к нижнему регистру
text = text.lower()
# Удаление специальных символов
text = re.sub(r'[^\w\s]', ' ', text)
# Удаление лишних пробелов
text = re.sub(r'\s+', ' ', text).strip()
# Удаление стоп-слов
if self.stop_words:
words = text.split()
words = [w for w in words if w not in self.stop_words and len(w) > 2]
text = ' '.join(words)
return text
def _tokenize(self, text: str) -> List[str]:
"""Токенизация текста"""
if NLTK_AVAILABLE:
try:
return word_tokenize(text.lower())
except:
pass
# Простая токенизация
return re.findall(r'\b\w+\b', text.lower())
def _calculate_readability(self, text: str) -> float:
"""Расчет читабельности"""
if not text:
return 0.0
sentences = re.split(r'[.!?]+', text)
sentences = [s.strip() for s in sentences if s.strip()]
if not sentences:
return 0.0
words = text.split()
avg_sentence_length = len(words) / len(sentences)
# Простая формула читабельности
if avg_sentence_length < 10:
readability = 0.9
elif avg_sentence_length < 15:
readability = 0.7
elif avg_sentence_length < 20:
readability = 0.5
elif avg_sentence_length < 25:
readability = 0.3
else:
readability = 0.1
return round(readability, 2)
def _analyze_sentiment(self, text: str) -> float:
"""Анализ тональности"""
if not text:
return 0.0
positive_words = [
'успех', 'успешно', 'улучшил', 'повысил', 'оптимизировал', 'решил',
'добился', 'освоил', 'внедрил', 'создал', 'разработал', 'завершил',
'достиг', 'получил', 'научился', 'понял', 'изучил'
]
negative_words = [
'проблема', 'ошибка', 'сложность', 'неудача', 'исправил', 'столкнулся',
'трудность', 'баг', 'дефект', 'недочет', 'неполадка', 'сбой', 'затруднение'
]
text_lower = text.lower()
positive_count = sum(1 for word in positive_words if word in text_lower)
negative_count = sum(1 for word in negative_words if word in text_lower)
total = positive_count + negative_count
if total == 0:
return 0.0
sentiment = (positive_count - negative_count) / total
return round(sentiment, 2)
def _calculate_trust_score_simple(self, plagiarism: float, readability: float, sentiment: float) -> float:
"""Расчет оценки доверия"""
# Веса
plagiarism_weight = 0.5
readability_weight = 0.3
sentiment_weight = 0.2
# Оценка плагиата (меньше плагиата - лучше)
plagiarism_score = (1 - plagiarism) * 100
# Оценка читабельности
readability_score = readability * 100
# Оценка тональности (абсолютное значение)
sentiment_score = abs(sentiment) * 100
# Итоговая оценка
trust_score = (
plagiarism_score * plagiarism_weight +
readability_score * readability_weight +
sentiment_score * sentiment_weight
)
return round(max(0, min(100, trust_score)), 2)
def _postprocess_text(self, text: str, specialization: str) -> str:
"""Постобработка текста"""
if not text:
return ""
# Удаление повторяющихся слов
words = text.split()
cleaned_words = []
prev_word = ""
for word in words:
if word.lower() != prev_word.lower():
cleaned_words.append(word)
prev_word = word
text = ' '.join(cleaned_words)
# Исправление очевидных ошибок
corrections = {
'под руководством руководством': 'под руководством',
'в процессе процессе': 'в процессе',
'с использованием использования': 'с использованием',
' ,': ',',
' .': '.',
' ;': ';',
' ': ' '
}
for wrong, correct in corrections.items():
text = text.replace(wrong, correct)
# Капитализация первого слова
if text:
text = text[0].upper() + text[1:]
# Добавление точки в конце если нужно
if text and not text.endswith(('.', '!', '?')):
text += '.'
return text.strip()
def _correct_text(self, text: str) -> str:
"""Коррекция текста"""
return self._postprocess_text(text, 'it')
# Совместимость с оригинальным интерфейсом
def _normalize(self, text: str) -> set:
"""Совместимый метод нормализации"""
return set(self._tokenize(text))
def _calculate_jaccard_similarity(self, set1: set, set2: set) -> float:
"""Совместимый метод расчета схожести Жаккара"""
intersection = len(set1.intersection(set2))
union = len(set1.union(set2))
return intersection / union if union > 0 else 0.0
# FastAPI приложение
def create_app():
"""Создание FastAPI приложения"""
try:
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Optional, Dict, Any
app = FastAPI(title="Practice AI Backend Simple", version="1.0")
# Инициализация бэкенда
backend = PracticeAIBackendSimple()
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# Модели запросов
class TextRequest(BaseModel):
text: str
specialization: Optional[str] = "it"
class HonestyRequest(BaseModel):
text: str
history: List[str]
class ReputationRequest(BaseModel):
entries: List[Dict[str, Any]]
class SkillGapRequest(BaseModel):
texts: List[str]
target_specialization: str
# Эндпоинты
@app.get("/")
async def root():
return {
"service": "Practice AI Backend",
"version": "1.0",
"status": "running",
"ml_available": ML_AVAILABLE,
"transformers_available": TRANSFORMERS_AVAILABLE
}
@app.post("/api/analyze/honesty")
async def analyze_honesty(request: HonestyRequest):
try:
result = backend.analyze_honesty(request.text, request.history)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/improve/text")
async def improve_text(request: TextRequest):
try:
improved = backend.improve_text(request.text, request.specialization)
return {"improved_text": improved}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/calculate/reputation")
async def calculate_reputation(request: ReputationRequest):
try:
result = backend.calculate_reputation(request.entries)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/detect/specialization")
async def detect_specialization(request: TextRequest):
try:
spec, scores = backend.detect_specialization(request.text)
return {
"specialization": spec,
"confidence_scores": scores
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/analyze/skillgap")
async def analyze_skillgap(request: SkillGapRequest):
try:
result = backend.analyze_skill_gap(
request.texts,
request.target_specialization
)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/generate/thought")
async def generate_thought(request: TextRequest):
try:
thought = backend.generate_thought(request.specialization, request.text)
return {"generated_text": thought}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/health")
async def health_check():
return {
"status": "healthy",
"ml_available": ML_AVAILABLE,
"transformers_available": TRANSFORMERS_AVAILABLE,
"nltk_available": NLTK_AVAILABLE
}
return app
except ImportError:
print("FastAPI не установлен. Используйте: pip install fastapi uvicorn")
return None
# CLI интерфейс
def main():
"""Основная функция CLI"""
import argparse
import sys
parser = argparse.ArgumentParser(description="Practice AI Backend")
parser.add_argument("--mode", choices=["api", "cli"], default="cli", help="Режим работы")
parser.add_argument("--host", default="0.0.0.0", help="Хост для API")
parser.add_argument("--port", type=int, default=8000, help="Порт для API")
args = parser.parse_args()
if args.mode == "api":
app = create_app()
if app:
import uvicorn
print(f"Запуск API сервера на http://{args.host}:{args.port}")
uvicorn.run(app, host=args.host, port=args.port)
else:
print("Не удалось запустить API сервер")
sys.exit(1)
else:
# CLI режим
backend = PracticeAIBackendSimple()
print("=== Practice AI Backend CLI ===")
print("Доступные команды:")
print(" 1. Анализ честности текста")
print(" 2. Улучшение текста")
print(" 3. Определение специальности")
print(" 4. Расчет репутации")
print(" 5. Генерация текста")
print(" 6. Анализ пробелов в навыках")
print(" 0. Выход")
while True:
try:
choice = input("\nВыберите команду (0-6): ").strip()
if choice == "0":
print("Выход...")
break
elif choice == "1":
text = input("Введите текст для анализа: ")
history = input("Введите историю (через ;; ): ").split(";;")
result = backend.analyze_honesty(text, history)
print(f"Результат анализа:")
print(f" Оценка доверия: {result['trust_score']}")
print(f" Оригинальность: {result['is_original']}")
print(f" Читабельность: {result['readability']}")
print(f" Тональность: {result['sentiment']}")
elif choice == "2":
text = input("Введите текст для улучшения: ")
spec = input("Специальность (по умолчанию it): ") or "it"
improved = backend.improve_text(text, spec)
print(f"Улучшенный текст: {improved}")
elif choice == "3":
text = input("Введите текст: ")
spec, scores = backend.detect_specialization(text)
print(f"Определенная специальность: {spec}")
print(f"Оценки уверенности: {scores}")
elif choice == "4":
print("Введите записи в формате JSON (пустая строка для завершения):")
entries = []
while True:
line = input().strip()
if not line:
break
try:
entry = json.loads(line)
entries.append(entry)
except:
print("Ошибка парсинга JSON")
result = backend.calculate_reputation(entries)
print(f"Репутация: {result['score']} ({result['level']})")
print(f"Детализация: {result['breakdown']}")
elif choice == "5":
spec = input("Специальность (по умолчанию it): ") or "it"
context = input("Контекст (опционально): ")
thought = backend.generate_thought(spec, context if context else None)
print(f"Сгенерированный текст: {thought}")
elif choice == "6":
print("Введите тексты для анализа (пустая строка для завершения):")
texts = []
while True:
text = input().strip()
if not text:
break
texts.append(text)
target_spec = input("Целевая специальность (по умолчанию it): ") or "it"
result = backend.analyze_skill_gap(texts, target_spec)
print(f"Покрытие навыков: {result['skill_coverage']}%")
print(f"Уровень: {result['proficiency_level']}")
print("Рекомендации:")
for rec in result['recommendations']:
print(f" - {rec}")
else:
print("Неверный выбор")
except KeyboardInterrupt:
print("\nВыход...")
break
except Exception as e:
print(f"Ошибка: {e}")
if __name__ == "__main__":
main()