Spaces:
Sleeping
Sleeping
File size: 13,558 Bytes
d36fbf5 6e346f9 d36fbf5 3a0fb78 d36fbf5 8428681 6e346f9 3a0fb78 6e346f9 3a0fb78 7fe1e6f 90769c8 7fe1e6f 3a0fb78 90769c8 7da0fa8 7fe1e6f 6e346f9 55ae362 e2c8e01 3a0fb78 8428681 e2c8e01 3a0fb78 e2c8e01 b79dbf3 8428681 b79dbf3 3a0fb78 23bf6ca 8428681 23bf6ca 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 d218894 3a0fb78 1d39f94 d218894 3a0fb78 1d39f94 3a0fb78 |
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 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
import os
import gradio as gr
from llama_index.llms.openrouter import OpenRouter
from llama_index.core.llms import ChatMessage
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core import Settings, StorageContext, load_index_from_storage
import nest_asyncio
nest_asyncio.apply()
# === Глобальная инициализация ===
embed_model = HuggingFaceEmbedding(model_name='intfloat/multilingual-e5-large-instruct')
Settings.embed_model = embed_model
Settings.llm = OpenRouter(
api_key="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",
model="deepseek/deepseek-r1-0528-qwen3-8b:free",
max_tokens=10000,
context_window=20000,
)
system_prompt = """
Ты — эксперт по адаптации к изменениям климата.
У тебя есть база знаний с кейсами и нормативными документами.
Пользователь вводит запрос, связанный с климатическим риском в регионе или отрасли.
Твоя задача — на основе информации из базы знаний предложить 2–3 релевантных адаптационных мероприятия,
которые помогут снизить климатический риск, о котором спрашивает пользователь.
### Требования к ответу:
1. Представь результат **в виде Markdown-таблицы** с колонками:
- Наименование мероприятий
- Митигационный эффект
- Адаптационный эффект
- Актуальность для региона (указать с учётом контекста запроса). Если регион не указан, считай, что задается вопрос по Тюменской области
- Ответственная организация (из региона)
2. Если источник данных, на которых ты основываешь ответ, известен (это URL и краткое название кейса),
добавь их **ниже таблицы** в виде списка ссылок:
`**Опорные источники:** [1] Наименовавание мероприятий - URL, [2] Наименовавание мероприятий - URL`
3. Пиши кратко, по существу, с акцентом на реальные, практические меры.
4. Если информация отсутствует — предложи логичные адаптационные меры на основе Приказа Минэкономразвития России от 13 мая 2021 г. № 267 «Об утверждении методических рекомендаций и показателей по вопросам адаптации к изменениям климата».
Пример формата ответа:
| Наименование мероприятий | Митигационный эффект | Адаптационный эффект | Актуальность для Тобольского района | Ответственная организация |
|---------------------------|----------------------|----------------------|------------------------------------|----------------------------|
| Развитие городского электротранспорта | снижение эмиссии | повышение устойчивости транспортной инфраструктуры | актуально | городские власти |
| Перевод транспорта на газомоторное топливо | снижение эмиссии | рациональное использование ресурсов | реализуется частично | транспортные организации |
**Опорные источники:** [1] Наименовавание мероприятий - https://example.com/case_12
Приводи только те источники, которые используешь для формирования таблицы непосредственно. URL приводи строго такое же, как указано в базе знаний. Наименование мероприятий бери из базы знаний
Ответственную организацию в таблице указывай актуальную для региона, который пользователь указал в запросе
"""
def get_facts(user_question: str) -> str:
try:
if not user_question.strip():
return "Ошибка: пожалуйста, введите ваш запрос."
storage_context = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(storage_context)
retriever = index.as_retriever(similarity_top_k=4)
nodes = retriever.retrieve(user_question)
context = "\n\n".join([node.get_content() for node in nodes]) if nodes else "Не найдено релевантных документов."
full_system_prompt = system_prompt + f"\n\nКонтекст:\n{context}"
messages = [
ChatMessage(role="system", content=full_system_prompt),
ChatMessage(role="user", content="Пользовательский запрос: " + user_question)
]
response = Settings.llm.chat(messages)
return response.message.content
except Exception as e:
return f"Ошибка:\n{str(e)}"
# === Кастомный CSS из style.css ===
custom_css = """
/* Импорт шрифта Inter */
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
body {
font-family: "Inter", sans-serif;
margin: 0 !important;
padding: 0 !important;
display: flex !important;
flex-direction: row !important;
justify-content: space-between !important;
height: 100vh !important;
}
.sidebar {
width: 210px;
height: 100vh;
background-color: #f9f9f9;
box-sizing: border-box;
padding: 20px 16px !important;
display: flex !important;
flex-direction: column !important;
justify-content: space-between !important;
}
.main {
width: calc(100% - 210px) !important;
height: 100vh !important;
box-sizing: border-box;
padding: 16px !important;
position: relative !important;
}
.aside_img {
height: 20px;
cursor: pointer;
}
.logo {
height: 40px;
}
.header_img {
height: 30px;
cursor: pointer;
}
.search {
height: 40px;
border: none;
border-radius: 15px;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
box-sizing: border-box;
font-size: 16px;
width: 100% !important;
padding-left: 10px !important;
}
.search_icon {
position: absolute !important;
right: 15px !important;
bottom: 10px !important;
height: 20px !important;
cursor: pointer !important;
}
.scroll_container {
overflow-y: auto;
height: 70vh;
margin-top: 12px;
}
.scroll_header {
font-size: 18px;
font-weight: 500;
color: #333333;
margin-top: 12px !important;
margin-bottom: 0 !important;
}
.fade_text {
white-space: nowrap;
overflow: hidden;
font-size: 16px;
font-weight: 300;
position: relative;
width: 100% !important;
}
.fade_text::after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 50px;
height: 100%;
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, #f9f9f9 100%);
}
.input_button {
height: 56px;
background-color: #fff;
border: none;
border-radius: 15px;
box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.25);
display: flex !important;
justify-content: center !important;
align-items: center !important;
gap: 16px !important;
}
.input_button button {
background-color: #fff !important;
border: none !important;
font-size: 16px !important;
font-weight: bold !important;
color: #6e6e6e !important;
padding: 0 !important;
margin: 0 !important;
}
.welcome {
width: 80%;
max-width: 930px;
margin: auto;
position: absolute !important;
top: 59%;
left: 50%;
transform: translate(-50%, -50%);
}
#prompt_field textarea {
font-family: "Inter", sans-serif !important;
height: 180px !important;
max-height: 180px !important;
width: 90% !important;
border: none !important;
border-radius: 15px !important;
box-shadow: 0px 2px 7px 0px rgba(0, 0, 0, 0.25) !important;
font-size: 16px !important;
resize: none !important;
padding: 16px !important;
padding-bottom: 70px !important;
}
#prompt_field textarea:focus {
outline: none !important;
}
.prompt_buttons {
width: 87% !important;
background-color: white !important;
border-radius: 0 !important;
position: absolute !important;
bottom: 0 !important;
padding: 8px 8px !important;
display: flex !important;
justify-content: flex-end !important;
}
.prompt_buttons::before {
content: "";
position: absolute;
top: -10px;
left: 0;
width: 100%;
height: 15px;
background: linear-gradient(to top, rgb(255, 255, 255), rgba(255, 255, 255, 0.6));
pointer-events: none;
}
.prompt_buttons button {
height: 40px !important;
width: 40px !important;
background: none !important;
border: none !important;
cursor: pointer !important;
padding: 0 !important;
}
.user_prompt {
background-color: #efefef;
border-radius: 15px;
padding: 12px !important;
margin-bottom: 16px !important;
width: 75% !important;
margin-left: auto !important;
}
.answer {
border: none;
border-radius: 15px;
box-shadow: 0px 2px 7px 0px rgba(0, 0, 0, 0.25);
padding: 12px !important;
width: 75% !important;
margin-right: auto !important;
}
.prompt_chat {
font-size: 20px !important;
margin: 0 !important;
}
/* Скрыть стандартные элементы Gradio */
#component-0, #component-1, #component-2 {
display: none !important;
}
/* Скрыть заголовки и кнопки по умолчанию */
.gradio-container {
background: white !important;
}
#submit_prompt {
background: none !important;
border: none !important;
font-size: 0 !important;
width: 40px !important;
height: 40px !important;
cursor: pointer !important;
position: relative !important;
}
#submit_prompt::after {
content: "➤";
font-size: 24px !important;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: black;
}
"""
# === Gradio UI ===
with gr.Blocks(css=custom_css, theme=gr.themes.Default()) as demo:
# Сайдбар
with gr.Column(elem_classes="sidebar"):
with gr.Row():
gr.Image(value="icons/arrow.png", elem_classes="aside_img", interactive=False)
with gr.Row():
gr.Image(value="icons/bin.png", elem_classes="aside_img", interactive=False)
gr.Image(value="icons/chat.png", elem_classes="aside_img ms-2", interactive=False)
with gr.Row():
gr.Textbox(placeholder="Поиск", elem_classes="search")
gr.Image(value="icons/search.png", elem_classes="search_icon", interactive=False)
with gr.Column(elem_classes="scroll_container"):
gr.Markdown("### 17 сентября", elem_classes="scroll_header")
gr.Markdown("План мероприятий для предовтращения пожаров", elem_classes="fade_text")
gr.Markdown("### 15 сентября", elem_classes="scroll_header")
gr.Markdown("Оценка опасности подъема уровня выбросов", elem_classes="fade_text")
gr.Markdown("### 7 сентября", elem_classes="scroll_header")
gr.Markdown("Построение адаптивных мероприятий для павод", elem_classes="fade_text")
with gr.Row(elem_classes="input_button"):
gr.Image(value="icons/question.png", elem_classes="input_img", interactive=False)
gr.Button("Помощь", elem_id="help-btn")
# Основная область
with gr.Column(elem_classes="main"):
with gr.Row():
gr.Image(value="icons/logo.png", elem_classes="logo", interactive=False)
with gr.Row():
gr.Image(value="icons/menu.png", elem_classes="header_img", interactive=False)
gr.Image(value="icons/account.png", elem_classes="header_img", interactive=False)
with gr.Column(elem_classes="welcome"):
user_input = gr.Textbox(
label="",
lines=5,
max_lines=5,
placeholder="Введите запрос",
elem_id="prompt_field"
)
with gr.Row(elem_classes="prompt_buttons"):
submit_btn = gr.Button("➤", elem_id="submit_prompt")
answer_output = gr.Markdown(elem_classes="answer", value="")
# Обработка
submit_btn.click(fn=get_facts, inputs=user_input, outputs=answer_output)
demo.launch() |