import re # Добавляем импорт 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 # Авторизация в GigaChat Pro gc_key = os.getenv('GC_KEY') chat_pro = GigaChat(credentials=gc_key, model='GigaChat-Pro', max_tokens=68, temperature=1, verify_ssl_certs=False) # Загрузка данных из Excel-файла 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() 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 create_gauge(value): fig = go.Figure(go.Indicator( mode="gauge+number", value=value, gauge={ 'axis': {'range': [0, 100]}, 'bar': {'color': "black"}, # Цвет стрелки 'steps': [ {'range': [0, 40], 'color': "#55efc4"}, # Мягкий зеленый {'range': [40, 70], 'color': "#ffeaa7"}, # Желтый {'range': [70, 100], 'color': "#ff7675"} # Мягкий красный ], 'threshold': { 'line': {'color': "black", 'width': 4}, 'thickness': 0.75, 'value': value } }, number={'font': {'size': 48}} # Размер шрифта числа )) fig.update_layout(paper_bgcolor="#f8f9fa", font={'color': "#2d3436", 'family': "Arial"}, width=250, height=150) return fig # Функция для генерации случайных значений спидометров def generate_random_gauges(): return create_gauge(random.randint(60, 90)), create_gauge(random.randint(60, 90)), create_gauge(random.randint(60, 90)) # Функция для смены вкладки def change_tab(id): return gr.Tabs(selected=id) # Вспомогательная функция для добавления префиксов и суффиксов def add_prefix_suffix(prompt, prefix, suffix): return f"{prefix}\n{prompt}\n{suffix}" # Функция для обрезки сообщения до последнего знака препинания 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 # Функция для генерации сообщения с GigaChat Pro def generate_message_gigachat_pro(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}" # Функция для повторной генерации сообщения, пока оно не станет короче 250 знаков def generate_message_gigachat_pro_with_retry(prompt): for _ in range(10): message = generate_message_gigachat_pro(prompt) if len(message) <= 250: return message return message # Функция для создания задания для копирайтера def generate_standard_prompt(description, advantages, key_message, *selected_values): prompt = ( f"Сгенерируй смс-сообщение для клиента.\n" f"Описание предложения: {description}\n" f"Преимущества: {advantages}\n" "В тексте смс запрещено использование:\n" "- Запрещенные слова: № один, номер один, № 1, вкусный, дешёвый, продукт, спам, доступный, банкротство, долги, займ, срочно, сейчас, лучший, главный, номер 1, гарантия, успех, лидер;\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 знаков с пробелами.\n" ) if key_message.strip(): prompt += f"Убедись, что в готовом тексте есть следующая ключевая информация: {key_message.strip()}" return prompt # Функция для создания задания для редактора def generate_personalization_prompt(key_message, *selected_values): prompt = "Адаптируй, не превышая длину сообщения в 250 знаков с пробелами, текст с учетом следующих особенностей:\n" gender, generation, psychotype = selected_values[0], selected_values[1], selected_values[2] combined_instruction = "" additional_instructions = "" print(f"Выбранные значения: Пол={gender}, Поколение={generation}, Психотип={psychotype}") # Проверяем, выбраны ли все три параметра: Пол, Поколение, Психотип if gender and generation and psychotype: # Получаем данные с листа "Пол Поколение Психотип" sheet = features.get("Пол Поколение Психотип", {}) # Ищем ключ, соответствующий комбинации "Пол", "Поколение", "Психотип" key = (gender, generation, psychotype) if key in sheet: combined_instruction = sheet[key] print(f"Найдена комбинированная инструкция: {combined_instruction}") else: print(f"Комбинированная инструкция для ключа {key} не найдена.") # Если не найдена комбинированная инструкция, добавляем индивидуальные инструкции if not combined_instruction: print("Добавляем индивидуальные инструкции для Пол, Поколение, Психотип.") for i, feature in enumerate(["Пол", "Поколение", "Психотип"]): if selected_values[i]: try: instruction = features[feature][selected_values[i]] additional_instructions += f"{instruction}\n" print(f"Добавлена инструкция из {feature}: {instruction}") 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" print(f"Добавлена инструкция из {feature}: {instruction}") except KeyError: return f"Ошибка: выбранное значение {selected_values[i]} не найдено в данных." # Формируем итоговый промпт if combined_instruction: prompt += combined_instruction # Добавляем комбинированную инструкцию, если она есть if additional_instructions: prompt += additional_instructions # Добавляем остальные инструкции prompt += "Убедись, что в готовом тексте до 250 знаков с пробелами.\n" prompt += f"Убедись, что в готовом тексте есть следующая ключевая информация: {key_message.strip()}" if "призыва к действию" in prompt and "минимум прямых призывов к действию" in prompt: prompt = re.sub(r"Убедись, что готовый текст начинается с призыва к действию с продуктом.\n", "", prompt) return prompt.strip() # Функция для постепенной генерации всех сообщений через yield def generate_all_messages(desc, benefits, key_message, gender, generation, psychotype, business_stage, industry, opf): # Генерация задания для копирайтера standard_prompt = generate_standard_prompt(desc, benefits, key_message) yield standard_prompt, None, None, None, None, None, None, None # Небольшая пауза для демонстрации постепенной генерации time.sleep(1) # Генерация задания для редактора personalization_prompt = generate_personalization_prompt(key_message, gender, generation, psychotype, business_stage, industry, opf) yield standard_prompt, personalization_prompt, None, None, None, None, None, None # Небольшая пауза для демонстрации постепенной генерации time.sleep(1) # Варианты предложений для начала и конца prefixes = [ "Начни сообщение с призыва к действию с продуктом.", "Начни сообщение с указания на пользу продукта. Используй глагол в побудительном наклонении.", "Начни сообщение с вопроса, который указывает на пользу продукта для клиента." ] suffixes = [ "Убедись, что готовый текст начинается с призыва к действию с продуктом.", "Убедись, что готовый текст начинается с указания на пользу продукта и использования глагола в побудительном наклонении.", "Убедись, что готовый текст начинается с вопроса, который указывает на пользу продукта для клиента." ] non_personalized_messages = [] personalized_messages = [] # Генерация и постепенная подача каждого сообщения for i in range(3): # Генерация неперсонализированного сообщения prompt = add_prefix_suffix(standard_prompt, prefixes[i], suffixes[i]) non_personalized_message = generate_message_gigachat_pro_with_retry(prompt) non_personalized_length = len(non_personalized_message) # Подсчитываем количество знаков non_personalized_display = f"{non_personalized_message}\n\n------\nКоличество знаков: {non_personalized_length}" non_personalized_messages.append(non_personalized_display) # Выводим неперсонализированное сообщение yield ( standard_prompt, personalization_prompt, # Задания для копирайтера и редактора non_personalized_messages[0] if i >= 0 else None, # Первое неперсонализированное сообщение personalized_messages[0] if len(personalized_messages) > 0 else None, # Первое персонализированное сообщение, если оно уже есть non_personalized_messages[1] if i >= 1 else None, # Второе неперсонализированное сообщение personalized_messages[1] if len(personalized_messages) > 1 else None, # Второе персонализированное сообщение, если оно уже есть non_personalized_messages[2] if i >= 2 else None, # Третье неперсонализированное сообщение personalized_messages[2] if len(personalized_messages) > 2 else None # Третье персонализированное сообщение, если оно уже есть ) # Генерация персонализированного сообщения full_personalized_prompt = f"{personalization_prompt}\n\nТекст для адаптации: {non_personalized_message}" personalized_message = generate_message_gigachat_pro_with_retry(full_personalized_prompt) personalized_length = len(personalized_message) # Подсчитываем количество знаков personalized_display = f"{personalized_message}\n\n------\nКоличество знаков: {personalized_length}" personalized_messages.append(personalized_display) # Выводим персонализированное сообщение yield ( standard_prompt, personalization_prompt, # Задания для копирайтера и редактора non_personalized_messages[0] if len(non_personalized_messages) > 0 else None, # Первое неперсонализированное сообщение personalized_messages[0] if len(personalized_messages) > 0 else None, # Первое персонализированное сообщение non_personalized_messages[1] if len(non_personalized_messages) > 1 else None, # Второе неперсонализированное сообщение personalized_messages[1] if len(personalized_messages) > 1 else None, # Второе персонализированное сообщение non_personalized_messages[2] if len(non_personalized_messages) > 2 else None, # Третье неперсонализированное сообщение personalized_messages[2] if len(personalized_messages) > 2 else None # Третье персонализированное сообщение ) # Небольшая пауза между выводом каждого сообщения time.sleep(1) # Интерфейс Gradio with gr.Blocks() as demo: with gr.Tabs() as tabs: # Вкладка 1: Исходные данные with gr.TabItem("Исходные данные", id=0): with gr.Row(): with gr.Column(): desc = gr.Textbox( label="Описание предложения (предзаполненный пример можно поменять на свой)", lines=7, value=( "Необходимо предложить клиенту оформить дебетовую премиальную бизнес-карту Mastercard Preffered. " "Обслуживание карты стоит 700 рублей в месяц, но клиент может пользоваться ей бесплатно. " "Что необходимо сделать, чтобы воспользоваться предложением:\n" "1. Оформить премиальную бизнес-карту в офисе банка или онлайн в интернет-банке СберБизнес.\n" "2. Забрать карту.\n" "3. В течение календарного месяца совершить по ней покупки на сумму от 100 000 рублей.\n" "4. В течение следующего месяца пользоваться ей бесплатно." ) ) benefits = gr.Textbox( label="Преимущества (предзаполненный пример можно поменять на свой)", lines=5, value=( "Предложение по бесплатному обслуживанию — бессрочное.\n" "Оплата покупок без отчётов и платёжных поручений.\n" "Платёжные документы без комиссии.\n" "Лимиты на расходы сотрудников.\n" "Мгновенные переводы на карты любых банков." ) ) key_message = gr.Textbox( label="Ключевое сообщение (предзаполненный пример можно поменять на свой)", lines=3, value="Бесплатное обслуживание при покупках от 100 000 рублей в месяц." ) 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("Создать") # Вкладка 2: Промпты with gr.TabItem("Ассистент", id=1): with gr.Row(): with gr.Column(): non_personalized_prompt = gr.Textbox( label="Задание для копирайтера", lines=25, interactive=False) with gr.Column(): personalized_prompt = gr.Textbox(label="Задание для редактора", lines=25) # Увеличенная высота # Вкладка 3: Сообщения with gr.TabItem("Сообщения", id=2): with gr.Row(): gr.Markdown("### Копирайтер") gr.Markdown("### Редактор") with gr.Row(): non_personalized_1 = gr.Textbox(label="Стандартное сообщение 1", lines=4, interactive=False) personalized_1 = gr.Textbox(label="Персонализированное сообщение 1", lines=4, interactive=False) with gr.Row(): non_personalized_2 = gr.Textbox(label="Стандартное сообщение 2", lines=4, interactive=False) personalized_2 = gr.Textbox(label="Персонализированное сообщение 2", lines=4, interactive=False) with gr.Row(): non_personalized_3 = gr.Textbox(label="Стандартное сообщение 3", lines=4, interactive=False) personalized_3 = gr.Textbox(label="Персонализированное сообщение 3", lines=4, interactive=False) # Четвертый ряд with gr.Row(): btn_check = gr.Button("Проверить", elem_id="check3") btn_check.click(fn=change_tab, inputs=[gr.Number(value=3, visible=False)], outputs=tabs) # Сначала переключаем вкладку, потом запускаем генерацию сообщений btn_to_prompts.click( fn=change_tab, inputs=[gr.Number(value=1, visible=False)], # Переключение на вкладку "Ассистент" (id=1) outputs=tabs # Обновляем вкладку ).then( fn=generate_all_messages, inputs=[desc, benefits, key_message, gender, generation, psychotype, business_stage, industry, opf], # Входные текстовые поля outputs=[ non_personalized_prompt, personalized_prompt, # Поля для задания копирайтера и редактора (на вкладке "Ассистент") non_personalized_1, personalized_1, # Сообщения на вкладке "Сообщения" non_personalized_2, personalized_2, non_personalized_3, personalized_3 ] ) # Вкладка 4: Проверка with gr.TabItem("Проверка", id=3): with gr.Row(): gr.Markdown("### Редактор") gr.Markdown("### Корректор") gr.Markdown("### Аналитик") with gr.Row(): personalized_message_1 = gr.Textbox(label="Персонализированное сообщение 1", lines=5) check_message_1 = gr.Textbox(label="Проверка сообщения 1", lines=5) with gr.Column(): gr.HTML("