From 15c265ecc383155543ab67a61939210749333784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sun, 28 Jun 2026 09:15:33 +0400 Subject: [PATCH] =?UTF-8?q?docs:=20=D1=80=D0=B5=D1=86=D0=B5=D0=BD=D0=B7?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BD=D0=B0=20=D0=BF=D0=BB=D0=B0=D0=BD=20Opus?= =?UTF-8?q?=20=E2=80=94=20decoupling=20=D0=BF=D0=B0=D0=B9=D0=BF=D0=BB?= =?UTF-8?q?=D0=B0=D0=B9=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- History/opus-decoupling-review-2026-06-28.md | 62 ++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 History/opus-decoupling-review-2026-06-28.md diff --git a/History/opus-decoupling-review-2026-06-28.md b/History/opus-decoupling-review-2026-06-28.md new file mode 100644 index 0000000..442aa08 --- /dev/null +++ b/History/opus-decoupling-review-2026-06-28.md @@ -0,0 +1,62 @@ +# Рецензия 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 вопроса выше — и можно делать.