# Контракт-чек LLM-сервис скрининга рисков в договорах (PDF/DOCX) по ГК РФ / ГК РБ. Telegram-бот MVP + B2B API сейчас; веб + подписки — позже. Pipeline: `PDF/DOCX → текст (pymupdf/tesseract OCR) → чанки → Ollama Cloud (LLM, json-schema + repair-loop) → markdown-отчёт` с цитатами, ссылкой на пункт и дисклеймером «не заменяет юриста». ## Документы - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — **единственный источник истины** для production-рефакторинга. Hexagonal-архитектура, RabbitMQ-конвейер, схемы БД, очередь/ретраи/DLQ, конфиг, deploy, observability. - [`docs/DEPLOY.md`](docs/DEPLOY.md) — руководство по развёртыванию: локально, Docker Compose, VPS, seed-token, backup, troubleshooting. - [`docs/BUSINESS_IDEA.md`](docs/BUSINESS_IDEA.md) — идея и бизнес-модель. - [`docs/IMPLEMENTATION_PLAN.md`](docs/IMPLEMENTATION_PLAN.md) — исходный «ленивый» план. Этап 0 (прототип) актуален; этапы 1+ superseded в `docs/ARCHITECTURE.md`. - [`docs/TICKETS.md`](docs/TICKETS.md) — тикеты реализации со статусами. ## Архитектура (одна строка) Hexagonal (ports & adapters). Ядро `contract_check.core` владеет всем состоянием (Postgres/MinIO/RabbitMQ/LLM/кредиты). Четыре сервиса собираются из него: ``` Telegram ──► bot (aiogram, HTTP-only) ──HTTP──► api (FastAPI) ──publish──► RabbitMQ владеет: PG, MinIO, │ Redis, кредитами ▼ extract.q ──► worker-extract (pymupdf+tesseract, CPU) │ ▼ publish analyze.q ──► worker-analyze (LLM, I/O) → Report ``` - **api** — единственный писатель для пользовательских мутаций (upload → reserve credit → MinIO → publish `DocumentUploaded`). - **worker-extract** — CPU: достаёт текст (PDF/DOCX), при необходимости OCR, грузит `.txt` в MinIO, публикует `DocumentExtracted`. - **worker-analyze** — I/O: LLM-анализ по чек-листу, валидация/repair, сохраняет `Report`, `status=done`. - **bot** — адаптер: HTTP-клиент к api, **не импортирует** core.db/s3/llm/mq (граница проверяется тестом `tests/unit/test_bot_boundary.py`). ## Структура репозитория ``` src/contract_check/ __main__.py # указывает на prototype (stage-0 CLI сохранён) core/ # общий домен (импортируется каждым сервисом) config.py logging.py telemetry.py sentry.py metrics.py credits.py tokens.py api_keys.py rate_limit.py redis_client.py db/ models.py session.py enums.py mq/ topology.py publisher.py consumer.py messages.py s3/ port.py minio_storage.py llm/ port.py ollama_cloud.py factory.py analysis/ extractor.py chunker.py checklist.py report_schema.py ocr.py analyzer.py api/ # FastAPI-образ app.py deps.py middleware.py services.py __main__.py admin/ # серверный UI по /admin (users; позже — подписки) routes/ health.py documents.py reports.py me.py metrics.py b2b.py worker_extract/ # CPU-образ (pymupdf + tesseract) consumer.py handler.py extract_document.py __main__.py worker_analyze/ # I/O-образ (LLM provider) consumer.py handler.py __main__.py bot/ # aiogram-адаптер (самый «тощий» образ: только core.logging) client.py config.py handlers.py __main__.py prototype/ # stage-0 standalone CLI (бенчмарк go/no-go) docs/ # документация проекта (README остаётся в корне) ARCHITECTURE.md # архитектура, схемы БД, конфиг DEPLOY.md # руководство по развёртыванию TICKETS.md # статусы тикетов IMPLEMENTATION_PLAN.md # исходный план (superseded для этапов 1+) BUSINESS_IDEA.md # продукт / бизнес-модель srv/ # Dockerfile-ы (один на сервис, deps заточены) api/Dockerfile worker-extract/Dockerfile worker-analyze/Dockerfile bot/Dockerfile prototype/Dockerfile migrations/ # alembic (async): 0001_initial, 0002_api_keys tests/ conftest.py unit/ chunker, extractor, llm_ollama_cloud, credits, messages, rate_limit, checklist_report, bot_client, bot_boundary integration/ upload_pipeline, extract_worker, analyze_worker, b2b_api, credits_db docker-compose.yml # default = инфра; --profile services = стек pyproject.toml # hatchling + PEP 735 dependency-groups (db/mq/s3/obs/api/extract/analyze/bot/prototype/dev) .env.example # полный список env (см. docs/ARCHITECTURE.md §11) ``` ## Быстрый старт ### Локально (разработка) ```bash uv sync --group dev # все группы для локальной разработки cp .env.example .env # впишите OLLAMA_HOST / OLLAMA_API_KEY docker compose up -d # только инфра (postgres/redis/rabbitmq/minio) uv run alembic upgrade head # миграции uv run python -m contract_check.api # api на :8000 uv run python -m contract_check.worker_extract # воркер экстракции uv run python -m contract_check.worker_analyze # воркер анализа uv run python -m contract_check.bot # Telegram-бот ``` ### Через Docker Compose ```bash cp .env.example .env # заполнить секреты (DB, Rabbit, MinIO, Ollama, BOT_TOKEN, ...) docker compose up -d # только инфра с healthchecks docker compose --profile services up -d --build # + api, worker-extract, worker-analyze, bot ``` Профиль `services` собирает 4 образа из `srv//Dockerfile` и поднимает их с `depends_on: condition: service_healthy`. Observability (Prometheus/Grafana/Tempo/OTel) и edge (Nginx/certbot) — за будущими профилями `obs`/`edge` (`docs/ARCHITECTURE.md §20`, шаги 1–5 реализованы; шаг 6 — позже). Порты на хосте (смещены, чтобы не конфликтовать): Postgres `15432`, Redis `17379`, RabbitMQ AMQP `5672` / UI `15672`, MinIO `9000` / console `9001`, api `8000` / metrics `9100`, worker metrics `9101`/`9102`. ### Stage-0 прототип (бенчмарк) ```bash uv sync --group prototype uv run python -m contract_check prototype contract.pdf # отчёт в stdout uv run contract-check contract.pdf -o report.md # или консольная команда uv run python -m contract_check prototype contract.pdf --json metrics.json # + метрики go/no-go ``` ## API (кратко) JSON-роуты под `/api/v1`; серверный admin UI — по `/admin/*` (FastAPI + Jinja2 + HTMX). Auth зависит от роута: - **Пользовательские роуты** (bot / web / Mini App) — `Authorization: Bearer `. JWT выдаётся через `/api/v1/auth/telegram/*` после проверки identity от Telegram. - **Адаптер-level** (только `/api/v1/auth/telegram/bot`) — `Authorization: Bearer `. - **B2B** — `X-API-Key`. Управление B2B-ключами требует пользовательский JWT. - **Health/metrics** — без auth. - **/admin** — HttpOnly cookie с user JWT + `users.role = admin` (или `ADMIN_REQUIRED_ROLE`). | Метод | Путь | Auth | Назначение | |---|---|---|---| | GET | `/healthz`, `/readyz`, `/metrics` | — | liveness / readiness / Prometheus | | POST | `/api/v1/auth/telegram/bot` | service token | бот меняет verified `telegram_id` на JWT | | POST | `/api/v1/auth/telegram/web` | — | Telegram Login Widget → JWT | | POST | `/api/v1/auth/telegram/miniapp` | — | Mini App `initData` → JWT | | POST | `/api/v1/auth/register` | — | регистрация email/password | | POST | `/api/v1/auth/login` | — | вход email/password → JWT-пара | | GET | `/api/v1/auth/me` | user JWT | introspect JWT | | POST | `/api/v1/documents` | user JWT | multipart upload → reserve credit → MinIO → publish → `202 {document_id, correlation_id}` | | GET | `/api/v1/documents/{id}` | user JWT | статус + stage (для поллинга) | | GET | `/api/v1/reports/{document_id}` | user JWT | `202 {status, stage}` или `200 {markdown, findings, ...}` | | GET | `/api/v1/me` | user JWT | `{telegram_id, credits_left}` | | POST | `/api/v1/analyze` | `X-API-Key` | B2B: анализ документа | | GET | `/api/v1/b2b/reports/{id}`, `/api/v1/b2b/usage` | `X-API-Key` | B2B: отчёт / usage | | POST/GET | `/api/v1/b2b/keys`, `/api/v1/b2b/keys/{id}/revoke`, `.../usage` | user JWT | управление B2B-ключами | | GET/POST | `/admin/users*` | admin cookie | управление пользователями (list, create, edit, ban, role, credits) | Полная спецификация — `docs/ARCHITECTURE.md §15` (актуализируется). ## Проверки (DoD) ```bash uv run ruff check . && uv run mypy src && uv run pytest -q # unit, быстро uv run pytest -m integration -q # интеграционные (нужны контейнеры) ```