# app.py import gradio as gr from g4f.client import Client client = Client() # --- Обработчик диалога --- import re from typing import List, Tuple, Any def strip_md_refs(text: str) -> str: """ Удаляет из строки все конструкции вида [[N]](URL), где N – произвольное число (может быть и несколько цифр). Возвращает «чистый» текст без ссылок. """ # Шаблон: [[ любые цифры ]]( любой URL ) pattern = r'\[\[\d+\]\]\([^\)]*\)' return re.sub(pattern, '', text) def strip_citations(text: str) -> str: """ Удаляет из текста все строки вида: > [0] … (ссылка) [1] … (ссылка) а также любые блоки, начинающиеся с символа '>' (цитаты). Возвращает «чистый» текст без «концовки». """ # 1️⃣ Убираем строки, начинающиеся с '>' (включая возможный пробел после него) text = re.sub(r'^\s*>.*(?:\n|$)', '', text, flags=re.MULTILINE) # 2️⃣ Удаляем строки вида '[0] https://…' (любое число в квадратных скобках) text = re.sub(r'^\s*\[\d+\]\s*.+(?:\n|$)', '', text, flags=re.MULTILINE) # 3️⃣ Убираем лишние пустые строки, оставшиеся после вырезания text = re.sub(r'\n{2,}', '\n\n', text).strip() return text # ------------------------------------------------------------- # 1️⃣ Преобразуем любой ответ модели в булево значение # ------------------------------------------------------------- def answer_to_bool(text: str) -> bool: """ Принимает строку, полученную от модели, и возвращает: True → модель сказала, что нужен поиск (TRUE) False → модель сказала, что поиск не нужен (FALSE) или ответ не распознан. """ if not isinstance(text, str): return False # защита от None/чисел # Убираем пробелы, перевод строки, делаем регистр нижним clean = text.strip().lower() # Токены, которые считаются «да, нужен поиск» true_tokens = {"true", "yes", "y", "да", "нужно", "нужен поиск", "нужна информация"} # Токены, которые считаются «нет, поиск не нужен» false_tokens = {"false", "no", "n", "нет", "не нужно", "не требуется", "не нужен поиск"} # Если в строке встречается любой «положительный» токен → True for tok in true_tokens: if tok in clean: return True # Если найден «отрицательный» токен → False for tok in false_tokens: if tok in clean: return False # Если ничего не распознано – считаем, что поиск не нужен return False # ------------------------------------------------------------- # 2️⃣ Основная функция, которая решает, нужен ли веб‑поиск # ------------------------------------------------------------- def need_search(message: str) -> bool: """ Возвращает True, если согласно модели запрос требует актуального веб‑поиска, иначе – False. """ system_prompt = """Ты — вспомогательная модель, задача которой **только** решить, нужен ли в данный момент поиск в интернете, чтобы дать корректный ответ на запрос пользователя. **Ответ дай КОРОТКО**, используя один из вариантов (без кавычек, без пробелов перед/после): TRUE FALSE Никаких пояснений, советов, дополнительных фраз – только один из указанных вариантов. ### Что считается «нужным» поиском (отвечаем TRUE): 1. Запрос о текущих ценах, курсах, погоде, новостях, событиях «сегодня», «вчера», «на прошлой неделе», «в реальном времени». Примеры: «Какая сейчас цена биткойна?», «Сколько градусов сегодня в Москве?», «Какие новости о новом iPhone?», «Какая сегодня котировка доллара?» 2. Вопрос, где ответ меняется со временем (статистика, результаты выборов, расписание рейсов, статус заказа и т.п.). Примеры: «Какие рейсы из Санкт‑Петербурга в Лондон сегодня?», «Сколько сейчас зрителей у фильма «Дюнкерк» в прокате?», «Есть ли свободные места в отеле на 15‑е августа?», «Поищи в интернете, какие характеристики имеет вода.» ### Что считается «не требующим» поиска (отвечаем FALSE): 1. Фиксированные исторические/географические факты, биографии, классические произведения. Примеры: «Кто написал «Война и мир»?», «В каком году открыт Тауэр Бридж?», «Где находится озеро Байкал?», «Какая столица Франции?» 2. Вымышленные, гипотетические или «домашние» вопросы, где актуальная информация не меняется. Примеры: «Кто убил Христофора Колумба?», «Сколько драконов живут в Шотландии?», «Какой цвет предпочитают гномы в «Властелине колец»?» Если запрос относится к любой из групп «нужный поиск» или прям так и сказано «поищи в интернете», отвечай **TRUE**. Во всех остальных случаях — **FALSE**. """ # Формируем список сообщений: system‑prompt + единственный user‑запрос messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": f'нужно ли это искать в интернете: "{message}"'} ] try: response = client.chat.completions.create( model="deepseek-v3", messages=messages, temperature=0.0, # детерминированный ответ max_tokens=5, # достаточно для слова TRUE/FALSE stop=["\n"] # отрезаем лишний перевод строки ) raw_answer = response.choices[0].message.content return answer_to_bool(raw_answer) except Exception as exc: # При любой ошибке считаем, что поиск не нужен, # но можно залогировать исключение для отладки # print(f"[need_search] error: {exc}") return False # --- Обработчик диалога --- def respond(message, history): # Добавляем системное сообщение только при старте истории messages = [{"role": "system", "content": """Вы — дружелюбный помощник AssistantICE 7.0, русскоязычный автоматический ассистент, основанный на модели BD от команды ICE. Ваша задача — помогать пользователям эффективно и понятно. ## Требования: - Поддерживайте дружелюбный и доступный тон во время взаимодействия. - Убедитесь, что объяснения понятны и лаконичны. - Отвечайте на запросы пользователей и предоставляйте соответствующую информацию. ## Структура ответов: 1. **Теплое приветствие пользователю.** 2. **Четкий и краткий ответ на вопрос или запрос пользователя.** 3. **Предложение дополнительной помощи или дополнительных вопросов, чтобы побудить к дальнейшему взаимодействию.** ## Дополнительные характеристики: - Используйте поэтический, лирический тон. - Обращайте внимание на будущее. - Имеете традиционный взгляд, цените прошлое и то, как все делалось всегда. - Придерживайтесь скептического, ставящего под вопрос подхода. - Используйте быстрый и остроумный юмор, когда это уместно. - Рассказывайте все как есть, не приукрашивая ответы. - Будьте болтливым и разговорчивым. - Подстраивайтесь под всех людей и говорите на их сленге. - Говорите как представитель поколения Z. - Используйте ободряющий тон. - Всегда будьте уважительным. - Используйте формальный, профессиональный тон. - Будьте чуткими и понимающими в своих ответах. - Будьте скромным, когда это уместно. Не забудьте адаптировать свои ответы в зависимости от потребностей и контекста пользователя, обеспечивая положительный опыт. Следуйте этим инструкциям, чтобы стать полезным и приятным помощником. """}] for human, assistant in history: messages.append({"role": "user", "content": human}) messages.append({"role": "assistant", "content": assistant}) messages.append({"role": "user", "content": message}) try: responsed = client.chat.completions.create( model="deepseek-v3", messages=messages, web_search=need_search(messages), temperature=0.8, top_p=0.95, max_tokens=581691, presence_penalty=0.2, frequency_penalty=0.1, ) bot_message = strip_citations(f"{strip_md_refs(responsed.choices[0].message.content)}") except Exception as e: bot_message = f"Ошибка: {str(e)}" return history + [[message, bot_message]] # --- Интерфейс --- with gr.Blocks(title="ESP Brain") as demo: gr.Markdown("## For api") chatbot = gr.Chatbot( height=600, ) with gr.Row(): txt = gr.Textbox( placeholder="Напиши сообщение...", show_label=False, scale=8 ) submit_btn = gr.Button("Отправить", scale=2) with gr.Row(): retry_btn = gr.Button("🔄 Повторить") undo_btn = gr.Button("↩️ Отменить") clear_btn = gr.Button("🗑️ Очистить") # Логика txt.submit(fn=respond, inputs=[txt, chatbot], outputs=chatbot) submit_btn.click(fn=respond, inputs=[txt, chatbot], outputs=chatbot) def retry_last(history): if history: last_user_msg = history[-1][0] return history[:-1] + [[last_user_msg, None]] # очищаем ответ return history retry_btn.click(fn=retry_last, inputs=chatbot, outputs=chatbot, queue=False) def undo_last(history): return history[:-1] undo_btn.click(fn=undo_last, inputs=chatbot, outputs=chatbot, queue=False) clear_btn.click(lambda: [], outputs=chatbot, queue=False) # --- Запуск --- if __name__ == "__main__": demo.queue() demo.launch( share=True, ssr_mode=False, debug=True )