Files
contracts/History/opus-architecture-research-2026-06-27-review.md
T

134 lines
11 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.
# Рецензия DeepSeek V4 Pro на отчёт Opus
Дата: 27.06.2026
---
## Общая оценка: ⭐⭐⭐⭐⭐ (5/5)
Opus сделал ТО, ЧТО НУЖНО: прочитал код, нашёл расхождения с документацией, не писал код, не рисовал графы. Это эталонный анализ.
---
## Что Opus нашёл такого, чего я НЕ заметил
### 🔴 КРИТИЧЕСКОЕ: PyPDF2 теряет структуру таблиц
Я читал `parse.py`, видел `PyPDF2`, но **не осознал** что он извлекает текст построчно и не сохраняет колонки. Для спецификаций — это катастрофа. Таблица «наименование | цена | кол-во | сумма | дата» после PyPDF2 станет плоским текстом, и LLM придётся угадывать где какая колонка.
**Opus прав на 100%.** Это вероятно главный риск точности прямо сейчас.
Надо: `pdfplumber` (для цифровых PDF) или `camelot`.
### 🔴 КРИТИЧЕСКОЕ: Кода сверки CRM↔фискальная НЕТ
Я знал это из ответов заказчика, но Opus проверил grep'ом (`crm|фискал|fiscal|сверк|PAYG|reconcil`) — нашёл только «акт сверки» как тип мусора. Это greenfield. Меняет планирование: не «эволюция», а «новый модуль».
### 🟡 ВАЖНОЕ: service-description.md устарел
Я дал Opus читать `service-description.md` как «актуальный». Opus обнаружил что он описывает парсинг через Lucee (Java POI/PDFBox), а **реальный код** (`upload.py``parse.py`) парсит на ВМ через PyPDF2/python-docx. Без Java. Без Lucee.
**Моя ошибка:** я не перепроверил service-description.md на соответствие коду. Надо поправить доку.
### 🟡 ВАЖНОЕ: llm_prompt.py зависит от Lucee
`build_prompt()` (основной пайплайн) ходит HTTP в Lucee за активным промптом. А `build_classify_prompt()` берёт из БД напрямую. Непоследовательно + точка отказа.
### 🟡 ВАЖНОЕ: chat.cfm содержит захардкоженный API-ключ
Плюс ссылается на `spec_rows` вместо `spec_current` — сломан вдвойне.
### 🟢 Безопасность: verify=False в llm.py
TLS-проверка отключена. Надо включить.
---
## Где Opus прав, а где нет
| Тезис Opus | Моё мнение |
|------------|------------|
| **Гипотеза 1 (гибрид): ⚠ частично** — «текущий код — чистый pipeline, и это правильно» | ✅ **Согласен.** 131 тест на детерминированные функции — сильный аргумент против агентов. |
| **Гипотеза 2 (фильтр мусора): ⚠ частично** — «этап 1 по имени файла ненадёжен» | ⚠ **Частично.** Для имён типа «счет-фактура №123 от 01.01.2025.docx» regex надёжен. Но для «scan001.pdf» — да, бесполезен. Вывод: этап 1 — приоритизация, не жёсткий дроп. |
| **Гипотеза 3 (парсинг на ВМ): ❌ отвергнута** | ✅ **Полностью согласен.** Моя гипотеза была основана на устаревшей документации. Парсинг уже на ВМ. |
| **Гипотеза 4 (трёхуровневый matching): ⚠ частично** — «difflib опасен на коротких токенах» | ✅ **Согласен.** Примеры «IPv4↔IPv6», «10 кВт↔15 кВт» убедительны. Матчить по структурным атрибутам, не по сырой строке. |
| **Гипотеза 5 (MVP): ⚠ частично** — «нужен CRM-прогон уже в v1» | ✅ **Согласен.** Иначе v1 будет «готов», а к цели не приблизимся. |
| **Гипотеза 6 (PG-очередь): ✅ подтверждена** | ✅ **Согласен.** Плюс Opus добавил: сейчас очереди нет вообще, in-process ThreadPool. |
| **«LLM бесплатный ≠ быстрый»** — не наращивать проходы | ✅ **Согласен.** 5-30s × 100+ файлов = узкое место. |
| **«Не отказываться от детерминированного matching»** | ✅ **Согласен.** Воспроизводимость и аудит важнее «бесплатности». |
### Единственное где я НЕ согласен:
**Opus говорит «не использовать имя файла для фильтрации».**
Я считаю: regex `(сч[её]т|акт|плат[её]ж|УПД|сверк|инвойс|invoice)` по имени файла — ДОСТАТОЧНО надёжен для пред-фильтрации. «Счет-фактура №123.docx» — это всегда мусор. Да, «scan001.pdf» не отфильтруется — но он уйдёт на этап 2 (первые 2KB текста). Риск ложного срабатывания на договоре с именем «счет-фактура» — ноль.
---
## Что Opus упустил
1. **testgen/** — в репо есть `testgen/generate.py`, `testgen/pools.py`, `testgen/templates.py`. Это генератор синтетических тестовых данных. Opus предлагает «тестировать матчинг на синтетике», но не знает что инструмент уже есть.
2. **convert_server.py роутинг** — Opus не проверил ВСЕ эндпоинты. Например, `/api/batch-progress`, `/api/apply-groups`, `/api/cleanup`, `/api/sync` — они есть, но не проанализированы.
3. **services/llm.py — расхождение ВМ↔репо** — Opus заметил `verify=False`, но не предложил КАК выяснить какая версия каноническая (репо или ВМ). Я поднимал этот вопрос в `opus-plan-review-2026-06-27.md`.
4. **LibreOffice — узкое место для .doc**`/convert-doc` запускает LibreOffice subprocess последовательно с таймаутом 30с. На пачке старых .doc файлов это потенциально медленнее даже LLM. Но на практике .doc — редкость (заказчик говорил про docx/pdf).
5. **OCR не нужен** — заказчик уточнил: «только текст, сканов не будет». Значит риск «потребуется внешний OCR-сервис» снимается.
---
## Дополнения из второго анализа (подтверждения)
Второй анализ полностью подтвердил выводы Opus. Дополнительно акцентировано:
- **Дрейф документации** — не только `service-description.md`, но и другие .md могут устареть. Правило: **код > документация**. Всегда проверять.
- **base64 в БД** — `upload.py` кладёт файлы как base64 в `documents`. На 100+ многомегабайтных файлах это раздует БД. Риск масштабирования.
- **Нет устойчивой очереди** — `classify.py` = `ThreadPoolExecutor` в одном процессе. Падение процесса = потеря прогресса. PG-очередь добавит durability.
---
## Итог: что я беру в работу
### Немедленно (следующий чат, по команде «делай»):
1. **Заменить PyPDF2 на pdfplumber** в `services/parse.py` — критично для точности
2. **Починить chat.cfm** — убрать ключ, `spec_rows``spec_current`
3. **Включить verify=True** в `services/llm.py`
4. **Отвязать llm_prompt.py от Lucee**`build_prompt``db.prompts.get_active()`
5. **Поправить service-description.md** — парсинг на ВМ, не через Lucee
### Вторая очередь:
6. **Детерминированная проверка `sum == price*qty`** — бесплатный сигнал ошибок
7. **Золотой набор** — 30-50 реальных документов с ручной разметкой
8. **Метрики** — логировать `_safe_json_parse` срабатывания, UNRESOLVED, латентность
### Третья очередь (после золотого набора):
9. **Спроектировать нейтральную CanonicalRow** для CRM↔фискальная
10. **Прототип матчинга** на синтетике из testgen/
---
## Вывод
Opus дал **отличный анализ**. Главная ценность — нашёл расхождения кода и документации, которые я пропустил. PyPDF2 — критическая находка. То что кода CRM-сверки нет — меняет приоритеты: не «эволюция», а «новый модуль».
**Что отсеялось после двух анализов:**
- OCR не нужен (только текст, без сканов)
- LLM не использовать агрессивнее (бесплатный ≠ быстрый, 5-30s/вызов)
- Агенты не нужны (131 тест на pipeline, детерминизм > гибкость)
- RabbitMQ не нужен (PG-очередь достаточна)
**Что осталось в работе (5 немедленных + 3 второй очереди):**
1. pdfplumber вместо PyPDF2
2. chat.cfm: ключ + таблица
3. llm.py: verify=True
4. llm_prompt.py: отвязать от Lucee
5. service-description.md: поправить
6. Проверка sum==price*qty
7. Золотой набор 30-50 документов
8. Метрики (_safe_json_parse, UNRESOLVED, латентность)
Следующий шаг: команда «делай» → правлю пункты 1-5.