|
import re |
|
import gradio as gr |
|
import os |
|
import pandas as pd |
|
import time |
|
from langchain.schema import SystemMessage |
|
from langchain_community.chat_models.gigachat import GigaChat |
|
from openpyxl import load_workbook |
|
import plotly.graph_objects as go |
|
import random |
|
import pymorphy3 |
|
import string |
|
import json |
|
from mistralai import Mistral |
|
from collections import defaultdict |
|
import requests |
|
import base64 |
|
import io |
|
from transformers import AutoTokenizer, AutoModel |
|
from utils import best_text_choice |
|
|
|
tokenizer = AutoTokenizer.from_pretrained("ai-forever/ru-en-RoSBERTa") |
|
model = AutoModel.from_pretrained("ai-forever/ru-en-RoSBERTa") |
|
|
|
unique_sms_df = pd.read_parquet('unique_texts.parquet') |
|
|
|
MISTRAL_API_KEY = os.getenv('MISTRAL_API_KEY') |
|
token = os.getenv('GITHUB_TOKEN') |
|
|
|
|
|
client_mistral_generate = Mistral(api_key=MISTRAL_API_KEY) |
|
|
|
|
|
client_mistral_check = Mistral(api_key=MISTRAL_API_KEY) |
|
|
|
morph = pymorphy3.MorphAnalyzer() |
|
|
|
|
|
gc_key = os.getenv('GC_KEY') |
|
chat_pro = GigaChat(credentials=gc_key, model='GigaChat', max_tokens=68, temperature=1.15, verify_ssl_certs=False) |
|
chat_pro = GigaChat( |
|
credentials=gc_key, |
|
model='GigaChat-Pro-preview', |
|
base_url='https://gigachat-preview.devices.sberbank.ru/api/v1/', |
|
max_tokens=68, |
|
temperature=1.15, |
|
verify_ssl_certs=False |
|
) |
|
|
|
chat_pro_check = GigaChat( |
|
credentials=gc_key, |
|
model='GigaChat-Pro-preview', |
|
base_url='https://gigachat-preview.devices.sberbank.ru/api/v1/', |
|
max_tokens=3000, |
|
temperature=0.8, |
|
verify_ssl_certs=False |
|
) |
|
|
|
approach_stats = { |
|
"Начни сообщение с призыва к действию с продуктом. Не начинай с вопроса.": {"failed_checks": defaultdict(int), "total_attempts": 0}, |
|
"Начни сообщение с указания на пользу продукта. Используй глагол в побудительном наклонении. Не начинай с вопроса.": {"failed_checks": defaultdict(int), "total_attempts": 0}, |
|
"Начни сообщение с вопроса, который указывает на пользу продукта для клиента.": {"failed_checks": defaultdict(int), "total_attempts": 0} |
|
} |
|
|
|
|
|
try: |
|
data = pd.read_excel('Признаки.xlsx', sheet_name=None) |
|
except Exception as e: |
|
print(f"Ошибка при загрузке Excel-файла: {e}") |
|
data = {} |
|
|
|
|
|
features = {} |
|
for sheet_name, df in data.items(): |
|
try: |
|
if sheet_name == "Пол Поколение Психотип": |
|
features[sheet_name] = df.set_index(['Пол', 'Поколение', 'Психотип'])['Инструкция'].to_dict() |
|
elif sheet_name == "Подход": |
|
features[sheet_name] = df |
|
else: |
|
features[sheet_name] = df.set_index(df.columns[0]).to_dict()[df.columns[1]] |
|
except Exception as e: |
|
print(f"Ошибка при обработке данных листа {sheet_name}: {e}") |
|
features[sheet_name] = {} |
|
|
|
def save_statistics_to_github(approach_stats): |
|
repo = "fruitpicker01/Storage_dev" |
|
timestamp = int(time.time()) |
|
json_path = f"check_{timestamp}.json" |
|
csv_path = "checks.csv" |
|
url_json = f"https://api.github.com/repos/{repo}/contents/{json_path}" |
|
url_csv = f"https://api.github.com/repos/{repo}/contents/{csv_path}" |
|
headers = { |
|
"Authorization": f"token {token}", |
|
"Content-Type": "application/json" |
|
} |
|
|
|
|
|
json_data = { |
|
"timestamp": timestamp, |
|
"approach_stats": approach_stats |
|
} |
|
json_content = json.dumps(json_data, ensure_ascii=False, indent=4) |
|
json_content_encoded = base64.b64encode(json_content.encode('utf-8')).decode('utf-8') |
|
data_json = { |
|
"message": f"Добавлен новый файл {json_path}", |
|
"content": json_content_encoded |
|
} |
|
|
|
response = requests.put(url_json, headers=headers, data=json.dumps(data_json)) |
|
if response.status_code in [200, 201]: |
|
print("JSON-файл успешно сохранен на GitHub") |
|
else: |
|
print(f"Ошибка при сохранении JSON-файла: {response.status_code} {response.text}") |
|
|
|
|
|
import pandas as pd |
|
rows = [] |
|
for approach, stats in approach_stats.items(): |
|
for check_name, count in stats["failed_checks"].items(): |
|
rows.append({ |
|
"Timestamp": timestamp, |
|
"Approach": approach, |
|
"Check": check_name, |
|
"Failed_Count": count, |
|
"Total_Attempts": stats["total_attempts"] |
|
}) |
|
df = pd.DataFrame(rows) |
|
|
|
|
|
response = requests.get(url_csv, headers=headers) |
|
if response.status_code == 200: |
|
|
|
content = response.json() |
|
csv_content = base64.b64decode(content['content']).decode('utf-8') |
|
existing_df = pd.read_csv(io.StringIO(csv_content)) |
|
df = pd.concat([existing_df, df], ignore_index=True) |
|
sha = content['sha'] |
|
else: |
|
|
|
sha = None |
|
|
|
csv_content = df.to_csv(index=False) |
|
csv_content_encoded = base64.b64encode(csv_content.encode('utf-8')).decode('utf-8') |
|
data_csv = { |
|
"message": "Обновление файла checks.csv", |
|
"content": csv_content_encoded |
|
} |
|
if sha: |
|
data_csv["sha"] = sha |
|
|
|
response = requests.put(url_csv, headers=headers, data=json.dumps(data_csv)) |
|
if response.status_code in [200, 201]: |
|
print("CSV-файл успешно сохранен на GitHub") |
|
else: |
|
print(f"Ошибка при сохранении CSV-файла: {response.status_code} {response.text}") |
|
|
|
|
|
def change_tab(id): |
|
return gr.Tabs(selected=id) |
|
|
|
|
|
def add_prefix_suffix(prompt, prefix, suffix, product_name): |
|
new_prompt = f"{prefix}\n" |
|
|
|
new_prompt += f"Не изменяй название продукта: {product_name}.\n" |
|
new_prompt += f"{prompt}\n" |
|
|
|
new_prompt += f"Убедись, что в готовом тексте без изменений, синонимов и перестановок слов используется наименование продукта: {product_name}.\n" |
|
new_prompt += f"{suffix}" |
|
return new_prompt |
|
|
|
|
|
def clean_message(message): |
|
if not message.endswith(('.', '!', '?')): |
|
last_period = max(message.rfind('.'), message.rfind('!'), message.rfind('?')) |
|
if last_period != -1: |
|
message = message[:last_period + 1] |
|
return message |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_message_mistral_generate(prompt): |
|
try: |
|
messages = [SystemMessage(content=prompt)] |
|
res = chat_pro(messages) |
|
cleaned_message = clean_message(res.content.strip()) |
|
return cleaned_message |
|
except Exception as e: |
|
return f"Ошибка при обращении к GigaChat-Pro: {e}" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_message_mistral_check(prompt): |
|
try: |
|
messages = [SystemMessage(content=prompt)] |
|
res2 = chat_pro_check(messages) |
|
cleaned_message = clean_message(res2.content.strip()) |
|
return cleaned_message |
|
except Exception as e: |
|
return f"Ошибка при обращении к GigaChat-Pro: {e}" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def replace_k_with_thousands(message): |
|
if not isinstance(message, str): |
|
return message |
|
message = re.sub(r'(\d+)[kкКK]', r'\1 000', message, flags=re.IGNORECASE) |
|
return message |
|
|
|
def correct_dash_usage(text): |
|
morph = pymorphy3.MorphAnalyzer() |
|
|
|
text = re.sub(r'\s[-–—]\s', ' — ', text) |
|
|
|
|
|
text = re.sub(r'(?<=\d)[-–—](?=\d)', '–', text) |
|
|
|
|
|
text = re.sub(r'(?<=[a-zA-Zа-яА-Я0-9])[-–—](?=[a-zA-Zа-яА-Я0-9])', '-', text) |
|
|
|
|
|
text = re.sub(r'"([^\"]+)"', r'«\1»', text) |
|
|
|
|
|
if text.count('"') == 1: |
|
text = text.replace('"', '') |
|
|
|
|
|
if (text.startswith('"') and text.endswith('"')) or (text.startswith('«') and text.endswith('»')): |
|
text = text[1:-1].strip() |
|
|
|
|
|
text = re.sub(r'(\d+)[kкКK]', r'\1 000', text, flags=re.IGNORECASE) |
|
|
|
|
|
greeting_patterns = [ |
|
r"привет\b", r"здравствуй", r"добрый\s(день|вечер|утро)", |
|
r"дорогой\b", r"уважаемый\b", r"дорогая\b", r"уважаемая\b", |
|
r"господин\b", r"госпожа\b", r"друг\b", r"коллега\b", |
|
r"товарищ\b", r"приятель\b", r"подруга\b" |
|
] |
|
|
|
def is_greeting_sentence(sentence): |
|
words = sentence.split() |
|
if len(words) < 5: |
|
for word in words: |
|
parsed = morph.parse(word.lower())[0] |
|
for pattern in greeting_patterns: |
|
if re.search(pattern, parsed.normal_form): |
|
return True |
|
return False |
|
|
|
|
|
sentences = re.split(r'(?<=[.!?])\s+', text) |
|
|
|
|
|
if sentences and is_greeting_sentence(sentences[0]): |
|
sentences = sentences[1:] |
|
|
|
|
|
text = ' '.join(sentences) |
|
|
|
def restore_yo(text): |
|
morph = pymorphy3.MorphAnalyzer() |
|
words = text.split() |
|
restored_words = [] |
|
|
|
for word in words: |
|
|
|
if word.isupper(): |
|
restored_words.append(word) |
|
continue |
|
|
|
|
|
if word.lower() == "все": |
|
restored_words.append(word) |
|
continue |
|
|
|
|
|
parsed = morph.parse(word)[0] |
|
restored_word = parsed.word |
|
|
|
|
|
if word and word[0].isupper(): |
|
restored_word = restored_word.capitalize() |
|
|
|
restored_words.append(restored_word) |
|
|
|
return ' '.join(restored_words) |
|
|
|
text = restore_yo(text) |
|
|
|
|
|
text = re.sub(r'\bИп\b', 'ИП', text, flags=re.IGNORECASE) |
|
text = re.sub(r'\bОоо\b', 'ООО', text, flags=re.IGNORECASE) |
|
text = re.sub(r'\bРф\b', 'РФ', text, flags=re.IGNORECASE) |
|
|
|
|
|
text = re.sub(r'\bпользовуйтесь\b', 'пользуйтесь', text, flags=re.IGNORECASE) |
|
text = re.sub(r'\bею\b', 'ей', text, flags=re.IGNORECASE) |
|
text = re.sub(r'\bповышьте\b', 'повысьте', text, flags=re.IGNORECASE) |
|
|
|
text = re.sub(r'\bСбербизнес\b', 'СберБизнес', text, flags=re.IGNORECASE) |
|
text = re.sub(r'\bСбербизнеса\b', 'СберБизнес', text, flags=re.IGNORECASE) |
|
text = re.sub(r'\bСбербизнесе\b', 'СберБизнес', text, flags=re.IGNORECASE) |
|
text = re.sub(r'\bСбербанк\b', 'СберБанк', text, flags=re.IGNORECASE) |
|
text = re.sub(r'\bвашего ООО\b', 'вашей компании', text, flags=re.IGNORECASE) |
|
text = re.sub(r'\b0₽\b', '0 р', text, flags=re.IGNORECASE) |
|
text = re.sub(r'\b₽\b', 'р', text, flags=re.IGNORECASE) |
|
|
|
|
|
|
|
|
|
text = re.sub(r'\bруб\.(?=\W|$)', 'р', text, flags=re.IGNORECASE) |
|
|
|
text = re.sub(r'\bруб(?:ля|лей)\b', 'р', text, flags=re.IGNORECASE) |
|
|
|
|
|
text = re.sub(r'(\d+)\s+тысяч(?:а|и)?(?:\s+рублей)?', r'\1 000 р', text, flags=re.IGNORECASE) |
|
text = re.sub(r'(\d+)\s*тыс\.\s*руб\.', r'\1 000 р', text, flags=re.IGNORECASE) |
|
text = re.sub(r'(\d+)\s*тыс\.\s*р\.', r'\1 000 р', text, flags=re.IGNORECASE) |
|
text = re.sub(r'(\d+)\s*тыс\.\s*р', r'\1 000 р', text, flags=re.IGNORECASE) |
|
|
|
|
|
text = re.sub(r'(\d+)\s+миллиона\b|\bмиллионов\b', r'\1 млн', text, flags=re.IGNORECASE) |
|
text = re.sub(r'(\d+)\s*млн\s*руб\.', r'\1 млн р', text, flags=re.IGNORECASE) |
|
|
|
|
|
text = re.sub(r'(\d+)\s*р\b', r'\1 р', text) |
|
|
|
|
|
def remove_specific_sentences(text): |
|
sentences = re.split(r'(?<=[.!?])\s+', text) |
|
filtered_sentences = [ |
|
sentence for sentence in sentences |
|
if not re.search(r'\bникаких\s+(посещений|визитов)\b', sentence, flags=re.IGNORECASE) |
|
] |
|
return ' '.join(filtered_sentences) |
|
|
|
|
|
text = re.sub(r'\b(\d+)\s+000\s+000\s*р\b', r'\1 млн р', text, flags=re.IGNORECASE) |
|
text = re.sub(r' р р ', r' р ', text, flags=re.IGNORECASE) |
|
text = remove_specific_sentences(text) |
|
|
|
return text |
|
|
|
|
|
|
|
def append_errors_to_prompt(prompt, checks): |
|
|
|
error_messages = { |
|
"forbidden_words": "Не использовать запрещённые слова: номер один, №1, № 1, номер, вкусный, дешёвый, продукт, спам, банкротство, долг, займ, срочный, главный, гарантия, успех, лидер.", |
|
"client_addressing": "Не обращаться к клиенту напрямую.", |
|
"promises": "Не давать обещания и гарантии.", |
|
|
|
"participles": "Не использовать причастия.", |
|
|
|
"superlative_adjectives": "Не использовать превосходную степень прилагательных.", |
|
"passive_voice": "Избегать страдательного залога.", |
|
"written_out_ordinals": "Не использовать порядковые числительные от 10 прописью.", |
|
"subordinate_clauses_chain": "Избегать цепочек с придаточными предложениями.", |
|
"repeating_conjunctions": "Не использовать разделительные повторяющиеся союзы.", |
|
"introductory_phrases": "Не использовать вводные конструкции.", |
|
"amplifiers": "Не использовать усилители.", |
|
"time_parasites": "Не использовать 'паразиты времени'.", |
|
"multiple_nouns": "Избегать нескольких существительных подряд.", |
|
|
|
"compound_sentences": "Избегать сложноподчиненных предложений.", |
|
"dates_written_out": "Не писать даты прописью.", |
|
|
|
|
|
"synonymous_members": "Не использовать близкие по смыслу однородные члены предложения.", |
|
"clickbait_phrases": "Не использовать кликбейтные фразы.", |
|
"abstract_claims": "Избегать абстрактных заявлений без доказательств.", |
|
|
|
|
|
"cliches_and_bureaucratese": "Не использовать речевые клише, рекламные штампы, канцеляризмы.", |
|
"no_contradictions": "Избегать противоречий с описанием предложения.", |
|
"contains_key_message": "Обязательно включить ключевое сообщение.", |
|
"product_name_consistency": "Обязательно использовать точное наименование продукта." |
|
} |
|
|
|
|
|
for check_name, passed in checks.items(): |
|
if passed is False: |
|
error_message = error_messages.get(check_name, f"Ошибка в проверке {check_name}.") |
|
error_instruction = "Следующую ошибку необходимо избежать:\n" + error_message |
|
prompt += f"\n\n{error_instruction}" |
|
break |
|
|
|
return prompt |
|
|
|
|
|
def notify_failed_length(message_length): |
|
if message_length < 160: |
|
gr.Warning(f"Сообщение слишком короткое: {message_length} знаков. Минимум 160.") |
|
return False |
|
elif message_length > 250: |
|
gr.Warning(f"Сообщение слишком длинное: {message_length} знаков. Максимум 250.") |
|
return False |
|
return True |
|
|
|
|
|
def notify_failed_checks(checks): |
|
translation = { |
|
"forbidden_words": "Запрещенные слова", |
|
"client_addressing": "Обращение к клиенту", |
|
"promises": "Обещания и гарантии", |
|
|
|
"participles": "Причастия", |
|
|
|
"superlative_adjectives": "Превосходная степень", |
|
"passive_voice": "Страдательный залог", |
|
"written_out_ordinals": "Порядковые числительные", |
|
"subordinate_clauses_chain": "Цепочки с придаточными предложениями", |
|
"repeating_conjunctions": "Разделительные повторяющиеся союзы", |
|
"introductory_phrases": "Вводные конструкции", |
|
"amplifiers": "Усилители", |
|
"time_parasites": "Паразиты времени", |
|
"multiple_nouns": "Несколько существительных подряд", |
|
|
|
"compound_sentences": "Сложноподчиненные предложения", |
|
"dates_written_out": "Даты прописью", |
|
|
|
|
|
"synonymous_members": "Близкие по смыслу однородные члены предложения", |
|
"clickbait_phrases": "Кликбейтные фразы", |
|
"abstract_claims": "Абстрактные заявления без доказательств", |
|
|
|
|
|
"cliches_and_bureaucratese": "Речевые клише, рекламные штампы, канцеляризмы", |
|
"no_contradictions": "Противоречия с описанием предложения", |
|
"contains_key_message": "Отсутствие ключевого сообщения", |
|
"product_name_consistency": "Несовпадение наименования продукта" |
|
} |
|
|
|
|
|
for check_name, passed in checks.items(): |
|
if not passed: |
|
failed_check = translation.get(check_name, check_name) |
|
gr.Warning(f"Сообщение не прошло следующую проверку: {failed_check}") |
|
break |
|
else: |
|
|
|
gr.Warning("ВСЕ ПРОВЕРКИ ПРОЙДЕНЫ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_message_mistral_with_retry(prompt, approach_name, description, key_message, product_name, benefits): |
|
global approach_stats |
|
last_message = None |
|
for attempt in range(20): |
|
gr.Info(f"Итерация {attempt + 1}: генерируется сообщение...") |
|
message = generate_message_mistral_generate(prompt) |
|
if message is None: |
|
print("Не удалось получить сообщение от Mistral, повторная попытка...") |
|
time.sleep(1) |
|
continue |
|
message = replace_k_with_thousands(message) |
|
message = correct_dash_usage(message) |
|
message_length = len(message) |
|
if not notify_failed_length(message_length): |
|
last_message = message |
|
time.sleep(1) |
|
continue |
|
checks = perform_checks(message, description, key_message, product_name, benefits) |
|
last_message = message |
|
|
|
if approach_name not in approach_stats: |
|
approach_stats[approach_name] = {"failed_checks": defaultdict(int), "total_attempts": 0} |
|
approach_stats[approach_name]["total_attempts"] += 1 |
|
for check_name, passed in checks.items(): |
|
if passed is False: |
|
approach_stats[approach_name]["failed_checks"][check_name] += 1 |
|
break |
|
notify_failed_checks(checks) |
|
if all(checks.values()): |
|
return message |
|
prompt = append_errors_to_prompt(prompt, checks) |
|
time.sleep(1) |
|
gr.Info("Переходим к следующему циклу итераций.") |
|
return last_message |
|
|
|
|
|
|
|
def generate_standard_prompt(description, benefits, key_message, *selected_values): |
|
prompt = ( |
|
f"Сгенерируй смс-сообщение для клиента. Напиши 3 или 4 предложения.\n" |
|
f"Описание предложения: {description}\n" |
|
f"Преимущества: {benefits}\n" |
|
"В тексте смс запрещено использование:\n" |
|
"- Запрещенные слова: № один, номер один, № 1, вкусный, дешёвый, продукт, спам, доступный, банкротство, долги, займ, срочно, сейчас, лучший, главный, номер 1, гарантия, успех, лидер, никакой;\n" |
|
"- Повторы слов;\n" |
|
"- Обращение к клиенту;\n" |
|
"- Приветствие клиента;\n" |
|
"- Обещания и гарантии;\n" |
|
"- Использовать составные конструкции из двух глаголов;\n" |
|
"- Причастия и причастные обороты;\n" |
|
"- Деепричастия и деепричастные обороты;\n" |
|
"- Превосходная степень прилагательных;\n" |
|
"- Страдательный залог;\n" |
|
"- Порядковые числительные от 10 прописью;\n" |
|
"- Цепочки с придаточными предложениями;\n" |
|
"- Разделительные повторяющиеся союзы;\n" |
|
"- Вводные конструкции;\n" |
|
"- Усилители;\n" |
|
"- Паразиты времени;\n" |
|
"- Несколько существительных подряд, в том числе отглагольных;\n" |
|
"- Производные предлоги;\n" |
|
"- Сложные предложения, в которых нет связи между частями;\n" |
|
"- Сложноподчинённые предложения;\n" |
|
"- Даты прописью;\n" |
|
"- Близкие по смыслу однородные члены предложения;\n" |
|
"- Шокирующие, экстравагантные, кликбейтные фразы;\n" |
|
"- Абстрактные заявления без поддержки фактами и отсутствие доказательства пользы для клиента;\n" |
|
"- Гарантирующие фразы;\n" |
|
"- Узкоспециализированные термины;\n" |
|
"- Фразы, способные создать двойственное ощущение, обидеть;\n" |
|
"- Речевые клише, рекламные штампы, канцеляризмы;\n" |
|
"Убедись, что в готовом тексте до 250, но не менее 160 знаков с пробелами. Убедись, что в готовом тексте не менее трех предложений.\n" |
|
) |
|
if key_message.strip(): |
|
prompt += f"Убедись, что в готовом тексте есть следующая ключевая информация: {key_message.strip()}" |
|
|
|
return prompt |
|
|
|
|
|
|
|
def generate_personalization_prompt(key_message, *selected_values, prefix, suffix, product_name): |
|
prompt = f"{prefix}\n" |
|
prompt += f"Напиши 3 или 4 предложения.\n" |
|
prompt += f"Не изменяй название продукта: {product_name}.\n" |
|
prompt += "Адаптируй, не превышая длину сообщения в 250 знаков с пробелами (но и не менее 160 знаков с пробелами), текст с учетом следующих особенностей:\n" |
|
gender, generation, psychotype = selected_values[0], selected_values[1], selected_values[2] |
|
combined_instruction = "" |
|
additional_instructions = "" |
|
|
|
|
|
if gender and generation and psychotype: |
|
|
|
sheet = features.get("Пол Поколение Психотип", {}) |
|
|
|
|
|
key = (gender, generation, psychotype) |
|
if key in sheet: |
|
combined_instruction = sheet[key] |
|
|
|
|
|
if not combined_instruction: |
|
for i, feature in enumerate(["Пол", "Поколение", "Психотип"]): |
|
if selected_values[i]: |
|
try: |
|
instruction = features[feature][selected_values[i]] |
|
additional_instructions += f"{instruction}\n" |
|
except KeyError: |
|
return f"Ошибка: выбранное значение {selected_values[i]} не найдено в данных." |
|
|
|
|
|
for i, feature in enumerate(features.keys()): |
|
if feature not in ["Пол", "Поколение", "Психотип", "Пол Поколение Психотип"]: |
|
if i < len(selected_values) and selected_values[i]: |
|
try: |
|
instruction = features[feature][selected_values[i]] |
|
additional_instructions += f"{instruction}\n" |
|
except KeyError: |
|
return f"Ошибка: выбранное значение {selected_values[i]} не найдено в данных." |
|
|
|
|
|
if combined_instruction: |
|
prompt += combined_instruction |
|
if additional_instructions: |
|
prompt += additional_instructions |
|
|
|
prompt += f"{suffix}" |
|
prompt += f"Убедись, что в готовом тексте без изменений, синонимов и перестановок слов используется наименование продукта: {product_name}.\n" |
|
prompt += f"Убедись, что в готовом тексте не менее трех предложений.\n" |
|
if key_message.strip(): |
|
prompt += f"\nУбедись, что в готовом тексте есть следующая ключевая информация: {key_message.strip()}" |
|
|
|
return prompt.strip() |
|
|
|
|
|
def clean_prompt_for_display(prompt, prefixes, suffixes): |
|
|
|
for prefix in prefixes: |
|
prompt = prompt.replace(prefix, "") |
|
for suffix in suffixes: |
|
prompt = prompt.replace(suffix, "") |
|
|
|
|
|
lines = prompt.split('\n') |
|
non_empty_lines = [line for line in lines if line.strip() != ''] |
|
cleaned_prompt = '\n'.join(non_empty_lines) |
|
|
|
return cleaned_prompt.strip() |
|
|
|
def are_messages_equal(message1, message2): |
|
return message1.strip().lower() == message2.strip().lower() |
|
|
|
|
|
def generate_all_messages(desc, benefits, key_message, gender, generation, psychotype, business_stage, industry, opf, product_name): |
|
standard_prompt = generate_standard_prompt(desc, benefits, key_message) |
|
standard_prompt_for_display = f"Не изменяй название продукта: {product_name}.\n{standard_prompt}\nУбедись, что в готовом тексте без изменений, синонимов и перестановок слов используется наименование продукта: {product_name}.\n" |
|
approach_mapping = { |
|
"Указание на пользу": ( |
|
"Начни сообщение с указания на пользу продукта. Используй глагол в побудительном наклонении. Не начинай с вопроса.", |
|
"Убедись, что готовый текст начинается с указания на пользу продукта и использования глагола в побудительном наклонении и не начинается с вопроса." |
|
), |
|
"Призыв к действию": ( |
|
"Начни сообщение с призыва к действию с продуктом. Не начинай с вопроса.", |
|
"Убедись, что готовый текст начинается с призыва к действию с продуктом и не начинается с вопроса." |
|
), |
|
"Вопрос": ( |
|
"Начни сообщение с вопроса, который указывает на пользу продукта для клиента.", |
|
"Убедись, что готовый текст начинается с вопроса, который указывает на пользу продукта для клиента." |
|
) |
|
} |
|
approaches_df = features.get("Подход", None) |
|
if approaches_df is None: |
|
gr.Warning("Таблица 'Подход' не найдена.") |
|
return |
|
filters = [] |
|
for param_name, param_value in [('Пол', gender), ('Поколение', generation), ('Психотип', psychotype)]: |
|
if param_value: |
|
filters.append(approaches_df[param_name].fillna('') == param_value) |
|
else: |
|
filters.append(approaches_df[param_name].isnull() | (approaches_df[param_name] == '')) |
|
combined_filter = filters[0] |
|
for f in filters[1:]: |
|
combined_filter &= f |
|
matching_rows = approaches_df[combined_filter] |
|
if matching_rows.empty: |
|
gr.Warning("Подход не найден для выбранных параметров.") |
|
return |
|
approach_list = [] |
|
for approaches in matching_rows['Подход']: |
|
approach_names = [a.strip() for a in approaches.split(',')] |
|
approach_list.extend(approach_names) |
|
approach_list = list(set(approach_list)) |
|
selected_approaches_text_content = ', '.join(approach_list) |
|
|
|
yield selected_approaches_text_content, standard_prompt_for_display, None, None, None |
|
|
|
non_personalized_messages = '' |
|
personalized_messages = '' |
|
flag = 1 |
|
|
|
|
|
if len(approach_list) == 0: |
|
gr.Warning("Не выбрано ни одного подхода.") |
|
return |
|
elif len(approach_list) == 1: |
|
approaches_for_messages = [approach_list[0]] * 3 |
|
elif len(approach_list) >= 2: |
|
approaches_for_messages = [approach_list[0], approach_list[1], approach_list[0]] |
|
else: |
|
approaches_for_messages = approach_list[:3] |
|
|
|
for approach_name in approaches_for_messages: |
|
if approach_name not in approach_mapping: |
|
gr.Warning(f"Неизвестный подход: {approach_name}") |
|
continue |
|
current_prefix, current_suffix = approach_mapping[approach_name] |
|
personalization_prompt = generate_personalization_prompt( |
|
key_message, gender, generation, psychotype, business_stage, industry, opf, |
|
prefix=current_prefix, suffix=current_suffix, product_name=product_name |
|
) |
|
display_personalization_prompt = clean_prompt_for_display(personalization_prompt, [current_prefix], [current_suffix]) |
|
if flag == 1: |
|
yield selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt, None, None |
|
flag += 1 |
|
prompt = add_prefix_suffix(standard_prompt, current_prefix, current_suffix, product_name) |
|
non_personalized_message = generate_message_mistral_with_retry(prompt, approach_name, desc, key_message, product_name, benefits) |
|
non_personalized_length = len(non_personalized_message) |
|
non_personalized_display = f"{non_personalized_message}\n------\nКоличество знаков: {non_personalized_length}" |
|
if non_personalized_messages: |
|
non_personalized_messages += '\n\n' + non_personalized_display |
|
else: |
|
non_personalized_messages = non_personalized_display |
|
yield ( |
|
selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt, |
|
non_personalized_messages, personalized_messages |
|
) |
|
|
|
|
|
personalized_message = None |
|
for attempt in range(5): |
|
personalized_message_candidate = generate_message_mistral_with_retry( |
|
f"{personalization_prompt}\n\nТекст для адаптации: {non_personalized_message}", |
|
approach_name, desc, key_message, product_name, benefits |
|
) |
|
if not are_messages_equal(personalized_message_candidate, non_personalized_message): |
|
personalized_message = personalized_message_candidate |
|
break |
|
else: |
|
print(f"Попытка {attempt + 1}: Персонализированное сообщение совпадает со стандартным. Повторная генерация...") |
|
time.sleep(1) |
|
else: |
|
|
|
personalized_message = personalized_message_candidate |
|
print("Не удалось сгенерировать уникальное персонализированное сообщение после 5 попыток. Используется последнее сообщение.") |
|
|
|
personalized_length = len(personalized_message) |
|
personalized_display = f"{personalized_message}\n------\nКоличество знаков: {personalized_length}" |
|
if personalized_messages: |
|
personalized_messages += '\n\n' + personalized_display |
|
else: |
|
personalized_messages = personalized_display |
|
|
|
yield ( |
|
selected_approaches_text_content, standard_prompt_for_display, display_personalization_prompt, |
|
non_personalized_messages, personalized_messages |
|
) |
|
time.sleep(1) |
|
|
|
save_statistics_to_github(approach_stats) |
|
|
|
def rank_messages(non_personalized_messages, personalized_messages): |
|
|
|
|
|
def clean_message(msg): |
|
|
|
if '------' in msg: |
|
msg = msg.split('------')[0].strip() |
|
return msg.strip() |
|
|
|
|
|
non_personalized_list = [ |
|
clean_message(msg) |
|
for msg in non_personalized_messages.strip().split('\n\n') |
|
if msg.strip() |
|
] |
|
|
|
|
|
personalized_list = [ |
|
clean_message(msg) |
|
for msg in personalized_messages.strip().split('\n\n') |
|
if msg.strip() |
|
] |
|
|
|
|
|
ranked_non_personalized_df = best_text_choice( |
|
non_personalized_list, unique_sms_df, tokenizer, model |
|
) |
|
|
|
|
|
ranked_personalized_df = best_text_choice( |
|
personalized_list, unique_sms_df, tokenizer, model |
|
) |
|
|
|
|
|
ranked_non_personalized_messages_list = ranked_non_personalized_df['text'].tolist() |
|
ranked_personalized_messages_list = ranked_personalized_df['text'].tolist() |
|
|
|
|
|
def reconstruct_messages(messages_list): |
|
display_list = [] |
|
for msg in messages_list: |
|
msg_length = len(msg) |
|
display_list.append(f"{msg}\n------\nКоличество знаков: {msg_length}") |
|
return display_list |
|
|
|
ranked_non_personalized_display = reconstruct_messages(ranked_non_personalized_messages_list) |
|
ranked_personalized_display = reconstruct_messages(ranked_personalized_messages_list) |
|
|
|
|
|
ranked_non_personalized_messages = '\n\n'.join(ranked_non_personalized_display) |
|
ranked_personalized_messages = '\n\n'.join(ranked_personalized_display) |
|
|
|
return ranked_non_personalized_messages, ranked_personalized_messages |
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_forbidden_words(message): |
|
morph = pymorphy3.MorphAnalyzer() |
|
|
|
|
|
forbidden_patterns = [ |
|
r'№\s?1\b', r'номер\sодин\b', r'номер\s1\b', |
|
r'вкусный', r'дешёвый', r'продукт', |
|
r'спам', r'банкротство', r'долг[и]?', r'займ', |
|
r'срочный', r'главный', |
|
r'гарантия', r'успех', r'лидер', 'никакой' |
|
] |
|
|
|
|
|
message_without_punctuation = message.translate(str.maketrans('', '', string.punctuation)) |
|
|
|
|
|
placeholder = "заменабессроч" |
|
message_without_punctuation = re.sub(r'\b\w*бессроч\w*\b', placeholder, message_without_punctuation, flags=re.IGNORECASE) |
|
|
|
|
|
if re.search(r'лучш', message_without_punctuation, re.IGNORECASE): |
|
return False |
|
|
|
|
|
words = message_without_punctuation.split() |
|
lemmas = [morph.parse(word)[0].normal_form for word in words] |
|
|
|
|
|
lemmas = [re.sub(r'заменабессроч', 'бессроч', word) for word in lemmas] |
|
normalized_message = ' '.join(lemmas) |
|
|
|
|
|
for pattern in forbidden_patterns: |
|
if re.search(pattern, normalized_message, re.IGNORECASE): |
|
print(f"Не пройдена проверка: Запрещенные слова. Сообщение: {message}") |
|
return False |
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def check_no_greeting(message): |
|
morph = pymorphy3.MorphAnalyzer() |
|
|
|
greeting_patterns = [ |
|
r"привет\b", r"здравствуй", r"добрый\s(день|вечер|утро)", |
|
r"дорогой\b", r"уважаемый\b", r"дорогая\b", r"уважаемая\b", |
|
r"господин\b", r"госпожа\b", r"друг\b", r"коллега\b", |
|
r"товарищ\b", r"приятель\b", r"друг\b", r"подруга\b" |
|
] |
|
|
|
|
|
greeting_regex = re.compile('|'.join(greeting_patterns), re.IGNORECASE) |
|
|
|
|
|
if greeting_regex.search(message.strip()): |
|
print(f"Не пройдена проверка: Обращение к клиенту и приветствие клиента. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_promises(message): |
|
morph = pymorphy3.MorphAnalyzer() |
|
promise_patterns = [ |
|
"обещать", "обещание", "гарантировать", "обязаться", "обязать", "обязательство", "обязательный" |
|
] |
|
|
|
words = message.split() |
|
lemmas = [morph.parse(word)[0].normal_form for word in words] |
|
|
|
for pattern in promise_patterns: |
|
if pattern in lemmas: |
|
print(f"Не пройдена проверка: Обещания и гарантии. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_double_verbs(message): |
|
morph = pymorphy3.MorphAnalyzer() |
|
|
|
words = re.split(r'\s+|[.!?]', message) |
|
morphs = [morph.parse(word)[0] for word in words] |
|
|
|
for i in range(len(morphs) - 1): |
|
|
|
if (morphs[i].tag.POS in {'VERB', 'INFN'}) and (morphs[i+1].tag.POS in {'VERB', 'INFN'}): |
|
|
|
if morphs[i].normal_form in ['хотеть', 'начинать', 'начать']: |
|
return True |
|
else: |
|
print(f"Не пройдена проверка: Составные конструкции из двух глаголов. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_participles(message): |
|
morph = pymorphy3.MorphAnalyzer() |
|
words = message.split() |
|
exceptions = {"повышенный", "увеличенный", "пониженный", "сниженный"} |
|
|
|
for word in words: |
|
parsed_word = morph.parse(word)[0] |
|
lemma = parsed_word.normal_form |
|
if 'PRTF' in parsed_word.tag and lemma not in exceptions: |
|
print(f"Не пройдена проверка: Причастия и причастные обороты. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_adverbial_participles(message): |
|
morph = pymorphy3.MorphAnalyzer() |
|
words = message.split() |
|
morphs = [morph.parse(word)[0] for word in words] |
|
|
|
for morph in morphs: |
|
if 'GRND' in morph.tag: |
|
print(f"Не пройдена проверка: Деепричастия и деепричастные обороты. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_superlative_adjectives(message): |
|
morph = pymorphy3.MorphAnalyzer() |
|
words = message.split() |
|
morphs = [morph.parse(word)[0] for word in words] |
|
|
|
for morph in morphs: |
|
if 'Supr' in morph.tag: |
|
print(f"Не пройдена проверка: Превосходная степень прилагательных. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_passive_voice(message): |
|
morph = pymorphy3.MorphAnalyzer() |
|
words = message.split() |
|
morphs = [morph.parse(word)[0] for word in words] |
|
|
|
for morph in morphs: |
|
if 'PRTF' in morph.tag and ('passive' in morph.tag or 'в' in morph.tag): |
|
print(f"Не пройдена проверка: Страдательный залог. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_written_out_ordinals(message): |
|
morph = pymorphy3.MorphAnalyzer() |
|
ordinal_words = [ |
|
"десятый", "одиннадцатый", "двенадцатый", "тринадцатый", "четырнадцатый", "пятнадцатый", |
|
"шестнадцатый", "семнадцатый", "восемнадцатый", "девятнадцатый", "двадцатый" |
|
] |
|
|
|
words = message.split() |
|
lemmas = [morph.parse(word)[0].normal_form for word in words] |
|
|
|
for word in ordinal_words: |
|
if word in lemmas: |
|
print(f"Не пройдена проверка: Порядковые числительные от 10 прописью. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_subordinate_clauses_chain(message): |
|
|
|
subordinate_clause_patterns = [ |
|
r'\b(который|которая|которое|которые)\b', |
|
r'\b(если|потому что|так как|что|когда)\b', |
|
r'\b(хотя|несмотря на то что)\b' |
|
] |
|
|
|
sentences = re.split(r'[.!?]\s*', message) |
|
|
|
count = 0 |
|
for sentence in sentences: |
|
for pattern in subordinate_clause_patterns: |
|
if re.search(pattern, sentence): |
|
count += 1 |
|
|
|
|
|
return count < 2 |
|
|
|
|
|
|
|
def check_no_repeating_conjunctions(message): |
|
|
|
repeating_conjunctions_patterns = r'\b(и|ни|то|не то|или|либо)\b\s*(.*?)\s*,\s*\b\1\b' |
|
|
|
|
|
sentences = re.split(r'[.!?]\s*', message) |
|
|
|
|
|
for sentence in sentences: |
|
if re.search(repeating_conjunctions_patterns, sentence, re.IGNORECASE): |
|
print(f"Не пройдена проверка: Разделительные повторяющиеся союзы. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_introductory_phrases(message): |
|
introductory_phrases = [ |
|
r'\b(во-первых|во-вторых|с одной стороны|по сути|по правде говоря)\b', |
|
r'\b(может быть|кстати|конечно|естественно|безусловно|возможно)\b' |
|
] |
|
|
|
for pattern in introductory_phrases: |
|
if re.search(pattern, message, re.IGNORECASE): |
|
print(f"Не пройдена проверка: Вводные конструкции. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_amplifiers(message): |
|
amplifiers = [ |
|
r'\b(очень|крайне|чрезвычайно|совсем|полностью|чисто)\b' |
|
] |
|
|
|
for pattern in amplifiers: |
|
if re.search(pattern, message, re.IGNORECASE): |
|
print(f"Не пройдена проверка: Усилители. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_time_parasites(message): |
|
time_parasites = [ |
|
r'\b(немедленно|срочно|в данный момент)\b' |
|
] |
|
|
|
for pattern in time_parasites: |
|
if re.search(pattern, message, re.IGNORECASE): |
|
print(f"Не пройдена проверка: Паразиты времени. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_multiple_nouns(message): |
|
noun_count = 0 |
|
words = re.split(r'\s+|[.!?]', message) |
|
morph = pymorphy3.MorphAnalyzer() |
|
|
|
for word in words: |
|
parsed_word = morph.parse(word)[0] |
|
|
|
|
|
if 'NOUN' in parsed_word.tag: |
|
noun_count += 1 |
|
|
|
elif re.match(r'[.!?]', word): |
|
noun_count = 0 |
|
else: |
|
noun_count = 0 |
|
|
|
if noun_count > 2: |
|
print(f"Не пройдена проверка: Несколько существительных подряд. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_derived_prepositions(message): |
|
derived_prepositions = [ |
|
r'\b(в течение|в ходе|вследствие|в связи с|по мере|при помощи|согласно|вопреки|на основании|на случай|в продолжение|по причине|вблизи|вдалеке|вокруг|внутри|вдоль|посередине|вне|снаружи|благодаря|невзирая на|исходя из)\b' |
|
] |
|
|
|
for pattern in derived_prepositions: |
|
if re.search(pattern, message, re.IGNORECASE): |
|
print(f"Не пройдена проверка: Производные предлоги. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_compound_sentences(message): |
|
subordinating_conjunctions = [ |
|
r'\bкогда\b', r'\bкак только\b', r'\bпока\b', r'\bпосле того как\b', |
|
r'\bпотому что\b', r'\bтак как\b', r'\bоттого что\b', r'\bблагодаря тому что\b', |
|
r'\bчтобы\b', r'\bдля того чтобы\b', r'\bесли\b', r'\bкогда бы\b', r'\bесли бы\b', |
|
r'\bхотя\b', r'\bнесмотря на то что\b', r'\bкак\b', r'\bбудто\b', r'\bсловно\b', r'\bкак будто\b' |
|
] |
|
|
|
|
|
for pattern in subordinating_conjunctions: |
|
if re.search(pattern, message) and not re.search(r'\?', message): |
|
print(f"Не пройдена проверка: Сложноподчиненные предложения. Сообщение: {message}") |
|
return False |
|
return True |
|
|
|
|
|
|
|
def check_no_dates_written_out(message): |
|
|
|
months = [ |
|
"января", "февраля", "марта", "апреля", "мая", "июня", |
|
"июля", "августа", "сентября", "октября", "ноября", "декабря" |
|
] |
|
|
|
|
|
date_written_out_patterns = [ |
|
r'\b(первого|второго|третьего|четвертого|пятого|шестого|седьмого|восьмого|девятого|десятого|одиннадцатого|двенадцатого|тринадцатого|четырнадцатого|пятнадцатого|шестнадцатого|семнадцатого|восемнадцатого|девятнадцатого|двадцатого|двадцать первого|двадцать второго|двадцать третьего|двадцать четвертого|двадцать пятого|двадцать шестого|двадцать седьмого|двадцать восьмого|двадцать девятого|тридцатого|тридцать первого)\b' |
|
] |
|
|
|
for month in months: |
|
for pattern in date_written_out_patterns: |
|
if re.search(f'{pattern}\\s{month}', message, re.IGNORECASE): |
|
print(f"Не пройдена проверка: Даты прописью. Сообщение: {message}") |
|
return False |
|
|
|
return True |
|
|
|
|
|
|
|
def check_no_word_repetitions(message): |
|
morph = pymorphy3.MorphAnalyzer() |
|
|
|
|
|
ignore_words = set([ |
|
'и', 'а', 'но', 'или', 'да', 'ни', 'как', 'так', |
|
'в', 'на', 'под', 'над', 'за', 'к', 'до', 'по', 'из', 'у', 'о', 'про', 'для', |
|
'не', 'вот', 'это', 'тот', 'тем', 'при', 'чем', |
|
'же', 'ли', 'бы', 'то', 'р', 'от' |
|
]) |
|
|
|
|
|
words = re.findall(r'\b\w+(?:-\w+)*\b', message.lower()) |
|
|
|
|
|
normalized_words = {} |
|
|
|
for word in words: |
|
if word not in ignore_words: |
|
|
|
if '-' in word: |
|
|
|
base_word = word |
|
else: |
|
|
|
base_word = morph.parse(word)[0].normal_form |
|
|
|
|
|
if base_word in normalized_words: |
|
print(f"Не пройдена проверка: Повторы слов. Сообщение: {message}") |
|
return False |
|
|
|
|
|
normalized_words[base_word] = True |
|
|
|
|
|
return True |
|
|
|
|
|
|
|
import re |
|
import json |
|
|
|
def parse_json_response(response): |
|
try: |
|
|
|
match = re.search(r'\{.*', response) |
|
if match: |
|
json_str = match.group(0) |
|
|
|
if json_str.count('"') % 2 != 0: |
|
json_str += '"' |
|
if json_str.count('{') > json_str.count('}'): |
|
json_str += '}' |
|
result = json.loads(json_str) |
|
return result |
|
|
|
|
|
else: |
|
decision_match = re.search(r'decision:\s*(true|false)', response) |
|
explanation_match = re.search(r'explanation:\s*"(.+?)"', response) |
|
|
|
result = {} |
|
if decision_match: |
|
decision_value = decision_match.group(1) |
|
result['decision'] = True if decision_value == 'true' else False |
|
|
|
if explanation_match: |
|
result['explanation'] = explanation_match.group(1) |
|
|
|
if result: |
|
return result |
|
else: |
|
print("JSON не найден, и ключи 'decision' и 'explanation' не извлечены") |
|
return None |
|
except Exception as e: |
|
print(f"Ошибка при разборе JSON: {e}") |
|
return None |
|
|
|
|
|
def cut_message(message): |
|
|
|
|
|
if '------' in message: |
|
message = message.split('------')[0].strip() |
|
return message |
|
|
|
|
|
def check_disconnected_sentences(message): |
|
message_clean = cut_message(message) |
|
print() |
|
print("Проверка 22: Проверка сложных предложений без логической связи") |
|
print() |
|
prompt = f'''Проверь следующий текст на наличие сложных предложений, где отсутствует логическая связь между частями: |
|
"{message_clean}" |
|
Определи, есть ли в тексте предложения с несколькими частями, которые кажутся несвязанными, не поддерживают общую мысль или делают текст трудным для понимания. |
|
Обрати внимание, что в контексте коротких рекламных сообщений допустимы краткие предложения, перечисления и фразы, которые вместе передают связную информацию о продукте или услуге. Не считай такие сообщения несвязанными, если их части логически связаны с предложением продукта или условиями его получения. |
|
Пример ответа: |
|
{{"decision": false, "explanation": "Текст понятен, и все предложения логически связаны между собой."}} |
|
Если в тексте **есть** сложные предложения без логической связи между частями, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}; |
|
если таких предложений **нет**, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}. |
|
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь **только** в формате JSON с закрывающими кавычками и скобками.**''' |
|
|
|
response = generate_message_mistral_check(prompt) |
|
time.sleep(3) |
|
print("Mistral response:", response) |
|
result = parse_json_response(response) |
|
if result is not None: |
|
decision = result.get("decision", False) |
|
explanation = result.get("explanation", "") |
|
print("Explanation:", explanation) |
|
return not decision |
|
else: |
|
return None |
|
|
|
|
|
def check_synonymous_members(message): |
|
print() |
|
print("Проверка 23: Проверка на близкие по смыслу однородные члены") |
|
print() |
|
message_clean = cut_message(message) |
|
prompt = f'''Проверь следующий текст на наличие однородных членов предложения, которые имеют одинаковый или практически одинаковый смысл и повторяют одну и ту же идею: |
|
"{message_clean}" |
|
Обрати внимание, что слова или выражения могут описывать разные аспекты продукта или услуги, и это не считается избыточным, если они не полностью дублируют значение друг друга. Например, такие слова как "премиальная" и "бизнес" могут описывать разные качества и не должны считаться синонимами. |
|
Пример ответа: |
|
{{"decision": true, "explanation": "В предложении используются синонимы 'быстрый' и 'скорый', которые повторяют одну и ту же идею."}} |
|
Если такие слова или выражения есть, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}; |
|
если таких слов или выражений нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}. |
|
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**''' |
|
|
|
response = generate_message_mistral_check(prompt) |
|
time.sleep(3) |
|
print("Mistral response:", response) |
|
result = parse_json_response(response) |
|
if result is not None: |
|
decision = result.get("decision", False) |
|
explanation = result.get("explanation", "") |
|
print("Explanation:", explanation) |
|
return not decision |
|
else: |
|
return None |
|
|
|
|
|
|
|
def check_clickbait_phrases(message, description, benefits, key_message): |
|
message_clean = cut_message(message) |
|
print() |
|
print() |
|
print("СООБЩЕНИЕ:", message_clean) |
|
print() |
|
print("Проверка 24: Проверка на шокирующие, экстравагантные или кликбейтные фразы") |
|
print() |
|
prompt = f'''Проверь следующий текст на наличие шокирующих, экстравагантных или кликбейтных фраз: |
|
"{message_clean}" |
|
Инструкции: |
|
1. Игнорируй фразы, которые основаны на фактической информации, даже если они выглядят сенсационно, такие как "лимит до миллиона" или "льготный период до 365 дней". Если эти данные подтверждаются и не являются преувеличением, их не следует считать кликбейтом. |
|
2. Ищи фразы, которые явно преувеличивают или вводят в заблуждение, обещая нечто чрезмерно идеализированное или сенсационное, что не может быть доказано или подтверждено. Примеры кликбейтных фраз: "Шокирующая правда", "Вы не поверите, что произошло", "Это изменит вашу жизнь за один день". |
|
3. Стандартные рекламные призывы к действию, такие как "купите сейчас" или "узнайте больше", не считаются кликбейтом, если они не преувеличивают преимущества или не используют явную манипуляцию эмоциями. |
|
4. Не считай фразы, используемые в исходном описании продукта, кликбейтными. Исходное описание: "{description}". |
|
5. Не считай фразы, используемые в преимуществах продукта, кликбейтными. Преимущества: "{benefits}". |
|
6. Не считай фразы, используемые в ключевом сообщении, кликбейтными. Ключевое сообщение: "{key_message}". |
|
Пример ответа: |
|
{{"decision": false, "explanation": "Текст нейтрален и не содержит кликбейтных фраз."}} |
|
|
|
Если текст содержит кликбейтные фразы, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}; |
|
если таких фраз нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}. |
|
|
|
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**''' |
|
|
|
response = generate_message_mistral_check(prompt) |
|
time.sleep(3) |
|
print("Mistral response:", response) |
|
result = parse_json_response(response) |
|
if result is not None: |
|
decision = result.get("decision", False) |
|
explanation = result.get("explanation", "") |
|
print("Explanation:", explanation) |
|
return not decision |
|
else: |
|
return None |
|
|
|
|
|
|
|
def check_abstract_claims(message, description, benefits, key_message): |
|
print() |
|
print("Проверка 25: Проверка на абстрактные заявления без поддержки фактами") |
|
print() |
|
message_clean = cut_message(message) |
|
prompt = f'''Проверь следующий текст на наличие чрезмерно абстрактных или неподкрепленных фактическими данными утверждений, которые могут усложнить понимание преимуществ продукта или услуги: |
|
"{message_clean}" |
|
|
|
Инструкции: |
|
1. Исключи фразы, которые содержат конкретные числовые данные, обещания о времени выполнения или другие факты, которые могут быть проверены (например, "от 1 минуты", "24/7", "в течение 24 часов"). |
|
2. Не считай абстрактными фразами выражения, которые описывают конкретные выгодные условия, если они сопровождаются фактами или цифрами (например, "выгодные условия при покупке от 100 000 рублей" или "индивидуальные условия с процентной ставкой 3%"). |
|
3. Помечай абстрактными фразами любые утверждения, которые звучат эмоционально, но не сопровождаются конкретикой, такие как: |
|
- "выгодное финансирование" |
|
- "развивайте свой бизнес быстрее" |
|
- "повышение эффективности" |
|
- "эффективное управление" |
|
- "надёжное решение" |
|
- "оптимизируйте управление финансами" |
|
- "выгодные условия для бизнеса" |
|
- "лёгкие условия и кэшбэк" |
|
- "мобильно, удобно, комфортно" |
|
- "Действуйте сейчас!" |
|
- "Контролируйте лимиты на расходы сотрудников" |
|
- "Оптимизируйте свои бизнес-затраты" |
|
- "Снизьте финансовую нагрузку" |
|
4. Ищи общие фразы, которые не дают представления о конкретной пользе, такие как "лучшее решение", "высокое качество", "отличный сервис", если они не сопровождаются пояснением о том, почему это так. |
|
5. Учитывай, что в рекламных сообщениях допустимы эмоциональные и обобщённые фразы, если они достаточно конкретны для понимания аудитории, однако они должны сопровождаться фактами или подробными примерами. |
|
6. Не считай фразы, используемые в исходном описании продукта, абстрактными. Исходное описание: "{description}". |
|
7. Не считай фразы, используемые в преимуществах продукта, абстрактными. Преимущества: "{benefits}". |
|
8. Не считай фразы, используемые в ключевом сообщении, абстрактными. Ключевое сообщение: "{key_message}". |
|
|
|
Пример ответа: |
|
{{"decision": false, "explanation": "Текст не содержит абстрактные утверждения без конкретики."}} |
|
|
|
Если в тексте присутствуют абстрактные или неподкрепленные заявления, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}; |
|
если таких утверждений нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}. |
|
|
|
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**''' |
|
|
|
response = generate_message_mistral_check(prompt) |
|
time.sleep(3) |
|
print("Mistral response:", response) |
|
result = parse_json_response(response) |
|
if result is not None: |
|
decision = result.get("decision", False) |
|
explanation = result.get("explanation", "") |
|
print("Explanation:", explanation) |
|
return not decision |
|
else: |
|
return None |
|
|
|
|
|
|
|
def check_specialized_terms(message): |
|
print() |
|
print("Проверка 26: Проверка на узкоспециализированные термины") |
|
print() |
|
message_clean = cut_message(message) |
|
prompt = f'''Проверь следующий текст на наличие узкоспециализированных терминов или жаргона, которые могут быть непонятны широкой аудитории: |
|
"{message_clean}" |
|
|
|
Инструкции: |
|
1. Игнорируй общеупотребительные термины, известные широкой аудитории, такие как "ИП", "ООО", "РФ", а также термины, связанные с обычными финансовыми продуктами (например, "кредитная карта", "интернет-банк", "Mastercard"). |
|
2. Ищи термины, характерные для узких профессиональных областей, таких как медицина, ИТ, право, инженерия и другие специализированные сферы. |
|
3. Пример специализированных терминов: "интероперабельность", "кибернетика", "гипертензия", "аутентификация" и т.п. |
|
|
|
Определи, содержит ли текст термины, которые известны только специалистам в определенной области и могут вызвать затруднения у обычных читателей. |
|
|
|
Пример ответа: |
|
{{"decision": false, "explanation": "В тексте отсутствуют узкоспециализированные термины."}} |
|
|
|
Если в тексте есть такие узкоспециализированные термины, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}; |
|
если таких терминов нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}. |
|
|
|
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**''' |
|
|
|
response = generate_message_mistral_check(prompt) |
|
time.sleep(3) |
|
print("Mistral response:", response) |
|
result = parse_json_response(response) |
|
if result is not None: |
|
decision = result.get("decision", False) |
|
explanation = result.get("explanation", "") |
|
print("Explanation:", explanation) |
|
return not decision |
|
else: |
|
return None |
|
|
|
|
|
def check_offensive_phrases(message): |
|
print() |
|
print("Проверка 27: Проверка на двусмысленные или обидные фразы") |
|
print() |
|
message_clean = cut_message(message) |
|
prompt = f'''Проверь следующий текст на наличие фраз, которые могут быть истолкованы двусмысленно или вызвать негативные эмоции у читателя: |
|
"{message_clean}" |
|
Определи, есть ли в тексте выражения, которые могут быть восприняты как оскорбительные, обидные или неуместные. |
|
Обрати внимание, что фразы, используемые в обычном деловом контексте и не содержащие явных оскорблений, дискриминации или непристойностей, не считаются проблемными. |
|
Например, фразы, объясняющие преимущества продукта, такие как "без отчётов и комиссий", являются допустимыми. |
|
Пример ответа: |
|
{{"decision": false, "explanation": "Текст не содержит обидных или двусмысленных фраз."}} |
|
Если такие фразы есть, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}; |
|
если таких фраз нет, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}. |
|
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**''' |
|
|
|
response = generate_message_mistral_check(prompt) |
|
time.sleep(3) |
|
print("Mistral response:", response) |
|
result = parse_json_response(response) |
|
if result is not None: |
|
decision = result.get("decision", False) |
|
explanation = result.get("explanation", "") |
|
print("Explanation:", explanation) |
|
return not decision |
|
else: |
|
return None |
|
|
|
|
|
def check_cliches_and_bureaucratese(message, description, benefits, key_message): |
|
print() |
|
print("Проверка 28: Проверка на речевые клише, рекламные штампы и канцеляризмы") |
|
print() |
|
message_clean = cut_message(message) |
|
prompt = f'''Проверь следующий текст на наличие речевых клише, излишне употребляемых фраз, рекламных штампов и канцеляризмов, которые делают текст менее выразительным и оригинальным: |
|
"{message_clean}" |
|
Обрати внимание **только** на избитые фразы, которые чрезмерно используются в рекламных текстах и не несут дополнительной ценности. |
|
**Не считай клише или канцеляризмами следующие типы выражений:** |
|
- Стандартные призывы к действию (например, "Получите", "Оформите", "Закажите сейчас"), но **не** их комбинации с общими, неопределёнными фразами, как например, "за считанные минуты", "быстро, удобно". |
|
- Информацию о ценах, скидках, акциях или условиях покупки (например, "при покупках от 100 000 рублей в месяц"). |
|
- Описания способов оформления или получения услуг (например, "оформление возможно онлайн или в офисе"). |
|
- Стандартные отраслевые термины и фразы, необходимые для понимания сообщения (например, "премиальная бизнес-карта", "Mastercard Preferred"), но **не** их использование в комбинации с общими словами, как например, "идеальное решение для вашего бизнеса". |
|
- Не считай фразы, используемые в исходном описании продукта, как клише. Исходное описание: "{description}". |
|
- Не считай фразы, используемые в преимуществах продукта, как клише. Преимущества: "{benefits}". |
|
- Не считай фразы, используемые в ключевом сообщении, как клише. Ключевое сообщение: "{key_message}". |
|
**Считай клише или канцеляризмами следующие типы выражений:** |
|
- Избитые фразы, такие как: |
|
- "Обеспечьте стабильность и развитие вашего бизнеса" |
|
- "Заботьтесь о будущем семьи, сохраняя ресурсы." |
|
- "Получите необходимые средства для развития бизнеса и обеспечения финансовой стабильности!" |
|
- "Ваш бизнес ждёт выгодное финансирование! Развивайте свой бизнес быстрее!" |
|
- "Без лишней волокиты" |
|
- "Быстро, удобно, без лишних хлопот!" |
|
- "За считанные минуты" |
|
- "Это идеальное предложение для вашего бизнеса!" |
|
- "Удобное и надёжное решение для роста вашего капитала". |
|
Пример ответа: |
|
{{"decision": false, "explanation": "Текст не содержит клише или канцеляризмов."}} |
|
Если в тексте **нет** таких выражений, **верни только** JSON {{"decision": false, "explanation": "<пояснение>"}}; |
|
если в тексте **есть** такие выражения, **верни только** JSON {{"decision": true, "explanation": "<пояснение>"}}. |
|
**Не добавляй никакого дополнительного текста. Перед ответом убедись, что отвечаешь только в формате JSON с закрывающими кавычками и скобками.**''' |
|
|
|
response = generate_message_mistral_check(prompt) |
|
time.sleep(3) |
|
print("Mistral response:", response) |
|
result = parse_json_response(response) |
|
if result is not None: |
|
decision = result.get("decision", False) |
|
explanation = result.get("explanation", "") |
|
print("Explanation:", explanation) |
|
return not decision |
|
else: |
|
return None |
|
|
|
|
|
def check_no_contradictions(message, description, benefits, key_message): |
|
print() |
|
print("Проверка 29: Проверка на отсутствие противоречий с описанием предложения") |
|
print() |
|
message_clean = cut_message(message) |
|
prompt = f'''Проверь, не противоречит ли следующее сообщение описанию предложения и ключевому сообщению. Учти, что сообщение является выжимкой из описания предложения и не может содержать столько же информации в том же объеме, сколько описание предложения - важно, чтобы в сообщении не было указано ложных фактов. |
|
Описание предложения: |
|
"{description}" |
|
Преимушества: |
|
"{benefits}" |
|
Ключевое сообщение: |
|
"{key_message}" |
|
Сообщение: |
|
"{message}" |
|
Если сообщение не содержит фактов, которые отсутствуют в описании предложения и ключевом сообщении, **верни только** JSON {{"decision": false, "explanation": "Противоречий не обнаружено."}}. |
|
Если сообщение содержит факты, которые отсутствуют в описании предложения и ключевом сообщении, **верни только** JSON {{"decision": true, "explanation": "<описание противоречий>"}}. |
|
**Не добавляй никакого дополнительного текста. Отвечай только в формате JSON с закрывающими кавычками и скобками.**''' |
|
|
|
response = generate_message_mistral_check(prompt) |
|
time.sleep(3) |
|
print("Mistral response:", response) |
|
result = parse_json_response(response) |
|
if result is not None: |
|
decision = result.get("decision", False) |
|
explanation = result.get("explanation", "") |
|
print("Explanation:", explanation) |
|
return not decision |
|
else: |
|
return None |
|
|
|
|
|
def check_contains_key_message(message, key_message): |
|
print() |
|
print("Проверка 30: Проверка на наличие ключевого сообщения") |
|
print() |
|
message_clean = cut_message(message) |
|
prompt = f'''Проверь, содержит ли следующее сообщение ключевое сообщение. |
|
Сообщение: |
|
"{message}" |
|
Ключевой текст: |
|
"{key_message}" |
|
Если сообщение **содержит всю** информацию из ключевого текста, **верни только** JSON {{"decision": false, "explanation": "Ключевое текст присутствует."}}. |
|
Если сообщение **не содержит всю** информацию из ключевого текста, **верни только** JSON {{"decision": true, "explanation": "Ключевое текст отсутствует."}}. |
|
**Не добавляй никакого дополнительного текста. Отвечай только в формате JSON с закрывающими кавычками и скобками.**''' |
|
|
|
response = generate_message_mistral_check(prompt) |
|
time.sleep(3) |
|
print("Mistral response:", response) |
|
result = parse_json_response(response) |
|
if result is not None: |
|
decision = result.get("decision", False) |
|
explanation = result.get("explanation", "") |
|
print("Explanation:", explanation) |
|
return not decision |
|
else: |
|
return None |
|
|
|
|
|
def check_product_name_consistency(message, product_name): |
|
print() |
|
print("Проверка 31: Проверка на точное совпадение названий продуктов") |
|
print() |
|
|
|
message_clean = cut_message(message) |
|
|
|
prompt = f'''Проверь, совпадает ли название продукта в сообщении с эталонным названием продукта с учётом возможных изменений в падежах. |
|
Не допускай никаких других изменений, таких как перестановка слов, добавление или удаление кавычек, изменение порядка слов, пропуск или добавление слов. Допускаются только изменения, связанные с падежами. Название должно совпадать с учётом падежных форм. |
|
Эталонное название продукта: |
|
"{product_name}" |
|
Сообщение: |
|
"{message_clean}" |
|
Если название продукта в сообщении **совпадает** с эталонным названием продукта с учётом падежных изменений, **верни только** JSON {{"decision": false, "explanation": "Название продукта совпадает с описанием с учётом падежа."}}. |
|
Если название продукта **не** совпадает, **верни только** JSON {{"decision": true, "explanation": "<описание несоответствия>"}}. |
|
**Не добавляй никакого дополнительного текста. Ответ должен быть только в формате JSON с закрывающими кавычками и скобками.**''' |
|
|
|
response = generate_message_mistral_check(prompt) |
|
time.sleep(3) |
|
print("Mistral response:", response) |
|
result = parse_json_response(response) |
|
if result is not None: |
|
decision = result.get("decision", False) |
|
explanation = result.get("explanation", "") |
|
print("Explanation:", explanation) |
|
return not decision |
|
else: |
|
return None |
|
|
|
|
|
|
|
|
|
def safe_check(func, *args): |
|
try: |
|
return func(*args) |
|
except Exception as e: |
|
|
|
print(f"Ошибка в {func.__name__}: {e}") |
|
return None |
|
|
|
def perform_checks(message, description, key_message, product_name, benefits): |
|
checks = {} |
|
|
|
|
|
morphological_checks = [ |
|
("forbidden_words", check_forbidden_words), |
|
("client_addressing", check_no_greeting), |
|
("promises", check_no_promises), |
|
|
|
("participles", check_no_participles), |
|
|
|
("superlative_adjectives", check_no_superlative_adjectives), |
|
("passive_voice", check_no_passive_voice), |
|
("written_out_ordinals", check_no_written_out_ordinals), |
|
("subordinate_clauses_chain", check_no_subordinate_clauses_chain), |
|
("repeating_conjunctions", check_no_repeating_conjunctions), |
|
("introductory_phrases", check_no_introductory_phrases), |
|
("amplifiers", check_no_amplifiers), |
|
("time_parasites", check_no_time_parasites), |
|
("multiple_nouns", check_no_multiple_nouns), |
|
|
|
("compound_sentences", check_no_compound_sentences), |
|
("dates_written_out", check_no_dates_written_out), |
|
|
|
] |
|
|
|
|
|
llm_checks_group1 = [ |
|
("no_contradictions", check_no_contradictions), |
|
("abstract_claims", check_abstract_claims), |
|
("cliches_and_bureaucratese", check_cliches_and_bureaucratese), |
|
("clickbait_phrases", check_clickbait_phrases), |
|
("contains_key_message", check_contains_key_message), |
|
("synonymous_members", check_synonymous_members), |
|
("product_name_consistency", check_product_name_consistency) |
|
] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for check_name, check_func in morphological_checks: |
|
result = safe_check(check_func, message) |
|
checks[check_name] = result |
|
if result is False: |
|
return checks |
|
|
|
|
|
for check_name, check_func in llm_checks_group1: |
|
if check_name == "no_contradictions": |
|
result = safe_check(check_func, message, description, benefits, key_message) |
|
elif check_name == "contains_key_message": |
|
result = safe_check(check_func, message, key_message) |
|
elif check_name == "product_name_consistency": |
|
result = safe_check(check_func, message, product_name) |
|
elif check_name == "clickbait_phrases": |
|
result = safe_check(check_func, message, description, benefits, key_message) |
|
elif check_name == "abstract_claims": |
|
result = safe_check(check_func, message, description, benefits, key_message) |
|
elif check_name == "cliches_and_bureaucratese": |
|
result = safe_check(check_func, message, description, benefits, key_message) |
|
else: |
|
result = safe_check(check_func, message) |
|
checks[check_name] = result |
|
if result is False: |
|
return checks |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return checks |
|
|
|
|
|
def format_checks(checks): |
|
translation = { |
|
"forbidden_words": "Запрещенные слова", |
|
"client_addressing": "Обращение к клиенту", |
|
"promises": "Обещания и гарантии", |
|
|
|
"participles": "Причастия", |
|
|
|
"superlative_adjectives": "Превосходная степень", |
|
"passive_voice": "Страдательный залог", |
|
"written_out_ordinals": "Порядковые числительные", |
|
"subordinate_clauses_chain": "Цепочки с придаточными предложениями", |
|
"repeating_conjunctions": "Разделительные повторяющиеся союзы", |
|
"introductory_phrases": "Вводные конструкции", |
|
"amplifiers": "Усилители", |
|
"time_parasites": "Паразиты времени", |
|
"multiple_nouns": "Несколько существительных подряд", |
|
|
|
"compound_sentences": "Сложноподчиненные предложения", |
|
"dates_written_out": "Даты прописью", |
|
|
|
|
|
|
|
"synonymous_members": "Близкие по смыслу однородные члены предложения", |
|
"clickbait_phrases": "Кликбейтные фразы", |
|
"abstract_claims": "Абстрактные заявления без доказательств", |
|
|
|
|
|
"cliches_and_bureaucratese": "Речевые клише, рекламные штампы, канцеляризмы", |
|
"no_contradictions": "Отсутствие противоречий с описанием предложения", |
|
"contains_key_message": "Наличие ключевого сообщения", |
|
"product_name_consistency": "Совпадение наименования продукта" |
|
} |
|
formatted_results = [] |
|
for rule, result in checks.items(): |
|
if result is True: |
|
symbol = '✔️' |
|
elif result is False: |
|
symbol = '❌' |
|
else: |
|
symbol = '❓' |
|
formatted_results.append(f"{translation[rule]}: {symbol}") |
|
return " \n".join(formatted_results) |
|
|
|
|
|
|
|
with gr.Blocks() as demo: |
|
|
|
|
|
with gr.Tabs() as tabs: |
|
|
|
|
|
with gr.TabItem("Исходные данные", id=0): |
|
with gr.Row(): |
|
with gr.Column(): |
|
desc = gr.Textbox( |
|
label="Описание предложения (предзаполненный пример можно поменять на свой)", |
|
lines=4, |
|
value=( |
|
"Кредитная бизнес-карта для предпринимателей. " |
|
"Лимит — до 1 000 000 рублей. " |
|
"Стандартный льготный период без начисления процентов за покупки — 120 дней. " |
|
"Оформление онлайн — по заявке в интернет-банке СберБизнес." |
|
) |
|
) |
|
product_name = gr.Textbox( |
|
label="Неизменяемая часть наименования продукта (пример можно поменять на свой)", |
|
lines=1, |
|
value=( |
|
"кредитная бизнес-карта для предпринимателей" |
|
), |
|
interactive=True |
|
) |
|
benefits = gr.Textbox( |
|
label="Преимущества (предзаполненный пример можно поменять на свой)", |
|
lines=5, |
|
value=( |
|
"Повышенный льготный период при покупках у партнёров банка — до 365 дней.\n" |
|
"Бесплатное годовое обслуживание кредитной бизнес-карты.\n" |
|
"Возможность устанавливать лимиты по кредитной бизнес-карте онлайн.\n" |
|
"Кредитная бизнес-карта участвует в акциях банка и партнёров." |
|
) |
|
) |
|
|
|
key_message = gr.Textbox( |
|
label="Ключевое сообщение (предзаполненный пример можно поменять на свой)", |
|
lines=2, |
|
value="Повышенный льготный период при покупках у партнёров банка — до 365 дней и бесплатное годовое обслуживание кредитной бизнес-карты." |
|
) |
|
|
|
with gr.Column(): |
|
gender = gr.Dropdown(label="Пол", choices=[None] + list(features.get('Пол', {}).keys())) |
|
generation = gr.Dropdown(label="Поколение", choices=[None] + list(features.get('Поколение', {}).keys())) |
|
psychotype = gr.Dropdown(label="Психотип", choices=[None] + list(features.get('Психотип', {}).keys())) |
|
business_stage = gr.Dropdown(label="Стадия бизнеса", choices=[None] + list(features.get('Стадия бизнеса', {}).keys())) |
|
industry = gr.Dropdown(label="Отрасль", choices=[None] + list(features.get('Отрасль', {}).keys())) |
|
opf = gr.Dropdown(label="ОПФ", choices=[None] + list(features.get('ОПФ', {}).keys())) |
|
btn_to_prompts = gr.Button("Создать") |
|
|
|
|
|
with gr.TabItem("Ассистент", id=1): |
|
selected_approaches_text = gr.Textbox( |
|
label="Выбранные подходы согласно рекомендациям Нейролаборатории", |
|
interactive=False, |
|
lines=1 |
|
) |
|
with gr.Row(): |
|
with gr.Column(): |
|
non_personalized_prompt = gr.Textbox( |
|
label="Задание для копирайтера", |
|
lines=20, |
|
interactive=False) |
|
with gr.Column(): |
|
personalized_prompt = gr.Textbox( |
|
label="Задание для редактора", |
|
lines=20 |
|
) |
|
|
|
|
|
|
|
with gr.TabItem("Сообщения", id=2): |
|
with gr.Row(): |
|
gr.Markdown("### Копирайтер") |
|
gr.Markdown("### Редактор") |
|
|
|
with gr.Row(): |
|
non_personalized_messages = gr.Textbox(label="Стандартные сообщения", lines=18, interactive=False) |
|
personalized_messages = gr.Textbox(label="Персонализированные сообщения", lines=18, interactive=False) |
|
rank_button = gr.Button("Ранжировать") |
|
|
|
|
|
btn_to_prompts.click( |
|
fn=change_tab, |
|
inputs=[gr.Number(value=1, visible=False)], |
|
outputs=tabs |
|
).then( |
|
fn=generate_all_messages, |
|
inputs=[desc, benefits, key_message, gender, generation, psychotype, business_stage, industry, opf, product_name], |
|
outputs=[ |
|
selected_approaches_text, |
|
non_personalized_prompt, personalized_prompt, |
|
non_personalized_messages, personalized_messages |
|
] |
|
) |
|
|
|
rank_button.click( |
|
fn=rank_messages, |
|
inputs=[non_personalized_messages, personalized_messages], |
|
outputs=[non_personalized_messages, personalized_messages] |
|
) |
|
|
|
demo.launch() |
|
|