102 lines
8.7 KiB
Markdown
102 lines
8.7 KiB
Markdown
# Пайплайн обработки договора
|
||
|
||
## Схема
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ 👤 ПОЛЬЗОВАТЕЛЬ │
|
||
│ Открывает / → выбирает файлы → жмёт «Обработать» │
|
||
└──────────────────────────┬──────────────────────────────────────────┘
|
||
│ multipart/form-data (files)
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ app.py: _upload_page() POST │
|
||
│ Принимает файлы, запускает пайплайн │
|
||
└──────────────────────────┬──────────────────────────────────────────┘
|
||
│
|
||
┌──────────────┼──────────────┐
|
||
▼ ▼ ▼
|
||
file.docx file.pdf file.zip
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ ① parser.py │
|
||
│ docx → python-docx → elements JSON │
|
||
│ pdf → pdfplumber → elements JSON │
|
||
│ doc → libreoffice → docx → python-docx │
|
||
│ zip → zipfile → каждый файл рекурсивно │
|
||
│ │
|
||
│ Выход: [{type:"paragraph", style, text}, {type:"table", rows}] │
|
||
│ НИЧЕГО не фильтрует, не теряет │
|
||
└──────────────────────────┬──────────────────────────────────────────┘
|
||
│ elements JSON
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ ② textify.py │
|
||
│ elements → линейный текст │
|
||
│ │
|
||
│ [No Spacing] Приложение № 1 │
|
||
│ [Heading 3] Состав и стоимость Услуг │
|
||
│ --- Таблица (9×6) --- │
|
||
│ | № | Наименование | Цена | Объем | Сумма | Дата | │
|
||
│ | 1 | Аренда стойко-места | 213 905 | 3 | 641 716 | 01.04.2026 | │
|
||
└──────────────────────────┬──────────────────────────────────────────┘
|
||
│ текст (строка)
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ ③ extractor.py + llm_client.py │
|
||
│ текст → промпт → LLM (gpt-oss-120b) → JSON │
|
||
│ │
|
||
│ Промпт: «Найди таблицы услуг, извлеки строки в JSON» │
|
||
│ Ответ: [{"row_num":1, "name":"Аренда...", "price":213905, │
|
||
│ "qty":3, "sum":641716, "date_start":"2026-04-01"}, ...] │
|
||
└──────────────────────────┬──────────────────────────────────────────┘
|
||
│ строки спецификации
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ ④ differ.py │
|
||
│ Сравнение с предыдущим допником │
|
||
│ │
|
||
│ rows_old vs rows_new → changes: │
|
||
│ added: новая услуга │
|
||
│ changed: цена 213905 → 250000, дата 01.04 → 25.04 │
|
||
│ deleted: услуга убрана │
|
||
│ unchanged: без изменений │
|
||
└──────────────────────────┬──────────────────────────────────────────┘
|
||
│ изменения
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ ⑤ Сохранение в PostgreSQL │
|
||
│ │
|
||
│ documents ← исходный файл + распарсенный текст │
|
||
│ contracts ← договор │
|
||
│ supplements ← допник │
|
||
│ spec_rows ← строки спецификации │
|
||
│ spec_history ← история изменений │
|
||
└──────────────────────────┬──────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ Ответ пользователю: HTML-страница с таблицей результатов │
|
||
│ «Извлечено 14 строк, добавлено 14, изменено 0, удалено 0» │
|
||
│ + полная таблица услуг с ценами │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Кратко
|
||
|
||
```
|
||
файлы → parser → textify → LLM → differ → PostgreSQL → HTML-страница
|
||
```
|
||
|
||
## Слои
|
||
|
||
| # | Слой | Что делает |
|
||
|---|---|---|
|
||
| ① | `parser.py` | docx/pdf/doc/zip → JSON |
|
||
| ② | `textify.py` | JSON → текст |
|
||
| ③ | `extractor.py` + `llm_client.py` | текст → LLM → строки |
|
||
| ④ | `differ.py` | сравнение с предыдущим |
|
||
| ⑤ | `db.py` + `schema.py` | сохранение в БД |
|
||
| — | `app.py` | сборка, приём формы, отдача HTML |
|