AnatoliiG commited on
Commit ·
7e12323
1
Parent(s): 047143f
forcing web tool
Browse files- src/ui/callbacks.py +13 -9
- src/utils/helpers.py +20 -21
src/ui/callbacks.py
CHANGED
|
@@ -110,18 +110,18 @@ def bot_response(
|
|
| 110 |
) + "🤔 Агент анализирует необходимость поиска..."
|
| 111 |
yield history
|
| 112 |
|
| 113 |
-
# Промпт Роутера на английском (модели Qwen/Claude понимают его лучше)
|
| 114 |
agent_messages = [
|
| 115 |
{
|
| 116 |
"role": "system",
|
| 117 |
"content": (
|
| 118 |
-
"
|
| 119 |
-
"
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
|
|
|
| 125 |
|
| 126 |
for msg in history[-3:]:
|
| 127 |
if msg["role"] == "user":
|
|
@@ -131,11 +131,15 @@ def bot_response(
|
|
| 131 |
eval_response = engine.generate(
|
| 132 |
messages=agent_messages,
|
| 133 |
max_tokens=150,
|
| 134 |
-
temperature=0.0,
|
| 135 |
stream=False,
|
| 136 |
)
|
| 137 |
|
| 138 |
raw_response = eval_response["choices"][0]["message"]["content"]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
clean_query = extract_search_query(raw_response)
|
| 140 |
|
| 141 |
if clean_query:
|
|
|
|
| 110 |
) + "🤔 Агент анализирует необходимость поиска..."
|
| 111 |
yield history
|
| 112 |
|
|
|
|
| 113 |
agent_messages = [
|
| 114 |
{
|
| 115 |
"role": "system",
|
| 116 |
"content": (
|
| 117 |
+
"Ты строгий системный классификатор. Твоя единственная цель - определить, нужен ли интернет-поиск.\n"
|
| 118 |
+
"ПРАВИЛО 1: Если вопрос касается недавних событий, атак, инцидентов, новостей, дат или меняющихся фактов — поиск НУЖЕН ОБЯЗАТЕЛЬНО, даже если ты думаешь, что знаешь ответ.\n"
|
| 119 |
+
"ПРАВИЛО 2: Отвечай СТРОГО и ТОЛЬКО в формате JSON. Запрещено использовать теги <think> и писать текст.\n\n"
|
| 120 |
+
"Формат (если нужен поиск):\n"
|
| 121 |
+
'{"search": true, "query": "оптимальный и короткий поисковый запрос"}\n\n'
|
| 122 |
+
"Формат (если это просто болтовня или математика):\n"
|
| 123 |
+
'{"search": false, "query": null}'
|
| 124 |
+
]
|
| 125 |
|
| 126 |
for msg in history[-3:]:
|
| 127 |
if msg["role"] == "user":
|
|
|
|
| 131 |
eval_response = engine.generate(
|
| 132 |
messages=agent_messages,
|
| 133 |
max_tokens=150,
|
| 134 |
+
temperature=0.0, # 0.0 для максимальной предсказуемости
|
| 135 |
stream=False,
|
| 136 |
)
|
| 137 |
|
| 138 |
raw_response = eval_response["choices"][0]["message"]["content"]
|
| 139 |
+
|
| 140 |
+
# ВАЖНО: Выводим в терминал (консоль), что ответил роутер, чтобы отлаживать
|
| 141 |
+
print(f"\n--- [DEBUG] ОТВЕТ РОУТЕРА ---\n{raw_response}\n---------------------------\n")
|
| 142 |
+
|
| 143 |
clean_query = extract_search_query(raw_response)
|
| 144 |
|
| 145 |
if clean_query:
|
src/utils/helpers.py
CHANGED
|
@@ -68,42 +68,41 @@ def web_search(query: str, max_results: int = 3) -> list:
|
|
| 68 |
|
| 69 |
|
| 70 |
def extract_search_query(text: str):
|
| 71 |
-
"""
|
| 72 |
if not text:
|
| 73 |
return None
|
| 74 |
|
| 75 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
try:
|
| 77 |
-
|
|
|
|
| 78 |
if json_match:
|
| 79 |
data = json.loads(json_match.group(1))
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
|
|
|
|
| 83 |
if bare_json:
|
| 84 |
data = json.loads(bare_json.group(0))
|
| 85 |
-
if
|
| 86 |
return data.get("query")
|
| 87 |
-
|
| 88 |
-
|
|
|
|
| 89 |
|
| 90 |
-
#
|
| 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 |
-
|
| 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
|
|
|
|
| 68 |
|
| 69 |
|
| 70 |
def extract_search_query(text: str):
|
| 71 |
+
"""Надежно извлекает запрос из ответа роутера."""
|
| 72 |
if not text:
|
| 73 |
return None
|
| 74 |
|
| 75 |
+
# Вырезаем теги мыслей, если упрямая модель их всё же сгенерировала
|
| 76 |
+
text_clean = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL).strip()
|
| 77 |
+
if not text_clean:
|
| 78 |
+
text_clean = text # Если ответ состоял только из тегов, ищем внутри них
|
| 79 |
+
|
| 80 |
try:
|
| 81 |
+
# 1. Ищем блок ```json ... ```
|
| 82 |
+
json_match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", text_clean, re.DOTALL)
|
| 83 |
if json_match:
|
| 84 |
data = json.loads(json_match.group(1))
|
| 85 |
+
# Проверяем флаг search (строго True)
|
| 86 |
+
if data.get("search") is True or str(data.get("search")).lower() == "true":
|
| 87 |
+
return data.get("query")
|
| 88 |
+
return None
|
| 89 |
|
| 90 |
+
# 2. Ищем просто фигурные скобки (голый JSON)
|
| 91 |
+
bare_json = re.search(r"\{.*?\}", text_clean, re.DOTALL)
|
| 92 |
if bare_json:
|
| 93 |
data = json.loads(bare_json.group(0))
|
| 94 |
+
if data.get("search") is True or str(data.get("search")).lower() == "true":
|
| 95 |
return data.get("query")
|
| 96 |
+
return None
|
| 97 |
+
except Exception as e:
|
| 98 |
+
print(f"Ошибка парсинга JSON роутера: {e}")
|
| 99 |
|
| 100 |
+
# 3. Резервный вариант на случай старых галлюцинаций
|
|
|
|
| 101 |
if "'type': 'search'" in text or '"type": "search"' in text:
|
| 102 |
match = re.search(r"['\"]text['\"]\s*:\s*['\"](.*?)['\"]", text)
|
| 103 |
if match:
|
| 104 |
raw_q = match.group(1)
|
|
|
|
| 105 |
sub_match = re.search(r'"([^"]+)"', raw_q)
|
| 106 |
+
return (sub_match.group(1) if sub_match else raw_q).strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
return None
|