docs: архитектурный анализ + History + gitignore (2026-06-27)
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
# Запрос к Опусу — анализ и план развития Contracts App (v1.0.178)
|
||||
|
||||
Дата: 26.06.2025. **Только инструкции, код не менять.**
|
||||
|
||||
---
|
||||
|
||||
## 1. Контекст: что за сервис
|
||||
|
||||
Сервис **«Сверка договоров»** — обработка договоров облачного провайдера НУБЕС с контрагентами.
|
||||
Пользователь загружает ZIP-архивы с документами (договоры, допсоглашения, спецификации),
|
||||
система классифицирует, группирует по номерам договоров и сравнивает
|
||||
спецификации услуг — показывает diff (ADD/UPDATE/DELETE/UNRESOLVED).
|
||||
|
||||
**Архитектура:**
|
||||
- **Lucee (CFML)** — фронтенд, домен contractor.luceek8s.dev.nubes.ru
|
||||
- **Python (VM)** — бэкенд, домен contracts.kube5s.ru, порт 8766
|
||||
- **PostgreSQL** — хранение
|
||||
- **nginx** — прокси
|
||||
- **LLM** — api.aillm.ru, модель gpt-oss-120b, HTTP/2 через httpx
|
||||
|
||||
**Пайплайн:**
|
||||
1. Upload (.docx/.pdf/.doc/.zip) → парсинг (python-docx/pdfplumber) → elements_json
|
||||
2. Classify — LLM определяет: тип (contract/supplement/specification/other),
|
||||
own_number, parent_number, counterparty, doc_date
|
||||
3. Group — Python normalize_number() + matching по номеру договора
|
||||
4. Compare — SSE, LLM сравнивает спецификации: ADD/UPDATE/DELETE/UNRESOLVED
|
||||
|
||||
---
|
||||
|
||||
## 2. Текущий код — ключевые файлы
|
||||
|
||||
### 2.1. Classify
|
||||
|
||||
**Файл:** `contractor/deploy/services/classify.py`
|
||||
|
||||
- `MAX_WORKERS = 4` — параллельные запросы к LLM
|
||||
- `_smart_extract()` — выжимка текста: заголовок ~1500 симв + regex-хиты по маркерам
|
||||
- `_call_llm_classify()` — вызов LLM, парсинг JSON из ответа
|
||||
- `classify_batch()` — ThreadPoolExecutor, классифицирует все pending документы
|
||||
- `_safe_json_parse()` — чинит битый JSON из LLM (markdown, trailing commas)
|
||||
|
||||
**Промпт classify:** `llm_prompt.py` → `build_classify_prompt(header_text)`.
|
||||
Запрашивает у LLM:
|
||||
```json
|
||||
{"doc_type": "contract|supplement|specification|other",
|
||||
"own_number": "XXX001-03700",
|
||||
"parent_number": "XXX001-03700", // для допников/спек
|
||||
"doc_date": "2025-01-01",
|
||||
"counterparty": "ООО \"Ромашка\""}
|
||||
```
|
||||
|
||||
**Проблема classify:** при 50+ файлах синхронный вызов таймаутился.
|
||||
Сейчас: `subprocess.Popen` → classify_worker.py (async, 202 Accepted).
|
||||
Но фронтенд (`app.js` `runClassify()`) не полностью синхронизирован с async-ответом.
|
||||
|
||||
### 2.2. Group
|
||||
|
||||
**Файл:** `contractor/deploy/services/grouping.py`
|
||||
|
||||
- `normalize_number()` — uppercase + только буквы/цифры.
|
||||
«МЭС-123/2024» == «МЭС 123/2024» после нормализации.
|
||||
- `group_documents()` — иерархический алгоритм:
|
||||
1. contract → якорь группы
|
||||
2. supplement/spec → matching по parent_number (нормализованный)
|
||||
3. Оставшиеся → виртуальные группы по own_number
|
||||
4. Без номеров → `__unresolved__`
|
||||
- `apply_groups()` — создаёт записи в DB (contracts + supplements)
|
||||
|
||||
**Известные баги normalize_number:**
|
||||
- Кириллическая `О` vs цифра `0` — не различаются
|
||||
- Латинская `C` vs кириллическая `С` — не различаются
|
||||
- `Ё` → выпадает
|
||||
|
||||
### 2.3. Compare
|
||||
|
||||
**Файлы:** `contractor/deploy/convert_server.py` (SSE endpoint `/process-v2`),
|
||||
`contractor/deploy/compare.js` (фронтенд SSE-клиент).
|
||||
|
||||
**Промпты compare:** `llm_prompt.py`
|
||||
- `FALLBACK_EXTRACT` — для первого документа (базовый договор/спецификация):
|
||||
ADD всех строк.
|
||||
- `FALLBACK_DIFF` — для допсоглашений: UPDATE/DELETE/ADD/UNRESOLVED.
|
||||
Поддерживает два режима:
|
||||
- `"mode": "partial"` — точечные изменения
|
||||
- `"mode": "full_replace"` — полная замена спецификации
|
||||
|
||||
**Проблема compare:** оба режима — «ADD всех строк». LLM не всегда корректно
|
||||
сопоставляет строки между v1 и v2 спецификации.
|
||||
|
||||
### 2.4. ZIP handling
|
||||
|
||||
**Файлы:** `contractor/deploy/files.js` (`addZipFile()`),
|
||||
`contractor/deploy/convert_server.py` (`_handle_unzip_upload()`).
|
||||
|
||||
Текущая логика:
|
||||
1. ZIP загружается через FormData на `/unzip-upload`
|
||||
2. Бэкенд распаковывает, возвращает base64 каждого файла
|
||||
3. Фронтенд для каждого: base64 → Blob → File → upload (как addRegularFile)
|
||||
4. Дубликаты по имени: confirm-диалог, перезапись
|
||||
|
||||
**Чего нет:**
|
||||
- `zip_source` — связь файла с родительским ZIP
|
||||
- Группировка в таблице по ZIP-источнику
|
||||
- ID файла = `zip_source + "/" + filename`
|
||||
|
||||
### 2.5. Фронтенд
|
||||
|
||||
**Ключевые JS-модули:** `contractor/deploy/` — state.js, files.js, groups.js, compare.js, app.js, app_utils.js
|
||||
|
||||
**Таблица файлов:** `files.js` `renderFiles()` — плоский список, без группировки по ZIP.
|
||||
Уже есть `max-height: 50vh; overflow-y: auto` (скролл).
|
||||
|
||||
**Степпер:** `app.js` `renderStepper()` — 4 шага: Загрузка → Классификация → Группировка → Сравнение.
|
||||
|
||||
---
|
||||
|
||||
## 3. Что сказал заказчик (26.06.2025)
|
||||
|
||||
> «А что касается вводных — файлы могут быть в зипах. Как правило, по 1 контрагенту,
|
||||
> но я считаю, что по контрагенту может быть несколько зипов, и теоретически
|
||||
> могу представить ситуацию, когда будет в одном зипе по нескольким как-то
|
||||
> связанным контрагентам (какие-то агентские схемы). Наверно, вероятность того,
|
||||
> что в одном зипе один контрагент — весьма высокая, но не 100%.»
|
||||
|
||||
Также обсуждалось:
|
||||
- Привязка файлов к родительскому ZIP
|
||||
- В таблице — ZIP как группирующий заголовок, ниже со сдвигом — вложенные файлы
|
||||
- ID файла = имя зипа + "/" + имя файла
|
||||
|
||||
---
|
||||
|
||||
## 4. Что обсуждали мы (ключевые выводы)
|
||||
|
||||
1. **НУБЕС — всегда Исполнитель.** Контрагенты — Заказчики.
|
||||
2. **Один batch = один контрагент (почти всегда).**
|
||||
Хоть 1 зип, хоть 5 зипов — всё по одному контрагенту.
|
||||
3. **Агентские схемы — исключение**, не основная логика.
|
||||
4. **`zip_source`** — для визуальной группировки в таблице.
|
||||
Не влияет на логику classify/group/compare.
|
||||
5. **Два сценария UI:**
|
||||
- «Точный» (≤100 файлов) — текущий UI с таблицей и ручным контролем
|
||||
- «Поток» (100+) — упрощённый UI: прогресс-бар, авто-пайплайн, сводный отчёт
|
||||
(без таблицы — 1000 DOM-строк вешают браузер)
|
||||
6. **Коллизия одинаковых имён из разных ZIP:**
|
||||
- ID = `zip_source + "/" + filename` → разные сущности
|
||||
- Compare покажет diff между версиями
|
||||
|
||||
---
|
||||
|
||||
## 5. Что нужно от Опуса
|
||||
|
||||
### 5.1. ПЛАН по zip_source
|
||||
|
||||
Как именно внедрить привязку файлов к родительскому ZIP на ВСЕХ слоях:
|
||||
- `unzip.py` → возвращать `zip_source`
|
||||
- `db/documents.py` → поле `zip_source`
|
||||
- `files.js` `renderFiles()` → группировка + отступ
|
||||
- `state.js` → поле `zip_source`
|
||||
- `index.cfm` / Lucee → надо ли менять?
|
||||
|
||||
**Варианты отображения:**
|
||||
- Вариант А: ZIP как секция-заголовок, под ним файлы с отступом
|
||||
- Вариант Б: древовидная структура (раскрывающиеся ZIP'ы)
|
||||
- Вариант В: цветовая маркировка по ZIP
|
||||
|
||||
Какой лучше для пользователя и почему?
|
||||
|
||||
### 5.2. ПЛАН по двум сценариям UI
|
||||
|
||||
Как архитектурно разделить «Точный» и «Поток» режимы:
|
||||
- Общий код (classify/group/compare не меняются)
|
||||
- Разный UI: что показывать, что скрывать
|
||||
- Как переключаться между режимами (авто по кол-ву файлов? ручной выбор?)
|
||||
- «Поток» — сводный отчёт: структура, что в нём
|
||||
|
||||
### 5.3. АНАЛИЗ текущих промптов
|
||||
|
||||
Внимательно изучи `llm_prompt.py`:
|
||||
- Есть ли проблемы в классификации (counterparty не определяется)
|
||||
- Достаточно ли хорош diff-промпт (compare)
|
||||
- Нужны ли разные варианты промптов для разных сценариев:
|
||||
- Только НУБЕС-один контрагент (стандартный)
|
||||
- Два контрагента (агентские схемы)
|
||||
- Мусорные документы (акты сверки, счета)
|
||||
- Нужна ли корректировка глоссария, примеров
|
||||
|
||||
### 5.4. РЕКОМЕНДАЦИИ по улучшению
|
||||
|
||||
Что ещё можно улучшить, исходя из анализа кода и требований заказчика:
|
||||
- Приоритеты: что делать в первую очередь
|
||||
- Риски: что может сломаться
|
||||
- Оценка трудозатрат (грубо: маленькая/средняя/большая задача)
|
||||
|
||||
---
|
||||
|
||||
## 6. Ограничения
|
||||
|
||||
- **Код не менять.** Только инструкции и план.
|
||||
- Ответ — в чат, подробно, с обоснованием каждого решения.
|
||||
- Если есть несколько вариантов — перечислить с плюсами/минусами.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Несколько вопросов по сервису «Сверка договоров»
|
||||
|
||||
Чтобы настроить сервис точнее — пара коротких вопросов. Отвечать подробно не нужно,
|
||||
достаточно отметить вариант или написать пару слов.
|
||||
|
||||
Это не финальный список: когда вы поработаете с сервисом, наверняка появятся
|
||||
свои пожелания и вопросы — тогда обсудим остальное.
|
||||
|
||||
---
|
||||
|
||||
## 1. Объём
|
||||
Сколько файлов обычно загружаете за один раз?
|
||||
(нужно, чтобы решить — оставить подробную таблицу или сделать упрощённый режим для больших пачек)
|
||||
|
||||
- [ ] A. до 10–20
|
||||
- [ ] B. 50–100
|
||||
- [ ] C. 100+ (сотни/тысячи)
|
||||
|
||||
## 2. Как называется НУБЕС в договорах
|
||||
В каждом договоре две стороны: вы (Исполнитель) и контрагент (Заказчик).
|
||||
Сейчас система иногда путает, кто из них контрагент.
|
||||
|
||||
**Под какими названиями НУБЕС встречается в документах?**
|
||||
(например: «ООО НУБЕС», «Облачные технологии», ИНН …)
|
||||
|
||||
> _ваш ответ:_
|
||||
|
||||
## 3. «Мусорные» документы
|
||||
В архивах иногда попадаются не-договорные бумаги. Какие можно игнорировать при сверке?
|
||||
|
||||
- [ ] акты сверки
|
||||
- [ ] счета / счета-фактуры / УПД
|
||||
- [ ] акты оказанных услуг
|
||||
- [ ] платёжные поручения
|
||||
- [ ] другое: _______________
|
||||
|
||||
## 4. ZIP-архивы
|
||||
Мы поняли так: обычно **один архив = один контрагент**, но по одному контрагенту
|
||||
может быть несколько архивов, а изредка в одном архиве — несколько связанных
|
||||
контрагентов (агентские схемы). **Всё верно?** Если есть нюансы — допишите.
|
||||
|
||||
> _ваш ответ:_
|
||||
|
||||
## 5. Что важнее всего в результате
|
||||
На что смотрите в первую очередь, когда сверка готова?
|
||||
(например: что изменилось в ценах, какие позиции не сопоставились, итоговая сумма…)
|
||||
|
||||
> _ваш ответ:_
|
||||
|
||||
---
|
||||
|
||||
Спасибо! Этого пока достаточно — остальное уточним по ходу.
|
||||
Reference in New Issue
Block a user