Репозиторий содержит только backend по контракту RLT 2026 (contract (3).md): FastAPI + PostgreSQL/pgvector + rubert-tiny2 (sentence-transformers).
Пайплайн: pgvector — отбор топ-K кандидатов (по умолчанию 20) → rubert-tiny2 — точное косинусное реранжирование → топ-3 и поле reason. Работа offline, без внешних LLM API; при сборке Docker-образа модель кэшируется под /models.
- Docker 20.10+ и Docker Compose v2
- ~4 GB RAM для контейнера
api
В ./data/ должен лежать CSV закупок с именем по умолчанию:
Закупки для интенсива_сжатые.csv в каталоге ./data/
(при необходимости задайте другой путь в TENDERS_FILE в .env).
Демо-хардкод тендеров отключён: без файла сервис не стартует.
Строки закупок всегда различаются по pn_lot (основной ключ), если эта колонка есть в CSV; пустые pn_lot отбрасываются. Без колонки pn_lot используется только lot_number (в лог пишется предупреждение).
copy .env.example .envdocker compose up --build- Swagger: http://localhost:8000/docs
Первый старт: загрузка модели + полная векторизация всех лотов из CSV (батчи по TENDER_EMBED_BATCH, по умолчанию 256). На большом файле это может занять десятки минут и больше; при нехватке памяти уменьшите батч (например 128) в .env / docker-compose.
| Требование | Реализация |
|---|---|
| ИИ только локально, rubert-tiny2 + sentence-transformers | app/services/matcher.py, загрузка один раз в lifespan |
PostgreSQL + pgvector, vector(312), ivfflat |
app/database.py, ensure_ivfflat_index() после загрузки тендеров |
| Два этапа: pgvector → rubert → топ-3 | get_candidates() → matcher.match_item() (пересчёт cosine по кандидатам) |
get_candidates(query_embedding, top_k=20) |
app/database.py |
| ОКПД2 — пред-фильтр по первым 4 цифрам | get_candidates + при малом числе кандидатов — поиск без фильтра |
text_for_matching номенклатуры: category + name + char values |
TenderMatcher.build_item_text в matcher.py |
| Все 5 эндпоинтов | /health, /upload-nomenclature, /match, /results, /export/csv |
| Экспорт Excel | GET /export/csv → openpyxl / pandas.ExcelWriter |
asyncio.run_in_executor для матчинга |
app/routers/matching.py |
Структура app/, matcher.py в services/ |
app/main.py, app/services/matcher.py |
| docker-compose: app + postgresql | docker-compose.yml |
.env.example, MODEL_PATH / MODEL_NAME |
.env.example, Dockerfile / compose |
Фронтенд в этом репозитории не ведётся (по отдельному решению команды).
| Метод | Путь | Описание |
|---|---|---|
| GET | /health |
status, tenders_loaded, model_ready, embedding_dim, tenders_file, version |
| POST | /upload-nomenclature |
CSV → nomenclature_items + эмбеддинги |
| POST | /match |
Тело: { "item_ids": [] } — пустой список = все товары; JSON по контракту |
| GET | /results?min_score= |
История match_results |
| GET | /export/csv |
Скачать .xlsx |
curl http://localhost:8000/healthpython -m venv .venv
.venv\Scripts\activate
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
docker run -d --name pg -e POSTGRES_PASSWORD=procurement -e POSTGRES_USER=procurement -e POSTGRES_DB=procurement -p 5432:5432 pgvector/pgvector:pg16
set DATABASE_URL=postgresql://procurement:procurement@localhost:5432/procurement
set TENDERS_FILE=C:\Users\ASUS\Desktop\hackatone\data\Закупки для интенсива_сжатые.csv
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000app/
main.py
database.py # модели, init_db, get_candidates, ivfflat
routers/
nomenclature.py
matching.py
services/
matcher.py # rubert + реранжирование + reason
tenders_loader.py # CSV/XLSX тендеров, группировка по lot_number, эмбеддинги
requirements.txt
.env.example
docker-compose.yml
Dockerfile
Переменные окружения: см. .env.example (DATABASE_URL, TENDERS_FILE, TENDER_EMBED_BATCH, MODEL_NAME, MODEL_PATH, PGVECTOR_TOP_K, API_PORT).
Это сетевой сбой или лимит Docker Hub. По умолчанию API собирается с AWS Public ECR (public.ecr.aws/docker/library/python:3.11-slim). Если нужен именно Hub:
set PYTHON_IMAGE=python:3.11-slim
docker compose build --no-cache apiЛибо зеркало Google: PYTHON_IMAGE=mirror.gcr.io/library/python:3.11-slim. Повторите docker compose up --build.