From f25f524a49c12d85b7f52a48cd85e86cb2341383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Mon, 15 Jun 2026 07:02:17 +0400 Subject: [PATCH] =?UTF-8?q?doc:=20=D0=BF=D0=B0=D0=B9=D0=BF=D0=BB=D0=B0?= =?UTF-8?q?=D0=B9=D0=BD=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=B4=D0=BE=D0=B3=D0=BE=D0=B2=D0=BE=D1=80=D0=B0=20?= =?UTF-8?q?=E2=80=94=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=B0=D1=8F=20=D1=81=D1=85?= =?UTF-8?q?=D0=B5=D0=BC=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- history/block-diagram.md | 69 ++++++++++++++++++++++++++ history/pipeline.md | 101 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 history/block-diagram.md create mode 100644 history/pipeline.md diff --git a/history/block-diagram.md b/history/block-diagram.md new file mode 100644 index 0000000..fbd30d7 --- /dev/null +++ b/history/block-diagram.md @@ -0,0 +1,69 @@ +# Блок-схема сервиса Contracts + +## Полный пайплайн обработки допника + +```mermaid +flowchart TD + A["📄 POST /api/contracts/{id}/supplements\n(file.docx)"] --> B["💾 documents.original_bytes\n(status=uploaded)"] + B --> C["🔧 parser.parse()\nbytes → elements JSON"] + C --> D["📝 textify.to_text()\nelements → линейный текст"] + D --> E["📄 documents.parsed_text\n(status=parsed)"] + E --> F["🤖 extractor.extract()\nтекст → LLM → строки JSON"] + F --> G["📊 spec_rows\n(row_num, name, price, qty, sum, date)"] + G --> H["🔄 differ.diff()\nсравнение с пред. допником"] + H --> I["📋 spec_history\n(added/changed/deleted/unchanged)"] + I --> J["✅ ответ API\n{rows, diff_summary}"] +``` + +## Архитектура слоёв + +```mermaid +flowchart LR + subgraph "Внешний мир" + USER["👤 Пользователь"] + end + + subgraph "Flask" + APP["app.py\nсборка"] + API["api.py\n/contracts"] + UPL["upload.py\n/upload"] + TEST["test_routes.py\n/test"] + end + + subgraph "Бизнес-логика" + PARSER["parser.py\nbytes→JSON"] + TEXTIFY["textify.py\nJSON→текст"] + LLM["llm_client.py\nHTTP/2→LLM"] + EXTRACT["extractor.py\nтекст→строки"] + DIFF["differ.py\nсравнение"] + end + + subgraph "Данные" + DB["db.py\nconnect/query"] + SCH["schema.py\nDDL"] + PG[("PostgreSQL\n5 таблиц")] + end + + USER -->|"POST docx"| API + USER -->|"GET /test"| TEST + API --> PARSER + PARSER --> TEXTIFY + TEXTIFY --> EXTRACT + EXTRACT --> LLM + EXTRACT --> DIFF + API --> DB + DB --> PG + APP --> SCH + SCH --> DB +``` + +## Цепочка данных + +```mermaid +flowchart LR + A["docx\n(байты)"] -->|parser| B["elements\nJSON"] + B -->|textify| C["текст\nстрока"] + C -->|extractor + LLM| D["spec_rows\nJSON"] + D -->|differ| E["changes\nJSON"] + E -->|api| F["ответ\nпользователю"] +``` diff --git a/history/pipeline.md b/history/pipeline.md new file mode 100644 index 0000000..b86ae2e --- /dev/null +++ b/history/pipeline.md @@ -0,0 +1,101 @@ +# Пайплайн обработки договора + +## Схема + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ 👤 ПОЛЬЗОВАТЕЛЬ │ +│ Открывает / → выбирает файлы → жмёт «Обработать» │ +└──────────────────────────┬──────────────────────────────────────────┘ + │ 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 |