63 lines
3.8 KiB
Markdown
63 lines
3.8 KiB
Markdown
# Рецензия 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 вопроса выше — и можно делать.
|