134 lines
11 KiB
Markdown
134 lines
11 KiB
Markdown
# Рецензия 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.
|