Files
contractor/Files/sonnet-v2-request.md
T

88 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Контекст
Проект «Сверка договоров» — автоматическое сравнение договоров и допсоглашений (ДС) облачного провайдера.
### Текущая реализация (Lucee CFML + PostgreSQL)
**Таблицы:**
- `contracts` (id UUID, number, client)
- `documents` (id UUID, filename, mime_type, original_bytes, elements_json JSONB, parsed_text)
- `supplements` (id UUID, contract_id, document_id, type: 'initial'/'additional')
- `spec_rows` (id UUID, supplement_id, row_num INTEGER, name TEXT, price NUMERIC, qty NUMERIC, sum NUMERIC, date_start TEXT)
- `prompts` (contract_id UUID, prompt_text TEXT)
**Текущий поток:**
1. Загрузка PDF/DOCX через ВМ-прокси (contracts.kube5s.ru) — т.к. k8s Ingress с ddos-guard рвёт HTTP/2 при прямых POST > ~50KB. ВМ проксирует на Lucee (proxy_buffering off, client_max_body_size 100m). Для .doc — конвертация через libreoffice на ВМ.
2. Парсинг (PDFBox/POI/Tabula) → elements_json (параграфы + таблицы)
3. Для каждого документа отдельный вызов LLM (api.aillm.ru, gpt-oss-120b):
- Промпт: «Извлеки строки спецификации в JSON {rows: [{row_num, name, price, qty, sum, date_start}]}»
- Ответ LLM: JSON со строками
4. Сохраняется в spec_rows (DELETE старых + INSERT новых)
5. Сравнение: differ.cfm сравнивает initial VS каждый additional по row_num → added/deleted/changed
**Проблемы текущего подхода:**
- Сравнение по row_num — хрупкое. Если в ДС поменялся порядок строк — всё ломается
- Нет кумулятивного статуса — только попарный diff
- LLM используется только как экстрактор, а не как интерпретатор изменений
- Нет обработки неясных ситуаций (LLM не может сказать «не знаю»)
- Нет истории версий (event sourcing)
**Архитектура загрузки (важно):**
- Фронтенд Lucee (contractor.luceek8s.dev.nubes.ru) НЕ может принимать большие POST напрямую — k8s Ingress + ddos-guard рвут HTTP/2 стримы случайным образом (ERR_HTTP2_PROTOCOL_ERROR)
- Загрузка идёт через ВМ-прокси (contracts.kube5s.ru, nginx): proxy_buffering off, client_max_body_size 100m
- ВМ может выполнять дополнительную логику при необходимости (конвертация .doc, валидация, etc.)
- Прямая загрузка на pythonk8s (contractor.pythonk8s.services.ngcloud.ru) имеет ту же проблему с HTTP/2
**Инфраструктура:**
- Lucee 6.0 CFML на k8s (contractor.luceek8s.dev.nubes.ru)
- Python Flask на k8s (contractor.pythonk8s.services.ngcloud.ru)
- Python Flask на ВМ (contracts.kube5s.ru) — nginx, libreoffice, прокси для k8s
- PostgreSQL (через JDBC на Lucee, psycopg2 на Python)
- LLM API: api.aillm.ru/v1/chat/completions (gpt-oss-120b, 120B параметров, локальная модель, бесплатно, без ограничений по токенам)
- LLM НЕ поддерживает structured output / JSON mode — только промпт
- Клиентский HTTP/2 через httpx на Python, cfhttp на Lucee
- **Проблема Lucee:** cfhttp блокирует поток на время ответа LLM (до 120с на каждый вызов). Если 5 допников — Lucee висит 10 минут, ничего не отдавая клиенту
## Что нужно
Разработать архитектуру и логику **кумулятивного статуса договора** (event sourcing):
### Идея (требует детальной проработки)
1. **LLM получает**: текущую спецификацию договора (все строки) + полный текст ДС
2. **LLM возвращает**: список операций (ADD/UPDATE/DELETE/UNRESOLVED):
```json
[
{"action":"UPDATE","target_hash":"abc123","new_values":{"price":150000},"comment":"..."},
{"action":"DELETE","target_hash":"def456","comment":"..."},
{"action":"ADD","new_row":{"name":"...","price":...,"qty":...,"sum":...,"date_start":"..."}},
{"action":"UNRESOLVED","reason":"Не удалось сопоставить строку..."}
]
```
3. **Lucee исполняет** операции как лог (event sourcing), без хардкода
4. Связывание строк — по хэшу названия услуги (md5 нормализованного имени), НЕ по row_num
### Требования к архитектуре
- Event sourcing: лог операций, можно восстановить состояние на любую дату
- Обработка «полной замены» спецификации в ДС (пачка DELETE + ADD)
- UNRESOLVED: если LLM не уверен — оператор решает вручную (нужен UI)
- Два режима сравнения: А) ДС → изменения поверх договора, Б) ДС → полная замена спецификации
- Отказоустойчивость: если LLM ошибся, не испортить основные данные
- Поскольку загрузка идёт через ВМ — можно добавить промежуточную логику на ВМ если нужно
### Вопрос
Разработай полную архитектуру:
1. Структуру таблиц БД (лог операций, кумулятивный статус)
2. Промпт для LLM (как передать контекст: текущую спецификацию + текст ДС)
3. Логику Lucee (CFML) для исполнения лога операций
4. Как обрабатывать UNRESOLVED (UI + процесс)
5. Как откатывать ошибочные операции
6. Нужно ли сохранять старый экстрактор строк или заменить полностью?
7. ВМ-прокси как async-буфер для LLM-вызовов
- Lucee через cfhttp висит синхронно до 120с на каждый вызов LLM
- Предложение: ВМ (Flask + httpx) забирает задачу от Lucee,
асинхронно вызывает LLM, отдаёт готовый JSON команд
- Lucee не блокируется, httpx работает с HTTP/2 надёжнее cfhttp
- Как должен выглядеть API между Lucee и ВМ для этого?
- Нужна ли очередь задач или можно синхронно (ВМ ждёт, Lucee ждёт ВМ)?
- Стоит ли объединить все вызовы LLM в один запрос к ВМ (все ДС сразу)?