File size: 7,633 Bytes
5759868 b6d731b 8199364 5759868 8109cc7 d061e47 5759868 b6d731b 5759868 b6d731b 5759868 b6d731b e9c73b1 b6d731b be458aa b6d731b d6c895c b6d731b e9c73b1 be458aa 5759868 be458aa 5759868 e9c73b1 5759868 0c818cc 5759868 9c65c0f 5759868 0ffefbc 5759868 cbd3887 0aa6d2c cbd3887 0ffefbc cbd3887 0ffefbc cbd3887 0ffefbc cbd3887 0ffefbc cbd3887 0ffefbc cbd3887 5759868 3592961 0aa6d2c 3592961 5759868 0aa6d2c 5759868 be458aa 0ffefbc e9c73b1 5759868 be458aa 3f5fea2 3592961 5759868 7a668f2 cbd3887 be458aa 3f5fea2 3592961 5759868 3f5fea2 5759868 2a48bd3 8199364 2a48bd3 | 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 | import gradio as gr
import numpy as np
from llm import get_llm_answer
from retrieval import Retrieval
from parse_documents import _parse_single_year
from vocabulary.parse_vocabulary import VOCABULARY_MANAGER
class Perform:
def __init__(self):
self.retrieval = Retrieval()
lengthh = len(self.retrieval.paragraphs_df)
self.scores = None
self.sorted_idx = None
self.years_mask = np.ones(lengthh, dtype=bool)
def get_years_range_mask(self, year_from, year_to):
try:
year_from = _parse_single_year(year_from)
year_to = _parse_single_year(year_to)
if year_from > year_to:
year_from, year_to = year_to, year_from
except (ValueError, TypeError):
raise ValueError(f"Некорректный диапазон лет: {year_from} - {year_to}")
self.years_mask = (
(self.retrieval.paragraphs_df["end_year"] >= year_from) &
(self.retrieval.paragraphs_df["start_year"] <= year_to)
).values
def perform_search(self, query, top_k, year_from, year_to):
self.get_years_range_mask(year_from, year_to)
# если есть query → считаем scores
if query:
self.scores = self.retrieval.search(query)
self.sorted_idx = np.argsort(self.scores)[::-1]
# если нет query и scores нет → используем только фильтр
if self.scores is None:
filtered = np.where(self.years_mask)[0]
if len(filtered) <= top_k:
return None, None, filtered, "Показаны все записи по фильтру лет"
return None, None, filtered[-top_k:], "Показаны записи по фильтру лет"
# применяем mask к отсортированным индексам
filtered_sorted = self.sorted_idx[self.years_mask[self.sorted_idx]]
if len(filtered_sorted) == 0:
return self.scores, None, [], "⚠️ Нет результатов в выбранном диапазоне лет"
top_k_indices = filtered_sorted[:top_k]
return self.scores, None, top_k_indices, f"Найдено {len(filtered_sorted)} результатов"
def format_retrieval_results(self, indices):
if len(indices) == 0:
return "Нет результатов"
texts = self.retrieval.paragraphs_df["text"].iloc[indices]
return "\n\n".join(texts)
perform = Perform()
def ui_search(query, top_k, year_from, year_to):
return perform.perform_search(query, top_k, year_from, year_to)
def ui_format_results(indices, top_k):
if indices is None:
return "Нет результатов"
indices = indices[:top_k]
return perform.format_retrieval_results(indices)
def ask_llm(query, filtered_indices_state):
"""Этап 2: Отправка отфильтрованных чанков в LLM с потоковой выдачей"""
if not query:
yield "Введите вопрос"
return
context = perform.format_retrieval_results(filtered_indices_state)
if not context or context == "Нет валидных чанков":
yield "Нет валидных чанков для отправки"
return
# Формируем промпт и отправляем в LLM
prompt = VOCABULARY_MANAGER.wrap_prompt(context, query)
# Потоковая выдача ответа
full_answer = ""
for chunk in get_llm_answer(prompt):
full_answer += chunk
yield full_answer
# Создаем интерфейс Gradio
with gr.Blocks(title="RAG Application", theme=gr.themes.Soft()) as iface:
gr.Markdown("## Справочник по общественного истории транспорта Рязани")
# Строка 1: поиск и фильтр по датам
with gr.Row():
search_query_input = gr.Textbox(
label="Запрос для поиска",
placeholder="Введите запрос для поиска",
lines=1,
scale=3
)
year_from_input = gr.Textbox(
label="От года",
value='1918',
placeholder="Год",
lines=1,
scale=1
)
year_to_input = gr.Textbox(
label="До года",
value='2026',
placeholder="Год",
lines=1,
scale=1
)
# Строка 2: top-k параметр
with gr.Row():
top_k_slider = gr.Slider(
minimum=1,
maximum=100,
value=30,
step=1,
label="Top-k для поиска",
scale=2
)
# Строка 3: кнопка поиска и статус
with gr.Row():
search_btn = gr.Button(
"🔍 Выполнить поиск",
variant="primary",
scale=1
)
search_status = gr.Textbox(
label="Статус",
interactive=False,
scale=3
)
with gr.Row():
with gr.Column(scale=2):
# Большое текстовое поле для результатов retrieval
retrieval_results = gr.Textbox(
label="Результаты поиска",
placeholder="Результаты поиска появятся здесь",
lines=15,
max_lines=30,
interactive=False
)
with gr.Row():
with gr.Column(scale=1):
# Ввод вопроса для LLM
llm_query_input = gr.Textbox(
label="Ваш вопрос по результатам поиска",
placeholder="Введите вопрос по историческим документам...",
lines=2
)
with gr.Column(scale=2):
# Кнопка отправки в LLM
llm_btn = gr.Button("Спросить LLM", variant="secondary")
with gr.Column(scale=3):
# Ответ LLM с потоковой выдачей
llm_answer = gr.Markdown(
label="Ответ LLM (появляется постепенно)"
)
# Состояния для хранения данных между вызовами
all_scores_state = gr.State()
all_chunk_ids_state = gr.State()
top_k_indices_state = gr.State()
# Обработчик поиска
search_btn.click(
fn=ui_search,
inputs=[search_query_input, top_k_slider, year_from_input, year_to_input],
outputs=[all_scores_state, all_chunk_ids_state, top_k_indices_state, search_status]
).then(
fn=ui_format_results,
inputs=[top_k_indices_state, top_k_slider],
outputs=[retrieval_results]
)
# Обработчик изменения слайдера top_k
top_k_slider.change(
fn=ui_format_results,
inputs=[top_k_indices_state, top_k_slider],
outputs=[retrieval_results]
)
# Отправка в LLM с потоковой выдачей
llm_btn.click(
fn=ask_llm,
inputs=[llm_query_input, top_k_indices_state],
outputs=[llm_answer]
)
if __name__ == "__main__":
iface.launch(ssr_mode=False,
share=True
) |