Трекер региональных инициатив. Каталог, рейтинг и AI-предварительный отбор практик — учебный проект, вдохновлённый тем, как устроены открытые платформы для сбора и оценки региональных инициатив и практик.
Демо: rit-web-sigma.vercel.app ·
API health-check: rit-api.vercel.app/healthz
(тестовый вход: admin@rit.dev / ChangeMe123!)
Интерактивная документация API (
/docs) в проде намеренно отключена (docs_url=NoneприENVIRONMENT=production) — не выставлять схему API публично считается хорошей практикой. Открыть её можно только локально, запустив бэкенд сENVIRONMENT=development(значение по умолчанию).
Сервис для сбора, рейтингования и предварительного AI-отбора региональных инициатив. Логика в одном цикле: аналитик подаёт инициативу с описанием → куратор запускает AI-оценку → система считает балл, пишет заключение и проверяет заявку на дубли по смыслу → куратор смотрит разбивку и решает, рекомендовать инициативу или отклонить → результат виден на дашборде — по сферам, регионам, статусам и AI-баллам.
Три роли с разными правами (аналитик подаёт, куратор модерирует и оценивает, админ ещё и ведёт справочник регионов), и ничего не скрыто от того, кто читает: у каждой инициативы — полная история изменений (кто, что и когда сделал) и разбивка AI-балла по факторам, а не голая цифра. ИИ во всей этой схеме — только советчик: он не может сам ни отклонить, ни утвердить заявку, финальное решение всегда остаётся за человеком.
Сделан как портфолио-проект под аналитическую роль — не как набор фич ради фич, а как ответ на конкретную проблему (ниже) с обоснованным выбором стека, реализованными (а не только продекларированными) практиками безопасности и полным циклом от идеи до продакшн-деплоя с настоящей базой данных и CI/CD.
Открытые платформы для сбора инициатив и практик почти всегда масштабируются асимметрично: подать заявку может кто угодно, а прочитать и оценить её — только эксперт с профильной компетенцией. Заявок в итоге всегда больше, чем экспертных часов на их разбор.
Механика проста и от конкретной платформы не зависит. При 1000 заявках и 5 минутах на прочтение и первичное решение по каждой — это больше 80 часов работы до того, как эксперт дойдёт до содержательной оценки хоть одной идеи. Причём заметная часть этого времени уходит не на суть, а на рутину: отсеять формально слабые заявки, заметить очевидные дубли, сгруппировать похожее по теме.
Именно эта рутинная часть — предварительный балл по понятным критериям и поиск дублей по смыслу — поддаётся автоматизации без риска подменить экспертизу: критерии прозрачны, решение остаётся за человеком, а алгоритм только сокращает путь к содержательным заявкам.
Прототип проверяет ровно это: AI-скоринг и обнаружение похожих заявок как первый фильтр, финальное решение — всегда за куратором. Использует ли эту схему уже какая-то конкретная программа — я не проверял и не утверждаю; гипотеза здесь не про чужой процесс, а про то, что подобное узкое место закономерно возникает на таком масштабе и закономерно решается таким способом.
- CRUD над сущностью «инициатива»: регион, сфера, статус, автор, KPI-скор
- AI-скоринг: LLM оценивает новизну/реализуемость/влияние и пишет
краткое summary для эксперта — с heuristic-фолбэком, если
ANTHROPIC_API_KEYне задан (см.app/services/scoring.py) - Дедупликация: лексическое сравнение с похожими заявками (в проде — pgvector cosine similarity)
- Дашборд: сводка по статусам/сферам/регионам
- Ежедневный дайджест:
scripts/daily_ingest.py, запускается по cron через GitHub Actions, оценивает новые заявки и шлёт сводку в Telegram - Человек всегда финализирует решение — AI даёт рекомендацию, не статус
| Слой | Технологии | Почему |
|---|---|---|
| Frontend | React 18 + TypeScript (strict) + Vite + Tailwind | Быстрый HMR, типобезопасность на границе с API |
| Данные на клиенте | TanStack Query + TanStack Table | Кэш из коробки, таблицы на тысячах строк без блокировки рендера |
| Формы | React Hook Form + Zod | Zod-схемы зеркалят Pydantic-схемы бэка — граница валидируется дважды независимо |
| Backend | FastAPI (async) + Pydantic v2 | Автогенерация OpenAPI, контракт синхронен с фронтом |
| ORM/миграции | SQLAlchemy 2.0 (async) + Alembic | Параметризованные запросы по умолчанию — SQL-инъекция архитектурно исключена |
| БД | PostgreSQL (+ pgvector в проде), SQLite для dev/тестов | |
| AI | Claude API со структурированным выводом | Изолировано за одним сервисным модулем — провайдер меняется, бизнес-логика нет |
- Пароли — argon2id (passlib), не bcrypt/md5
- JWT access (15 мин) + refresh (7 дн) в httpOnly + Secure + SameSite=strict cookie, не в localStorage
- Роли проверяются на бэкенде в service-слое (
require_role) — фронтовый флаг роли только для UI - Только параметризованные запросы через SQLAlchemy — ни одной f-строки в SQL
- Security-заголовки (HSTS, CSP, X-Frame-Options, …) — одним middleware, не по маршруту вручную
- Явный CORS allow-list, без
*приcredentials: true - Rate limiting на
/auth/*(slowapi) - Секреты —
.envв.gitignore, в репозитории только.env.example - Приложение отказывается стартовать в production с плейсхолдер-секретом,
секретом короче 32 байт или
COOKIE_SECURE=false(app/core/config.py, проверено тестами вtest_config.py) - Неизменяемый аудит-лог: кто/что/когда изменил
- CI гоняет
bandit(SAST),pip-auditиnpm auditна каждый PR и раз в неделю по расписанию mypy --strictиruff— 0 замечаний на момент публикации
# Backend
cd apps/api
python3.12 -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
cp .env.example .env
alembic upgrade head
python -m scripts.seed # тестовый админ: admin@rit.dev / ChangeMe123!
uvicorn app.main:app --reload
# Frontend (в другом терминале)
cd apps/web
npm install
cp .env.example .env
npm run devBackend поднимется на :8000, frontend — на :5173.
cd infra
cp .env.example .env # заполнить POSTGRES_PASSWORD и JWT_SECRET_KEY
docker compose up --buildFrontend — :8080, backend — :8000, Postgres — :5432.
cd apps/api && pytest -q # 15/15, sqlite in-memory, без внешних зависимостей
cd apps/web && npm run build # typecheck (tsc -b) + production buildRIT/
├── apps/
│ ├── web/ React + Vite + Tailwind
│ └── api/ FastAPI (routers → services → repositories)
│ ├── app/
│ ├── alembic/ версионированные миграции
│ └── scripts/ seed.py, daily_ingest.py
├── infra/
│ └── docker-compose.yml
├── .github/workflows/
│ ├── ci.yml lint + typecheck + test + build на каждый PR
│ ├── security.yml bandit + pip-audit + npm audit
│ └── daily-digest.yml ежедневный cron: скоринг + Telegram-дайджест
└── .pre-commit-config.yaml
На выборке из 3 тестовых инициатив прототип отрабатывает скоринг + дедупликацию за секунды на heuristic-фолбэке; с реальным LLM время растёт до нескольких секунд на заявку, что на масштабе сотен заявок в день всё ещё на порядки быстрее ручного первичного просмотра.
MIT — см. LICENSE.