From 3d7b12d63d00254921ae622dab86edc0e1322839 Mon Sep 17 00:00:00 2001 From: dmitriylaukhin Date: Fri, 26 Jun 2026 17:47:28 +0500 Subject: [PATCH] =?UTF-8?q?deploy:=20=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC?= =?UTF-8?q?=D0=B0=20=D0=B6=D0=B8=D0=B2=D0=B0=D1=8F=20=D0=BD=D0=B0=20med.se?= =?UTF-8?q?condbrain.tools=20+=20=D0=B0=D0=BA=D1=82=D1=83=D0=B0=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=B9=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - infra/deploy/ — реальный compose (API-контейнер за Caddy) + Caddyfile - docs/quality_report.json — отчёт: 8871 позиций, 73.9% автонормализации - README переписан под факт: стек, результаты, запуск (ru-text 8.6) Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 71 +++++++++++++++++++++------------ docs/quality_report.json | 15 +++++++ infra/deploy/Caddyfile | 19 +++++++++ infra/deploy/README.md | 19 +++++++++ infra/deploy/docker-compose.yml | 29 ++++++++++++++ 5 files changed, 128 insertions(+), 25 deletions(-) create mode 100644 docs/quality_report.json create mode 100644 infra/deploy/Caddyfile create mode 100644 infra/deploy/README.md create mode 100644 infra/deploy/docker-compose.yml diff --git a/README.md b/README.md index a593cf8..c164c1d 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,58 @@ -# medtech-hackathon +# MedArchive — обработка архива прайсов клиник-партнёров -Terricon Valley · Medtech Hackathon (26–28.06.2026) · **Кейс 1 — автоматическая обработка архива прайсов клиник-партнёров.** +Terricon Valley · Medtech Hackathon (26–28.06.2026). -Превращаем архив разнородных прайс-листов клиник (Excel / PDF / скан / фото / .doc, RU/KZ/EN, грязные данные) в единый сравнимый каталог **услуга → клиника → цена** с поиском, сравнением цен и экспортом. +**Живой демо: https://med.secondbrain.tools** -## Стек +Система автоматически разбирает архив разнородных прайс-листов клиник (PDF, сканы, DOCX, +XLSX/XLS), извлекает услуги и цены, нормализует их к справочнику из 1281 услуги и даёт поиск +и сравнение «кто оказывает услугу и по какой цене». -- **ETL/бэкенд:** Python + FastAPI (pandas для Excel, LLM-vision для грязного, pdfplumber, Tesseract `kaz+rus` fallback) -- **Канонизация услуг:** sentence-transformers + pgvector + детерминированная матрица конфликтов (см. словарик) -- **БД:** PostgreSQL + pgvector -- **Фронт:** Next.js + Tailwind + shadcn/ui (дизайн на фирменной палитре) -- **Деплой:** Docker Compose + Caddy → `med.secondbrain.tools` (Selectel KZ) +## Результат на тестовом архиве (8 клиник, 10 файлов) -## Языки +- **8 871 позиция** извлечена: 6 клиник из 8 — нативными парсерами, трудные PDF (битый + текстовый слой, нестандартная вёрстка) — через Gemini Vision. +- **73,9% позиций нормализованы автоматически** (цель ТЗ — не ниже 70%). +- Остальное уходит в очередь ручной разметки, как и предполагает ТЗ. -KAZ / RUS / ENG — сквозное требование (данные и UI). +Полный отчёт о качестве — `docs/quality_report.json`. -## Структура (план) +## Стек и решения -``` -contracts/ — модель данных + Zod-схемы + openapi.yaml (агент A) -db/ — миграции (агент A) -etl/ — парсинг и нормализация (агент B) -api/ — REST API поиска/сравнения (агент C) -web/ — Next.js UI (агент D) -infra/ — docker-compose, Caddyfile (агент E) -docs/ — документация, ADR (агент F) -pitch/ — слайды и демо-сценарий (агент F) -fixtures/ — синтетические + эталонные данные +| Слой | Решение | +|---|---| +| Извлечение | Python: pdfplumber + PyMuPDF, openpyxl/xlrd, python-docx. Таблицы из PDF без линий восстанавливаются по координатам слов | +| Vision-добор | Gemini (`gemini-2.5-flash`) — для сканов и битого текстового слоя | +| Нормализация | Каскад: код тарификатора → точное совпадение → эмбеддинги Gemini → нечёткое сравнение → очередь `unmatched` | +| Валидация | Правила ТЗ §4.4: положительность цены, резидент ≥ нерезидент, аномалии, версионирование | +| Хранилище | SQLite (MVP, разворачивается без Docker). Боевая схема PostgreSQL + pgvector — в `db/migrations` | +| API | FastAPI: 7 эндпоинтов ТЗ + `/stats`, OpenAPI на `/docs` | +| Фронт | Одностраничный (Tailwind): поиск услуги и сравнение цен по клиникам | +| Деплой | Контейнер API за Caddy на Selectel KZ | + +## Запуск локально + +```bash +python -m venv .venv && .venv/bin/pip install -e . +# архив прайсов → data/raw/, справочник → data/reference/dictionary.xlsx +GEMINI_API_KEY=... python scripts/run_pipeline.py # → data/medarchive.db + отчёт +uvicorn api.main:app # → http://localhost:8000/docs +# открыть web/index.html?api=http://localhost:8000 ``` -Скелет кода поднимается в Фазе 0 хакатона после заморозки контрактов. +Эмбеддинги и Vision вызываются через Gemini API, поэтому локальная модель не нужна и контейнер +остаётся лёгким. Поиск и сравнение в готовой базе работают без обращений к API. -## Полный план +## Структура -В хранилище Obsidian: `Base/_Inbox/Хакатон Medtech/` (план, словарик нормализации, дизайн-система, памятка). +``` +contracts/ — общие модели данных (контракт между модулями) +db/ — схема PostgreSQL + pgvector +etl/ — извлечение, нормализация, валидация, конвейер +api/ — REST API поиска и сравнения +web/ — одностраничный фронт +infra/ — деплой (compose, Caddyfile) +docs/ — отчёт о качестве, план +pitch/ — презентация и демо-сценарий +scripts/ — запуск конвейера и проверки +``` diff --git a/docs/quality_report.json b/docs/quality_report.json new file mode 100644 index 0000000..1c07a49 --- /dev/null +++ b/docs/quality_report.json @@ -0,0 +1,15 @@ +{ + "documents": 10, + "partners": 8, + "items": 8871, + "auto_matched": 6555, + "auto_matched_pct": 73.9, + "unmatched": 2316, + "by_method": { + "code": 1414, + "exact": 167, + "embedding": 4903, + "fuzzy": 71, + "null": 2316 + } +} \ No newline at end of file diff --git a/infra/deploy/Caddyfile b/infra/deploy/Caddyfile new file mode 100644 index 0000000..2a2a40a --- /dev/null +++ b/infra/deploy/Caddyfile @@ -0,0 +1,19 @@ +{ + email laukhin@dementiy.com + acme_ca https://acme-v02.api.letsencrypt.org/directory +} + +med.secondbrain.tools { + handle_path /api/* { + reverse_proxy api:8000 + } + handle /demo* { + root * /srv + file_server + } + handle { + root * /web + file_server + encode gzip + } +} diff --git a/infra/deploy/README.md b/infra/deploy/README.md new file mode 100644 index 0000000..948ab91 --- /dev/null +++ b/infra/deploy/README.md @@ -0,0 +1,19 @@ +# Деплой MedArchive + +Лёгкая схема: контейнер API (FastAPI на готовой SQLite-базе) за общим Caddy на Selectel KZ. +Рантайму не нужны ни Gemini, ни модель — эмбеддинги и Vision отработали на этапе сборки базы. + +## Шаги + +1. Залить на сервер: код (`api/`, `contracts/`), фронт (`web/`), базу (`data/medarchive.db`) и + отчёт (`data/quality_report.json`) — в `/root/medarchive/`. +2. Положить `docker-compose.yml` и `Caddyfile` из этой папки в каталог с Caddy. +3. `docker compose up -d` — поднимется `medarchive-api` (ставит зависимости и запускает uvicorn). +4. Маршруты Caddy: `/api/*` → контейнер API, `/` → фронт, `/demo` → презентация. + +## Проверка + +```bash +curl https://med.secondbrain.tools/api/health +curl https://med.secondbrain.tools/api/stats +``` diff --git a/infra/deploy/docker-compose.yml b/infra/deploy/docker-compose.yml new file mode 100644 index 0000000..01df1ba --- /dev/null +++ b/infra/deploy/docker-compose.yml @@ -0,0 +1,29 @@ +services: + caddy: + image: caddy:2-alpine + container_name: caddy + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - ./site:/srv:ro + - /root/medarchive/web:/web:ro + - caddy_data:/data + - caddy_config:/config + + api: + image: python:3.12-slim + container_name: medarchive-api + restart: unless-stopped + working_dir: /app + volumes: + - /root/medarchive:/app + environment: + MEDARCHIVE_DB: /app/data/medarchive.db + command: sh -c "pip install --quiet --no-cache-dir fastapi 'uvicorn[standard]' pydantic && uvicorn api.main:app --host 0.0.0.0 --port 8000" + +volumes: + caddy_data: + caddy_config: