Spaces:
Sleeping
Sleeping
fruitpicker01
commited on
Commit
•
981362f
1
Parent(s):
19f6073
Update app.py
Browse files
app.py
CHANGED
@@ -18,9 +18,6 @@ import base64
|
|
18 |
import io
|
19 |
from transformers import AutoTokenizer, AutoModel
|
20 |
from utils import best_text_choice
|
21 |
-
import asyncio
|
22 |
-
import inspect
|
23 |
-
from together import AsyncTogether
|
24 |
|
25 |
tokenizer = AutoTokenizer.from_pretrained("ai-forever/ru-en-RoSBERTa")
|
26 |
model = AutoModel.from_pretrained("ai-forever/ru-en-RoSBERTa")
|
@@ -28,22 +25,13 @@ model = AutoModel.from_pretrained("ai-forever/ru-en-RoSBERTa")
|
|
28 |
unique_sms_df = pd.read_parquet('unique_texts.parquet')
|
29 |
|
30 |
MISTRAL_API_KEY = os.getenv('MISTRAL_API_KEY')
|
31 |
-
MISTRAL_API_KEY_2 = os.getenv('MISTRAL_API_KEY_2')
|
32 |
-
MISTRAL_API_KEY_3 = os.getenv('MISTRAL_API_KEY_3')
|
33 |
-
MISTRAL_API_KEY_4 = os.getenv('MISTRAL_API_KEY_4')
|
34 |
-
MISTRAL_API_KEY_5 = os.getenv('MISTRAL_API_KEY_5')
|
35 |
token = os.getenv('GITHUB_TOKEN')
|
36 |
|
37 |
-
async_client = AsyncTogether(api_key=os.environ.get("TOGETHER_API_KEY"))
|
38 |
-
|
39 |
# Клиент для генерации сообщений
|
40 |
client_mistral_generate = Mistral(api_key=MISTRAL_API_KEY)
|
41 |
|
42 |
# Клиент для выполнения проверок
|
43 |
-
client_mistral_check = Mistral(api_key=
|
44 |
-
client_mistral_check_2 = Mistral(api_key=MISTRAL_API_KEY_3)
|
45 |
-
client_mistral_check_3 = Mistral(api_key=MISTRAL_API_KEY_4)
|
46 |
-
client_mistral_check_4 = Mistral(api_key=MISTRAL_API_KEY_5)
|
47 |
|
48 |
morph = pymorphy3.MorphAnalyzer()
|
49 |
|
@@ -200,8 +188,7 @@ def clean_message(message):
|
|
200 |
# except Exception as e:
|
201 |
# return f"Ошибка при обращении к GigaChat-Pro: {e}"
|
202 |
|
203 |
-
|
204 |
-
|
205 |
#def generate_message_mistral_generate(prompt):
|
206 |
# try:
|
207 |
# messages = [SystemMessage(content=prompt)]
|
@@ -214,173 +201,34 @@ async def generate_message_mistral_generate(prompt, max_retries=5):
|
|
214 |
retries = 0
|
215 |
while retries < max_retries:
|
216 |
try:
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
except Exception as e:
|
230 |
-
error_message = str(e)
|
231 |
-
# Проверяем, связана ли ошибка с превышением лимита или разрывом соединения
|
232 |
-
if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
|
233 |
-
wait_time = 3 # Время ожидания перед повторной попыткой
|
234 |
-
print(f"Лимит запросов превышен или сервер не ответил. Ожидание {wait_time} секунд перед повторной попыткой...")
|
235 |
-
await asyncio.sleep(wait_time)
|
236 |
-
retries += 1
|
237 |
-
else:
|
238 |
-
# Для других типов ошибок выводим сообщение и прекращаем попытки
|
239 |
-
print(f"Ошибка при вызове Together API: {e}")
|
240 |
-
return None
|
241 |
-
|
242 |
-
# Если все попытки исчерпаны, сообщаем об этом
|
243 |
-
print("Не удалось получить ответ от Together API после максимального количества попыток.")
|
244 |
-
return None
|
245 |
-
|
246 |
-
# retries = 0
|
247 |
-
# while retries < max_retries:
|
248 |
-
# try:
|
249 |
-
# chat_response = await client_mistral_generate.chat.complete_async(
|
250 |
-
# model="open-mistral-nemo",
|
251 |
-
# temperature=1.0,
|
252 |
-
# min_tokens=71,
|
253 |
-
# max_tokens=96,
|
254 |
-
# messages=[
|
255 |
-
# {
|
256 |
-
# "role": "user",
|
257 |
-
# "content": prompt
|
258 |
-
# },
|
259 |
-
# ]
|
260 |
-
# )
|
261 |
-
# cleaned_message = clean_message(chat_response.choices[0].message.content.strip())
|
262 |
-
# return cleaned_message
|
263 |
-
# except Exception as e:
|
264 |
-
# error_message = str(e)
|
265 |
-
# if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
|
266 |
-
# wait_time = 3
|
267 |
-
# print(f"Rate limit exceeded or server did not respond. Waiting {wait_time} seconds before retrying...")
|
268 |
-
# await asyncio.sleep(wait_time)
|
269 |
-
# retries += 1
|
270 |
-
# else:
|
271 |
-
# print(f"Error calling Mistral: {e}")
|
272 |
-
# return None
|
273 |
-
# print("Failed to get response from Mistral after maximum retries.")
|
274 |
-
# return None
|
275 |
-
|
276 |
-
async def generate_message_mistral_check(prompt, max_retries=5):
|
277 |
-
#def generate_message_mistral_check(prompt):
|
278 |
-
# try:
|
279 |
-
# messages = [SystemMessage(content=prompt)]
|
280 |
-
# res2 = chat_pro_check(messages)
|
281 |
-
# cleaned_message = clean_message(res2.content.strip())
|
282 |
-
# return cleaned_message
|
283 |
-
# except Exception as e:
|
284 |
-
# return f"Ошибка при обращении к GigaChat-Pro: {e}"
|
285 |
-
retries = 0
|
286 |
-
while retries < max_retries:
|
287 |
-
try:
|
288 |
-
# Отправляем запрос к модели Qwen через Together API
|
289 |
-
response = await async_client.chat.completions.create(
|
290 |
-
model="Meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
|
291 |
-
messages=[{"role": "user", "content": prompt}],
|
292 |
)
|
293 |
-
|
294 |
-
# Извлекаем и очищаем ответ
|
295 |
-
cleaned_message = clean_message(response.choices[0].message.content.strip())
|
296 |
return cleaned_message
|
297 |
-
|
298 |
except Exception as e:
|
299 |
error_message = str(e)
|
300 |
-
# Проверяем, связана ли ошибка с превышением лимита или разрывом соединения
|
301 |
if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
|
302 |
-
wait_time = 3
|
303 |
-
print(f"
|
304 |
-
|
305 |
retries += 1
|
306 |
else:
|
307 |
-
|
308 |
-
print(f"Ошибка при вызове Together API: {e}")
|
309 |
return None
|
310 |
-
|
311 |
-
# Если все попытки исчерпаны, сообщаем об этом
|
312 |
-
print("Не удалось получить ответ от Together API после максимального количества попыток.")
|
313 |
return None
|
314 |
|
315 |
-
|
316 |
-
#def generate_message_mistral_check(prompt):
|
317 |
-
# try:
|
318 |
-
# messages = [SystemMessage(content=prompt)]
|
319 |
-
# res2 = chat_pro_check(messages)
|
320 |
-
# cleaned_message = clean_message(res2.content.strip())
|
321 |
-
# return cleaned_message
|
322 |
-
# except Exception as e:
|
323 |
-
# return f"Ошибка при обращении к GigaChat-Pro: {e}"
|
324 |
-
|
325 |
-
retries = 0
|
326 |
-
while retries < max_retries:
|
327 |
-
try:
|
328 |
-
# Отправляем запрос к модели Qwen через Together API
|
329 |
-
response = await async_client.chat.completions.create(
|
330 |
-
model="Meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
|
331 |
-
messages=[{"role": "user", "content": prompt}],
|
332 |
-
)
|
333 |
-
|
334 |
-
# Извлекаем и очищаем ответ
|
335 |
-
cleaned_message = clean_message(response.choices[0].message.content.strip())
|
336 |
-
return cleaned_message
|
337 |
-
|
338 |
-
except Exception as e:
|
339 |
-
error_message = str(e)
|
340 |
-
# Проверяем, связана ли ошибка с превышением лимита или разрывом соединения
|
341 |
-
if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
|
342 |
-
wait_time = 3 # Время ожидания перед повторной попыткой
|
343 |
-
print(f"Лимит запросов превышен или сервер не ответил. Ожидание {wait_time} секунд перед повторной попыткой...")
|
344 |
-
await asyncio.sleep(wait_time)
|
345 |
-
retries += 1
|
346 |
-
else:
|
347 |
-
# Для других типов ошибок выводим сообщение и прекращаем попытки
|
348 |
-
print(f"Ошибка при вызове Together API: {e}")
|
349 |
-
return None
|
350 |
-
|
351 |
-
# Если все попытки исчерпаны, сообщаем об этом
|
352 |
-
print("Не удалось получить ответ от Together API после максимального количества попыток.")
|
353 |
-
return None
|
354 |
-
|
355 |
-
# retries = 0
|
356 |
-
# while retries < max_retries:
|
357 |
-
# try:
|
358 |
-
# chat_response = await client_mistral_check_2.chat.complete_async(
|
359 |
-
# model="open-mistral-nemo",
|
360 |
-
# temperature=0.2,
|
361 |
-
# messages=[
|
362 |
-
# {
|
363 |
-
# "role": "user",
|
364 |
-
# "content": prompt
|
365 |
-
# },
|
366 |
-
# ]
|
367 |
-
# )
|
368 |
-
# cleaned_message = clean_message(chat_response.choices[0].message.content.strip())
|
369 |
-
# return cleaned_message
|
370 |
-
# except Exception as e:
|
371 |
-
# error_message = str(e)
|
372 |
-
# if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
|
373 |
-
# wait_time = 3
|
374 |
-
# print(f"Rate limit exceeded or server did not respond. Waiting {wait_time} seconds before retrying...")
|
375 |
-
# await asyncio.sleep(wait_time)
|
376 |
-
# retries += 1
|
377 |
-
# else:
|
378 |
-
# print(f"Error calling Mistral: {e}")
|
379 |
-
# return None
|
380 |
-
# print("Failed to get response from Mistral after maximum retries.")
|
381 |
-
# return None
|
382 |
-
|
383 |
-
async def generate_message_mistral_check_3(prompt, max_retries=5):
|
384 |
#def generate_message_mistral_check(prompt):
|
385 |
# try:
|
386 |
# messages = [SystemMessage(content=prompt)]
|
@@ -389,39 +237,32 @@ async def generate_message_mistral_check_3(prompt, max_retries=5):
|
|
389 |
# return cleaned_message
|
390 |
# except Exception as e:
|
391 |
# return f"Ошибка при обращении к GigaChat-Pro: {e}"
|
392 |
-
|
393 |
retries = 0
|
394 |
while retries < max_retries:
|
395 |
try:
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
messages=[
|
|
|
|
|
|
|
|
|
|
|
400 |
)
|
401 |
-
|
402 |
-
# Извлекаем и очищаем ответ
|
403 |
-
cleaned_message = clean_message(response.choices[0].message.content.strip())
|
404 |
return cleaned_message
|
405 |
-
|
406 |
except Exception as e:
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
return None
|
418 |
-
|
419 |
-
# Если все попытки исчерпаны, сообщаем об этом
|
420 |
-
print("Не удалось получить ответ от Together API после максимального количества попыток.")
|
421 |
-
return None
|
422 |
-
|
423 |
-
async def generate_message_mistral_check_4(prompt, max_retries=5):
|
424 |
-
#def generate_message_mistral_check(prompt):
|
425 |
# try:
|
426 |
# messages = [SystemMessage(content=prompt)]
|
427 |
# res2 = chat_pro_check(messages)
|
@@ -429,37 +270,6 @@ async def generate_message_mistral_check_4(prompt, max_retries=5):
|
|
429 |
# return cleaned_message
|
430 |
# except Exception as e:
|
431 |
# return f"Ошибка при обращении к GigaChat-Pro: {e}"
|
432 |
-
|
433 |
-
retries = 0
|
434 |
-
while retries < max_retries:
|
435 |
-
try:
|
436 |
-
# Отправляем запрос к модели Qwen через Together API
|
437 |
-
response = await async_client.chat.completions.create(
|
438 |
-
model="Meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
|
439 |
-
messages=[{"role": "user", "content": prompt}],
|
440 |
-
)
|
441 |
-
|
442 |
-
# Извлекаем и очищаем ответ
|
443 |
-
cleaned_message = clean_message(response.choices[0].message.content.strip())
|
444 |
-
return cleaned_message
|
445 |
-
|
446 |
-
except Exception as e:
|
447 |
-
error_message = str(e)
|
448 |
-
# Проверяем, связана ли ошибка с превышением лимита или разрывом соединения
|
449 |
-
if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
|
450 |
-
wait_time = 3 # Время ожидания перед повторной попыткой
|
451 |
-
print(f"Лимит запросов превышен или сервер не ответил. Ожидание {wait_time} секунд перед повторной попыткой...")
|
452 |
-
await asyncio.sleep(wait_time)
|
453 |
-
retries += 1
|
454 |
-
else:
|
455 |
-
# Для других типов ошибок выводим сообщение и прекращаем попытки
|
456 |
-
print(f"Ошибка при вызове Together API: {e}")
|
457 |
-
return None
|
458 |
-
|
459 |
-
# Если все попытки исчерпаны, сообщаем об этом
|
460 |
-
print("Не удалось получить ответ от Together API после максимального количества попыток.")
|
461 |
-
return None
|
462 |
-
|
463 |
|
464 |
# Функция для замены сокращений с 'k' или 'К' на тысячи
|
465 |
def replace_k_with_thousands(message):
|
@@ -628,7 +438,7 @@ def append_errors_to_prompt(prompt, checks):
|
|
628 |
"derived_prepositions": "Не использовать производные предлоги.",
|
629 |
"compound_sentences": "Избегать сложноподчиненных предложений.",
|
630 |
"dates_written_out": "Не писать даты прописью.",
|
631 |
-
|
632 |
"disconnected_sentences": "Избегать сложных предложений без логической связи.",
|
633 |
"synonymous_members": "Не использовать близкие по смыслу однородные члены предложения.",
|
634 |
"clickbait_phrases": "Не использовать кликбейтные фразы.",
|
@@ -653,8 +463,8 @@ def append_errors_to_prompt(prompt, checks):
|
|
653 |
|
654 |
|
655 |
def notify_failed_length(message_length):
|
656 |
-
if message_length <
|
657 |
-
gr.Warning(f"Сообщение слишком короткое: {message_length} знаков. Минимум
|
658 |
return False
|
659 |
elif message_length > 250:
|
660 |
gr.Warning(f"Сообщение слишком длинное: {message_length} знаков. Максимум 250.")
|
@@ -667,7 +477,7 @@ def notify_failed_checks(checks):
|
|
667 |
"forbidden_words": "Запрещенные слова",
|
668 |
"client_addressing": "Обращение к клиенту",
|
669 |
"promises": "Обещания и гарантии",
|
670 |
-
|
671 |
"participles": "Причастия",
|
672 |
"adverbial_participles": "Деепричастия",
|
673 |
"superlative_adjectives": "Превосходная степень",
|
@@ -682,7 +492,7 @@ def notify_failed_checks(checks):
|
|
682 |
"derived_prepositions": "Производные предлоги",
|
683 |
"compound_sentences": "Сложноподчиненные предложения",
|
684 |
"dates_written_out": "Даты прописью",
|
685 |
-
|
686 |
"disconnected_sentences": "Сложные предложения без логической связи",
|
687 |
"synonymous_members": "Близкие по смыслу однородные члены предложения",
|
688 |
"clickbait_phrases": "Кликбейтные фразы",
|
@@ -735,24 +545,24 @@ def notify_failed_checks(checks):
|
|
735 |
# return last_message
|
736 |
|
737 |
|
738 |
-
|
739 |
global approach_stats
|
740 |
last_message = None
|
741 |
for attempt in range(20):
|
742 |
-
gr.Info(f"Итерация {attempt + 1}:
|
743 |
-
message =
|
744 |
if message is None:
|
745 |
-
print("
|
746 |
-
|
747 |
continue
|
748 |
message = replace_k_with_thousands(message)
|
749 |
message = correct_dash_usage(message)
|
750 |
message_length = len(message)
|
751 |
if not notify_failed_length(message_length):
|
752 |
last_message = message
|
753 |
-
|
754 |
continue
|
755 |
-
checks =
|
756 |
last_message = message
|
757 |
# Инициализируем статистику для подхода, если ее нет
|
758 |
if approach_name not in approach_stats:
|
@@ -766,7 +576,7 @@ async def generate_message_mistral_with_retry(prompt, approach_name, description
|
|
766 |
if all(checks.values()):
|
767 |
return message
|
768 |
prompt = append_errors_to_prompt(prompt, checks)
|
769 |
-
|
770 |
gr.Info("Не удалось сгенерировать сообщение, соответствующее требованиям, за 20 итераций. Возвращаем последнее сгенерированное сообщение.")
|
771 |
return last_message
|
772 |
|
@@ -779,7 +589,7 @@ def generate_standard_prompt(description, benefits, key_message, *selected_value
|
|
779 |
f"Преимущества: {benefits}\n"
|
780 |
"В тексте смс запрещено использование:\n"
|
781 |
"- Запрещенные слова: № один, номер один, № 1, вкусный, дешёвый, продукт, спам, доступный, банкротство, долги, займ, срочно, сейчас, лучший, главный, номер 1, гарантия, успех, лидер, никакой;\n"
|
782 |
-
"-
|
783 |
"- Обращение к клиенту;\n"
|
784 |
"- Приветствие клиента;\n"
|
785 |
"- Обещания и гарантии;\n"
|
@@ -806,7 +616,7 @@ def generate_standard_prompt(description, benefits, key_message, *selected_value
|
|
806 |
"- Узкоспециализированные термины;\n"
|
807 |
"- Фразы, способные создать двойственное ощущение, обидеть;\n"
|
808 |
"- Речевые клише, рекламные штампы, канцеляризмы;\n"
|
809 |
-
"Убедись, что в готовом тексте до 250, но не менее
|
810 |
)
|
811 |
if key_message.strip():
|
812 |
prompt += f"Убедись, что в готовом тексте есть следующая ключевая информация: {key_message.strip()}"
|
@@ -818,7 +628,7 @@ def generate_standard_prompt(description, benefits, key_message, *selected_value
|
|
818 |
def generate_personalization_prompt(key_message, *selected_values, prefix, suffix, product_name):
|
819 |
prompt = f"{prefix}\n"
|
820 |
prompt += f"Не изменяй название продукта: {product_name}.\n"
|
821 |
-
prompt += "Адаптируй, не превышая длину сообщения в 250 знаков с пробелами (но и не менее
|
822 |
gender, generation, psychotype = selected_values[0], selected_values[1], selected_values[2]
|
823 |
combined_instruction = ""
|
824 |
additional_instructions = ""
|
@@ -882,7 +692,7 @@ def clean_prompt_for_display(prompt, prefixes, suffixes):
|
|
882 |
return cleaned_prompt.strip()
|
883 |
|
884 |
# Функция для постепенной генерации всех сообщений через yield
|
885 |
-
|
886 |
standard_prompt = generate_standard_prompt(desc, benefits, key_message)
|
887 |
standard_prompt_for_display = f"Не изменяй название продукта: {product_name}.\n{standard_prompt}\nУбедись, что в готовом тексте без изменений, синонимов и перестановок слов используется наименование продукта: {product_name}.\n"
|
888 |
approach_mapping = {
|
@@ -954,7 +764,7 @@ async def generate_all_messages(desc, benefits, key_message, gender, generation,
|
|
954 |
yield selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt, None, None
|
955 |
flag += 1
|
956 |
prompt = add_prefix_suffix(standard_prompt, current_prefix, current_suffix, product_name)
|
957 |
-
non_personalized_message =
|
958 |
non_personalized_length = len(non_personalized_message)
|
959 |
non_personalized_display = f"{non_personalized_message}\n------\nКоличество знаков: {non_personalized_length}"
|
960 |
if non_personalized_messages:
|
@@ -966,7 +776,7 @@ async def generate_all_messages(desc, benefits, key_message, gender, generation,
|
|
966 |
non_personalized_messages, personalized_messages
|
967 |
)
|
968 |
full_personalized_prompt = f"{personalization_prompt}\n\nТекст для адаптации: {non_personalized_message}"
|
969 |
-
personalized_message =
|
970 |
personalized_length = len(personalized_message)
|
971 |
personalized_display = f"{personalized_message}\n------\nКоличество знаков: {personalized_length}"
|
972 |
if personalized_messages:
|
@@ -977,7 +787,7 @@ async def generate_all_messages(desc, benefits, key_message, gender, generation,
|
|
977 |
selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt,
|
978 |
non_personalized_messages, personalized_messages
|
979 |
)
|
980 |
-
|
981 |
save_statistics_to_github(approach_stats)
|
982 |
|
983 |
def rank_messages(non_personalized_messages, personalized_messages):
|
@@ -1371,7 +1181,7 @@ def check_no_word_repetitions(message):
|
|
1371 |
'и', 'а', 'но', 'или', 'да', 'ни', 'как', 'так',
|
1372 |
'в', 'на', 'под', 'над', 'за', 'к', 'до', 'по', 'из', 'у', 'о', 'про', 'для',
|
1373 |
'не', 'вот', 'это', 'тот', 'тем', 'при', 'чем',
|
1374 |
-
'же', 'ли', 'бы', 'то', 'р',
|
1375 |
])
|
1376 |
|
1377 |
# Разбиваем текст на слова, удаляя знаки препинания
|
@@ -1451,7 +1261,7 @@ def cut_message(message):
|
|
1451 |
return message
|
1452 |
|
1453 |
# 22. Проверка сложных предложений без логической связи
|
1454 |
-
|
1455 |
message_clean = cut_message(message)
|
1456 |
print()
|
1457 |
print("Проверка 22: Проверка сложных предложений без логической связи")
|
@@ -1466,8 +1276,8 @@ async def check_disconnected_sentences(message):
|
|
1466 |
если таких предложений **нет**, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
|
1467 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь **только** в формате JSON с закрывающими кавычками и скобками.**'''
|
1468 |
|
1469 |
-
response =
|
1470 |
-
|
1471 |
print("Mistral response:", response) # Выводим полный ответ модели
|
1472 |
result = parse_json_response(response)
|
1473 |
if result is not None:
|
@@ -1479,7 +1289,7 @@ async def check_disconnected_sentences(message):
|
|
1479 |
return None
|
1480 |
|
1481 |
# 23. Проверка на близкие по смыслу однородные члены
|
1482 |
-
|
1483 |
print()
|
1484 |
print("Проверка 23: Проверка на близкие по смыслу однородные члены")
|
1485 |
print()
|
@@ -1493,8 +1303,8 @@ async def check_synonymous_members(message):
|
|
1493 |
если таких слов или выражений нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
|
1494 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1495 |
|
1496 |
-
response =
|
1497 |
-
|
1498 |
print("Mistral response:", response)
|
1499 |
result = parse_json_response(response)
|
1500 |
if result is not None:
|
@@ -1507,7 +1317,7 @@ async def check_synonymous_members(message):
|
|
1507 |
|
1508 |
|
1509 |
# 24. Проверка на шокирующие, экстравагантные или кликбейтные фразы
|
1510 |
-
|
1511 |
message_clean = cut_message(message)
|
1512 |
print()
|
1513 |
print()
|
@@ -1523,7 +1333,6 @@ async def check_clickbait_phrases(message, description, benefits, key_message):
|
|
1523 |
3. Стандартные рекламные призывы к действию, такие как "купите сейчас" или "узнайте больше", не считаются кликбейтом, если они не преувеличивают преимущества или не используют явную манипуляцию эмоциями.
|
1524 |
4. Не считай фразы, используемые в исходном описании продукта, кликбейтными. Исходное описание: "{description}".
|
1525 |
5. Не считай фразы, используемые в преимуществах продукта, кликбейтными. Преимущества: "{benefits}".
|
1526 |
-
6. Не считай фразы, используемые в ключевом сообщении, кликбейтными. Ключевое сообщение: "{key_message}".
|
1527 |
Пример ответа:
|
1528 |
{{"decision": false, "explanation": "Текст нейтрален и не содержит кликбейтных фраз."}}
|
1529 |
|
@@ -1532,8 +1341,8 @@ async def check_clickbait_phrases(message, description, benefits, key_message):
|
|
1532 |
|
1533 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1534 |
|
1535 |
-
response =
|
1536 |
-
|
1537 |
print("Mistral response:", response)
|
1538 |
result = parse_json_response(response)
|
1539 |
if result is not None:
|
@@ -1546,7 +1355,7 @@ async def check_clickbait_phrases(message, description, benefits, key_message):
|
|
1546 |
|
1547 |
|
1548 |
# 25. Проверка на абстрактные заявления без поддержки фактами
|
1549 |
-
|
1550 |
print()
|
1551 |
print("Проверка 25: Проверка на абстрактные заявления без поддержки фактами")
|
1552 |
print()
|
@@ -1573,9 +1382,8 @@ async def check_abstract_claims(message, description, benefits, key_message):
|
|
1573 |
- "Снизьте финансовую нагрузку"
|
1574 |
4. Ищи общие фразы, которые не дают представления о конкретной пользе, такие как "лучшее решение", "высокое качество", "отличный сервис", если они не сопровождаются пояснением о том, почему это так.
|
1575 |
5. Учитывай, что в рекламных сообщениях допустимы эмоциональные и обобщённые фразы, если они достаточно конкретны для понимания аудитории, однако они должны сопровождаться фактами или подробными примерами.
|
1576 |
-
6. Не считай фразы, используемые в исходном описании продукта,
|
1577 |
-
7. Не
|
1578 |
-
8. Не считай фразы, используемые в ключевом сообщении, абстрактными. Ключевое сообщение: "{key_message}".
|
1579 |
|
1580 |
Пример ответа:
|
1581 |
{{"decision": false, "explanation": "Текст не содержит абстрактные утверждения без конкретики."}}
|
@@ -1585,8 +1393,8 @@ async def check_abstract_claims(message, description, benefits, key_message):
|
|
1585 |
|
1586 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1587 |
|
1588 |
-
response =
|
1589 |
-
|
1590 |
print("Mistral response:", response)
|
1591 |
result = parse_json_response(response)
|
1592 |
if result is not None:
|
@@ -1599,7 +1407,7 @@ async def check_abstract_claims(message, description, benefits, key_message):
|
|
1599 |
|
1600 |
|
1601 |
# 26. Проверка на узкоспециализированные термины
|
1602 |
-
|
1603 |
print()
|
1604 |
print("Проверка 26: Проверка на узкоспециализированные термины")
|
1605 |
print()
|
@@ -1622,8 +1430,8 @@ async def check_specialized_terms(message):
|
|
1622 |
|
1623 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1624 |
|
1625 |
-
response =
|
1626 |
-
|
1627 |
print("Mistral response:", response)
|
1628 |
result = parse_json_response(response)
|
1629 |
if result is not None:
|
@@ -1635,7 +1443,7 @@ async def check_specialized_terms(message):
|
|
1635 |
return None
|
1636 |
|
1637 |
# 27. Проверка на двусмысленные или обидные фразы
|
1638 |
-
|
1639 |
print()
|
1640 |
print("Проверка 27: Проверка на двусмысленные или обидные фразы")
|
1641 |
print()
|
@@ -1651,8 +1459,8 @@ async def check_offensive_phrases(message):
|
|
1651 |
если таких фраз нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
|
1652 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1653 |
|
1654 |
-
response =
|
1655 |
-
|
1656 |
print("Mistral response:", response)
|
1657 |
result = parse_json_response(response)
|
1658 |
if result is not None:
|
@@ -1664,7 +1472,7 @@ async def check_offensive_phrases(message):
|
|
1664 |
return None
|
1665 |
|
1666 |
# 28. Проверка на речевые клише, рекламные штампы и канцеляризмы
|
1667 |
-
|
1668 |
print()
|
1669 |
print("Проверка 28: Проверка на речевые клише, рекламные штампы и канцеляризмы")
|
1670 |
print()
|
@@ -1677,9 +1485,8 @@ async def check_cliches_and_bureaucratese(message, description, benefits, key_me
|
|
1677 |
- Информацию о ценах, скидках, акциях или условиях покупки (например, "при покупках от 100 000 рублей в месяц").
|
1678 |
- Описания способов оформления или получения услуг (например, "оформление возможно онлайн или в офисе").
|
1679 |
- Стандартные отраслевые термины и фразы, необходимые для понимания сообщения (например, "премиальная бизнес-карта", "Mastercard Preferred"), но **не** их использование в комбинации с общими словами, как например, "идеальное решение для вашего бизнеса".
|
1680 |
-
-
|
1681 |
-
-
|
1682 |
-
- Не считай фразы, используемые в ключевом сообщении, как клише. Ключевое сообщение: "{key_message}".
|
1683 |
**Считай клише или канцеляризмами следующие типы выражений:**
|
1684 |
- Избитые фразы, такие как:
|
1685 |
- "Обеспечьте стабильность и развитие вашего бизнеса"
|
@@ -1697,8 +1504,8 @@ async def check_cliches_and_bureaucratese(message, description, benefits, key_me
|
|
1697 |
если в тексте **есть** такие выражения, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}.
|
1698 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1699 |
|
1700 |
-
response =
|
1701 |
-
|
1702 |
print("Mistral response:", response)
|
1703 |
result = parse_json_response(response)
|
1704 |
if result is not None:
|
@@ -1710,26 +1517,24 @@ async def check_cliches_and_bureaucratese(message, description, benefits, key_me
|
|
1710 |
return None
|
1711 |
|
1712 |
# 29. Проверка на соответствие описанию предложения и ключевому сообщению
|
1713 |
-
|
1714 |
print()
|
1715 |
print("Проверка 29: Проверка на отсутствие противоречий с описанием предложения")
|
1716 |
print()
|
1717 |
message_clean = cut_message(message)
|
1718 |
-
prompt = f'''Проверь, не противоречит ли следующее сообщение описанию
|
1719 |
Описание предложения:
|
1720 |
"{description}"
|
1721 |
-
Преимушества:
|
1722 |
-
"{benefits}"
|
1723 |
Ключевое сообщение:
|
1724 |
"{key_message}"
|
1725 |
Сообщение:
|
1726 |
"{message}"
|
1727 |
-
Если сообщение не содержит фактов, которые отсутствуют в описании
|
1728 |
-
Если сообщение содержит факты, которые
|
1729 |
**Не добавляй никакого дополнительного текста. Отвечай только в формате JSON с закрывающими кавычками и скобками.**'''
|
1730 |
|
1731 |
-
response =
|
1732 |
-
|
1733 |
print("Mistral response:", response)
|
1734 |
result = parse_json_response(response)
|
1735 |
if result is not None:
|
@@ -1741,7 +1546,7 @@ async def check_no_contradictions(message, description, benefits, key_message):
|
|
1741 |
return None
|
1742 |
|
1743 |
# 30. Проверка на наличие ключевого сообщения
|
1744 |
-
|
1745 |
print()
|
1746 |
print("Проверка 30: Проверка на наличие ключевого сообщения")
|
1747 |
print()
|
@@ -1755,8 +1560,8 @@ async def check_contains_key_message(message, key_message):
|
|
1755 |
Если сообщение **не содержит всю** информацию из ключевого текста, **верни только** JSON {{"decision": true, "explanation": "Ключевое текст отсутствует."}}.
|
1756 |
**Не добавляй никакого дополнительного текста. Отвечай только в формате JSON с закрывающими кавычками и скобками.**'''
|
1757 |
|
1758 |
-
response =
|
1759 |
-
|
1760 |
print("Mistral response:", response)
|
1761 |
result = parse_json_response(response)
|
1762 |
if result is not None:
|
@@ -1768,7 +1573,7 @@ async def check_contains_key_message(message, key_message):
|
|
1768 |
return None
|
1769 |
|
1770 |
# 31. Проверка на точное совпадение названий продуктов
|
1771 |
-
|
1772 |
print()
|
1773 |
print("Проверка 31: Проверка на точное совпадение названий продуктов")
|
1774 |
print()
|
@@ -1785,8 +1590,8 @@ async def check_product_name_consistency(message, product_name):
|
|
1785 |
Если название продукта **не** совпадает, **верни только** JSON {{"decision": true, "explanation": "<описание несоответствия>"}}.
|
1786 |
**Не добавляй никакого дополнительного текста. Ответ должен быть только в формате JSON с закрывающими кавычками и скобками.**'''
|
1787 |
|
1788 |
-
response =
|
1789 |
-
|
1790 |
print("Mistral response:", response)
|
1791 |
result = parse_json_response(response)
|
1792 |
if result is not None:
|
@@ -1800,63 +1605,61 @@ async def check_product_name_consistency(message, product_name):
|
|
1800 |
# ФУНКЦИИ ПРОВЕРОК (КОНЕЦ)
|
1801 |
|
1802 |
|
1803 |
-
|
1804 |
try:
|
1805 |
-
|
1806 |
-
return await func(*args)
|
1807 |
-
else:
|
1808 |
-
return func(*args)
|
1809 |
except Exception as e:
|
1810 |
-
|
1811 |
-
|
|
|
1812 |
|
1813 |
-
|
1814 |
checks = {}
|
1815 |
|
1816 |
# 2. Morphological checks using pymorphy3
|
1817 |
morphological_checks = [
|
1818 |
-
# ("no_word_repetitions", check_no_word_repetitions),
|
1819 |
("forbidden_words", check_forbidden_words),
|
1820 |
-
# ("double_verbs", check_no_double_verbs),
|
1821 |
-
("adverbial_participles", check_no_adverbial_participles),
|
1822 |
-
("derived_prepositions", check_no_derived_prepositions),
|
1823 |
-
("multiple_nouns", check_no_multiple_nouns),
|
1824 |
-
("introductory_phrases", check_no_introductory_phrases),
|
1825 |
-
("compound_sentences", check_no_compound_sentences),
|
1826 |
-
("superlative_adjectives", check_no_superlative_adjectives),
|
1827 |
("client_addressing", check_no_greeting),
|
1828 |
("promises", check_no_promises),
|
1829 |
-
("
|
1830 |
("participles", check_no_participles),
|
|
|
|
|
1831 |
("passive_voice", check_no_passive_voice),
|
1832 |
("written_out_ordinals", check_no_written_out_ordinals),
|
1833 |
("subordinate_clauses_chain", check_no_subordinate_clauses_chain),
|
1834 |
("repeating_conjunctions", check_no_repeating_conjunctions),
|
|
|
|
|
1835 |
("time_parasites", check_no_time_parasites),
|
|
|
|
|
|
|
1836 |
("dates_written_out", check_no_dates_written_out),
|
|
|
1837 |
]
|
1838 |
|
1839 |
# 3. LLM checks: check_clickbait_phrases, check_abstract_claims, check_cliches_and_bureaucratese
|
1840 |
llm_checks_group1 = [
|
|
|
1841 |
("no_contradictions", check_no_contradictions),
|
|
|
1842 |
("abstract_claims", check_abstract_claims),
|
1843 |
("cliches_and_bureaucratese", check_cliches_and_bureaucratese),
|
1844 |
-
("
|
1845 |
-
("contains_key_message", check_contains_key_message),
|
1846 |
-
("synonymous_members", check_synonymous_members),
|
1847 |
-
("product_name_consistency", check_product_name_consistency)
|
1848 |
]
|
1849 |
|
1850 |
# 4. Remaining LLM checks
|
1851 |
llm_checks_group2 = [
|
1852 |
-
("offensive_phrases", check_offensive_phrases),
|
1853 |
("disconnected_sentences", check_disconnected_sentences),
|
1854 |
-
("
|
|
|
|
|
1855 |
]
|
1856 |
|
1857 |
# Perform morphological checks
|
1858 |
for check_name, check_func in morphological_checks:
|
1859 |
-
result =
|
1860 |
checks[check_name] = result
|
1861 |
if result is False:
|
1862 |
return checks # Stop on first failure
|
@@ -1864,19 +1667,19 @@ async def perform_checks(message, description, key_message, product_name, benefi
|
|
1864 |
# Perform LLM checks group 1
|
1865 |
for check_name, check_func in llm_checks_group1:
|
1866 |
if check_name == "no_contradictions":
|
1867 |
-
result =
|
1868 |
elif check_name == "contains_key_message":
|
1869 |
-
result =
|
1870 |
elif check_name == "product_name_consistency":
|
1871 |
-
result =
|
1872 |
elif check_name == "clickbait_phrases":
|
1873 |
-
result =
|
1874 |
elif check_name == "abstract_claims":
|
1875 |
-
result =
|
1876 |
elif check_name == "cliches_and_bureaucratese":
|
1877 |
-
result =
|
1878 |
else:
|
1879 |
-
result =
|
1880 |
checks[check_name] = result
|
1881 |
if result is False:
|
1882 |
return checks
|
@@ -1884,7 +1687,7 @@ async def perform_checks(message, description, key_message, product_name, benefi
|
|
1884 |
|
1885 |
# Perform remaining LLM checks
|
1886 |
for check_name, check_func in llm_checks_group2:
|
1887 |
-
result =
|
1888 |
checks[check_name] = result
|
1889 |
if result is False:
|
1890 |
return checks # Stop on first failure
|
@@ -1897,7 +1700,7 @@ def format_checks(checks):
|
|
1897 |
"forbidden_words": "Запрещенные слова",
|
1898 |
"client_addressing": "Обращение к клиенту",
|
1899 |
"promises": "Обещания и гарантии",
|
1900 |
-
|
1901 |
"participles": "Причастия",
|
1902 |
"adverbial_participles": "Деепричастия",
|
1903 |
"superlative_adjectives": "Превосходная степень",
|
@@ -1912,7 +1715,7 @@ def format_checks(checks):
|
|
1912 |
"derived_prepositions": "Производные предлоги",
|
1913 |
"compound_sentences": "Сложноподчиненные предложения",
|
1914 |
"dates_written_out": "Даты прописью",
|
1915 |
-
|
1916 |
# Проверки на LLM
|
1917 |
"disconnected_sentences": "Сложные предложения без логической связи",
|
1918 |
"synonymous_members": "Близкие по смыслу однородные члены предложения",
|
@@ -1979,7 +1782,7 @@ with gr.Blocks() as demo:
|
|
1979 |
key_message = gr.Textbox(
|
1980 |
label="Ключевое сообщение (предзаполненный пример можно поменять на свой)",
|
1981 |
lines=1,
|
1982 |
-
value="Повышенный льготный период при покупках у партнёров банка — до 365 дней и бесплатное годовое
|
1983 |
)
|
1984 |
|
1985 |
with gr.Column():
|
|
|
18 |
import io
|
19 |
from transformers import AutoTokenizer, AutoModel
|
20 |
from utils import best_text_choice
|
|
|
|
|
|
|
21 |
|
22 |
tokenizer = AutoTokenizer.from_pretrained("ai-forever/ru-en-RoSBERTa")
|
23 |
model = AutoModel.from_pretrained("ai-forever/ru-en-RoSBERTa")
|
|
|
25 |
unique_sms_df = pd.read_parquet('unique_texts.parquet')
|
26 |
|
27 |
MISTRAL_API_KEY = os.getenv('MISTRAL_API_KEY')
|
|
|
|
|
|
|
|
|
28 |
token = os.getenv('GITHUB_TOKEN')
|
29 |
|
|
|
|
|
30 |
# Клиент для генерации сообщений
|
31 |
client_mistral_generate = Mistral(api_key=MISTRAL_API_KEY)
|
32 |
|
33 |
# Клиент для выполнения проверок
|
34 |
+
client_mistral_check = Mistral(api_key=MISTRAL_API_KEY)
|
|
|
|
|
|
|
35 |
|
36 |
morph = pymorphy3.MorphAnalyzer()
|
37 |
|
|
|
188 |
# except Exception as e:
|
189 |
# return f"Ошибка при обращении к GigaChat-Pro: {e}"
|
190 |
|
191 |
+
def generate_message_mistral_generate(prompt, max_retries=5):
|
|
|
192 |
#def generate_message_mistral_generate(prompt):
|
193 |
# try:
|
194 |
# messages = [SystemMessage(content=prompt)]
|
|
|
201 |
retries = 0
|
202 |
while retries < max_retries:
|
203 |
try:
|
204 |
+
chat_response = client_mistral_generate.chat.complete(
|
205 |
+
model="mistral-large-latest",
|
206 |
+
temperature=1.0,
|
207 |
+
min_tokens=81,
|
208 |
+
max_tokens=108,
|
209 |
+
messages=[
|
210 |
+
{
|
211 |
+
"role": "user",
|
212 |
+
"content": prompt
|
213 |
+
},
|
214 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
215 |
)
|
216 |
+
cleaned_message = clean_message(chat_response.choices[0].message.content.strip())
|
|
|
|
|
217 |
return cleaned_message
|
|
|
218 |
except Exception as e:
|
219 |
error_message = str(e)
|
|
|
220 |
if "Status 429" in error_message or "Server disconnected without sending a response" in error_message:
|
221 |
+
wait_time = 3
|
222 |
+
print(f"Превышен лимит запросов или сервер не ответил. Ожидание {wait_time} секунд перед повторной попыткой...")
|
223 |
+
time.sleep(wait_time)
|
224 |
retries += 1
|
225 |
else:
|
226 |
+
print(f"Ошибка при обращении к Mistral: {e}")
|
|
|
227 |
return None
|
228 |
+
print("Не удалось получить ответ от Mistral после максимального числа попыток.")
|
|
|
|
|
229 |
return None
|
230 |
|
231 |
+
def generate_message_mistral_check(prompt, max_retries=5):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
232 |
#def generate_message_mistral_check(prompt):
|
233 |
# try:
|
234 |
# messages = [SystemMessage(content=prompt)]
|
|
|
237 |
# return cleaned_message
|
238 |
# except Exception as e:
|
239 |
# return f"Ошибка при обращении к GigaChat-Pro: {e}"
|
|
|
240 |
retries = 0
|
241 |
while retries < max_retries:
|
242 |
try:
|
243 |
+
chat_response = client_mistral_check.chat.complete(
|
244 |
+
model="mistral-large-latest",
|
245 |
+
temperature=0.2,
|
246 |
+
messages=[
|
247 |
+
{
|
248 |
+
"role": "user",
|
249 |
+
"content": prompt
|
250 |
+
},
|
251 |
+
]
|
252 |
)
|
253 |
+
cleaned_message = clean_message(chat_response.choices[0].message.content.strip())
|
|
|
|
|
254 |
return cleaned_message
|
|
|
255 |
except Exception as e:
|
256 |
+
if "Status 429" in str(e) or "Server disconnected without sending a response" in str(e):
|
257 |
+
wait_time = 3 # Можно установить фиксированную задержку
|
258 |
+
print(f"Превышен лимит запросов. Ожидание {wait_time} секунд перед повторной попыткой...")
|
259 |
+
time.sleep(wait_time)
|
260 |
+
retries += 1
|
261 |
+
else:
|
262 |
+
print(f"Ошибка при обращении к Mistral: {e}")
|
263 |
+
return None
|
264 |
+
|
265 |
+
#def generate_check_gigachat_pro(prompt):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
266 |
# try:
|
267 |
# messages = [SystemMessage(content=prompt)]
|
268 |
# res2 = chat_pro_check(messages)
|
|
|
270 |
# return cleaned_message
|
271 |
# except Exception as e:
|
272 |
# return f"Ошибка при обращении к GigaChat-Pro: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
273 |
|
274 |
# Функция для замены сокращений с 'k' или 'К' на тысячи
|
275 |
def replace_k_with_thousands(message):
|
|
|
438 |
"derived_prepositions": "Не использовать производные предлоги.",
|
439 |
"compound_sentences": "Избегать сложноподчиненных предложений.",
|
440 |
"dates_written_out": "Не писать даты прописью.",
|
441 |
+
"no_word_repetitions": "Избегать повторов слов.",
|
442 |
"disconnected_sentences": "Избегать сложных предложений без логической связи.",
|
443 |
"synonymous_members": "Не использовать близкие по смыслу однородные члены предложения.",
|
444 |
"clickbait_phrases": "Не использовать кликбейтные фразы.",
|
|
|
463 |
|
464 |
|
465 |
def notify_failed_length(message_length):
|
466 |
+
if message_length < 190:
|
467 |
+
gr.Warning(f"Сообщение слишком короткое: {message_length} знаков. Минимум 190.")
|
468 |
return False
|
469 |
elif message_length > 250:
|
470 |
gr.Warning(f"Сообщение слишком длинное: {message_length} знаков. Максимум 250.")
|
|
|
477 |
"forbidden_words": "Запрещенные слова",
|
478 |
"client_addressing": "Обращение к клиенту",
|
479 |
"promises": "Обещания и гарантии",
|
480 |
+
"double_verbs": "Два глагола подряд",
|
481 |
"participles": "Причастия",
|
482 |
"adverbial_participles": "Деепричастия",
|
483 |
"superlative_adjectives": "Превосходная степень",
|
|
|
492 |
"derived_prepositions": "Производные предлоги",
|
493 |
"compound_sentences": "Сложноподчиненные предложения",
|
494 |
"dates_written_out": "Даты прописью",
|
495 |
+
"no_word_repetitions": "Повторы слов",
|
496 |
"disconnected_sentences": "Сложные предложения без логической связи",
|
497 |
"synonymous_members": "Близкие по смыслу однородные члены предложения",
|
498 |
"clickbait_phrases": "Кликбейтные фразы",
|
|
|
545 |
# return last_message
|
546 |
|
547 |
|
548 |
+
def generate_message_mistral_with_retry(prompt, approach_name, description, key_message, product_name, benefits):
|
549 |
global approach_stats
|
550 |
last_message = None
|
551 |
for attempt in range(20):
|
552 |
+
gr.Info(f"Итерация {attempt + 1}: генерируется сообщение...")
|
553 |
+
message = generate_message_mistral_generate(prompt)
|
554 |
if message is None:
|
555 |
+
print("Не удалось получить сообщение от Mistral, повторная попытка...")
|
556 |
+
time.sleep(1)
|
557 |
continue
|
558 |
message = replace_k_with_thousands(message)
|
559 |
message = correct_dash_usage(message)
|
560 |
message_length = len(message)
|
561 |
if not notify_failed_length(message_length):
|
562 |
last_message = message
|
563 |
+
time.sleep(1)
|
564 |
continue
|
565 |
+
checks = perform_checks(message, description, key_message, product_name, benefits)
|
566 |
last_message = message
|
567 |
# Инициализируем статистику для подхода, если ее нет
|
568 |
if approach_name not in approach_stats:
|
|
|
576 |
if all(checks.values()):
|
577 |
return message
|
578 |
prompt = append_errors_to_prompt(prompt, checks)
|
579 |
+
time.sleep(1)
|
580 |
gr.Info("Не удалось сгенерировать сообщение, соответствующее требованиям, за 20 итераций. Возвращаем последнее сгенерированное сообщение.")
|
581 |
return last_message
|
582 |
|
|
|
589 |
f"Преимущества: {benefits}\n"
|
590 |
"В тексте смс запрещено использование:\n"
|
591 |
"- Запрещенные слова: № один, номер один, № 1, вкусный, дешёвый, продукт, спам, доступный, банкротство, долги, займ, срочно, сейчас, лучший, главный, номер 1, гарантия, успех, лидер, никакой;\n"
|
592 |
+
"- Повторы слов;\n"
|
593 |
"- Обращение к клиенту;\n"
|
594 |
"- Приветствие клиента;\n"
|
595 |
"- Обещания и гарантии;\n"
|
|
|
616 |
"- Узкоспециализированные термины;\n"
|
617 |
"- Фразы, способные создать двойственное ощущение, обидеть;\n"
|
618 |
"- Речевые клише, рекламные штампы, канцеляризмы;\n"
|
619 |
+
"Убедись, что в готовом тексте до 250, но не менее 190 знаков с пробелами. Убедись, что в готовом тексте не менее трех предложений.\n"
|
620 |
)
|
621 |
if key_message.strip():
|
622 |
prompt += f"Убедись, что в готовом тексте есть следующая ключевая информация: {key_message.strip()}"
|
|
|
628 |
def generate_personalization_prompt(key_message, *selected_values, prefix, suffix, product_name):
|
629 |
prompt = f"{prefix}\n"
|
630 |
prompt += f"Не изменяй название продукта: {product_name}.\n"
|
631 |
+
prompt += "Адаптируй, не превышая длину сообщения в 250 знаков с пробелами (но и не менее 190 знаков с пробелами), текст с учетом следующих особенностей:\n"
|
632 |
gender, generation, psychotype = selected_values[0], selected_values[1], selected_values[2]
|
633 |
combined_instruction = ""
|
634 |
additional_instructions = ""
|
|
|
692 |
return cleaned_prompt.strip()
|
693 |
|
694 |
# Функция для постепенной генерации всех сообщений через yield
|
695 |
+
def generate_all_messages(desc, benefits, key_message, gender, generation, psychotype, business_stage, industry, opf, product_name):
|
696 |
standard_prompt = generate_standard_prompt(desc, benefits, key_message)
|
697 |
standard_prompt_for_display = f"Не изменяй название продукта: {product_name}.\n{standard_prompt}\nУбедись, что в готовом тексте без изменений, синонимов и перестановок слов используется наименование продукта: {product_name}.\n"
|
698 |
approach_mapping = {
|
|
|
764 |
yield selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt, None, None
|
765 |
flag += 1
|
766 |
prompt = add_prefix_suffix(standard_prompt, current_prefix, current_suffix, product_name)
|
767 |
+
non_personalized_message = generate_message_mistral_with_retry(prompt, approach_name, desc, key_message, product_name, benefits)
|
768 |
non_personalized_length = len(non_personalized_message)
|
769 |
non_personalized_display = f"{non_personalized_message}\n------\nКоличество знаков: {non_personalized_length}"
|
770 |
if non_personalized_messages:
|
|
|
776 |
non_personalized_messages, personalized_messages
|
777 |
)
|
778 |
full_personalized_prompt = f"{personalization_prompt}\n\nТекст для адаптации: {non_personalized_message}"
|
779 |
+
personalized_message = generate_message_mistral_with_retry(full_personalized_prompt, approach_name, desc, key_message, product_name, benefits)
|
780 |
personalized_length = len(personalized_message)
|
781 |
personalized_display = f"{personalized_message}\n------\nКоличество знаков: {personalized_length}"
|
782 |
if personalized_messages:
|
|
|
787 |
selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt,
|
788 |
non_personalized_messages, personalized_messages
|
789 |
)
|
790 |
+
time.sleep(1)
|
791 |
save_statistics_to_github(approach_stats)
|
792 |
|
793 |
def rank_messages(non_personalized_messages, personalized_messages):
|
|
|
1181 |
'и', 'а', 'но', 'или', 'да', 'ни', 'как', 'так',
|
1182 |
'в', 'на', 'под', 'над', 'за', 'к', 'до', 'по', 'из', 'у', 'о', 'про', 'для',
|
1183 |
'не', 'вот', 'это', 'тот', 'тем', 'при', 'чем',
|
1184 |
+
'же', 'ли', 'бы', 'то', 'р',
|
1185 |
])
|
1186 |
|
1187 |
# Разбиваем текст на слова, удаляя знаки препинания
|
|
|
1261 |
return message
|
1262 |
|
1263 |
# 22. Проверка сложных предложений без логической связи
|
1264 |
+
def check_disconnected_sentences(message):
|
1265 |
message_clean = cut_message(message)
|
1266 |
print()
|
1267 |
print("Проверка 22: Проверка сложных предложений без логической связи")
|
|
|
1276 |
если таких предложений **нет**, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
|
1277 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь **только** в формате JSON с закрывающими кавычками и скобками.**'''
|
1278 |
|
1279 |
+
response = generate_message_mistral_check(prompt)
|
1280 |
+
time.sleep(3) # Задержка в 3 секунды между запросами
|
1281 |
print("Mistral response:", response) # Выводим полный ответ модели
|
1282 |
result = parse_json_response(response)
|
1283 |
if result is not None:
|
|
|
1289 |
return None
|
1290 |
|
1291 |
# 23. Проверка на близкие по смыслу однородные члены
|
1292 |
+
def check_synonymous_members(message):
|
1293 |
print()
|
1294 |
print("Проверка 23: Проверка на близкие по смыслу однородные члены")
|
1295 |
print()
|
|
|
1303 |
если таких слов или выражений нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
|
1304 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1305 |
|
1306 |
+
response = generate_message_mistral_check(prompt)
|
1307 |
+
time.sleep(3)
|
1308 |
print("Mistral response:", response)
|
1309 |
result = parse_json_response(response)
|
1310 |
if result is not None:
|
|
|
1317 |
|
1318 |
|
1319 |
# 24. Проверка на шокирующие, экстравагантные или кликбейтные фразы
|
1320 |
+
def check_clickbait_phrases(message, description, benefits):
|
1321 |
message_clean = cut_message(message)
|
1322 |
print()
|
1323 |
print()
|
|
|
1333 |
3. Стандартные рекламные призывы к действию, такие как "купите сейчас" или "узнайте больше", не считаются кликбейтом, если они не преувеличивают преимущества или не используют явную манипуляцию эмоциями.
|
1334 |
4. Не считай фразы, используемые в исходном описании продукта, кликбейтными. Исходное описание: "{description}".
|
1335 |
5. Не считай фразы, используемые в преимуществах продукта, кликбейтными. Преимущества: "{benefits}".
|
|
|
1336 |
Пример ответа:
|
1337 |
{{"decision": false, "explanation": "Текст нейтрален и не содержит кликбейтных фраз."}}
|
1338 |
|
|
|
1341 |
|
1342 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1343 |
|
1344 |
+
response = generate_message_mistral_check(prompt)
|
1345 |
+
time.sleep(3)
|
1346 |
print("Mistral response:", response)
|
1347 |
result = parse_json_response(response)
|
1348 |
if result is not None:
|
|
|
1355 |
|
1356 |
|
1357 |
# 25. Проверка на абстрактные заявления без поддержки фактами
|
1358 |
+
def check_abstract_claims(message, description, benefits):
|
1359 |
print()
|
1360 |
print("Проверка 25: Проверка на абстрактные заявления без поддержки фактами")
|
1361 |
print()
|
|
|
1382 |
- "Снизьте финансовую нагрузку"
|
1383 |
4. Ищи общие фразы, которые не дают представления о конкретной пользе, такие как "лучшее решение", "высокое качество", "отличный сервис", если они не сопровождаются пояснением о том, почему это так.
|
1384 |
5. Учитывай, что в рекламных сообщениях допустимы эмоциональные и обобщённые фразы, если они достаточно конкретны для понимания аудитории, однако они должны сопровождаться фактами или подробными примерами.
|
1385 |
+
6. Не считай фразы, используемые в исходном описании продукта, кликбейтными. Исходное описание: "{description}".
|
1386 |
+
7. Не считай фразы, используемые в преимуществах продукта, кликбейтными. Преимущества: "{benefits}".
|
|
|
1387 |
|
1388 |
Пример ответа:
|
1389 |
{{"decision": false, "explanation": "Текст не содержит абстрактные утверждения без конкретики."}}
|
|
|
1393 |
|
1394 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1395 |
|
1396 |
+
response = generate_message_mistral_check(prompt)
|
1397 |
+
time.sleep(3)
|
1398 |
print("Mistral response:", response)
|
1399 |
result = parse_json_response(response)
|
1400 |
if result is not None:
|
|
|
1407 |
|
1408 |
|
1409 |
# 26. Проверка на узкоспециализированные термины
|
1410 |
+
def check_specialized_terms(message):
|
1411 |
print()
|
1412 |
print("Проверка 26: Проверка на узкоспециализированные термины")
|
1413 |
print()
|
|
|
1430 |
|
1431 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1432 |
|
1433 |
+
response = generate_message_mistral_check(prompt)
|
1434 |
+
time.sleep(3)
|
1435 |
print("Mistral response:", response)
|
1436 |
result = parse_json_response(response)
|
1437 |
if result is not None:
|
|
|
1443 |
return None
|
1444 |
|
1445 |
# 27. Проверка на двусмысленные или обидные фразы
|
1446 |
+
def check_offensive_phrases(message):
|
1447 |
print()
|
1448 |
print("Проверка 27: Проверка на двусмысленные или обидные фразы")
|
1449 |
print()
|
|
|
1459 |
если таких фраз нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}.
|
1460 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1461 |
|
1462 |
+
response = generate_message_mistral_check(prompt)
|
1463 |
+
time.sleep(3)
|
1464 |
print("Mistral response:", response)
|
1465 |
result = parse_json_response(response)
|
1466 |
if result is not None:
|
|
|
1472 |
return None
|
1473 |
|
1474 |
# 28. Проверка на речевые клише, рекламные штампы и канцеляризмы
|
1475 |
+
def check_cliches_and_bureaucratese(message, description, benefits):
|
1476 |
print()
|
1477 |
print("Проверка 28: Проверка на речевые клише, рекламные штампы и канцеляризмы")
|
1478 |
print()
|
|
|
1485 |
- Информацию о ценах, скидках, акциях или условиях покупки (например, "при покупках от 100 000 рублей в месяц").
|
1486 |
- Описания способов оформления или получения услуг (например, "оформление возможно онлайн или в офисе").
|
1487 |
- Стандартные отраслевые термины и фразы, необходимые для понимания сообщения (например, "премиальная бизнес-карта", "Mastercard Preferred"), но **не** их использование в комбинации с общими словами, как например, "идеальное решение для вашего бизнеса".
|
1488 |
+
- Фразы, используемые в исходном описании продукта, кликбейтными. Исходное описание: "{description}".
|
1489 |
+
- Фразы, используемые в преимуществах продукта, кликбейтными. Преимущества: "{benefits}".
|
|
|
1490 |
**Считай клише или канцеляризмами следующие типы выражений:**
|
1491 |
- Избитые фразы, такие как:
|
1492 |
- "Обеспечьте стабильность и развитие вашего бизнеса"
|
|
|
1504 |
если в тексте **есть** такие выражения, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}.
|
1505 |
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**'''
|
1506 |
|
1507 |
+
response = generate_message_mistral_check(prompt)
|
1508 |
+
time.sleep(3)
|
1509 |
print("Mistral response:", response)
|
1510 |
result = parse_json_response(response)
|
1511 |
if result is not None:
|
|
|
1517 |
return None
|
1518 |
|
1519 |
# 29. Проверка на соответствие описанию предложения и ключевому сообщению
|
1520 |
+
def check_no_contradictions(message, description, key_message):
|
1521 |
print()
|
1522 |
print("Проверка 29: Проверка на отсутствие противоречий с описанием предложения")
|
1523 |
print()
|
1524 |
message_clean = cut_message(message)
|
1525 |
+
prompt = f'''Проверь, не противоречит ли следующее сообщение описанию предложения и ключевому сообщению. Учти, что сообщение является выжимкой из описания предложения и не может содержать столько же информации в том же объеме, сколько описание предложения - важно, чтобы в сообщении не было указано ложных фактов.
|
1526 |
Описание предложения:
|
1527 |
"{description}"
|
|
|
|
|
1528 |
Ключевое сообщение:
|
1529 |
"{key_message}"
|
1530 |
Сообщение:
|
1531 |
"{message}"
|
1532 |
+
Если сообщение не содержит фактов, которые отсутствуют в описании предложения и ключевом сообщении, **верни только** JSON {{"decision": false, "explanation": "Противоречий не обнаружено."}}.
|
1533 |
+
Если сообщение содержит факты, которые отсутствую�� в описании предложения и ключевом сообщении, **верни только** JSON {{"decision": true, "explanation": "<описание противоречий>"}}.
|
1534 |
**Не добавляй никакого дополнительного текста. Отвечай только в формате JSON с закрывающими кавычками и скобками.**'''
|
1535 |
|
1536 |
+
response = generate_message_mistral_check(prompt)
|
1537 |
+
time.sleep(3)
|
1538 |
print("Mistral response:", response)
|
1539 |
result = parse_json_response(response)
|
1540 |
if result is not None:
|
|
|
1546 |
return None
|
1547 |
|
1548 |
# 30. Проверка на наличие ключевого сообщения
|
1549 |
+
def check_contains_key_message(message, key_message):
|
1550 |
print()
|
1551 |
print("Проверка 30: Проверка на наличие ключевого сообщения")
|
1552 |
print()
|
|
|
1560 |
Если сообщение **не содержит всю** информацию из ключевого текста, **верни только** JSON {{"decision": true, "explanation": "Ключевое текст отсутствует."}}.
|
1561 |
**Не добавляй никакого дополнительного текста. Отвечай только в формате JSON с закрывающими кавычками и скобками.**'''
|
1562 |
|
1563 |
+
response = generate_message_mistral_check(prompt)
|
1564 |
+
time.sleep(3)
|
1565 |
print("Mistral response:", response)
|
1566 |
result = parse_json_response(response)
|
1567 |
if result is not None:
|
|
|
1573 |
return None
|
1574 |
|
1575 |
# 31. Проверка на точное совпадение названий продуктов
|
1576 |
+
def check_product_name_consistency(message, product_name):
|
1577 |
print()
|
1578 |
print("Проверка 31: Проверка на точное совпадение названий продуктов")
|
1579 |
print()
|
|
|
1590 |
Если название продукта **не** совпадает, **верни только** JSON {{"decision": true, "explanation": "<описание несоответствия>"}}.
|
1591 |
**Не добавляй никакого дополнительного текста. Ответ должен быть только в формате JSON с закрывающими кавычками и скобками.**'''
|
1592 |
|
1593 |
+
response = generate_message_mistral_check(prompt)
|
1594 |
+
time.sleep(3)
|
1595 |
print("Mistral response:", response)
|
1596 |
result = parse_json_response(response)
|
1597 |
if result is not None:
|
|
|
1605 |
# ФУНКЦИИ ПРОВЕРОК (КОНЕЦ)
|
1606 |
|
1607 |
|
1608 |
+
def safe_check(func, *args):
|
1609 |
try:
|
1610 |
+
return func(*args)
|
|
|
|
|
|
|
1611 |
except Exception as e:
|
1612 |
+
# Optionally, you can log the exception here if needed
|
1613 |
+
print(f"Ошибка в {func.__name__}: {e}")
|
1614 |
+
return None # Indicate that the check could not be performed
|
1615 |
|
1616 |
+
def perform_checks(message, description, key_message, product_name, benefits):
|
1617 |
checks = {}
|
1618 |
|
1619 |
# 2. Morphological checks using pymorphy3
|
1620 |
morphological_checks = [
|
|
|
1621 |
("forbidden_words", check_forbidden_words),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1622 |
("client_addressing", check_no_greeting),
|
1623 |
("promises", check_no_promises),
|
1624 |
+
("double_verbs", check_no_double_verbs),
|
1625 |
("participles", check_no_participles),
|
1626 |
+
("adverbial_participles", check_no_adverbial_participles),
|
1627 |
+
("superlative_adjectives", check_no_superlative_adjectives),
|
1628 |
("passive_voice", check_no_passive_voice),
|
1629 |
("written_out_ordinals", check_no_written_out_ordinals),
|
1630 |
("subordinate_clauses_chain", check_no_subordinate_clauses_chain),
|
1631 |
("repeating_conjunctions", check_no_repeating_conjunctions),
|
1632 |
+
("introductory_phrases", check_no_introductory_phrases),
|
1633 |
+
("amplifiers", check_no_amplifiers),
|
1634 |
("time_parasites", check_no_time_parasites),
|
1635 |
+
("multiple_nouns", check_no_multiple_nouns),
|
1636 |
+
("derived_prepositions", check_no_derived_prepositions),
|
1637 |
+
("compound_sentences", check_no_compound_sentences),
|
1638 |
("dates_written_out", check_no_dates_written_out),
|
1639 |
+
("no_word_repetitions", check_no_word_repetitions),
|
1640 |
]
|
1641 |
|
1642 |
# 3. LLM checks: check_clickbait_phrases, check_abstract_claims, check_cliches_and_bureaucratese
|
1643 |
llm_checks_group1 = [
|
1644 |
+
("product_name_consistency", check_product_name_consistency),
|
1645 |
("no_contradictions", check_no_contradictions),
|
1646 |
+
("clickbait_phrases", check_clickbait_phrases),
|
1647 |
("abstract_claims", check_abstract_claims),
|
1648 |
("cliches_and_bureaucratese", check_cliches_and_bureaucratese),
|
1649 |
+
("contains_key_message", check_contains_key_message)
|
|
|
|
|
|
|
1650 |
]
|
1651 |
|
1652 |
# 4. Remaining LLM checks
|
1653 |
llm_checks_group2 = [
|
|
|
1654 |
("disconnected_sentences", check_disconnected_sentences),
|
1655 |
+
("synonymous_members", check_synonymous_members),
|
1656 |
+
("specialized_terms", check_specialized_terms),
|
1657 |
+
("offensive_phrases", check_offensive_phrases),
|
1658 |
]
|
1659 |
|
1660 |
# Perform morphological checks
|
1661 |
for check_name, check_func in morphological_checks:
|
1662 |
+
result = safe_check(check_func, message)
|
1663 |
checks[check_name] = result
|
1664 |
if result is False:
|
1665 |
return checks # Stop on first failure
|
|
|
1667 |
# Perform LLM checks group 1
|
1668 |
for check_name, check_func in llm_checks_group1:
|
1669 |
if check_name == "no_contradictions":
|
1670 |
+
result = safe_check(check_func, message, description, key_message)
|
1671 |
elif check_name == "contains_key_message":
|
1672 |
+
result = safe_check(check_func, message, key_message)
|
1673 |
elif check_name == "product_name_consistency":
|
1674 |
+
result = safe_check(check_func, message, product_name)
|
1675 |
elif check_name == "clickbait_phrases":
|
1676 |
+
result = safe_check(check_func, message, description, benefits)
|
1677 |
elif check_name == "abstract_claims":
|
1678 |
+
result = safe_check(check_func, message, description, benefits)
|
1679 |
elif check_name == "cliches_and_bureaucratese":
|
1680 |
+
result = safe_check(check_func, message, description, benefits)
|
1681 |
else:
|
1682 |
+
result = safe_check(check_func, message)
|
1683 |
checks[check_name] = result
|
1684 |
if result is False:
|
1685 |
return checks
|
|
|
1687 |
|
1688 |
# Perform remaining LLM checks
|
1689 |
for check_name, check_func in llm_checks_group2:
|
1690 |
+
result = safe_check(check_func, message)
|
1691 |
checks[check_name] = result
|
1692 |
if result is False:
|
1693 |
return checks # Stop on first failure
|
|
|
1700 |
"forbidden_words": "Запрещенные слова",
|
1701 |
"client_addressing": "Обращение к клиенту",
|
1702 |
"promises": "Обещания и гарантии",
|
1703 |
+
"double_verbs": "Два глагола подряд",
|
1704 |
"participles": "Причастия",
|
1705 |
"adverbial_participles": "Деепричастия",
|
1706 |
"superlative_adjectives": "Превосходная степень",
|
|
|
1715 |
"derived_prepositions": "Производные предлоги",
|
1716 |
"compound_sentences": "Сложноподчиненные предложения",
|
1717 |
"dates_written_out": "Даты прописью",
|
1718 |
+
"no_word_repetitions": "Повторы слов",
|
1719 |
# Проверки на LLM
|
1720 |
"disconnected_sentences": "Сложные предложения без логической связи",
|
1721 |
"synonymous_members": "Близкие по смыслу однородные члены предложения",
|
|
|
1782 |
key_message = gr.Textbox(
|
1783 |
label="Ключевое сообщение (предзаполненный пример можно поменять на свой)",
|
1784 |
lines=1,
|
1785 |
+
value="Повышенный льготный период при покупках у партнёров банка — до 365 дней и бесплатное годовое обслуживание кредитной бизнес-карты."
|
1786 |
)
|
1787 |
|
1788 |
with gr.Column():
|