Информационная система оценки и оптимизации кредитного риска с применением ИИ и спецификации OpenAPI.
Изначальная задумка — не отдельный сайт, а компактная панель, которую можно закрепить рядом с бизнес‑системой или встроить как расширение браузера. Поэтому интерфейс ориентирован на узкие экраны и работу «в две колонки»:
- Режим расширения: UI открывается в сайдбаре браузера/плагина, тянет данные из backend и отображает ключевые KPI, скоринг, оптимизацию и очередь задач без перехода на отдельный сайт.
- Встраивание в контекст: тот же UI может быть iframe-панелью в CRM/ERP, чтобы скоринг и мониторинг происходили прямо в рабочем окне оператора.
- Мобильный режим: на узких экранах навигация превращается в выезжающее меню, карточки и таблицы переходят в одну колонку.
Это объясняет компактные виджеты, быстрые формы и отсутствие «толстых» страниц — всё рассчитано на быстрый взгляд.
docker compose up --buildЕсли после сборки backend падает с ошибкой ImportError: no pq wrapper available, убедитесь что используется psycopg[binary] (уже добавлено в backend/requirements.txt) и пересоберите контейнер.
Если frontend сообщает: "next start" does not work with "output: standalone", запуск уже исправлен — контейнер стартует через node .next/standalone/server.js.
| Переменная | Описание | Значение по умолчанию |
|---|---|---|
DATABASE_URL |
строка подключения к Postgres | postgresql+psycopg://postgres:postgres@db:5432/credit_risk |
API_KEY |
ключ для заголовка X-API-Key |
local-dev-key |
MODEL_PATH |
путь для модели ML | /data/model.joblib |
DATASET_PATH |
путь для синтетического датасета | /data/dataset.csv |
NEXT_PUBLIC_API_BASE |
базовый URL backend | http://localhost:8000 |
NEXT_PUBLIC_API_KEY |
ключ для frontend запросов | local-dev-key |
- Дашборд: метрики, графики и последние задачи.
- Скоринг заявки: форма кредитной заявки, PD/EL и факторы риска.
- Оптимизация портфеля: запуск задачи отбора заявок при бюджетном лимите.
- Задачи: список задач и статусов, журнал событий доступен через SSE.
- По умолчанию ключ:
local-dev-key(см.API_KEYвdocker-compose.yml). - Можно заменить ключ, задав
API_KEYв окружении backend иNEXT_PUBLIC_API_KEYдля frontend. - В интерфейсе ключ вводится в сайдбаре и сохраняется локально в браузере.
- Backend:
http://localhost:8000 - Docs:
http://localhost:8000/docs - OpenAPI:
http://localhost:8000/openapi.json
- UI → API: frontend обращается к backend через
frontend/lib/api-client.tsи передаётX-API-Key. - API → ML/DB:
/api/v1/scoreвалидирует вход, считает PD, пишет заявку и решение в БД./api/v1/jobsсоздаёт задачу и запускает асинхронный обработчик.
- DB → дашборд:
/api/v1/dashboardсобирает данные по заявкам, решениям и задачам.
- Где живёт:
backend/app/ml/model.py. - Что делает: обучает/грузит модель, возвращает
pdиtop_factors. - Как используется:
backend/app/api/v1/routes.pyвызываетscore_application, вычисляет ожидаемые потери (EL) по формулеEL = PD * LGD * EAD, где:- PD — вероятность дефолта (модель).
- LGD — допущение 0.5 (константа в коде).
- EAD — сумма кредита в рублях.
Все денежные входы и результаты интерпретируются как российские рубли (RUB).
Application: входные параметры клиента.Decision: результат скоринга (PD, EL, рекомендация).JobиJobEvent: очередь задач, прогресс и журнал событий.
Источник — backend/app/api/v1/routes.py (эндпоинт /api/v1/dashboard).
- Всего заявок (
total_applications)- Число активных заявок, пригодных для портфеля.
- Отказанные и удалённые скоринги сюда не входят.
- Доля одобрений (
approved_rate)- Доля решений, где
Decision.recommended == True. - Показывает качество входящего потока и агрессивность скоринга.
- Доля решений, где
- Средняя PD (
average_pd)- Среднее значение PD по всем решениям.
- Чем выше, тем рисковее портфель.
- Ожидаемые потери портфеля (
portfolio_el)- Сумма
Decision.expected_lossпо активным портфельным решениям. - Это агрегированный риск в рублях.
- Сумма
- Отказано (
rejected_count)- Количество неудалённых заявок с высоким риском и рекомендацией «Отказать».
- Откуда берётся: список
Decision.pd. - Что показывает: плотность риска по портфелю (сколько заявок попадает в низкий/средний/высокий PD).
- Почему важно: позволяет визуально отделить «здоровую» часть портфеля от высокорисковой.
- Откуда берётся: история
Decision.expected_loss. - Что показывает: изменение ожидаемых потерь по последовательным решениям (как меняется риск на потоке).
- Почему важно: видны сдвиги качества выдачи и всплески риска.
- Последние задачи (
recent_jobs) — отображаются на странице задач. - Прогресс задач (
job_durations) — сервисные метрики для мониторинга очереди.
Если в БД ещё нет активных портфельных заявок, backend возвращает пустые метрики и empty state на UI. Локальная frontend-симуляция включается только если API недоступен, и тогда интерфейс явно показывает предупреждение.
На frontend при отсутствии ответа API включается аналогичная симуляция
frontend/lib/dashboard-simulator.ts.
- PD: результат модели (
ScoreResponse.pd). - Ожидаемые потери:
pd * lgd * ead, гдеlgd = 0.5, аead = loan_amount. - Рекомендация:
- PD < 10%: «Одобрить».
- 10% <= PD < 20%: «На ручную проверку».
- PD >= 20%: «Отказать».
- Топ‑факторы:
ScoreResponse.top_factors— объяснение ключевых признаков. - История скорингов:
/api/v1/scores/historyвозвращает последние неудалённые скоринги. Записи можно удалить soft delete черезDELETE /api/v1/scores/{id}или очистить черезDELETE /api/v1/scores.
- Вход: список заявок + бюджет в рублях.
- Механика:
- Каждую заявку скорим через ML.
- Считаем
expected_loss = pd * lgd * ead. - Считаем процентный доход и
expected_profit = gross_interest_income - expected_loss. - Исключаем высокий риск, отказы и заявки с неположительной ожидаемой прибылью.
- Greedy-эвристика отбирает заявки по
expected_profit / loan_amountпри ограничении лимита выдачи.
- Где реализовано:
backend/app/jobs/runner.py(_run_optimize).
- Задача — асинхронная операция (оптимизация или переобучение модели).
- SSE‑лог:
/api/v1/jobs/{id}/eventsстримит события, события пишутся вJobEvent. - Зачем: позволяет пользователю видеть ход долгих операций в реальном времени.
- Темы: CSS‑переменные в
frontend/app/globals.cssзадают корректные цветовые значения для светлой и тёмной темы, включая графики и градиенты. - Адаптивность:
frontend/components/app-shell.tsxпревращает меню в выезжающую панель на мобильных экранах.
- Соберите и запустите все сервисы:
docker compose up --build
- Откройте UI:
http://localhost:3000
- Проверьте backend:
http://localhost:8000/healthhttp://localhost:8000/docs
- Запустите оптимизацию портфеля:
- UI → Оптимизация портфеля → Запустить оптимизацию
- Откройте журнал событий задачи:
- UI → Задачи → выберите задачу → наблюдайте SSE лог.
curl -X POST http://localhost:8000/api/v1/score \
-H "Content-Type: application/json" \
-H "X-API-Key: local-dev-key" \
-d '{
"age": 35,
"income": 120000,
"employment_years": 6,
"debt_to_income": 0.32,
"credit_history_months": 84,
"delinquencies": 0,
"loan_amount": 800000,
"loan_term_months": 36,
"interest_rate": 18.5
}'make export-openapiСнапшоты лежат в openapi/openapi.json и openapi/openapi.yaml.
SYNC-LAB точки:
backend/app/core/sync.py:scoring_semaphoreограничивает параллельные скоринги.backend/app/core/sync.py:model_lockзащищает модель во время retrain.backend/app/core/sync.py:job_channels(Condition) уведомляет SSE подписчиков.backend/app/jobs/runner.py: использование Semaphore/Lock при запуске задач.
cd backend
pytest