5.8 KiB
Диалог DeepSeek ↔ Opus — Decoupling пайплайна
Дата: 28.06.2026
Запрос DeepSeek (задание)
Файл: History/opus-decoupling-plan-2026-06-28.md
5 блоков, интерактивный режим:
- Модульность пайплайна — контракты между стадиями
- Интеграционное тестирование (fixture-based)
- Деплой и синхронизация
- Перспектива переезда на managed Flask
- Вопросы к заказчику
Ответ Opus — 5 развилок
Вопрос 1. Граница decoupling
Варианты:
- A. Единый интерфейс пайплайна (контракт оркестратора)
- B. Изолировать только внешние зависимости (LLM, БД)
- C. И то, и другое
Рекомендация Opus: C
Вопрос 2. База данных в тестах
Варианты:
- A. Абстрагировать репозиторий за интерфейсом → in-memory заглушка
- B. Реальный Postgres в Docker/testcontainers
- C. Гибрид: юнит-тесты на заглушке + интеграционные на Postgres
Рекомендация Opus: C
Вопрос 3. Мокирование LLM
Варианты:
- A. DI — LLM-клиент через зависимость
- B. Мокать на уровне httpx-транспорта
- C. Record/replay — реальные ответы как фикстуры
Рекомендация Opus: A + C
Вопрос 4. Тестовый фреймворк
Варианты:
- A. pytest + папка tests/ + conftest
- B. Только smoke-тесты
- C. Полное покрытие
Рекомендация Opus: A → C поэтапно
Вопрос 5. Деплой
Варианты:
- A. Не трогать, отдельная задача
- B. Заодно привести sync.sh/systemd
- C. Только задокументировать
Рекомендация Opus: A
Ответ DeepSeek
Все 5 рекомендаций — согласен:
- 1C, 2C, 3A+C, 4A, 5A
Запрос: жду финальный план.
Ответ Opus — Финальный план (5 фаз)
Фаза 1 — Контракты стадий (1C)
deploy/contracts.pyс dataclass'ами: ParseResult, ClassifyResult, GroupingResult, CompareOp- Разделить стадии на pure-core + HTTP-adapter
handle_upload→parse_multipart+store_documentparse_fileуже pure (эталон)classify_batch,group_documentsпринимаютrepoпараметром
Фаза 2 — Изоляция LLM (3A+C)
deploy/services/llm_client.py: протоколLLMClient.complete(prompt) -> str+HttpxLLMClientcall_llm/_call_llm_classifyпринимаютllm_client(defaultHttpxLLMClient())FakeLLMClientдля тестов — сохранённые реальные ответы- Зависит от Фазы 1
Фаза 3 — Изоляция БД (2C)
- Тонкий фасад
Repository(Protocol) поверхdb/*.py - Без переписывания SQL
MemRepositoryдля юнитов,PgRepositoryдля интеграции- Docker
postgres:15, очистка TRUNCATE в teardown - Параллельно с Фазой 2
Фаза 4 — pytest (4A→C)
- Структура:
deploy/tests/→conftest.py,fixtures/,unit/,integration/ - Фикстуры:
fake_llm,mem_repo,pg_repo,sample_docx - Порядок: smoke per-stage → полное покрытие → сквозной прогон
- Зависит от Фаз 1-3
Фаза 5 — Деплой (5A)
- Не трогаем
- Флаг
UPLOAD_BACKEND=vm|managed— только интерфейс
Ключевые файлы
- upload.py, classify.py, grouping.py, process.py, llm.py, connection.py — модифицируются
- Новые: contracts.py, llm_client.py, repository.py, tests/, docker-compose.test.yml
Верификация
pytest unit/— зелёные без сети/БДdocker-compose up postgres+pytest integration/— реальный SQL- Сквозной: sample.docx → пайплайн → ожидаемые ops
- convert_server.py без изменения поведения
- JS-тесты (131) — PASS
Scope
Включено: контракты, DI LLM+БД, pytest, фикстуры, флаг UPLOAD_BACKEND Исключено: деплой, переписывание SQL, изменения фронтенда, managed-upload
Ответ Opus — Финальные уточнения (3 вопроса)
1. Dataclass vs TypedDict
Решение: микс. Вход — dict (из БД/JSON), выход — frozen dataclass.
Конвертация dict→dataclass через классметод from_llm(raw: dict) на границе стадии.
2. Сигнатура LLMClient
Решение: один метод complete(prompt: str) -> str.
Парсинг JSON и prompt_id — НЕ дело LLM-клиента, остаются у вызывающего.
3. Уровень conftest.py
Решение: deploy/conftest.py. Корень Python-кода, чистые импорты.
Поправка по upload
handle_upload уже читает в BytesIO. parse_multipart(raw_bytes) всё равно выделяем.
Итоговый план (зафиксирован)
Фазы: Ф1 → (Ф2 ∥ Ф3) → Ф4, Ф5 отдельно.
Развилки: 1C, 2C, 3A+C, 4A, 5A.
План принят DeepSeek. Готов к исполнению — Фаза 1.