# План реализации «Контракт-чек» (Ollama Cloud / hosted LLM) > **Примечание:** этот документ описывает исходную «ленивую» архитектуру с arq+Redis > (очередь), Selectel S3 (хранилище) и единым Docker-образом с `MODE=api|worker|bot`. > **Этап 0 (прототип) актуален.** Этапы 1+ superseded [`ARCHITECTURE.md`](ARCHITECTURE.md), > где реализована production-архитектура: RabbitMQ-конвейер, MinIO, 4 worker-а > (extract, prescreen, analyze, notify), aiogram-бот, B2B API, 7 Dockerfile-ов в `srv/`, > dependency-groups (PEP 735). > Принцип: ленивая архитектура. Каждый этап — минимальный работающий > срез. Никаких абстракций «на потом». K8s, RabbitMQ, микросервисы — > не нужны до $10k MRR. > > **LLM:** [Ollama Cloud](https://ollama.com/cloud) — hosted-инференс > открытых моделей. Те же модели и API (`/api/chat`, `format: json`), > что и у локального Ollama, но без своего железа: платим подписку + > metered overage, инференс бежит у Ollama (NCP-партнёры, в основном США). --- ## О выборе Ollama Cloud - **API:** стандартный Ollama HTTP API на cloud-эндпоинте. Авторизация — API-ключ (bearer) из настроек аккаунта Ollama. Структурный вывод через `format: json` (или JSON-schema) для парсимого отчёта. - **Модели:** только cloud-enabled (см. `ollama.com/search?c=cloud`). Рекомендация — **`qwen2.5:14b`** (сильный русский + reasoning). Fallback — более лёгкая **`qwen2.5:7b`** при 429/квоте. Теги БЕЗ суффикса `-instruct` (его нет в Ollama): `qwen2.5:14b`, `llama3.1:8b`, `gemma2:9b` и т.п. - **`format: json` ≠ соответствие схеме.** Ollama гарантирует синтаксис JSON; соответствие вашей pydantic-схеме проверяем на клиенте + цикл repair/retry. Не полагаемся на модель. ## Стоимость и лимиты Ollama Cloud (актуально на момент планирования) | План | Цена | Конкарренси | Назначение | Статус | |-------|-------------|-------------|-------------------------|------------------| | Free | $0 | 1 | тесты | открыт | | Pro | $20/мес | 3 | рабочая лошадка MVP | открыт | | Max | $100/мес | 10 | тяжёлый поток | **приостановлен** | | Team | $25/seat | — | команда (5 seat min) | waitlist | | Enterprise | custom | custom | продакшн SaaS | по запросу | - **Usage:** rolling-лимиты — сессия 5 ч + недельный 7 дн. Pro = 50× Free. При превышении — **metered overage** (добиваем баланс, оплата по токенам модели). Жёсткой стены нет, но конкарренси = 3 на Pro. - **Хостинг: в основном США** (подключаются EU/Singapore). Это **ломает 152-ФЗ data-residency**, см. раздел «Риски». ## Экономика проекта (честно) - Опекс фиксированный до квоты: VPS ~4 €/мес + Ollama Cloud Pro $20/мес + Selectel S3 (копейки). Итого ~$25–30/мес на старте. - Рост объёма → metered overage Ollama Cloud по токенам. Маржа на pay-per-doc (199 ₽) остаётся, **если** один договор съедает мало квоты. Это **обязательно измерить на этапе 0** (квота/документ). - В отличие от локального Ollama — нет потолка по железу, но есть потолок по конкарренси (3) и плавающий cost при overage. В отличие от GigaChat — cost в $, не в ₽, и нет РФ-локализации данных. --- ## Архитектура — hexagonal (ports & adapters) Ядро и адаптеры разделены. Это снимает связность «бот знает про БД/S3/LLM» и позволяет добавлять каналы доставки (CLI, веб, B2B API) без дублирования бизнес-логики. - **Ядро (application core):** - `api` (FastAPI/uvicorn) — владеет БД, S3, кредитами, оплатой; принимает документы, **резервирует кредит на enqueue**, ставит arq-задачи в redis, отдаёт отчёты/профиль. Аутентификация адаптеров — общий `SERVICE_TOKEN`. - `worker` (arq) — разбирает очередь: S3 → экстракт → (OCR) → chunker → analyzer → Ollama Cloud → Report. Idempotency + refund при ошибке. - Общий доменный пакет `contract_check/` (`extractor`, `chunker`, `llm_client`, `analyzer`, `checklist`, `report_schema`, `ocr`, `storage`, `db`, `models`, `payments`, `quota`) — используется только ядром. - **Адаптеры (delivery = HTTP-клиенты к `api`):** - `bot` (aiogram) — Telegram: приём документа → `POST /documents` (multipart) → отдача отчёта. **Не трогает БД/S3/redis/LLM.** - `cli` — пользовательский/отладочный CLI поверх api (новый, отдельный от прототипа). - `web` (React SPA) — этап 2, тоже HTTP-клиент к api. - **Прототип `prototype.py` (stage 0)** — standalone, in-process LLM; артефакт go/no-go, **не адаптер и не ядро**. > Следствие для планирования: **api + worker строятся на этапе 1** (боту-адаптеру > нужен api). Этап 2 = добавление адаптера `web` + подписок (api уже есть). --- ## Этап 0 — Прототип (1 выходной) **Цель:** доказать, что модель через Ollama Cloud реально находит риски, и измерить задержку + **квоту на один договор** (для юнит-экономики). **Деливерэбл:** один файл `prototype.py` — end-to-end. ``` PDF/DOCX → pymupdf/python-docx → текст → prompt с чек-листом → Ollama Cloud (format: json) → отчёт в markdown ``` **Что делаем:** 1. Завести аккаунт Ollama, взять API-ключ, положить в `.env`. 2. Поставить `pymupdf`, `httpx` в venv. 3. Один скрипт: читает PDF → достаёт текст → промпт с чек-листом из 10 пунктов → дёргает Ollama Cloud → печатает отчёт. 4. Прогнать 3–5 реальных договоров (NDA, оказание услуг, поставка). 5. Зафиксировать: качество находок, задержка (сек), % битых JSON, **сколько квоты/токенов на договор**. **Чего НЕ делаем:** база, веб, бот, оплату, деплой. Один скрипт локально. **Критерий успеха:** отчёт по реальному договору содержит хотя бы 3 осмысленные находки с цитатами; задержка < 60 сек; квота/договор даёт понятную маржинальность при 199 ₽. --- ## Этап 1 — Telegram-бот MVP (2–3 недели) **Цель:** первые платящие пользователи. Бот = самый быстрый путь до ЦА. ### 1.1 Структура проекта ``` contract_check/ ├── pyproject.toml # hatchling, deps (core + adapters) ├── docker-compose.yml # postgres, redis, api, worker, bot (+ profile app = prototype) ├── .env.example ├── src/contract_check/ │ ├── main.py # диспетчер: MODE=api|worker|bot|cli │ ├── prototype.py # stage-0 standalone (in-process LLM) — НЕ в hexagonal │ │ # ── ЯДРО (application core): владеет БД/S3/кредитами/LLM ── │ ├── api.py # FastAPI: /documents, /reports/{id}, /me, /healthz │ ├── worker.py # arq analyze_document(doc_id): S3→extract→OCR→analyze→Report │ ├── analyzer.py extractor.py chunker.py ocr.py │ ├── llm_client.py checklist.py report_schema.py │ ├── storage.py db.py models.py payments.py quota.py │ │ # ── АДАПТЕРЫ (delivery): HTTP-клиенты к api, без БД/S3/LLM ── │ ├── bot.py # aiogram: приём документа → POST /documents → отчёт │ └── cli.py # CLI поверх api (новый, не прототип) ├── migrations/ # alembic └── tests/ ``` ### 1.2 База данных — 3 таблицы ```sql users (id, telegram_id, created_at, credits_left) documents (id, user_id, s3_key, status, created_at) reports (id, document_id, content_json, created_at) ``` `credits_left` — prepaid-кредиты (pay-per-doc). Подписок в v1 нет. `status` хранить как `TEXT` + `CHECK`, не Postgres-ENUM (миграции проще). ### 1.3 Поток (резервирование кредита на enqueue) ``` Юзер кидает PDF в бот → бот (adapter): multipart'ом шлёт файл в api: POST /documents (+ SERVICE_TOKEN) → api (core): get_or_create_user, проверяет credits_left > 0, РЕЗЕРВИРУЕТ (-= 1), грузит файл в S3, создаёт Document(status=queued), ставит arq-таску → 202 + job_id → worker (core): idempotency guard по status, достаёт из S3 → текст → (OCR если скан) → worker: chunker при необходимости → analyzer → Ollama Cloud → отчёт → worker: pydantic-валидация + repair-loop; пишет Report, status=done → при ошибке: status=failed, ВОЗВРАТ кредита (+1) → бот: опрашивает GET /reports/{id} (или push), отправляет отчёт (с разбивкой/файлом при >4096 симв.), футер-disclaimer ``` ### 1.4 Оплата (минимум) - ЮKassa: бот генерирует ссылку на оплату N ₽ → webhook пополняет `credits_left`. - Один тариф: 199 ₽ = 1 документ. Без подписок. ### 1.5 Деплой - **Один VPS** (Hetzner CX22 ~4 €/мес или Selectel под РФ-локацию БД). - Docker Compose: `postgres`, `redis`, `api`, `worker`, `bot`. **Без GPU.** (Бот — адаптер к api; веб-адаптер `web` добавится на этапе 2.) - S3 — Selectel Object Storage. - Домен + HTTPS только для webhook ЮKassa; на старте ngrok (с оговоркой: URL на free-ngrok меняется → лучше сразу дешёвый домен + Caddy). ### 1.6 Критерий успеха 10 платящих. MRR ~2 000 ₽. Отчёты не вызывают жалоб «ничего не нашёл». Ollama Cloud Pro покрывает нагрузку без ухода в overage. --- ## Этап 2 — Веб + подписки (3–4 недели) **Цель:** B2B-веб-интерфейс и подписки. ### 2.1 Что добавляем - **`api` уже построен на этапе 1** (T-E1-015). На этапе 2 добавляем: роуты подписок/счетов, Telegram Login auth (сессия/JWT) для веб-адаптера, и сам веб-адаптер `web` (React SPA) — ещё один HTTP-клиент к api. - React SPA (Vite): загрузка, история, профиль, подписка. Отдаётся статикой позже (отдельный `web`-контейнер / Nginx). - Auth: Telegram Login Widget (нужен публичный HTTPS-домен в BotFather) → сессия/JWT. - Подписки: solo (1 490 ₽), team (3 990 ₽). ЮKassa recurring. ### 2.2 Что меняем в базе ```sql ALTER TABLE users ADD COLUMN plan TEXT DEFAULT 'free'; ALTER TABLE users ADD COLUMN plan_renews_at TIMESTAMP; CREATE TABLE invoices (id, user_id, amount, status, provider, external_id, created_at); ``` `credits_left` остаётся для pay-per-doc; подписка = безлимит с monthly reset через cron-arq-таску. ### 2.3 Архитектура — без изменений в ядре `api` и `worker` работают с этапа 1; на этапе 2 добавляется только адаптер `web` (React, статика) перед Nginx → HTTPS (Let's Encrypt). Hexagonal-граница сохранена: `web` — такой же HTTP-клиент к api, как `bot`. LLM по-прежнему Ollama Cloud; при росте — Enterprise тариф или переход на свой GPU. ### 2.4 Критерий успеха 50 платящих. MRR ~30 000 ₽. Есть хотя бы один team-клиент. --- ## Этап 3 — B2B API (2 недели, только если есть спрос) **Цель:** сторонние сервисы дёргают анализ через API. ### 3.1 Что добавляем - API-ключи: таблица `api_keys`, header `X-API-Key`. - Rate-limit: Redis (token bucket). Лимит = **конкарренси/квота Ollama Cloud** (3 на Pro), а не ₽. - `POST /api/v1/analyze` (multipart) → 202 + job_id → `GET /api/v1/reports/{id}`. - Дашборд: ключи, usage, счета. ### 3.2 Чего НЕ делаем - Нет SDK, нет вебхуков (клиент поллит), нет OAuth2. - Один тариф API: 9 900 ₽/мес за 100 запросов. ### 3.3 Критерий успеха 3 API-клиента. MRR +30 000 ₽. --- ## Инфраструктура — сводка | Компонент | Этап 0 | Этап 1 | Этап 2–3 | |----------------|------------|------------------------------|--------------------------------| | Compute | ноутбук | 1 VPS (Docker Compose) | 1 VPS (апгрейд RAM) | | LLM | Ollama Cloud (Free/Pro) | Ollama Cloud Pro | Ollama Cloud Pro/Enterprise | | DB | — | Postgres (в compose) | Postgres (+ backup cron) | | Queue | — | Redis (в compose) | Redis (тот же) | | Object storage | — | Selectel S3 | Selectel S3 | | OCR | — | Tesseract (локально) | Tesseract (+ Yandex Vision) | | Payments | — | ЮKassa | ЮKassa + CloudPayments | | Monitoring | — | Docker logs | Uptime Kuma + Sentry (free) | | CI/CD | — | git push → ssh deploy | GitHub Actions → build → deploy| **K8s / RabbitMQ / Kafka / Elasticsearch / vLLM / TGI — НЕ НУЖНЫ.** Один VPS + Ollama Cloud держит всё до заметного объёма. Свой GPU — только если Ollama Cloud overage станет дороже self-host (отдельное решение позже). --- ## Чек-лист пунктов анализа (v1) Содержимое `checklist.py` — один список, без БД: 1. Неустойки / штрафы (размер, односторонний) 2. Подсудность (чужой регион) 3. Сроки оплаты (условия, просрочка) 4. IP-права (кому отходят результаты) 5. Одностороннее изменение условий 6. Гарантии и их срок 7. Форс-мажор (формулировки) 8. НДС (включён / сверх) 9. Ответственность сторон (cap, исключения) 10. Расторжение (условия, уведомление) --- ## Сроки (реалистично, соло, вечера/выходные) | Этап | Что | Время | |------|------------------|-----------| | 0 | Прототип | 1 выходной| | 1 | Telegram-бот MVP | 2–3 недели| | 2 | Веб + подписки | 3–4 недели| | 3 | B2B API | 2 недели | **До первого платящего (0+1): ~3–4 недели.** Заложить ~1–2 дня на подбор cloud-модели и тюнинг промпта (локальные/open модели капризнее GPT-4o). --- ## Риски Ollama Cloud (честно) 1. **152-ФЗ / data-residency.** Контракт улетает в Ollama Cloud (США). Это тот же класс риска, что и GPT-4o. Митигация: disclaimer, обезличивание ПДн перед отправкой, либо при необходимости — отказ от Ollama Cloud в пользу РФ-LLM (GigaChat/YandexGPT) или своего GPU-бокса. **Не позиционировать продукт как «данные не покидают РФ».** 2. **Квота/конкарренси.** Pro = 3 одновременных модели + rolling usage. Бурст платящих юзеров упрётся в очередь/429. Трекать usage, алертить у лимита, при росте — overage-баланс или Enterprise. 3. **Качество open-моделей.** 14B галлюцинирует/пересказывает цитаты сильнее GPT-4o/GigaChat. Всегда: цитата + номер пункта + ремонт-цикл валидации JSON. Disclaimer «не заменяет юриста» — в каждый отчёт. 4. **Зависимость от одного провайдера.** Один аккаунт Ollama (нельзя несколько). Иметь готовый план Б: GigaChat/YandexGPT-фолбэк или свой GPU при блокировке/превышении квоты. --- ## Что сознательно отложено (YAGNI) - Multi-tenant / организации / роли — пока все юзеры = solo. - Шаблоны договоров (генерация) — другой продукт. - ЭЦП / Госуслуги — чужой регуляторный ад. - Команда юристов (human-in-the-loop) — только если попросят. - Mobile app — веб + бот закрывают 100%. - White-label — один продукт, один бренд. - Свой GPU / vLLM / TGI / RAG-над-векторной-базой — пока Ollama Cloud дешевле; пересмотрим при выходе overage в минус.