Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import requests
|
| 3 |
+
import json
|
| 4 |
+
import gradio as gr
|
| 5 |
+
import pandas as pd
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
import pypdf
|
| 8 |
+
import warnings
|
| 9 |
+
from google.colab import userdata
|
| 10 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 11 |
+
|
| 12 |
+
warnings.filterwarnings("ignore", category=UserWarning)
|
| 13 |
+
print("--- Библиотеки установлены и импортированы ---")
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
# --- 2. Настройка API и LLM ---
|
| 17 |
+
print("--- Настройка API и LLM ---")
|
| 18 |
+
try:
|
| 19 |
+
GEMINI_API_KEY = userdata.get('GEMINI_API_KEY')
|
| 20 |
+
API_BASE_URL = "http://194.113.209.48:8000"
|
| 21 |
+
llm = ChatGoogleGenerativeAI(model="gemini-1.5-pro-latest", google_api_key=GEMINI_API_KEY, temperature=0.1)
|
| 22 |
+
print("✅ LLM настроена.")
|
| 23 |
+
except Exception as e:
|
| 24 |
+
print(f"❌ Ошибка настройки LLM: {e}")
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
# --- 3. Загрузка данных и контекста ---
|
| 28 |
+
# 3.1. Загружаем заранее проанализированный портфель из CSV
|
| 29 |
+
print("--- Загрузка данных портфеля ---")
|
| 30 |
+
try:
|
| 31 |
+
tagged_portfolio_df = pd.read_csv('sber_portfolio_analyzed5.csv')
|
| 32 |
+
tagged_portfolio_df['region_name'] = tagged_portfolio_df['region_name'].str.strip()
|
| 33 |
+
print(f"✅ Успешно загружен проанализированный портфель из {len(tagged_portfolio_df)} проектов.")
|
| 34 |
+
except FileNotFoundError:
|
| 35 |
+
print("❌ Ошибка: Файл 'sber_portfolio_analyzed5.csv' не найден. Возможно, нужно сначала запустить ячейку для генерации этого файла.")
|
| 36 |
+
tagged_portfolio_df = pd.DataFrame()
|
| 37 |
+
|
| 38 |
+
# 3.2. Извлекаем текст из PDF-отчета
|
| 39 |
+
print("--- Извлечение текста из PDF-отчета ---")
|
| 40 |
+
full_report_text = ""
|
| 41 |
+
try:
|
| 42 |
+
# Убедитесь, что имя файла верное и он загружен
|
| 43 |
+
with open('f356d44202ac9361579f509ebf965950.pdf', 'rb') as f:
|
| 44 |
+
reader = pypdf.PdfReader(f)
|
| 45 |
+
for page in reader.pages:
|
| 46 |
+
full_report_text += page.extract_text() + "\n"
|
| 47 |
+
print("✅ Текст из PDF-файла успешно извлечен.")
|
| 48 |
+
except Exception as e:
|
| 49 |
+
print(f"❌ Ошибка при чтении PDF-файла: {e}")
|
| 50 |
+
|
| 51 |
+
# 3.3. Загружаем справочники
|
| 52 |
+
print("--- Загрузка справочников ---")
|
| 53 |
+
try:
|
| 54 |
+
regions_response = requests.get(f"{API_BASE_URL}/api/regions")
|
| 55 |
+
regions_data = regions_response.json()
|
| 56 |
+
regions_list = sorted([item['name'] for item in regions_data])
|
| 57 |
+
print("✅ Справочник регионов загружен.")
|
| 58 |
+
except Exception as e:
|
| 59 |
+
print(f"❌ Не удалось загрузить справочник регионов: {e}")
|
| 60 |
+
regions_list = sorted(list(tagged_portfolio_df['region_name'].unique()))
|
| 61 |
+
|
| 62 |
+
# 3.4. Определяем константы (структура Наццелей) - ПОЛНАЯ ВЕРСИЯ
|
| 63 |
+
NATIONAL_GOALS_STRUCTURE = {
|
| 64 |
+
"Сохранение населения, укрепление здоровья и повышение благополучия людей, поддержка семьи": [
|
| 65 |
+
"2.а) повышение суммарного коэффициента рождаемости до 1,6 к 2030 году и до 1,8 к 2036 году",
|
| 66 |
+
"2.б) увеличение ожидаемой продолжительности жизни до 78 лет к 2030 году и до 81 года к 2036 году",
|
| 67 |
+
"2.в) обеспечение не ниже среднероссийских темпов повышения к 2030 году суммарного коэффициента рождаемости в отстающих субъектах РФ",
|
| 68 |
+
"2.г) снижение к 2036 году дифференциации показателей ожидаемой продолжительности жизни не менее чем на 25%",
|
| 69 |
+
"2.д) снижение к 2030 году суммарной продолжительности временной нетрудоспособности граждан",
|
| 70 |
+
"2.е) повышение к 2030 году уровня удовлетворенности граждан условиями для занятий физкультурой и спортом",
|
| 71 |
+
"2.ж) увеличение к 2030 году численности граждан, получающих услуги долговременного ухода, до 500 тыс. человек",
|
| 72 |
+
"2.з) повышение к 2030 году уровня удовлетворенности участников СВО условиями для медицинской реабилитации и трудоустройства",
|
| 73 |
+
"2.и) создание и запуск к 2030 году цифровой платформы по управлению здоровьем человека",
|
| 74 |
+
"2.к) снижение уровн�� бедности ниже 7% к 2030 году и ниже 5% к 2036 году, в т.ч. среди многодетных семей",
|
| 75 |
+
"2.л) снижение коэффициента Джини до 0,37 к 2030 году и до 0,33 к 2036 году",
|
| 76 |
+
"2.м) рост МРОТ к 2030 году более чем в два раза, не менее 35 тыс. рублей в месяц",
|
| 77 |
+
"2.н) утверждение в 2026 году новых систем оплаты труда работников бюджетной сферы"
|
| 78 |
+
],
|
| 79 |
+
"Реализация потенциала каждого человека, развитие его талантов, воспитание патриотичной и социально ответственной личности": [
|
| 80 |
+
"3.а) создание к 2030 году условий для воспитания гармонично развитой личности на основе традиционных ценностей",
|
| 81 |
+
"3.б) увеличение к 2030 году численности иностранных студентов до 500 тыс. человек",
|
| 82 |
+
"3.в) увеличение к 2030 году доли молодых людей, участвующих в проектах развития и воспитания, до 75%",
|
| 83 |
+
"3.г) увеличение к 2030 году доли молодых людей, верящих в самореализацию в России, до 85%",
|
| 84 |
+
"3.д) увеличение к 2030 году доли молодых людей в добровольческой деятельности до 45%",
|
| 85 |
+
"3.е) обеспечение к 2030 году функционирования эффективной системы выявления талантов у 100% обучающихся",
|
| 86 |
+
"3.ж) обеспечение продвижения традиционных ценностей в не менее чем 70% проектов в сфере культуры к 2030 году",
|
| 87 |
+
"3.з) повышение к 2030 году удовлетворенности граждан работой организаций культуры",
|
| 88 |
+
"3.и) формирование к 2030 году современной системы профразвития педагогических работников"
|
| 89 |
+
],
|
| 90 |
+
"Комфортная и безопасная среда для жизни": [
|
| 91 |
+
"4.а) улучшение качества среды для жизни в опорных населенных пунктах на 30% к 2030 году",
|
| 92 |
+
"4.б) обеспечение граждан жильем общей площадью не менее 33 кв. метров на человека к 2030 году",
|
| 93 |
+
"4.в) обновление к 2030 году жилищного фонда не менее чем на 20% по сравнению с 2019 годом",
|
| 94 |
+
"4.г) устойчивое сокращение непригодного для проживания жилищного фонда",
|
| 95 |
+
"4.д) повышение доступности жилья на первичном рынке",
|
| 96 |
+
"4.е) благоустройство не менее чем 30 тыс. общественных территорий к 2030 году",
|
| 97 |
+
"4.ж) реализация программы модернизации коммунальной инфраструктуры для 20 млн. человек к 2030 году",
|
| 98 |
+
"4.з) строительство и реконструкция не менее чем 2 тыс. объектов питьевого водоснабжения к 2030 году",
|
| 99 |
+
"4.и) рост энергоэффективности в ЖКХ и строительстве",
|
| 100 |
+
"4.к) обновление парка общественного транспорта до 85% от норматива к 2030 году",
|
| 101 |
+
"4.л) приведение в нормативное состояние 85% дорог агломераций и 60% региональных дорог к 2030 году",
|
| 102 |
+
"4.м) снижение смертности в ДТП в 1.5 раза к 2030 году",
|
| 103 |
+
"4.н) увеличение авиационной подвижности населения на 50% к 2030 году",
|
| 104 |
+
"4.о) капитальный ремонт всех нуждающихся зданий школ и детсадов до конца 2030 года",
|
| 105 |
+
"4.п) подключение к сетевому газу не менее 1,6 млн домовладений к 2030 году",
|
| 106 |
+
"4.р) оснащение 900 центров кинопоказа в малых населенных пунктах к 2030 году"
|
| 107 |
+
],
|
| 108 |
+
"Экологическое благополучие": [
|
| 109 |
+
"5.а) формирование экономики замкнутого цикла: 100% сортировка ТКО, захоронение не более 50%, вовлечение 25% отходов в оборот к 2030 году",
|
| 110 |
+
"5.б) снижение в два раза выбросов опасных загрязняющих веществ в городах с высоким уровнем загрязнения к 2036 году",
|
| 111 |
+
"5.в) ликвидация до конца 2030 года не менее 50 опасных объектов накопленного вреда",
|
| 112 |
+
"5.г) снижение к 2036 году в два раза объема неочищенных сточных вод",
|
| 113 |
+
"5.д) сохранение лесов, биоразнообразия и создание условий для экологического туризма"
|
| 114 |
+
],
|
| 115 |
+
"Устойчивая и динамичная экономика": [
|
| 116 |
+
"6.а) темп роста ВВП выше среднемирового, 4-е место в мире по ВВП по ППС к 2030 году",
|
| 117 |
+
"6.б) снижение доли импорта до 17% ВВП к 2030 году",
|
| 118 |
+
"6.в) увеличение объема инвестиций в основной капитал на 60% к 2030 году (относительно 2020)",
|
| 119 |
+
"6.г) рост доходов населения и пенсий не ниже уровня инфляции",
|
| 120 |
+
"6.д) реальный рост дохода на работника МСП в 1,2 раза выше роста ВВП",
|
| 121 |
+
"6.ж) вхождение в топ-25 стран мира по плотности роботизации к 2030 году",
|
| 122 |
+
"6.н) увеличение доли туристской отрасли в ВВП до 5% к 2030 году",
|
| 123 |
+
"6.о) прирост экспорта несырьевых неэнергетических товаров на две трети к 2030 году",
|
| 124 |
+
"6.п) увеличение производства продукции АПК на 25% к 2030 году (относительно 2021)",
|
| 125 |
+
"6.р) увеличение экспорта продукции АПК в 1.5 раза к 2030 году (относительно 2021)"
|
| 126 |
+
],
|
| 127 |
+
"Технологическое лидерство": [
|
| 128 |
+
"7.а) обеспечение технологической независимости (биоэкономика, БАС, ИИ, новые материалы и др.)",
|
| 129 |
+
"7.б) увеличение индекса производства в обрабатывающей промышленности на 40% к 2030 году (относительно 2022)",
|
| 130 |
+
"7.в) вхождение в топ-10 стран мира по объему научных исследований к 2030 году",
|
| 131 |
+
"7.г) увеличение внутренних затрат на исследования до 2% ВВП к 2030 году",
|
| 132 |
+
"7.д) увеличение доли отечественных высокотехнологичных товаров в 1.5 раза к 2030 году",
|
| 133 |
+
"7.е) увеличение выручки малых технологических компаний в 7 раз к 2030 году"
|
| 134 |
+
],
|
| 135 |
+
"Цифровая трансформация": [
|
| 136 |
+
"8.а) достижение 'цифровой зрелости' госуправления и ключевых отраслей экономики к 2030 году",
|
| 137 |
+
"8.б) формирование рынка данных",
|
| 138 |
+
"8.в) доступ к высокоскоростному интернету для 97% домохозяйств к 2030 году",
|
| 139 |
+
"8.г) рост инвестиций в отечественные ИТ-решения вдвое выше темпа роста ВВП",
|
| 140 |
+
"8.д) переход 80% организаций на российское ПО к 2030 году"
|
| 141 |
+
]
|
| 142 |
+
}
|
| 143 |
+
print("✅ Словарь НАЦЦЕЛЕЙ успешно загружен.")
|
| 144 |
+
|
| 145 |
+
# --- 4. Определение функций приложения ---
|
| 146 |
+
print("--- Определение функций приложения ---")
|
| 147 |
+
# (Здесь идут полные определения функций generate_regional_briefing, _find_relevant_goals, analyze_new_project)
|
| 148 |
+
# --- ФИНАЛЬНАЯ ВЕРСИЯ С ТАБЛИЧНЫМ ПРЕДСТАВЛЕНИЕМ АНАЛИЗА ---
|
| 149 |
+
|
| 150 |
+
def generate_regional_briefing(region_name, year):
|
| 151 |
+
if not region_name or not year:
|
| 152 |
+
yield "Пожалуйста, выберите регион и год.", None, None
|
| 153 |
+
return
|
| 154 |
+
|
| 155 |
+
if not 'full_report_text' in globals() or not full_report_text:
|
| 156 |
+
yield "Ошибка: Текст отчета ESG-индекса не был загружен.", None, None
|
| 157 |
+
return
|
| 158 |
+
|
| 159 |
+
yield "<h3 style='margin-top: 20px;'>⏳ Готовлю финальный отчет, это может занять до 30 секунд...</h3>", None, None
|
| 160 |
+
|
| 161 |
+
try:
|
| 162 |
+
region_id = next((item['id'] for item in regions_data if item['name'] == region_name), None)
|
| 163 |
+
params = {"year": int(year)}
|
| 164 |
+
response = requests.get(f"{API_BASE_URL}/api/region-performance/{region_id}/all", params=params)
|
| 165 |
+
response.raise_for_status()
|
| 166 |
+
urfu_data_raw = response.json()
|
| 167 |
+
urfu_data_simplified = {indicator.get('name'): indicator.get('region_value') for indicator in urfu_data_raw.get('indicators', []) if indicator.get('name') and indicator.get('region_value') is not None}
|
| 168 |
+
except Exception as e:
|
| 169 |
+
yield f"Не удалось получить данные от API УрФУ. Ошибка: {e}", None, None
|
| 170 |
+
return
|
| 171 |
+
|
| 172 |
+
region_portfolio = tagged_portfolio_df[tagged_portfolio_df['region_name'] == region_name]
|
| 173 |
+
|
| 174 |
+
portfolio_summary = "В данном регионе у Сбера отсутствуют проекты из анализируемого портфеля."
|
| 175 |
+
fig = None
|
| 176 |
+
|
| 177 |
+
if not region_portfolio.empty:
|
| 178 |
+
total_investment = region_portfolio['amount_bln_rub'].sum()
|
| 179 |
+
project_count = len(region_portfolio)
|
| 180 |
+
SUMMARIZATION_THRESHOLD = 5
|
| 181 |
+
|
| 182 |
+
if 'mapped_goal' in region_portfolio.columns and not region_portfolio['mapped_goal'].isnull().all():
|
| 183 |
+
if project_count > SUMMARIZATION_THRESHOLD:
|
| 184 |
+
summary_parts = []
|
| 185 |
+
goal_groups = region_portfolio.groupby('mapped_goal')['amount_bln_rub'].sum().sort_values(ascending=False).reset_index()
|
| 186 |
+
|
| 187 |
+
for _, row in goal_groups.iterrows():
|
| 188 |
+
goal_name = row['mapped_goal']
|
| 189 |
+
goal_sum = row['amount_bln_rub']
|
| 190 |
+
project_examples = region_portfolio[region_portfolio['mapped_goal'] == goal_name]['project_goal'].head(2).tolist()
|
| 191 |
+
examples_text = ", ".join(project_examples)
|
| 192 |
+
# ИЗМЕНЕНИЕ: Формируем HTML-элемент списка <li> и используем <strong> для жирности
|
| 193 |
+
summary_parts.append(
|
| 194 |
+
f"<li><strong>{goal_name}</strong> ({goal_sum:.1f} млрд руб.), ключевые проекты: {examples_text}</li>"
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
# ИЗМЕНЕНИЕ: Собираем полный HTML-список <ul>...</ul>
|
| 198 |
+
summary_text = "<ul>" + "".join(summary_parts) + "</ul>"
|
| 199 |
+
portfolio_summary = (
|
| 200 |
+
f"Портфель Сбера в регионе составляет {total_investment:.1f} млрд руб. и включает проекты, распределенные по следующим национальным целям:{summary_text}"
|
| 201 |
+
)
|
| 202 |
+
else:
|
| 203 |
+
projects_list = [
|
| 204 |
+
f"'{row['project_goal']}' ({row['amount_bln_rub']:.1f} млрд руб.), направленный на достижение цели \"{row.get('mapped_goal', 'не определена')}\""
|
| 205 |
+
for _, row in region_portfolio.iterrows()
|
| 206 |
+
]
|
| 207 |
+
projects_text = " и ".join(projects_list)
|
| 208 |
+
portfolio_summary = (
|
| 209 |
+
f"Текущий портфель Сбера в регионе составляет {total_investment:.1f} млрд руб. "
|
| 210 |
+
f"и включает следующие ключевые проекты: {projects_text}."
|
| 211 |
+
)
|
| 212 |
+
else:
|
| 213 |
+
projects_list = [
|
| 214 |
+
f"'{row['project_goal']}' ({row['amount_bln_rub']:.1f} млрд руб.)"
|
| 215 |
+
for _, row in region_portfolio.iterrows()
|
| 216 |
+
]
|
| 217 |
+
projects_text = ", ".join(projects_list)
|
| 218 |
+
portfolio_summary = (
|
| 219 |
+
f"Текущий портфель Сбера в регионе составляет {total_investment:.1f} млрд руб. и включает следующие проекты: {projects_text}."
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
if 'mapped_goal' in region_portfolio.columns and not region_portfolio['mapped_goal'].isnull().all():
|
| 223 |
+
goal_distribution = region_portfolio.groupby('mapped_goal')['amount_bln_rub'].sum()
|
| 224 |
+
plot_data = goal_distribution.reset_index()
|
| 225 |
+
if plot_data is not None and not plot_data.empty:
|
| 226 |
+
fig, ax = plt.subplots(figsize=(10, 6)); bars = ax.bar(plot_data['mapped_goal'], plot_data['amount_bln_rub'], color='#4CAF50'); ax.bar_label(bars, fmt='%.0f', padding=3)
|
| 227 |
+
plt.xticks(rotation=45, ha="right", fontsize=9); plt.title(f"Распределение портфеля Сбера по Наццелям\nв регионе: {region_name}", fontsize=12)
|
| 228 |
+
plt.ylabel("Сумма, млрд руб.", fontsize=10); plt.grid(axis='y', linestyle='--', alpha=0.7); plt.tight_layout()
|
| 229 |
+
|
| 230 |
+
prompt = f"""
|
| 231 |
+
Твоя роль: Ведущий аналитик-стратег ESG-дирекции Сбера.
|
| 232 |
+
Твоя задача: Подготовить исчер��ывающий и СТРУКТУРИРОВАННЫЙ аналитический отчет по региону, синтезируя информацию из предоставленных источников.
|
| 233 |
+
|
| 234 |
+
### ИСТОЧНИК 1: Статистические показатели по региону (от УрФУ)
|
| 235 |
+
Регион анализа: {region_name}
|
| 236 |
+
{json.dumps(urfu_data_simplified, ensure_ascii=False, indent=2)}
|
| 237 |
+
|
| 238 |
+
### ИСТОЧНИК 2: Полный текст отчета "ESG-индекс городов и регионов" (Сбер, ВЭБ.РФ)
|
| 239 |
+
```text
|
| 240 |
+
{full_report_text}
|
| 241 |
+
```
|
| 242 |
+
|
| 243 |
+
### ИСТОЧНИК 3: Данные по проектам в регионе
|
| 244 |
+
{portfolio_summary}
|
| 245 |
+
|
| 246 |
+
### ЗАДАЧА И СТРОГИЕ ПРАВИЛА:
|
| 247 |
+
1. Подготовь отчет в формате Markdown.
|
| 248 |
+
2. Сфокусируй детальный анализ ТОЛЬКО на двух Национальных целях: "Сохранение населения, укрепление здоровья и повышение благополучия людей, поддержка семьи" и "Комфортная и безопасная среда для жизни".
|
| 249 |
+
3. Относись ко всем данным как к реальным, не добавляй дисклеймеров.
|
| 250 |
+
4. Для раздела "Детальный анализ" **сгенерируй Markdown-таблицы**, как показано в шаблоне.
|
| 251 |
+
|
| 252 |
+
---
|
| 253 |
+
|
| 254 |
+
### Аналитический отчет: {region_name}, {year} год
|
| 255 |
+
|
| 256 |
+
**1. Обзор проектов в регионе**
|
| 257 |
+
* На основе ИСТОЧНИКА 3, опиши состав и объем проектов. Начинай с новой строки каждую новую Наццель и по ней проекты.
|
| 258 |
+
|
| 259 |
+
* **Задача для следующей строки:** На основе ИСТОЧНИКА 2, найди конкретные факты о регионе `{region_name}` (место в рейтинге, баллы, и т.д.).
|
| 260 |
+
* **Требование к форматированию:** Начни строку с "ESG-индекс городов и регионов Сбер - ВЭБ.РФ:". Сразу после этого, обычным черным текстом, продолжи своим аналитическим выводом.
|
| 261 |
+
|
| 262 |
+
**2. Детальный анализ по приоритетным Национальным целям**
|
| 263 |
+
* Для каждой из двух приоритетных Наццелей, создай подзаголовок и Markdown-таблицу по следующему образцу. Заполни 2-3 строки для каждой таблицы наиболее показательными данными.
|
| 264 |
+
|
| 265 |
+
**Национальная цель: "Сохранение населения, укрепление здоровья и повышение благополучия людей, поддержка семьи"**
|
| 266 |
+
| Показатель (данные УрФУ) | Значение в регионе | Влияние проектов Сбера |
|
| 267 |
+
| :--- | :--- | :--- |
|
| 268 |
+
| *[название показателя из ИСТОЧНИКА 1]* | *[значение из ИСТОЧНИКА 1]* | *[твой анализ ИСТОЧНИКА 3]* |
|
| 269 |
+
| *[название второго показателя]* | *[его значение]* | *[твой анализ]* |
|
| 270 |
+
|
| 271 |
+
**Национальная цель: "Комфортная и безопасная среда для жизни"**
|
| 272 |
+
| Показатель (данные УрФУ) | Значение в регионе | Влияние проектов Сбера |
|
| 273 |
+
| :--- | :--- | :--- |
|
| 274 |
+
| *[название показателя из ИСТОЧНИКА 1]* | *[значение из ИСТОЧНИКА 1]* | *[твой анализ ИСТОЧНИКА 3]* |
|
| 275 |
+
| *[название второго показателя]* | *[его значение]* | *[твой анализ]* |
|
| 276 |
+
|
| 277 |
+
**3. Стратегические рекомендации**
|
| 278 |
+
* На основе табличного анализа, сформулируй 2-3 ключевых вывода и предложи конкретные типы проектов для инвестиций.
|
| 279 |
+
"""
|
| 280 |
+
|
| 281 |
+
final_report = llm.invoke(prompt).content
|
| 282 |
+
yield final_report, fig, region_portfolio.drop(columns=['project_goal'], errors='ignore')
|
| 283 |
+
|
| 284 |
+
def _find_relevant_goals(project_description: str) -> list:
|
| 285 |
+
top_level_goals = list(NATIONAL_GOALS_STRUCTURE.keys())
|
| 286 |
+
prompt = f"""
|
| 287 |
+
Определи 1-3 наиболее релевантных национальных цели из списка для проекта. Верни ТОЛЬКО JSON-массив строк.
|
| 288 |
+
СПИСОК НАЦЦЕЛЕЙ: {json.dumps(top_level_goals, ensure_ascii=False)}
|
| 289 |
+
ОПИСАНИЕ ПРОЕКТА: "{project_description}"
|
| 290 |
+
Твой JSON-ответ:
|
| 291 |
+
"""
|
| 292 |
+
try:
|
| 293 |
+
response = llm.invoke(prompt)
|
| 294 |
+
return json.loads(response.content.strip().replace("```json", "").replace("```", ""))
|
| 295 |
+
except Exception:
|
| 296 |
+
return top_level_goals
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
def analyze_new_project(region_name, project_description):
|
| 300 |
+
if not all([region_name, project_description]):
|
| 301 |
+
yield "Пожалуйста, выберите регион и опишите проект."
|
| 302 |
+
return
|
| 303 |
+
|
| 304 |
+
yield "<h3 style='margin-top: 20px;'>⏳ Этап 1/2: Определяю релевантные наццели...</h3>"
|
| 305 |
+
relevant_goals_names = _find_relevant_goals(project_description)
|
| 306 |
+
relevant_goals_structure = {goal: NATIONAL_GOALS_STRUCTURE.get(goal, []) for goal in relevant_goals_names}
|
| 307 |
+
|
| 308 |
+
yield "<h3 style='margin-top: 20px;'>⏳ Этап 2/2: Готовлю детальный анализ, это может занять до 30 секунд...</h3>"
|
| 309 |
+
|
| 310 |
+
try:
|
| 311 |
+
region_id = next((item['id'] for item in regions_data if item['name'] == region_name), None)
|
| 312 |
+
params = {"year": 2024} # Можно использовать актуальный год
|
| 313 |
+
response = requests.get(f"{API_BASE_URL}/api/region-performance/{region_id}/all", params=params)
|
| 314 |
+
response.raise_for_status()
|
| 315 |
+
urfu_data_raw = response.json()
|
| 316 |
+
urfu_data_simplified = {indicator.get('name'): indicator.get('region_value') for indicator in urfu_data_raw.get('indicators', []) if indicator.get('name') and indicator.get('region_value') is not None}
|
| 317 |
+
except Exception as e:
|
| 318 |
+
yield f"Не удалось получить данные от API УрФУ. Ошибка: {e}"
|
| 319 |
+
return
|
| 320 |
+
|
| 321 |
+
goals_structure_str = json.dumps(relevant_goals_structure, ensure_ascii=False, indent=2)
|
| 322 |
+
|
| 323 |
+
# --- НАЧАЛО НОВОГО, РАСШИРЕННОГО ПРОМПТА ---
|
| 324 |
+
prompt = f"""
|
| 325 |
+
Твоя роль: Ведущий аналитик ESG-дирекции Сбера, обладающий глубокими знаниями в области национальных целей развития РФ.
|
| 326 |
+
|
| 327 |
+
Твоя задача: Подготовить ДЕТАЛЬНЫЙ и СТРУКТУРИРОВАННЫЙ аналитический отчет по новому инвестиционному проекту. Отчет должен быть выполнен в формате Markdown и содержать глубокий анализ, а не поверхностное описание.
|
| 328 |
+
|
| 329 |
+
### ВХОДНЫЕ ДАННЫЕ:
|
| 330 |
+
1. **Регион реализации:** {region_name}
|
| 331 |
+
2. **Описание проекта:** "{project_description}"
|
| 332 |
+
3. **Статистический контекст по региону (данные УрФУ):** {json.dumps(urfu_data_simplified, ensure_ascii=False, indent=2)}
|
| 333 |
+
4. **Структура релевантных Национальных целей и их задач:** {goals_structure_str}
|
| 334 |
+
|
| 335 |
+
### СТРОГИЕ ТРЕБОВАНИЯ К СТРУКТУРЕ И СОДЕРЖАНИЮ ОТЧЕТА:
|
| 336 |
+
|
| 337 |
+
**1. Краткое резюме проекта**
|
| 338 |
+
* Опиши суть проекта, его масштаб и основные цели.
|
| 339 |
+
|
| 340 |
+
**2. Анализ соответствия Национальным целям**
|
| 341 |
+
* Для КАЖДОЙ релевантной наццели из предоставленного списка:
|
| 342 |
+
* **Обоснуй, почему проект соответствует данной цели.** Четко свяжи деятельность по проекту с конкретными задачами (подпунктами) этой наццели.
|
| 343 |
+
* **Используй данные из статистического контекста (ВХОДНЫЕ ДАННЫЕ №3)**, чтобы показать АКТУАЛЬНОСТЬ проекта для региона. Например, если проект влияет на здравоохранение, приведи текущие показатели по ожидаемой продолжительности жизни или заболеваемости в регионе, чтобы подчеркнуть важность инвестиций.
|
| 344 |
+
|
| 345 |
+
**3. Оценка по ESG-критериям**
|
| 346 |
+
* **E (Environmental / Экология):** Оцени потенциальное положительное и отрицательное воздействие проекта на окружающую среду. Какие экологические риски и возможности он несет? (например, снижение выбросов, образование отходов, использование "зеленых" технологий).
|
| 347 |
+
* **S (Social / Социальная сфера):** Проанализируй социальный эффект. Создание рабочих мест (сколько, каких?), влияние на ка��ество жизни, доступность услуг, поддержка местных сообществ.
|
| 348 |
+
* **G (Governance / Управление):** Опиши потенциальные управленческие аспекты. Необходимость взаимодействия с региональными властями, прозрачность реализации, работа с заинтересованными сторонами.
|
| 349 |
+
|
| 350 |
+
**4. Предварительные риски и рекомендации**
|
| 351 |
+
* На основе всего анализа, выдели 2-3 ключевых риска (технологических, социальных, регуляторных), на которые стоит обратить внимание.
|
| 352 |
+
* Дай 1-2 стратегические рекомендации по усилению положительного эффекта от проекта.
|
| 353 |
+
|
| 354 |
+
**5. Итоговое заключение**
|
| 355 |
+
* Сделай общий вывод о стратегической привлекательности проекта для банка с точки зрения вклада в устойчивое развитие и национальные приоритеты.
|
| 356 |
+
|
| 357 |
+
---
|
| 358 |
+
**Начинай твой отчет.**
|
| 359 |
+
"""
|
| 360 |
+
# --- КОНЕЦ НОВОГО ПРОМПТА ---
|
| 361 |
+
|
| 362 |
+
analysis_report = llm.invoke(prompt).content
|
| 363 |
+
yield analysis_report
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
# --- 5. Запуск веб-интерфейса Gradio ---
|
| 367 |
+
print("🚀 Запускаем Gradio-приложение...")
|
| 368 |
+
with gr.Blocks(theme=gr.themes.Soft(primary_hue="green", secondary_hue="lime"), css=".gradio-container {background-color: #f5f5f5} th { white-space: nowrap; }") as demo:
|
| 369 |
+
gr.Markdown(
|
| 370 |
+
"""<div style="text-align: center;"><img src="https://www.sberbank.com/common/img/uploaded/logo/logo_sber_main_2020_ru.svg" width="200">
|
| 371 |
+
<h1>Интерактивный дашборд "Горизонт PRO"</h1><p>Анализ вклада в достижение Национальных целей РФ 2030/2036</p></div>"""
|
| 372 |
+
)
|
| 373 |
+
with gr.Tabs():
|
| 374 |
+
with gr.TabItem("📈 Стратегический обзор региона"):
|
| 375 |
+
with gr.Row():
|
| 376 |
+
region_input_1 = gr.Dropdown(regions_list, label="Выберите регион")
|
| 377 |
+
year_input_1 = gr.Dropdown([2024, 2023, 2022], label="Выберите год", value=2024)
|
| 378 |
+
submit_button_1 = gr.Button("Сформировать стратегическую справку", variant="primary")
|
| 379 |
+
gr.Markdown("---")
|
| 380 |
+
output_report_1 = gr.Markdown(label="Аналитическая справка")
|
| 381 |
+
|
| 382 |
+
# Элементы создаются, но не отображаются в интерфейсе
|
| 383 |
+
# Это необходимо, чтобы функция click могла в них вернуть значения без ошибки.
|
| 384 |
+
output_plot_1 = gr.Plot(visible=False)
|
| 385 |
+
output_table_1 = gr.DataFrame(label="Проекты в портфеле", visible=False)
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
with gr.TabItem("🔎 Экспресс-оценка нового проекта"):
|
| 389 |
+
# --- ИЗМЕНЕНИЕ №1: Выносим текст примера в отдельную переменную ---
|
| 390 |
+
example_text = "Мусоросортировочный завод на 150 000 тонн стоимостью 3 млрд руб"
|
| 391 |
+
|
| 392 |
+
gr.Markdown("Введите описание нового инвестиционного проекта для анализа его соответствия Национальным целям и потребностям региона.")
|
| 393 |
+
region_input_2 = gr.Dropdown(regions_list, label="Выберите регион реализации проекта")
|
| 394 |
+
project_input_2 = gr.Textbox(lines=4, label="Опишите проект", placeholder=f"Пример: {example_text}")
|
| 395 |
+
|
| 396 |
+
# --- ИЗМЕНЕНИЕ №2: Добавляем две кнопки в одну строку ---
|
| 397 |
+
with gr.Row():
|
| 398 |
+
# Кнопка для вставки примера
|
| 399 |
+
example_button = gr.Button("✍️ Вставить пример", variant="secondary")
|
| 400 |
+
# Основная кнопка для анализа
|
| 401 |
+
submit_button_2 = gr.Button("Провести экспресс-оценку", variant="primary", scale=2) # scale=2 делает ее в 2 раза шире
|
| 402 |
+
|
| 403 |
+
gr.Markdown("---")
|
| 404 |
+
output_report_2 = gr.Markdown(label="Результат оценки")
|
| 405 |
+
|
| 406 |
+
# --- Привязка функций к кнопкам (остается без изменений) ---
|
| 407 |
+
submit_button_1.click(
|
| 408 |
+
fn=generate_regional_briefing,
|
| 409 |
+
inputs=[region_input_1, year_input_1],
|
| 410 |
+
outputs=[output_report_1, output_plot_1, output_table_1]
|
| 411 |
+
)
|
| 412 |
+
|
| 413 |
+
# --- ��ЗМЕНЕНИЕ №3: Добавляем обработчик для новой кнопки ---
|
| 414 |
+
example_button.click(
|
| 415 |
+
fn=lambda: example_text, # Простая функция, которая просто возвращает текст примера
|
| 416 |
+
inputs=None, # У нее нет входных данных
|
| 417 |
+
outputs=project_input_2 # Результат (текст) отправляется в наше поле для ввода
|
| 418 |
+
)
|
| 419 |
+
|
| 420 |
+
submit_button_2.click(
|
| 421 |
+
fn=analyze_new_project,
|
| 422 |
+
inputs=[region_input_2, project_input_2],
|
| 423 |
+
outputs=[output_report_2]
|
| 424 |
+
)
|
| 425 |
+
|
| 426 |
+
demo.launch(share=True, debug=True)
|