|
|
|
|
|
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 – произвольное число (может быть и несколько цифр). |
|
|
Возвращает «чистый» текст без ссылок. |
|
|
""" |
|
|
|
|
|
pattern = r'\[\[\d+\]\]\([^\)]*\)' |
|
|
return re.sub(pattern, '', text) |
|
|
|
|
|
def strip_citations(text: str) -> str: |
|
|
""" |
|
|
Удаляет из текста все строки вида: |
|
|
> [0] … (ссылка) |
|
|
[1] … (ссылка) |
|
|
а также любые блоки, начинающиеся с символа '>' (цитаты). |
|
|
Возвращает «чистый» текст без «концовки». |
|
|
""" |
|
|
|
|
|
text = re.sub(r'^\s*>.*(?:\n|$)', '', text, flags=re.MULTILINE) |
|
|
|
|
|
|
|
|
text = re.sub(r'^\s*\[\d+\]\s*.+(?:\n|$)', '', text, flags=re.MULTILINE) |
|
|
|
|
|
|
|
|
text = re.sub(r'\n{2,}', '\n\n', text).strip() |
|
|
return text |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def answer_to_bool(text: str) -> bool: |
|
|
""" |
|
|
Принимает строку, полученную от модели, и возвращает: |
|
|
True → модель сказала, что нужен поиск (TRUE) |
|
|
False → модель сказала, что поиск не нужен (FALSE) или ответ не распознан. |
|
|
""" |
|
|
if not isinstance(text, str): |
|
|
return False |
|
|
|
|
|
|
|
|
clean = text.strip().lower() |
|
|
|
|
|
|
|
|
true_tokens = {"true", "yes", "y", "да", "нужно", "нужен поиск", "нужна информация"} |
|
|
|
|
|
|
|
|
false_tokens = {"false", "no", "n", "нет", "не нужно", "не требуется", "не нужен поиск"} |
|
|
|
|
|
|
|
|
for tok in true_tokens: |
|
|
if tok in clean: |
|
|
return True |
|
|
|
|
|
|
|
|
for tok in false_tokens: |
|
|
if tok in clean: |
|
|
return False |
|
|
|
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def need_search(message: str) -> bool: |
|
|
""" |
|
|
Возвращает True, если согласно модели запрос требует актуального веб‑поиска, |
|
|
иначе – False. |
|
|
""" |
|
|
system_prompt = """Ты — вспомогательная модель, задача которой **только** решить, |
|
|
нужен ли в данный момент поиск в интернете, чтобы дать корректный ответ на запрос пользователя. |
|
|
|
|
|
**Ответ дай КОРОТКО**, используя один из вариантов (без кавычек, без пробелов перед/после): |
|
|
TRUE |
|
|
FALSE |
|
|
|
|
|
Никаких пояснений, советов, дополнительных фраз – только один из указанных вариантов. |
|
|
|
|
|
### Что считается «нужным» поиском (отвечаем TRUE): |
|
|
1. Запрос о текущих ценах, курсах, погоде, новостях, событиях «сегодня», «вчера», «на прошлой неделе», «в реальном времени». |
|
|
Примеры: «Какая сейчас цена биткойна?», «Сколько градусов сегодня в Москве?», «Какие новости о новом iPhone?», «Какая сегодня котировка доллара?» |
|
|
|
|
|
2. Вопрос, где ответ меняется со временем (статистика, результаты выборов, расписание рейсов, статус заказа и т.п.). |
|
|
Примеры: «Какие рейсы из Санкт‑Петербурга в Лондон сегодня?», «Сколько сейчас зрителей у фильма «Дюнкерк» в прокате?», «Есть ли свободные места в отеле на 15‑е августа?», «Поищи в интернете, какие характеристики имеет вода.» |
|
|
|
|
|
### Что считается «не требующим» поиска (отвечаем FALSE): |
|
|
1. Фиксированные исторические/географические факты, биографии, классические произведения. |
|
|
Примеры: «Кто написал «Война и мир»?», «В каком году открыт Тауэр Бридж?», «Где находится озеро Байкал?», «Какая столица Франции?» |
|
|
|
|
|
2. Вымышленные, гипотетические или «домашние» вопросы, где актуальная информация не меняется. |
|
|
Примеры: «Кто убил Христофора Колумба?», «Сколько драконов живут в Шотландии?», «Какой цвет предпочитают гномы в «Властелине колец»?» |
|
|
|
|
|
Если запрос относится к любой из групп «нужный поиск» или прям так и сказано «поищи в интернете», отвечай **TRUE**. Во всех остальных случаях — **FALSE**. |
|
|
""" |
|
|
|
|
|
|
|
|
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, |
|
|
stop=["\n"] |
|
|
) |
|
|
raw_answer = response.choices[0].message.content |
|
|
return answer_to_bool(raw_answer) |
|
|
|
|
|
except Exception as 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 |
|
|
) |