AnatoliiG commited on
Commit
1080c74
·
1 Parent(s): c6c602e

prompt ban for web

Browse files
Files changed (2) hide show
  1. src/ui/callbacks.py +35 -26
  2. src/utils/helpers.py +37 -11
src/ui/callbacks.py CHANGED
@@ -5,7 +5,7 @@ import gradio as gr
5
 
6
  from src.core.engine import engine
7
  from src.utils.helpers import (
8
- extract_json_from_text,
9
  extract_text_from_file,
10
  get_clean_text,
11
  web_search,
@@ -35,7 +35,13 @@ def set_interactive(is_interactive):
35
  def bot_response(
36
  history, system_prompt, temperature, max_tokens, use_search, uploaded_file
37
  ):
38
- messages = [{"role": "system", "content": system_prompt}]
 
 
 
 
 
 
39
  file_info, file_content = "", ""
40
 
41
  # --- 1. ОБРАБОТКА ФАЙЛА ---
@@ -54,12 +60,22 @@ def bot_response(
54
  for msg in history[-7:]:
55
  content = str(msg["content"])
56
  if msg["role"] == "assistant":
57
- # Удаляем плашки статусов
58
  if "---\n\n" in content:
59
  content = content.split("---\n\n", 1)[-1]
60
- # Тотально вырезаем теги размышлений и HTML спойлеры из контекста!
 
61
  content = re.sub(r"<details.*?>.*?</details>", "", content, flags=re.DOTALL)
62
  content = re.sub(r"<think>.*?</think>", "", content, flags=re.DOTALL)
 
 
 
 
 
 
 
 
 
63
  content = content.strip()
64
  if not content:
65
  content = "*(ответ скрыт)*"
@@ -69,7 +85,7 @@ def bot_response(
69
  history.append({"role": "assistant", "content": "⏳ Инициализация..."})
70
  yield history
71
 
72
- # --- 3. АГЕНТ ПОИСКА (ПЕРЕПИСАНО НА JSON) ---
73
  search_info = ""
74
  if use_search:
75
  history[-1]["content"] = (
@@ -77,22 +93,19 @@ def bot_response(
77
  ) + "🤔 Агент анализирует необходимость поиска..."
78
  yield history
79
 
80
- # Просим модель отвечать ТОЛЬКО в JSON, запрещаем теги <think> для этой задачи
81
  agent_messages = [
82
  {
83
  "role": "system",
84
  "content": (
85
- "Ты системный маршрутизатор. Оцени, нужен ли поиск в интернете для ответа на последний вопрос пользователя. "
86
- "Ответь СТРОГО в формате JSON. Никаких тегов <think>, никаких рассуждений. Только JSON.\n"
87
- "Формат:\n"
88
- '{"search_needed": true, "query": "твой поисковый запрос"}\n'
89
- "или\n"
90
- '{"search_needed": false, "query": null}'
91
  ),
92
  }
93
  ]
94
 
95
- # Добавляем последние сообщения пользователя для контекста
96
  for msg in history[-3:]:
97
  if msg["role"] == "user":
98
  agent_messages.append({"role": "user", "content": msg["content"]})
@@ -101,17 +114,14 @@ def bot_response(
101
  eval_response = engine.generate(
102
  messages=agent_messages,
103
  max_tokens=150,
104
- temperature=0.0, # Обязательно 0 для роутера, чтобы не фантазировал!
105
  stream=False,
106
  )
107
 
108
  raw_response = eval_response["choices"][0]["message"]["content"]
109
- parsed_json = extract_json_from_text(raw_response)
110
-
111
- search_needed = parsed_json.get("search_needed", False)
112
- clean_query = parsed_json.get("query", None)
113
 
114
- if search_needed and clean_query:
115
  search_info = f'🌐 Ищем: *"{clean_query}"*...'
116
  history[-1]["content"] = (
117
  f"{file_info + '\n' if file_info else ''}{search_info}"
@@ -122,14 +132,13 @@ def bot_response(
122
 
123
  if search_results:
124
  search_info = f'🌐 Найдено {len(search_results)} результатов по запросу *"{clean_query}"*'
125
- search_context = (
126
- "СВЕЖИЕ РЕЗУЛЬТАТЫ ПОИСКА ИЗ ИНТЕРНЕТА ДЛЯ ТВОЕГО ОТВЕТА:\n\n"
127
- )
128
  for i, r in enumerate(search_results, 1):
129
  search_context += f"{i}. {r['title']} ({r['url']})\nСниппет: {r['snippet']}\n\n"
130
 
131
- # Инжектим результаты в системный промпт
132
- messages[0]["content"] += f"\n\n{search_context}"
 
133
  else:
134
  search_info = (
135
  f'🌐 Поиск по запросу *"{clean_query}"* не дал результатов.'
@@ -138,7 +147,7 @@ def bot_response(
138
  search_info = "⚡ Агент ответит из своих знаний (поиск не нужен)."
139
 
140
  except Exception as e:
141
- print(f"Ошибка при логике Агента: {e}")
142
  search_info = "⚡ Ошибка роутера. Отвечаю из базы знаний."
143
 
144
  if file_content:
@@ -170,7 +179,7 @@ def bot_response(
170
  partial_text += delta["content"]
171
  display_text = partial_text
172
 
173
- # Обработка тегов <think> для UI
174
  if "<think>" in partial_text and "</think>" not in partial_text:
175
  display_text = (
176
  partial_text.replace(
 
5
 
6
  from src.core.engine import engine
7
  from src.utils.helpers import (
8
+ extract_search_query,
9
  extract_text_from_file,
10
  get_clean_text,
11
  web_search,
 
35
  def bot_response(
36
  history, system_prompt, temperature, max_tokens, use_search, uploaded_file
37
  ):
38
+ # ПРЕДОХРАНИТЕЛЬ: Запрещаем основной модели выводить технические команды текстом.
39
+ system_guard = (
40
+ "\n\n[ВАЖНО]: Отвечай пользователю напрямую обычным текстом. "
41
+ "КАТЕГОРИЧЕСКИ ЗАПРЕЩАЕТСЯ выводить технические команды, массивы или JSON-вызовы (например [{'type': 'search'}]). "
42
+ "Если тебе нужно рассуждать перед ответом, оборачивай свои мысли СТРОГО в теги <think>твои мысли</think>."
43
+ )
44
+ messages = [{"role": "system", "content": system_prompt + system_guard}]
45
  file_info, file_content = "", ""
46
 
47
  # --- 1. ОБРАБОТКА ФАЙЛА ---
 
60
  for msg in history[-7:]:
61
  content = str(msg["content"])
62
  if msg["role"] == "assistant":
63
+ # Убираем плашки UI
64
  if "---\n\n" in content:
65
  content = content.split("---\n\n", 1)[-1]
66
+
67
+ # Тотально вырезаем теги размышлений и HTML спойлеры
68
  content = re.sub(r"<details.*?>.*?</details>", "", content, flags=re.DOTALL)
69
  content = re.sub(r"<think>.*?</think>", "", content, flags=re.DOTALL)
70
+
71
+ # ИСПРАВЛЕНИЕ: Вырезаем технические массивы поиска, если они проскочили в прошлых ответах
72
+ content = re.sub(
73
+ r"\[\s*\{.*?['\"]type['\"]\s*:\s*['\"]search['\"].*?\}\s*\]",
74
+ "",
75
+ content,
76
+ flags=re.DOTALL,
77
+ )
78
+
79
  content = content.strip()
80
  if not content:
81
  content = "*(ответ скрыт)*"
 
85
  history.append({"role": "assistant", "content": "⏳ Инициализация..."})
86
  yield history
87
 
88
+ # --- 3. АГЕНТ ПОИСКА (РОУТЕР) ---
89
  search_info = ""
90
  if use_search:
91
  history[-1]["content"] = (
 
93
  ) + "🤔 Агент анализирует необходимость поиска..."
94
  yield history
95
 
96
+ # Промпт Роутера на английском (модели Qwen/Claude понимают его лучше)
97
  agent_messages = [
98
  {
99
  "role": "system",
100
  "content": (
101
+ "You are an internal routing assistant. Decide if a web search is needed to answer the user's latest message.\n"
102
+ "Respond ONLY with a valid JSON. No explanations.\n"
103
+ 'Format if search needed: {"search": true, "query": "exact search terms"}\n'
104
+ 'Format if NOT needed: {"search": false}'
 
 
105
  ),
106
  }
107
  ]
108
 
 
109
  for msg in history[-3:]:
110
  if msg["role"] == "user":
111
  agent_messages.append({"role": "user", "content": msg["content"]})
 
114
  eval_response = engine.generate(
115
  messages=agent_messages,
116
  max_tokens=150,
117
+ temperature=0.0, # Обязательно 0! Нам нужна точность, а не креатив.
118
  stream=False,
119
  )
120
 
121
  raw_response = eval_response["choices"][0]["message"]["content"]
122
+ clean_query = extract_search_query(raw_response)
 
 
 
123
 
124
+ if clean_query:
125
  search_info = f'🌐 Ищем: *"{clean_query}"*...'
126
  history[-1]["content"] = (
127
  f"{file_info + '\n' if file_info else ''}{search_info}"
 
132
 
133
  if search_results:
134
  search_info = f'🌐 Найдено {len(search_results)} результатов по запросу *"{clean_query}"*'
135
+ search_context = "СВЕЖИЕ РЕЗУЛЬТАТЫ ПОИСКА ИЗ ИНТЕРНЕТА:\n\n"
 
 
136
  for i, r in enumerate(search_results, 1):
137
  search_context += f"{i}. {r['title']} ({r['url']})\nСниппет: {r['snippet']}\n\n"
138
 
139
+ messages[0]["content"] += (
140
+ f"\n\n{search_context}\n\n[УВЕДОМЛЕНИЕ]: Поиск выполнен. Сформулируй ответ на основе контекста выше."
141
+ )
142
  else:
143
  search_info = (
144
  f'🌐 Поиск по запросу *"{clean_query}"* не дал результатов.'
 
147
  search_info = "⚡ Агент ответит из своих знаний (поиск не нужен)."
148
 
149
  except Exception as e:
150
+ print(f"Ошибка Роутера: {e}")
151
  search_info = "⚡ Ошибка роутера. Отвечаю из базы знаний."
152
 
153
  if file_content:
 
179
  partial_text += delta["content"]
180
  display_text = partial_text
181
 
182
+ # Умный UI для тегов <think>
183
  if "<think>" in partial_text and "</think>" not in partial_text:
184
  display_text = (
185
  partial_text.replace(
src/utils/helpers.py CHANGED
@@ -67,17 +67,43 @@ def web_search(query: str, max_results: int = 3) -> list:
67
  return []
68
 
69
 
70
- def extract_json_from_text(text: str) -> dict:
71
- """Безопасно извлекает JSON из ответа модели (даже если есть маркдаун)."""
 
 
 
 
72
  try:
73
- # Ищем блок ```json ... ```
74
- json_match = re.search(r"```json\s*(.*?)\s*```", text, re.DOTALL)
75
- if json_match:
76
- return json.loads(json_match.group(1))
77
- # Иначе пробуем найти просто фигурные скобки
78
- json_match = re.search(r"\{.*\}", text, re.DOTALL)
79
  if json_match:
80
- return json.loads(json_match.group(0))
81
- return {}
 
 
 
 
 
 
82
  except Exception:
83
- return {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  return []
68
 
69
 
70
+ def extract_search_query(text: str):
71
+ """Извлекает поисковый запрос из JSON, Python-массивов или обычного текста."""
72
+ if not text:
73
+ return None
74
+
75
+ # 1. Ищем строгий JSON {"search": true, "query": "..."}
76
  try:
77
+ json_match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", text, re.DOTALL)
 
 
 
 
 
78
  if json_match:
79
+ data = json.loads(json_match.group(1))
80
+ return data.get("query") if data.get("search") else None
81
+
82
+ bare_json = re.search(r"\{.*?\}", text, re.DOTALL)
83
+ if bare_json:
84
+ data = json.loads(bare_json.group(0))
85
+ if "query" in data and ("search" in data or "search_needed" in data):
86
+ return data.get("query")
87
  except Exception:
88
+ pass
89
+
90
+ # 2. Ищем специфичный формат массива из логов модели (как на скриншоте)
91
+ # Пример: [{'text': '🔍 Поиск по запросу: "attacks on..."', 'type': 'search'}]
92
+ if "'type': 'search'" in text or '"type": "search"' in text:
93
+ match = re.search(r"['\"]text['\"]\s*:\s*['\"](.*?)['\"]", text)
94
+ if match:
95
+ raw_q = match.group(1)
96
+ # Вытаскиваем то, что внутри двойных кавычек (сам запрос)
97
+ sub_match = re.search(r'"([^"]+)"', raw_q)
98
+ clean_q = sub_match.group(1) if sub_match else raw_q
99
+ # Очищаем от мусорных слов, если они остались
100
+ return re.sub(r"^(?:🔍\s*)?Поиск по запросу:\s*", "", clean_q).strip()
101
+
102
+ # 3. Фолбэк: просто ищем текст в кавычках после слова query/запрос
103
+ match = re.search(
104
+ r'(?:запрос|query|ищем)\s*:?\s*["\']([^"\']+)["\']', text, re.IGNORECASE
105
+ )
106
+ if match:
107
+ return match.group(1)
108
+
109
+ return None