deploy: система живая на med.secondbrain.tools + актуальный README
- 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) <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
## Результат на тестовом архиве (8 клиник, 10 файлов)
|
||||||
- **Канонизация услуг:** sentence-transformers + pgvector + детерминированная матрица конфликтов (см. словарик)
|
|
||||||
- **БД:** PostgreSQL + pgvector
|
|
||||||
- **Фронт:** Next.js + Tailwind + shadcn/ui (дизайн на фирменной палитре)
|
|
||||||
- **Деплой:** Docker Compose + Caddy → `med.secondbrain.tools` (Selectel KZ)
|
|
||||||
|
|
||||||
## Языки
|
- **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)
|
| Извлечение | Python: pdfplumber + PyMuPDF, openpyxl/xlrd, python-docx. Таблицы из PDF без линий восстанавливаются по координатам слов |
|
||||||
etl/ — парсинг и нормализация (агент B)
|
| Vision-добор | Gemini (`gemini-2.5-flash`) — для сканов и битого текстового слоя |
|
||||||
api/ — REST API поиска/сравнения (агент C)
|
| Нормализация | Каскад: код тарификатора → точное совпадение → эмбеддинги Gemini → нечёткое сравнение → очередь `unmatched` |
|
||||||
web/ — Next.js UI (агент D)
|
| Валидация | Правила ТЗ §4.4: положительность цены, резидент ≥ нерезидент, аномалии, версионирование |
|
||||||
infra/ — docker-compose, Caddyfile (агент E)
|
| Хранилище | SQLite (MVP, разворачивается без Docker). Боевая схема PostgreSQL + pgvector — в `db/migrations` |
|
||||||
docs/ — документация, ADR (агент F)
|
| API | FastAPI: 7 эндпоинтов ТЗ + `/stats`, OpenAPI на `/docs` |
|
||||||
pitch/ — слайды и демо-сценарий (агент F)
|
| Фронт | Одностраничный (Tailwind): поиск услуги и сравнение цен по клиникам |
|
||||||
fixtures/ — синтетические + эталонные данные
|
| Деплой | Контейнер 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/ — запуск конвейера и проверки
|
||||||
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
```
|
||||||
@@ -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:
|
||||||
Reference in New Issue
Block a user