AnatoliiG commited on
Commit
f314e13
·
1 Parent(s): b41467e

fix web search bug

Browse files
Files changed (2) hide show
  1. src/ui/callbacks.py +33 -49
  2. src/ui/components.py +0 -2
src/ui/callbacks.py CHANGED
@@ -1,8 +1,8 @@
1
  import os
2
 
3
  import gradio as gr
4
- import PyPDF2 # Для нормального чтения PDF
5
- from duckduckgo_search import DDGS # Официальная библиотека поиска
6
 
7
  from src.core.engine import engine
8
  from src.utils.helpers import get_clean_text
@@ -29,15 +29,9 @@ def set_interactive(is_interactive):
29
 
30
 
31
  def web_search(query: str, max_results: int = 3) -> list:
32
- """
33
- Чистый и надежный поиск через API duckduckgo-search.
34
- Никаких костылей с регулярками и парсингом HTML.
35
- """
36
  try:
37
  with DDGS() as ddgs:
38
- # DDGS возвращает генератор, конвертируем в список
39
  results = list(ddgs.text(query, max_results=max_results))
40
-
41
  formatted_results = []
42
  for r in results:
43
  formatted_results.append(
@@ -59,16 +53,12 @@ def bot_response(
59
  messages = [{"role": "system", "content": system_prompt}]
60
  file_info, file_content = "", ""
61
 
62
- # ==========================================
63
- # 1. УМНОЕ ЧТЕНИЕ ФАЙЛОВ (Исправлен баг с PDF)
64
- # ==========================================
65
  if uploaded_file and os.path.exists(uploaded_file):
66
  filename = os.path.basename(uploaded_file)
67
  size_kb = os.path.getsize(uploaded_file) / 1024
68
  file_info = f"📎 **Файл прочитан:** `{filename}` ({size_kb:.1f} KB)"
69
 
70
  try:
71
- # Если это PDF - используем PyPDF2
72
  if filename.lower().endswith(".pdf"):
73
  with open(uploaded_file, "rb") as f:
74
  pdf_reader = PyPDF2.PdfReader(f)
@@ -76,7 +66,6 @@ def bot_response(
76
  page.extract_text() or "" for page in pdf_reader.pages
77
  ]
78
  file_content = "\n".join(text_parts)[:40000]
79
- # Иначе читаем как обычный текст
80
  else:
81
  with open(uploaded_file, "r", encoding="utf-8", errors="ignore") as f:
82
  file_content = f.read(40000)
@@ -86,9 +75,6 @@ def bot_response(
86
  except Exception as e:
87
  file_info = f"❌ **Ошибка файла:** `{filename}` ({e})"
88
 
89
- # ==========================================
90
- # 2. ФОРМИРОВАНИЕ ИСТОРИИ
91
- # ==========================================
92
  for msg in history[-7:]:
93
  messages.append(
94
  {"role": msg["role"], "content": get_clean_text(msg["content"])}
@@ -97,17 +83,13 @@ def bot_response(
97
  history.append({"role": "assistant", "content": "⏳ Инициализация..."})
98
  yield history
99
 
100
- # ==========================================
101
- # 3. АГЕНТСКИЙ ВЕБ-ПОИСК (LLM ДУМАЕТ САМА)
102
- # ==========================================
103
  search_info = ""
104
  if use_search:
105
  history[-1]["content"] = (
106
  file_info + "\n" if file_info else ""
107
- ) + "🤔 Агент анализирует необходимость поиска..."
108
  yield history
109
 
110
- # Скрытый "внутренний диалог" LLM: просим ее саму написать запрос
111
  agent_messages = [
112
  {
113
  "role": "system",
@@ -119,37 +101,41 @@ def bot_response(
119
  }
120
  ]
121
 
122
- # Даем агенту последние 3 сообщения, чтобы он понял контекст разговора
123
  for msg in history[-3:]:
124
  if msg["role"] == "user":
125
  agent_messages.append({"role": "user", "content": msg["content"]})
126
 
127
  try:
128
- # Делаем быстрый, скрытый запрос к модели (stream=False)
129
  eval_response = engine.generate(
130
- messages=agent_messages,
131
- max_tokens=20,
132
- temperature=0.1, # Низкая температура, чтобы Агент не фантазировал, а был точным
133
- stream=False,
134
  )
135
 
136
- generated_query = eval_response["choices"][0]["message"]["content"].strip()
137
- generated_query = generated_query.replace('"', "").replace(
138
- "'", ""
139
- ) # Очищаем запрос от случайных кавычек
 
 
 
 
 
 
 
 
 
140
 
141
- # Если Агент решил, что поиск нужен
142
- if generated_query and "NO_SEARCH" not in generated_query.upper():
143
- search_info = f'🌐 Агент ищет: *"{generated_query}"*...'
144
  history[-1]["content"] = (
145
  f"{file_info + '\n' if file_info else ''}{search_info}"
146
  )
147
  yield history
148
 
149
- search_results = web_search(generated_query)
150
 
151
  if search_results:
152
- search_info = f'🌐 Найдено {len(search_results)} результатов по запросу *"{generated_query}"*'
153
 
154
  search_context = (
155
  "СВЕЖИЕ РЕЗУЛЬТАТЫ ПОИСКА ИЗ ИНТЕРНЕТА ДЛЯ ТВОЕГО ОТВЕТА:\n\n"
@@ -157,22 +143,18 @@ def bot_response(
157
  for i, r in enumerate(search_results, 1):
158
  search_context += f"{i}. {r['title']} ({r['url']})\nСниппет: {r['snippet']}\n\n"
159
 
160
- # Незаметно подсовываем результаты в системный промпт основной модели
161
  messages.append({"role": "system", "content": search_context})
162
  else:
163
  search_info = (
164
- f'🌐 Поиск по запросу *"{generated_query}"* не дал результатов.'
165
  )
166
  else:
167
- search_info = "⚡ Агент решил отвечать на основе собственных знаний (поиск не потребовался)."
168
 
169
  except Exception as e:
170
  print(f"Ошибка при логике Агента: {e}")
171
- pass # Если скрытый запрос упал, просто продолжаем стандартную генерацию без поиска
172
 
173
- # ==========================================
174
- # 4. ИНЖЕКТ ФАЙЛА В КОНТЕКСТ
175
- # ==========================================
176
  if file_content:
177
  messages.append(
178
  {
@@ -181,7 +163,6 @@ def bot_response(
181
  }
182
  )
183
 
184
- # Формируем красивую плашку статуса перед финальным ответом
185
  status_header = (file_info + "\n" if file_info else "") + (
186
  search_info + "\n" if search_info else ""
187
  )
@@ -191,9 +172,6 @@ def bot_response(
191
  history[-1]["content"] = status_header + "⏳ Генерация ответа..."
192
  yield history
193
 
194
- # ==========================================
195
- # 5. СТРИМИНГ ФИНАЛЬНОГО ОТВЕТА
196
- # ==========================================
197
  try:
198
  stream = engine.generate(
199
  messages=messages,
@@ -206,8 +184,14 @@ def bot_response(
206
  delta = chunk["choices"][0].get("delta", {})
207
  if delta.get("content"):
208
  partial_text += delta["content"]
209
- # Показываем текст под плашкой статусов
210
- history[-1]["content"] = status_header + partial_text
 
 
 
 
 
 
211
  yield history
212
  except Exception as e:
213
  history[-1]["content"] = status_header + f"\n\n❌ Ошибка: {str(e)}"
 
1
  import os
2
 
3
  import gradio as gr
4
+ import PyPDF2
5
+ from duckduckgo_search import DDGS
6
 
7
  from src.core.engine import engine
8
  from src.utils.helpers import get_clean_text
 
29
 
30
 
31
  def web_search(query: str, max_results: int = 3) -> list:
 
 
 
 
32
  try:
33
  with DDGS() as ddgs:
 
34
  results = list(ddgs.text(query, max_results=max_results))
 
35
  formatted_results = []
36
  for r in results:
37
  formatted_results.append(
 
53
  messages = [{"role": "system", "content": system_prompt}]
54
  file_info, file_content = "", ""
55
 
 
 
 
56
  if uploaded_file and os.path.exists(uploaded_file):
57
  filename = os.path.basename(uploaded_file)
58
  size_kb = os.path.getsize(uploaded_file) / 1024
59
  file_info = f"📎 **Файл прочитан:** `{filename}` ({size_kb:.1f} KB)"
60
 
61
  try:
 
62
  if filename.lower().endswith(".pdf"):
63
  with open(uploaded_file, "rb") as f:
64
  pdf_reader = PyPDF2.PdfReader(f)
 
66
  page.extract_text() or "" for page in pdf_reader.pages
67
  ]
68
  file_content = "\n".join(text_parts)[:40000]
 
69
  else:
70
  with open(uploaded_file, "r", encoding="utf-8", errors="ignore") as f:
71
  file_content = f.read(40000)
 
75
  except Exception as e:
76
  file_info = f"❌ **Ошибка файла:** `{filename}` ({e})"
77
 
 
 
 
78
  for msg in history[-7:]:
79
  messages.append(
80
  {"role": msg["role"], "content": get_clean_text(msg["content"])}
 
83
  history.append({"role": "assistant", "content": "⏳ Инициализация..."})
84
  yield history
85
 
 
 
 
86
  search_info = ""
87
  if use_search:
88
  history[-1]["content"] = (
89
  file_info + "\n" if file_info else ""
90
+ ) + "🤔 Агент анализирует запрос..."
91
  yield history
92
 
 
93
  agent_messages = [
94
  {
95
  "role": "system",
 
101
  }
102
  ]
103
 
 
104
  for msg in history[-3:]:
105
  if msg["role"] == "user":
106
  agent_messages.append({"role": "user", "content": msg["content"]})
107
 
108
  try:
109
+ # Увеличиваем лимит токенов до 512, чтобы Reasoning-модель успела подумать
110
  eval_response = engine.generate(
111
+ messages=agent_messages, max_tokens=512, temperature=0.1, stream=False
 
 
 
112
  )
113
 
114
+ raw_query = eval_response["choices"][0]["message"]["content"].strip()
115
+
116
+ # ФИЛЬТРУЕМ ТЕГИ <think>
117
+ if "</think>" in raw_query:
118
+ # Берем всё, что модель написала ПОСЛЕ окончания размышлений
119
+ clean_query = raw_query.split("</think>")[-1].strip()
120
+ elif raw_query.startswith("<think>"):
121
+ # Если тег не закрылся из-за лимита токенов, отменяем поиск
122
+ clean_query = "NO_SEARCH"
123
+ else:
124
+ clean_query = raw_query.strip()
125
+
126
+ clean_query = clean_query.replace('"', "").replace("'", "")
127
 
128
+ if clean_query and "NO_SEARCH" not in clean_query.upper():
129
+ search_info = f'🌐 Ищем: *"{clean_query}"*...'
 
130
  history[-1]["content"] = (
131
  f"{file_info + '\n' if file_info else ''}{search_info}"
132
  )
133
  yield history
134
 
135
+ search_results = web_search(clean_query)
136
 
137
  if search_results:
138
+ search_info = f'🌐 Найдено {len(search_results)} результатов по запросу *"{clean_query}"*'
139
 
140
  search_context = (
141
  "СВЕЖИЕ РЕЗУЛЬТАТЫ ПОИСКА ИЗ ИНТЕРНЕТА ДЛЯ ТВОЕГО ОТВЕТА:\n\n"
 
143
  for i, r in enumerate(search_results, 1):
144
  search_context += f"{i}. {r['title']} ({r['url']})\nСниппет: {r['snippet']}\n\n"
145
 
 
146
  messages.append({"role": "system", "content": search_context})
147
  else:
148
  search_info = (
149
+ f'🌐 Поиск по запросу *"{clean_query}"* не дал результатов.'
150
  )
151
  else:
152
+ search_info = "⚡ Агент ответит из своих знаний (поиск не нужен)."
153
 
154
  except Exception as e:
155
  print(f"Ошибка при логике Агента: {e}")
156
+ pass
157
 
 
 
 
158
  if file_content:
159
  messages.append(
160
  {
 
163
  }
164
  )
165
 
 
166
  status_header = (file_info + "\n" if file_info else "") + (
167
  search_info + "\n" if search_info else ""
168
  )
 
172
  history[-1]["content"] = status_header + "⏳ Генерация ответа..."
173
  yield history
174
 
 
 
 
175
  try:
176
  stream = engine.generate(
177
  messages=messages,
 
184
  delta = chunk["choices"][0].get("delta", {})
185
  if delta.get("content"):
186
  partial_text += delta["content"]
187
+
188
+ # КРАСИВЫЙ UI-ФИЛЬТР ДЛЯ ФИНАЛЬНОГО ОТВЕТА
189
+ # Превращаем <think> в красивый блок цитаты
190
+ display_text = partial_text.replace(
191
+ "<think>", "*(🤔 Внутренние размышления модели:)*\n> "
192
+ ).replace("</think>", "\n\n")
193
+
194
+ history[-1]["content"] = status_header + display_text
195
  yield history
196
  except Exception as e:
197
  history[-1]["content"] = status_header + f"\n\n❌ Ошибка: {str(e)}"
src/ui/components.py CHANGED
@@ -1,5 +1,3 @@
1
- # src/ui/components.py (или где у вас находится функция create_ui)
2
-
3
  import gradio as gr
4
 
5
  from src.core.config import settings
 
 
 
1
  import gradio as gr
2
 
3
  from src.core.config import settings