DealDocumentScreening/docs/IMPLEMENTATION_PLAN.md
2026-08-24 01:00:11 +03:00

339 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# План реализации «Контракт-чек» (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 (копейки). Итого ~$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 таблицы
```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 — Веб + подписки (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 Что меняем в базе
```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 | Этап 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 в минус.