docs: архитектурный анализ + History + gitignore (2026-06-27)

This commit is contained in:
“Naeel”
2026-06-27 13:00:18 +04:00
parent 4a21d77f51
commit 82c5c075f1
154 changed files with 4789 additions and 1443 deletions
@@ -0,0 +1,151 @@
# Анализ ответа Опуса — zip_source, UI-режимы, промпты
Дата: 26.06.2025 | Ответ на [opus-request-zip-plan.md](../Files/opus-request-zip-plan.md)
---
## 1. Оценка ответа в целом
**Качество: высокое.** Опус прочитал реальный код, разобрался в архитектуре, дал конкретные диффы по слоям. Не «размышления вообще», а точные строки и функции. 95% рекомендаций — правильные.
**Что упущено:**
- Lucee-слой (`upload.cfm`, `api.cfm`) — тоже участвует в upload, но Опус его не проанализировал
- `confidence` — предлагает сохранять в БД, но не говорит где именно брать (LLM возвращает? парсить из промпта?)
- Порог 100 файлов для «Потока» — спорный, обсудим ниже
---
## 2. По пунктам
### 2.1. `zip_source` — ✅ СОГЛАСЕН полностью
План по слоям правильный. Ключевые моменты:
- **`zip_source` не участвует в classify/group/compare** — верно. Чисто визуальный атрибут.
- **Формат: имя ZIP с расширением** — да, `«Ромашка.zip»`.
- **PK не трогаем (UUID)**, «ID = zip/filename» только для отображения — верно.
- **Дедупликация по паре `(zip_source, name)`** — ⚠️ самый критичный момент. Опус прав: если не сменить ключ, одноимённые файлы из разных ZIP будут перезаписываться. Но **надо проверить**: текущий код в `addRegularFile()` (files.js) ищет по `f.name`. При добавлении `zip_source` нужно либо:
- Ключ = `zip_source + "/" + filename` (как предлагает заказчик)
- Или ключ = `(zip_source || "") + filename`
Я за вариант с конкатенацией в одну строку — проще для сравнения.
- **unzip.py не трогаем** — верно. Имя ZIP уже есть на фронте (`file.name`).
### 2.2. Вариант отображения — ✅ СОГЛАСЕН (Вариант А)
Заголовок-секция ZIP + отступ `padding-left: 24px`.
- Просто, без нового состояния
- Соответствует тому что описал заказчик
- Опциональное сворачивание — да, но не в первой итерации
### 2.3. Два UI-режима — ⚠️ ЧАСТИЧНО СОГЛАСЕН
**Плюсы:**
- Бэкенд не меняется — правильно
- `state.ui.mode` — хорошее место
- Сводный отчёт («зелёное сворачиваем, красное показываем») — отличная идея
- Авто-определение + ручной override — разумно
**Спорные моменты:**
- **Порог 100 файлов** — слишком низкий для автоматического предложения. При 100 файлах текущий UI работает нормально (скролл, 50vh). Реальный болевой порог — **200-300+**. Предлагаю порог **200**.
- **«Поток» сейчас не нужен.** Если заказчик работает с 5-50 файлами, весь Stream Mode — оверинжиниринг. Но архитектурно заложить `state.ui.mode` — дёшево и правильно.
### 2.4. Промпты — ✅ СОГЛАСЕН, с уточнениями
#### Classify — проблемы А-Д:
**А. Counterparty / блок про стороны НУБЕС** — 🔴 КРИТИЧНАЯ.
Опус абсолютно прав. Промпт сейчас не говорит что НУБЕС = Исполнитель. LLM возвращает случайную сторону. Чинится одной вставкой в промпт. Делать **первым**.
НО: Опус предлагает «ИНН 7727... (взять у заказчика)». Это перебор. Достаточно:
```
НУБЕС известен как: «НУБЕС», «ООО НУБЕС», «ООО "НУБЕС"», «Nubes».
counterparty — ВСЕГДА вторая сторона, НИКОГДА не НУБЕС.
```
**Б. parent_number у contract** — ✅ верно. `parent_number = null` для contract.
**В. Мусорные документы** — ✅ верно. Добавить примеры в `doc_type=other`.
**Г. Few-shot примеры в classify** — ✅ верно. 1-2 примера улучшат точность.
**Д. confidence** — ⚠️ спорно. Опус говорит «добавить колонку и показывать low в отчёте». Но `confidence` сейчас даже не сохраняется. Предлагаю **сначала убрать из промпта** (меньше путаницы), а потом, когда будет реальная потребность — добавить и колонку, и парсинг.
#### Compare (diff) — ✅ СОГЛАСЕН
- «UNRESOLVED вместо дубль-ADD при сомнении» — верно
- `temperature=0.1` для diff — проверить (скорее всего уже)
- `full_replace` → автоматическое удаление старых строк на стороне Python — **умная идея**, снижает нагрузку на LLM
#### Разные промпты под сценарии — ✅ СОГЛАСЕН
Не нужно. Один classify + один diff. Меньше рассинхрона.
### 2.5. Гомоглифы — ⚠️ ОСТОРОЖНО
Опус предлагает:
- `С/C → C` (латиница)
- `О/0` — «трактовать осторожно»
- `Ё → Е`
**Моё мнение:**
- `С→C` и `Ё→Е` — **опасно**. Это меняет семантику номера. `МЭС-123` ≠ `МЭC-123`. Лучше: **не заменять, а добавить второй проход сравнения** — если точное совпадение не найдено, попробовать с гомоглифами. Или нормализовать ОБА варианта (и кириллицу, и латиницу) к единому представлению, но сохранять оригинал для отображения.
- Конкретно для `Ё`: да, `Ё→Е` допустимо (в делопроизводстве Ё часто заменяют на Е). Но лучше сделать настраиваемым.
### 2.6. Приоритеты — ✅ СОГЛАСЕН с корректировкой
| Что | Приоритет Опуса | Моя оценка |
|-----|----------------|-----------|
| Counterparty в промпте | P0 | P0 ✅ |
| doc_type=other мусор | P0 | P0 ✅ |
| Гомоглифы | P0 | P1 ⚠️ (осторожно, не ломать) |
| zip_source | P1 | P1 ✅ |
| Группировка по ZIP в UI | P1 | P1 ✅ |
| Режим «Поток» | P2 | P3 (отложить, нет потребности) |
| confidence в БД | P3 | P3 (или убрать из промпта) |
| diff-UNRESOLVED | P3 | P2 (дёшево, большой эффект) |
---
## 3. Что Опус упустил
### 3.1. Lucee-слой
`upload.cfm` и `api.cfm` на Lucee тоже обрабатывают загрузку. Если файл идёт через Lucee (а не напрямую на VM), `zip_source` нужно прокинуть и там. Надо проверить — идёт ли upload через Lucee или напрямую на VM.
**Факт:** судя по `index.cfm`, JS грузится с VM (`contracts.kube5s.ru/static/app.js`), а upload идёт на `VM_API + '/upload'`. Значит Lucee в upload **не участвует**. `zip_source` в Lucee не нужен. Опус оказался прав молча.
### 3.2. Промпты в БД vs хардкод
Опус верно заметил: «промпты берутся из БД (Lucee), fallback — хардкод. Менять надо в БД через интерфейс промптов». Это **критично важно для исполнителя**: если просто поправить `FALLBACK_EXTRACT`/`FALLBACK_DIFF` в `llm_prompt.py` — в проде ничего не изменится, потому что используется версия из БД.
**Порядок правки промптов:**
1. Сначала в БД через UI (`/prompt.cfm`)
2. Потом в хардкоде (для fallback)
### 3.3. Порог для «Потока»
100 файлов — слишком консервативно. Таблица с `max-height: 50vh` и `overflow-y: auto` нормально работает при 100-150 файлах. Предлагаю **200** как порог для автопредложения. Но лучше — **сделать настраиваемым** (константа в начале app.js).
---
## 4. Итоговое мнение
**Ответ Опуса — хороший план.** 95% рекомендаций принимаю.
**Что делаем прямо сейчас (P0):**
1. Правка classify-промпта: блок про стороны НУБЕС + parent_number=null + примеры мусора
2. Правка diff-промпта: UNRESOLVED вместо дубль-ADD, temperature проверка
**Что делаем дальше (P1):**
3. `zip_source` сквозь все слои (БД → Python → JS)
4. Группировка по ZIP в таблице (Вариант А)
5. Дедупликация по `(zip_source, filename)`
**Что откладываем:**
6. Режим «Поток» — пока нет потребности
7. Гомоглифы — нужно больше примеров от заказчика
8. confidence — убрать из промпта, вернуть когда будет нужно
**Главный риск (ещё раз):** дедупликация в `addRegularFile()`. Без правки ключа на `(zip_source, name)` — фича сломается на первом же случае одинаковых имён в разных ZIP.