TG / app.py
FrostIce's picture
Update app.py
b0103da verified
# 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
)