refactor(ai): улучшить архитектуру генератора названий букетов
- Добавить константы для параметров генерации - Улучшить валидацию входных параметров - Оптимизировать выбор AI-сервиса - Реализовать нормализацию регистра названий - Добавить обработку ошибок при сохранении в базу данных - Улучшить логику фильтрации нежелательных префиксов - Рефакторить метод generate_and_store для лучшей читаемости
This commit is contained in:
@@ -19,6 +19,12 @@ class BouquetNameGenerator(BaseAIProductService):
|
|||||||
"Избегайте общих терминов. Фокусируйтесь на эмоциях, эстетике"
|
"Избегайте общих терминов. Фокусируйтесь на эмоциях, эстетике"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Константы
|
||||||
|
MAX_TOKENS_GENERATION = 3000
|
||||||
|
DEFAULT_COUNT = 500
|
||||||
|
MAX_GENERATION_COUNT = 1000
|
||||||
|
SKIP_PREFIXES = {'here', 'names', "i'm", 'sorry', 'i hope', 'hope'}
|
||||||
|
|
||||||
def generate(
|
def generate(
|
||||||
self,
|
self,
|
||||||
count: int = 500,
|
count: int = 500,
|
||||||
@@ -38,17 +44,17 @@ class BouquetNameGenerator(BaseAIProductService):
|
|||||||
Returns:
|
Returns:
|
||||||
Tuple: (success, message, data) где data содержит список названий
|
Tuple: (success, message, data) где data содержит список названий
|
||||||
"""
|
"""
|
||||||
|
# Валидация параметров
|
||||||
|
if count > self.MAX_GENERATION_COUNT:
|
||||||
|
count = self.MAX_GENERATION_COUNT
|
||||||
|
logger.warning(f"Count reduced to {self.MAX_GENERATION_COUNT}")
|
||||||
|
|
||||||
logger.info(f"Генерация {count} названий для букетов")
|
logger.info(f"Генерация {count} названий для букетов")
|
||||||
|
|
||||||
# Получаем доступный AI-сервис
|
# Получаем доступный AI-сервис
|
||||||
glm_service = self.get_glm_service()
|
service = self.get_glm_service() or self.get_openrouter_service()
|
||||||
if not glm_service:
|
if not service:
|
||||||
openrouter_service = self.get_openrouter_service()
|
return False, "Нет активных AI-интеграций", None
|
||||||
if not openrouter_service:
|
|
||||||
return False, "Нет активных AI-интеграций", None
|
|
||||||
service = openrouter_service
|
|
||||||
else:
|
|
||||||
service = glm_service
|
|
||||||
|
|
||||||
# Формируем промпт
|
# Формируем промпт
|
||||||
prompt = f"Сгенерируй {count} креативных и привлекательных названий для букетов цветов"
|
prompt = f"Сгенерируй {count} креативных и привлекательных названий для букетов цветов"
|
||||||
@@ -98,9 +104,7 @@ class BouquetNameGenerator(BaseAIProductService):
|
|||||||
for line in lines:
|
for line in lines:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
# Пропускаем пустые строки и заголовки
|
# Пропускаем пустые строки и заголовки
|
||||||
if not line or line.lower().startswith('here') or line.lower().startswith('names') or \
|
if not line or any(line.lower().startswith(prefix) for prefix in self.SKIP_PREFIXES):
|
||||||
line.lower().startswith('i\'m') or line.lower().startswith('sorry') or \
|
|
||||||
line.lower().startswith('i hope') or line.lower().startswith('hope'):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Удаляем номера списка
|
# Удаляем номера списка
|
||||||
@@ -119,7 +123,9 @@ class BouquetNameGenerator(BaseAIProductService):
|
|||||||
line = line.replace('**', '').replace('*', '').replace('"', '').replace("'", '').strip()
|
line = line.replace('**', '').replace('*', '').replace('"', '').replace("'", '').strip()
|
||||||
|
|
||||||
if line:
|
if line:
|
||||||
names.append(line)
|
# Приводим к нужному формату: первое слово с заглавной, остальные строчные
|
||||||
|
normalized_line = self._normalize_case(line)
|
||||||
|
names.append(normalized_line)
|
||||||
|
|
||||||
# Удаляем дубликаты
|
# Удаляем дубликаты
|
||||||
unique_names = []
|
unique_names = []
|
||||||
@@ -131,6 +137,28 @@ class BouquetNameGenerator(BaseAIProductService):
|
|||||||
|
|
||||||
return unique_names
|
return unique_names
|
||||||
|
|
||||||
|
def _normalize_case(self, text: str) -> str:
|
||||||
|
"""
|
||||||
|
Приводит текст к формату: первое слово с заглавной буквы, остальные строчные
|
||||||
|
Например: "романтический БУКЕТ роз" -> "Романтический букет роз"
|
||||||
|
Но сохраняет имена собственные: "Букет Van Gogh" -> "Букет Van Gogh"
|
||||||
|
"""
|
||||||
|
if not text:
|
||||||
|
return text
|
||||||
|
|
||||||
|
# Разбиваем текст на слова
|
||||||
|
words = text.split()
|
||||||
|
|
||||||
|
if not words:
|
||||||
|
return text
|
||||||
|
|
||||||
|
# Первое слово с заглавной буквы, остальные как есть (сохраняем имена собственные)
|
||||||
|
first_word = words[0].capitalize()
|
||||||
|
remaining_words = words[1:]
|
||||||
|
|
||||||
|
# Собираем обратно в строку
|
||||||
|
return ' '.join([first_word] + remaining_words)
|
||||||
|
|
||||||
def generate_and_store(
|
def generate_and_store(
|
||||||
self,
|
self,
|
||||||
count: int = 500,
|
count: int = 500,
|
||||||
@@ -142,23 +170,33 @@ class BouquetNameGenerator(BaseAIProductService):
|
|||||||
Генерирует названия и сохраняет в базу данных
|
Генерирует названия и сохраняет в базу данных
|
||||||
"""
|
"""
|
||||||
from products.models import BouquetName
|
from products.models import BouquetName
|
||||||
|
|
||||||
success, msg, data = self.generate(count, characteristics, occasion, language)
|
success, msg, data = self.generate(count, characteristics, occasion, language)
|
||||||
|
|
||||||
if success and data:
|
if success and data:
|
||||||
# Сохраняем названия в базу
|
# Сохраняем названия в базу
|
||||||
stored_count = 0
|
stored_count = 0
|
||||||
for name in data['names']:
|
failed_count = 0
|
||||||
BouquetName.objects.get_or_create(
|
|
||||||
name=name,
|
|
||||||
language=language,
|
|
||||||
defaults={
|
|
||||||
'is_approved': False
|
|
||||||
}
|
|
||||||
)
|
|
||||||
stored_count += 1
|
|
||||||
|
|
||||||
return True, f"Сгенерировано и сохранено {stored_count} названий для букетов", data
|
for name in data['names']:
|
||||||
|
try:
|
||||||
|
BouquetName.objects.get_or_create(
|
||||||
|
name=name,
|
||||||
|
language=language,
|
||||||
|
defaults={
|
||||||
|
'is_approved': False
|
||||||
|
}
|
||||||
|
)
|
||||||
|
stored_count += 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка сохранения названия '{name}': {e}")
|
||||||
|
failed_count += 1
|
||||||
|
|
||||||
|
success_msg = f"Сгенерировано и сохранено {stored_count} названий для букетов"
|
||||||
|
if failed_count > 0:
|
||||||
|
success_msg += f", не удалось сохранить {failed_count} названий"
|
||||||
|
|
||||||
|
return True, success_msg, data
|
||||||
|
|
||||||
return success, msg, data
|
return success, msg, data
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user