File size: 13,213 Bytes
ff4f22e
 
 
 
 
f5af3a0
bfb5463
 
b4ab96e
 
 
 
 
 
 
 
 
bfb5463
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f62e30
 
 
f5af3a0
7f62e30
 
 
 
 
 
 
f5af3a0
7f62e30
 
7d0d1db
7f62e30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5af3a0
7f62e30
 
 
ac7ad5f
 
 
7f62e30
ac7ad5f
7f62e30
ac7ad5f
7f62e30
b0103da
ac7ad5f
7f62e30
 
 
 
 
 
 
b0103da
7f62e30
 
 
 
 
e232a37
7d0d1db
f5af3a0
 
7f62e30
f5af3a0
 
7f62e30
 
 
f5af3a0
7f62e30
 
 
 
 
 
 
 
 
ff4f22e
 
 
63900d2
10f33fc
 
 
 
 
 
 
 
 
ff4f22e
10f33fc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ff4f22e
10f33fc
ff4f22e
 
 
 
 
 
 
 
 
 
58f0214
ff4f22e
665ba43
5e5fc87
f5af3a0
 
d37581f
f5af3a0
 
 
665ba43
ff4f22e
b4ab96e
58f0214
ff4f22e
8b454e9
 
ff4f22e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# 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
    )