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(
|
||||
self,
|
||||
count: int = 500,
|
||||
@@ -38,17 +44,17 @@ class BouquetNameGenerator(BaseAIProductService):
|
||||
Returns:
|
||||
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} названий для букетов")
|
||||
|
||||
# Получаем доступный AI-сервис
|
||||
glm_service = self.get_glm_service()
|
||||
if not glm_service:
|
||||
openrouter_service = self.get_openrouter_service()
|
||||
if not openrouter_service:
|
||||
return False, "Нет активных AI-интеграций", None
|
||||
service = openrouter_service
|
||||
else:
|
||||
service = glm_service
|
||||
service = self.get_glm_service() or self.get_openrouter_service()
|
||||
if not service:
|
||||
return False, "Нет активных AI-интеграций", None
|
||||
|
||||
# Формируем промпт
|
||||
prompt = f"Сгенерируй {count} креативных и привлекательных названий для букетов цветов"
|
||||
@@ -98,9 +104,7 @@ class BouquetNameGenerator(BaseAIProductService):
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
# Пропускаем пустые строки и заголовки
|
||||
if not line or line.lower().startswith('here') or line.lower().startswith('names') or \
|
||||
line.lower().startswith('i\'m') or line.lower().startswith('sorry') or \
|
||||
line.lower().startswith('i hope') or line.lower().startswith('hope'):
|
||||
if not line or any(line.lower().startswith(prefix) for prefix in self.SKIP_PREFIXES):
|
||||
continue
|
||||
|
||||
# Удаляем номера списка
|
||||
@@ -119,7 +123,9 @@ class BouquetNameGenerator(BaseAIProductService):
|
||||
line = line.replace('**', '').replace('*', '').replace('"', '').replace("'", '').strip()
|
||||
|
||||
if line:
|
||||
names.append(line)
|
||||
# Приводим к нужному формату: первое слово с заглавной, остальные строчные
|
||||
normalized_line = self._normalize_case(line)
|
||||
names.append(normalized_line)
|
||||
|
||||
# Удаляем дубликаты
|
||||
unique_names = []
|
||||
@@ -131,6 +137,28 @@ class BouquetNameGenerator(BaseAIProductService):
|
||||
|
||||
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(
|
||||
self,
|
||||
count: int = 500,
|
||||
@@ -148,17 +176,27 @@ class BouquetNameGenerator(BaseAIProductService):
|
||||
if success and data:
|
||||
# Сохраняем названия в базу
|
||||
stored_count = 0
|
||||
for name in data['names']:
|
||||
BouquetName.objects.get_or_create(
|
||||
name=name,
|
||||
language=language,
|
||||
defaults={
|
||||
'is_approved': False
|
||||
}
|
||||
)
|
||||
stored_count += 1
|
||||
failed_count = 0
|
||||
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user