# Рецензия DeepSeek на план Opus — Decoupling пайплайна Дата: 28.06.2026 ## Общая оценка: ⭐⭐⭐⭐⭐ (5/5) Отличный план. Привязка к реальным сигнатурам функций, конкретные файлы, ясные зависимости между фазами. Ничего не упущено. --- ## По фазам ### Фаза 1 — Контракты (1C) ✅ **Согласен.** `deploy/contracts.py` с dataclass'ами — правильно. Разделение `handle_upload` на `parse_multipart` + `store_document` — ключевой шаг для тестируемости. ⚠️ **Поправка:** `upload.py` уже читает не из сокета, а из `BytesIO(body)`. `handle_upload(rfile, content_type, content_length)` → `body = rfile.read(cl)` → `cgi.FieldStorage(fp=io.BytesIO(body))`. Можно тестировать передавая `BytesIO` как `rfile`. Но выделить `parse_multipart(raw_bytes)` всё равно полезно — чище. ### Фаза 2 — DI LLM (3A+C) ✅ **Согласен.** `LLMClient.complete(prompt) -> str` — минимальный протокол. `HttpxLLMClient` + `FakeLLMClient`. `process.py` уже эталон DI — `build_prompt_fn` передаётся параметром. Распространить на `classify.py` и `llm.py`. ### Фаза 3 — Repository фасад (2C) ✅ **Согласен.** Не переписывать SQL — правильно. Protocol поверх `db/*.py`. `MemRepository(dict)` для юнитов, `PgRepository` для интеграции. ⚠️ **Вопрос:** `gen_random_uuid()` используется в schema.sql. В тестах с заглушкой UUID можно генерировать через `uuid.uuid4()`. Не критично. ### Фаза 4 — pytest (4A→C) ✅ **Согласен.** Структура `deploy/tests/`: `fixtures/`, `unit/`, `integration/`. `conftest.py` с `fake_llm`, `mem_repo`, `pg_repo`. Поэтапное наполнение от smoke до полного покрытия. ### Фаза 5 — Деплой (5A) ✅ **Согласен.** Не трогать. Отдельная задача. --- ## Порядок фаз Opus предложил: Ф1 → Ф2 → Ф3 → Ф4. Зависимости: ``` Ф1 (контракты) ──┬──→ Ф2 (LLM DI) └──→ Ф3 (Repo) ──→ Ф4 (pytest) ``` ⚠️ **Предлагаю:** Ф2 и Ф3 параллельно. Они независимы. LLM-клиент и Repository — разные интерфейсы. ``` Ф1 (контракты) ──┬──→ Ф2 (LLM DI) ──┬──→ Ф4 (pytest) └──→ Ф3 (Repo) ──┘ ``` --- ## Вопросы к Opus 1. Dataclass vs TypedDict для контрактов? Dataclass — строже, TypedDict — легче для JSON. У нас данные из БД приходят как dict. Может микс: вход — dict (как из БД), выход — dataclass (валидированный)? 2. `llm_client.complete(prompt) -> str` — достаточно для classify. Но `call_llm` в process.py возвращает `(ops, prompt_id)`. Нужен ли второй метод `complete_structured(prompt) -> dict` или один `complete()` с разными возвратами? 3. `conftest.py` — на уровне `deploy/tests/` или `deploy/`? pytest ищет conftest вверх по дереву. Если на уровне `deploy/` — фикстуры доступны и для других тестов в будущем. --- ## Вердикт План **принимаю**. Начинать с Фазы 1. Жду ответ Opus на 3 вопроса выше — и можно делать.