DealDocumentScreening/docs/IMPLEMENTATION_PLAN.md
2026-08-12 21:29:36 +03:00

20 KiB
Raw Blame History

План реализации «Контракт-чек» (Ollama Cloud / hosted LLM)

Примечание: этот документ описывает исходную «ленивую» архитектуру с arq+Redis (очередь), Selectel S3 (хранилище) и единым Docker-образом с MODE=api|worker|bot. Этап 0 (прототип) актуален. Этапы 1+ superseded ARCHITECTURE.md, где реализована production-архитектура: RabbitMQ-конвейер, MinIO, два worker-а, aiogram-бот, B2B API, 5 Dockerfile-ов в srv/, dependency-groups (PEP 735).

Принцип: ленивая архитектура. Каждый этап — минимальный работающий срез. Никаких абстракций «на потом». K8s, RabbitMQ, микросервисы — не нужны до $10k MRR.

LLM: Ollama 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 (копейки). Итого ~$2530/мес на старте.
  • Рост объёма → 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. Прогнать 35 реальных договоров (NDA, оказание услуг, поставка).
  5. Зафиксировать: качество находок, задержка (сек), % битых JSON, сколько квоты/токенов на договор.

Чего НЕ делаем: база, веб, бот, оплату, деплой. Один скрипт локально.

Критерий успеха: отчёт по реальному договору содержит хотя бы 3 осмысленные находки с цитатами; задержка < 60 сек; квота/договор даёт понятную маржинальность при 199 ₽.


Этап 1 — Telegram-бот MVP (23 недели)

Цель: первые платящие пользователи. Бот = самый быстрый путь до ЦА.

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 таблицы

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 — Веб + подписки (34 недели)

Цель: 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 Что меняем в базе

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 Этап 23
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 23 недели
2 Веб + подписки 34 недели
3 B2B API 2 недели

До первого платящего (0+1): ~34 недели. Заложить ~12 дня на подбор 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 в минус.