From 875b330a98e76190f86c2c58aba9e1de87e7fe0e Mon Sep 17 00:00:00 2001 From: gitgernit Date: Sun, 23 Nov 2025 14:41:07 +0300 Subject: [PATCH 1/2] chore(): update rfc --- RFC.md | 66 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/RFC.md b/RFC.md index 4ab78a1..3809554 100644 --- a/RFC.md +++ b/RFC.md @@ -37,30 +37,48 @@ RFC определяет интерфейсы для API, операционны ## 4. Цели и критерии приёмки Цели: - Стабильный REST/HTTP API для вакансий и предсказаний -- Горизонтально масштабируемый ML embedding-сервис -- Повторяемый pipeline подготовки данных и обучения +- Стабильный и масштабируемый REST/HTTP API, утилизирующий ML алгоритмы ## 5. Высокоуровневая архитектура -- Web API (web_api): HTTP-сервис, инкапсулирующий бизнес логику. Проксируются запросы к ML -- ML сервис (ml): HTTP-сервис для генерации embenddings и предсказания -- PostgreSQL — является хранилищем данных +- Web API (web_api): HTTP-сервис, инкапсулирующий бизнес логику. Интегрируется с ML-сервисом +- ML сервис (ml): HTTP-сервис, инкапсулирующий ML бизнес логику +- PostgreSQL + pgvector - основное хранилище данных - Инфра: Docker Compose для локальной разработки; Coolify для CI/CD Коммуникация: -- HTTP rest-ful между web_api и ml; опционально gRPC в будущем -- Обмен артефактами через object storage и метаданные в PostgreSQL +- HTTP REST между web_api и ml; опционально gRPC / WebSocket в будущем +- Хранение данных в PostgreSQL + pgvector, хранение файловых данных в S3 -## 6. Контракты API (предварительно) -- Web API: - - POST /v1/predict — payload: vacancy/resume; returns: salary_prediction {value, confidence, model_version}. - - POST /v1/vacancies — сохраняет вакансию для последующей обработки. - - GET /v1/models — список доступных моделей и версий. -- ML service: - - POST /infer — принимает фичи/сырой текст, возвращает предсказание и мета. - - GET /health, GET /metrics. +Входные/выходные схемы должны быть описаны в формате OpenAPI -Входные/выходные схемы должны быть описаны в OpenAPI (yaml) и поддерживаться CI-валидатором +### 5.1. Домены приложения +- **Resume**: управление резюме пользователей, история версий, эмбеддинги, предикты зарплаты +- **Vacancy**: каталог вакансий с эмбеддингами для поиска похожих +- **User**: пользователи, профили, аутентификация +- **Auth Identity**: методы аутентификации (email/password) +- **Notification Device**: регистрация устройств для уведомлений + +### 5.2. Флоу создания резюме и предикта +1. Пользователь создает резюме через Gateway (AddResumeInteractor) +2. Gateway сохраняет резюме в хранилище и возвращает ответ +3. В фоне запускается ResumePredictionInteractor: + - Генерирует эмбеддинг резюме через ML Service (модель эмбеддинга) + - Сохраняет эмбеддинг в хранилище + - Ищет подходящие вакансии по векторному сходству (HNSW индекс, cosine similarity >= 0.5) + - Фильтрует и сортирует до 50 наиболее релевантных вакансий + - Запрашивает предикт зарплаты и рекомендуемые навыки через ML Service (алгоритм предикта) + - Сохраняет предикт в хранилище + +### 5.3. Структура хранилища +- **Users**: пользователи, профили +- **Resumes**: резюме с версионированием (up_resume_id, down_resume_id) +- **Resume Embeddings**: векторные представления резюме (384 измерения) +- **Resume Predictions**: предикты зарплаты и рекомендуемые навыки +- **Resume Experience/Education/Projects**: опыт, образование, проекты +- **Vacancies**: вакансии с зарплатами и требованиями +- **Vacancy Embeddings**: векторные представления вакансий (384 измерения) +- **Key Skills**: словарь навыков для автокомплита (GIN индекс с pg_trgm для ILIKE поиска) ## 7. Деплой и CI/CD - Локально: Docker Compose (just up/build) @@ -70,18 +88,18 @@ RFC определяет интерфейсы для API, операционны ## 8. Миграции данных и схем - Использовать alembic для миграций схем PostgreSQL. -- Версионировать фичи и контракт входных данных (jsonschema). -- При изменениях схем: обеспечить миграционные скрипты + миграционный план с откатом. +- Использовать вспомогательные скрипты для выгрузки датасета в хранилище ## 9. Безопасность и секреты - Секреты в ENV (environment secrets в CI). -- Валидация входящих данных и rate-limiting (Redis). +- Валидация входящих данных, шифрование конфиденциальных данных ## 10. Мониторинг и логирование -- Логи структурированные (JSON), собираются в централизованный collector (ELK/Prometheus + Grafana для метрик). -- Метрики: latency, error_rate, throughput, model_drift indicators (distribution shifts), resource usage. -- Алёрты: SLO/SLA для latency/errors. +- Prometheus метрики через prometheus-fastapi-instrumentator +- Логирование через стандартный Python logging +- Базовый healthcheck endpoint ## 11. Тестирование -- Unit tests - тестируют бизнес логику -- E2E - тестируют весь user flow +- Unit tests - тестируют бизнес логику (entities, factories, invariants) +- E2E - тестируют весь user flow через TestApiGateway +- Интеграционные тесты для взаимодействия с хранилищем From 0e41a1210ca0211186b16a4697543592073c9149 Mon Sep 17 00:00:00 2001 From: gitgernit Date: Sun, 23 Nov 2025 14:56:17 +0300 Subject: [PATCH 2/2] fix(): reset vectors similarity threshold --- src/dataset/load_dump.sh | 12 ++++++++---- .../adapters/data_gateways/vacancy.py | 2 +- uv.lock | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/dataset/load_dump.sh b/src/dataset/load_dump.sh index c9e6006..b951f41 100755 --- a/src/dataset/load_dump.sh +++ b/src/dataset/load_dump.sh @@ -1,16 +1,20 @@ #!/bin/bash -DB_URL="${DATABASE_URL:-postgresql://user:password@localhost:5432/dbname}" -DUMP_FILE="${1:-dump_data.sql}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +DUMP_FILE="${1:-$PROJECT_ROOT/dumps/data_dump.sql}" if [ ! -f "$DUMP_FILE" ]; then echo "Ошибка: файл $DUMP_FILE не найден" + echo "Использование: $0 [путь_к_дампу]" + echo "По умолчанию: $PROJECT_ROOT/dumps/data_dump.sql" exit 1 fi -echo "Импорт дампа из $DUMP_FILE в БД..." +DB_URL="${DATABASE_URL:-postgresql://postgres:postgres@localhost:5432/app}" + +echo "Импорт дампа из $DUMP_FILE в БД $DB_URL..." psql "$DB_URL" -f "$DUMP_FILE" echo "Импорт завершен!" - diff --git a/src/template_project/adapters/data_gateways/vacancy.py b/src/template_project/adapters/data_gateways/vacancy.py index d9de454..806ee58 100644 --- a/src/template_project/adapters/data_gateways/vacancy.py +++ b/src/template_project/adapters/data_gateways/vacancy.py @@ -22,7 +22,7 @@ class DefaultVacancyDataGateway(VacancyDataGateway): statement = ( select(Vacancy, label("resume_similarity", similarity_expr)) .join(VacancyEmbedding, vacancy_embedding_table.c.vacancy_id == vacancy_table.c.id) - .where(similarity_expr >= 0.5) + .where(similarity_expr >= 0.0) .order_by(distance_expr.asc()) .limit(100) ) diff --git a/uv.lock b/uv.lock index 6d06a66..5cd51c8 100644 --- a/uv.lock +++ b/uv.lock @@ -2406,7 +2406,6 @@ dependencies = [ { name = "fastapi" }, { name = "levenshtein" }, { name = "markupsafe" }, - { name = "prometheus-fastapi-instrumentator" }, { name = "pydantic", extra = ["email"] }, { name = "uuid-utils" }, { name = "uvicorn" }, @@ -2420,6 +2419,7 @@ backend = [ { name = "firebase-admin" }, { name = "httpx" }, { name = "pgvector" }, + { name = "prometheus-fastapi-instrumentator" }, { name = "psycopg", extra = ["binary"] }, { name = "python-multipart" }, { name = "sqlalchemy" }, @@ -2482,7 +2482,6 @@ requires-dist = [ { name = "fastapi", specifier = "==0.119.0" }, { name = "levenshtein", specifier = ">=0.27.3" }, { name = "markupsafe", git = "https://github.com/pallets/markupsafe?rev=3.0.2" }, - { name = "prometheus-fastapi-instrumentator", specifier = ">=7.1.0" }, { name = "pydantic", extras = ["email"], specifier = ">=2.12.4" }, { name = "uuid-utils", specifier = "==0.11.1" }, { name = "uvicorn", specifier = "==0.37.0" }, @@ -2496,6 +2495,7 @@ backend = [ { name = "firebase-admin", specifier = ">=7.1.0" }, { name = "httpx", specifier = "==0.28.1" }, { name = "pgvector", specifier = ">=0.4.1" }, + { name = "prometheus-fastapi-instrumentator", specifier = ">=7.1.0" }, { name = "psycopg", extras = ["binary"], specifier = ">=3.2.12" }, { name = "python-multipart", specifier = ">=0.0.20" }, { name = "sqlalchemy", specifier = "==2.0.44" },