Веб-сервис для автоматического сопоставления (матчинга) двух текстовых строк с помощью классического машинного обучения (CatBoost). Сервис состоит из двух независимых шагов:
- Шаг 1 — Обучение. Пользователь загружает CSV с эталонными парами строк, выбирает набор математических признаков (фич), сервис генерирует «сложные» негативные примеры, обучает модель и отдаёт готовый артефакт (ZIP с весами модели и конфигом).
- Шаг 2 — Тестирование (инференс). Пользователь загружает обученную модель (ZIP) и две произвольные строки — сервис возвращает вероятность (уверенность) того, что строки описывают один и тот же объект.
| Слой | Стек |
|---|---|
| Frontend | React 19, Vite, TypeScript, Tailwind CSS, Express (node-сервер + прокси), SSE (EventSource) |
| Backend | Python 3.12, FastAPI, BackgroundTasks, SSE |
| ML Core | CatBoost, scikit-learn (TF-IDF), rapidfuzz, pandas, numpy |
| Инфраструктура | Docker, docker-compose |
Почему Python 3.12, а не 3.14? ML-зависимости (CatBoost, numpy, pandas, scikit-learn) распространяются бинарными wheel'ами; на момент сборки для Python 3.14 они ещё не собраны. Чтобы
docker compose upработал «из коробки», бэкенд использует Python 3.12.
docker compose up --buildПосле старта откройте http://localhost:3000.
- Фронтенд (React) обслуживается node-сервером на порту
3000. - Все запросы
/api/*он прозрачно проксирует на backend (FastAPI, порт8000, не публикуется наружу). - Параметры запуска — в файле
.env.
Браузер ──(/, статика)──▶ Frontend (node:3000)
│ │
│ /api/v1/* (CSV, SSE, │ reverse-proxy
│ ZIP, текст) ▼
└────────────────────▶ Backend (FastAPI:8000)
│
├─ TF-IDF индекс ──▶ hard negatives (класс 0)
├─ расчёт выбранных фич ──▶ матрица признаков
├─ CatBoostClassifier.fit() в BackgroundTasks
│ └─ статус/прогресс ──▶ SSE ──▶ браузер
└─ ZIP (model.cbm + meta.json)
- Фронт шлёт
POST /api/v1/training/start(CSV + список фич) → получаетtask_id. - Бэкенд регистрирует задачу в in-memory сторе и запускает фоновую корутину.
- Фронт подписывается на
GET /api/v1/training/status/{task_id}(SSE) и в реальном времени показывает лог:preparing → tfidf → negatives → training (итерации CatBoost) → completed. - Каждая строка CSV — это правильный матч (класс 1). По всем «Строкам 2» строится временный TF-IDF индекс; для каждой «Строки 1» косинусным расстоянием подбираются самые похожие чужие «Строки 2» — это hard negatives (класс 0).
- Для всех пар считаются только выбранные фичи, на них обучается CatBoost.
- По кнопке «Скачать» фронт забирает
GET /api/v1/training/download/{task_id}— ZIP сmodel.cbmиmeta.json.
- Фронт шлёт
POST /api/v1/inference/predict(ZIP модели + две строки). - Бэкенд распаковывает ZIP, читает из
meta.jsonсписок фич, считает их для двух строк, прогоняет вектор черезmodel.cbmи возвращаетmatch_probability(вероятность класса 1).
levenshtein, jaro_winkler, dice, number_match, word_intersection, length_diff, tfidf_cosine.
Математика зашита на бэкенде в backend/src/features_registry/.
MLMatcher/
├── backend/ # FastAPI-сервис (см. backend/README.md)
├── frontend/ # React SPA + node-прокси (см. frontend/README.md)
├── docker-compose.yml # оркестрация двух сервисов
├── .env # переменные окружения
└── .vscode/launch.json # дебаг бэкенда через debugpy
Подробности — в backend/README.md и frontend/README.md.
Согласно ТЗ статус задач хранится в оперативной памяти процесса (in-memory dict + BackgroundTasks).
Поэтому бэкенд работает в один воркер uvicorn: статус не шарится между процессами и не переживает
рестарт контейнера. Для горизонтального масштабирования потребуется внешний стор (Redis) — это вне ТЗ.