@Eugene Sukhodolskiy Eugene Sukhodolskiy authored 1 day ago
.claude/ plans feat: classify AI errors — ValidationError→invalid, fatal/network→failed 1 day ago
alembic feat: dockerize app, add structured logging, fix rate limiter 1 day ago
docs feat: implement review items 1-7 1 day ago
src/ vmk_data_collector fix: use configured model name in ai_enricher instead of hardcoded llama3.2 1 day ago
tests fix: code review critical and high issues 1 day ago
.dockerignore feat: dockerize app, add structured logging, fix rate limiter 1 day ago
.env.example feat: dockerize app, add structured logging, fix rate limiter 1 day ago
.gitignore feat: core pipeline + FastAPI API (Phases 0-6) 1 day ago
Dockerfile feat: dockerize app, add structured logging, fix rate limiter 1 day ago
README.md docs: update README with Docker guide and parser API reference 1 day ago
alembic.ini feat: core pipeline + FastAPI API (Phases 0-6) 1 day ago
docker-compose.yml feat: dockerize app, add structured logging, fix rate limiter 1 day ago
pyproject.toml feat: implement review items 8-14 1 day ago
README.md

VMK 360 Data Collector

Описание

Сервис приёма, нормализации и ИИ-обогащения данных об объектах недвижимости.

  • Приём: Принимает полусырые данные от парсеров через REST API
  • Валидация: AI определяет, является ли payload объявлением о недвижимости
  • Нормализация: Приводит неструктурированные данные к единому формату
  • Обогащение: Анализ изображений (vision) + текстовый анализ (NER, summary, оценка цены)
  • Хранение: Сохраняет в PostgreSQL с полной историей изменений (snapshots)

Быстрый старт (Docker)

# 1. Клонировать и перейти в директорию
cd vmk_data_collector

# 2. Скопировать конфиг
#   Отредактируй .env под себя (OLLAMA_BASE_URL, порты и т.д.)
cp .env.example .env

# 3. Запуск всего стека (PostgreSQL + FastAPI)
docker compose up -d --build

# 4. Проверка
#   Документация API: http://localhost:8020/docs
#   Health check:      http://localhost:8020/api/v1/health

Стоп/очистка

docker compose down            # остановить
docker compose down -v        # остановить + удалить данные БД

Ручной старт (для разработки)

# 1. PostgreSQL должен быть доступен (локально или в Docker)
#    Порт по умолчанию: 5432 (хост) / 5433 (если через docker-compose)

# 2. Установка зависимостей
pip install -e ".[dev]"

# 3. Применение миграций
alembic upgrade head

# 4. Запуск приложения
uvicorn vmk_data_collector.main:app --reload --port 8020

API для парсеров

POST /api/v1/ingest

Принимает сырые данные от парсеров, валидирует payload и ставит задачу в очередь на обработку.

Заголовки

Заголовок Обязательный Значение
Content-Type Да application/json

Тело запроса

{
  "source_slug": "avito",
  "external_id": "avito-12345678",
  "payload": {
    "title": "2-комнатная квартира, 65 м², 5/25 этаж",
    "description": "Продается просторная двухкомнатная квартира в новостройке. Рядом метро.",
    "price": 8500000,
    "url": "https://avito.ru/item/12345678",
    "images": [
      "https://avito.ru/img1.jpg",
      "https://avito.ru/img2.jpg"
    ],
    "contact_phone": "+7 (999) 123-45-67",
    "address": "Москва, Тверская ул., 1",
    "area": 65.5,
    "rooms": 2,
    "floor": 5
  }
}

Поля payload

Поле Тип Обязательное Описание
title string Нет Заголовок объявления
description string Нет Описание объявления
price number / string Нет Цена (руб.)
url string Нет Ссылка на источник
images string[] Нет Массив URL изображений
contact_phone string Нет Телефон продавца
address string Нет Адрес объекта
area number / string Нет Площадь (м²)
rooms integer / string Нет Количество комнат
floor integer / string Нет Этаж

Важно: хотя бы одно из полей title или description должно присутствовать.

Ответ 202 Accepted (успех)

{
  "job_id": 42,
  "property_id": null,
  "status": "pending",
  "reason": null,
  "message": "Queued for processing",
  "snapshot_id": null
}

Ответ 422 Unprocessable Entity (ошибка валидации)

{
  "detail": "Invalid payload: 1 validation error for PayloadSchema..."
}

Статусы обработки

После приёма задача (job_id) проходит через pipeline:

Статус Значение
pending Задача поставлена в очередь
processing AI нормализует данные
completed Объект сохранён в БД
invalid AI определил, что это не недвижимость
failed Ошибка на стадии обработки (Ollama недоступен и т.д.)

Примеры (curl)

Успешный ingest

curl -X POST http://localhost:8020/api/v1/ingest \
  -H "Content-Type: application/json" \
  -d '{
    "source_slug": "avito",
    "external_id": "avito-99999",
    "payload": {
      "title": "3-комнатная квартира, 120 м²",
      "description": "Элитная квартира в центре",
      "price": 25000000,
      "url": "https://avito.ru/item/99999",
      "address": "Москва, Сити"
    }
  }'

Проверка архивации (404/410 = объявление снято)

curl -X POST http://localhost:8020/api/v1/listings/1/archive-check

Конфигурация

Все настройки задаются через переменные окружения (.env или docker-compose.yml):

Переменная Дефолт Описание
APP_PORT 8020 Порт приложения (хост)
DATABASE_URL postgresql+asyncpg://postgres:postgres@postgres:5432/vmk_data PostgreSQL (async)
OLLAMA_BASE_URL http://192.168.1.75:11434 Ollama API
OLLAMA_TEXT_MODEL gemma4:e2b-it-q4_K_M Модель для текстовых задач
OLLAMA_VISION_MODEL gemma4:e2b-it-q4_K_M Модель для анализа изображений
OLLAMA_TIMEOUT 120 Таймаут запроса к Ollama (сек)
LOG_LEVEL INFO Уровень логирования

Архитектура

┌──────────────┐     ┌──────────────┐     ┌──────────────────┐
│   Парсеры    │────▶│  FastAPI     │────▶│  PostgreSQL      │
│  (curl/HTTP) │     │  /api/v1/    │     │  (raw_data +     │
└──────────────┘     │  ingest      │     │   property_...)   │
                     └──────┬───────┘     └──────────────────┘
                            │
                            ▼
                     ┌──────────────┐
                     │  QueueWorker │
                     │  (async bg)  │
                     └──────┬───────┘
                            │
              ┌─────────────┼─────────────┐
              ▼             ▼             ▼
        ┌─────────┐   ┌─────────┐   ┌─────────┐
        │AI Норма-│   │AI Анализ│   │AI Обога-│
        │лизатор  │   │изображ. │   │щение    │
        └────┬────┘   └────┬────┘   └────┬────┘
             │             │             │
             └─────────────┴─────────────┘
                           │
                           ▼
                     ┌──────────────┐
                     │   Ollama     │
                     │ 192.168.1.75 │
                     └──────────────┘
  • API Layer — FastAPI, Pydantic валидация, structured logging (structlog)
  • Service LayerPropertyPipeline, AI-нормализация, обогащение, QueueWorker
  • Repository Layer — абстракция доступа к PostgreSQL (async SQLAlchemy)
  • Domain Layer — чистые сущности недвижимости
  • Infrastructure Layer — Ollama client, image downloader, rate limiter

Документация


Логирование

Все логи выводятся в stdout в формате JSON (structlog), что удобно для сбора через docker compose logs:

# Следить за логами в реальном времени
docker compose logs -f app

# Посмотреть последние 50 строк
docker compose logs --tail=50 app

Ключевые события для мониторинга:

  • ingest_request — новый запрос от парсера
  • ingest_accepted — задача поставлена в очередь
  • ingest_validation_failed — ошибка валидации payload
  • pipeline_start — начало обработки задачи
  • pipeline_completed — задача успешно завершена
  • pipeline_not_real_estate — AI отбросил объект
  • ollama_chat_request/response — запросы к Ollama