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

7.2 KiB
Raw Blame History

Контекст

Проект «Сверка договоров» — автоматическое сравнение договоров и допсоглашений (ДС) облачного провайдера.

Текущая реализация (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):
[
  {"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":"Не удалось сопоставить строку..."}
]
  1. Lucee исполняет операции как лог (event sourcing), без хардкода
  2. Связывание строк — по хэшу названия услуги (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 в один запрос к ВМ (все ДС сразу)?