docs: код-ревью (2026-08-26) + задания Sonnet по архитектуре (v2.0.16)
Deploy contracts-flask / validate (push) Canceled after 0s
Deploy contracts-flask / validate (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Sonnet: код-ревью contracts-flask (2026-08-26)
|
||||
|
||||
## Контекст
|
||||
|
||||
- Написан промпт для Sonnet: `contracts-flask/docs/prompt-sonnet-architecture.md`
|
||||
(задача — прочитать файлы текущего сервиса сверки и дать описание/архитектуру).
|
||||
- Sonnet прочитал все указанные файлы и вместо описания выдал **код-ревью** с 10 находками
|
||||
(2 критические, 3 средних, 2 XSS, 3 мелких). Архитектурное описание — отдельным документом (не выдано).
|
||||
|
||||
## Находки Sonnet
|
||||
|
||||
### 🔴 Критические
|
||||
|
||||
1. **`process.py` + `spec_events.py` — UPDATE/DELETE никогда не применяются (сломана частичная сверка).**
|
||||
LLM возвращает `target_id: "r1"` (индекс строки), а `apply_ops()` читает `op.get("target_hash", "")`.
|
||||
Поля `target_hash` в ответе LLM нет → все UPDATE/DELETE из `mode=partial` молча уходят в `UNRESOLVED`.
|
||||
Трассировка: `_build_spec_text()` показывает строки `[id: r1]` → LLM возвращает `target_id: "r1"` →
|
||||
`process.py` передаёт ops без трансляции r1→hash → `spec_events.py` `th = op.get("target_hash","")` = "" → UNRESOLVED.
|
||||
Работает только `mode=full_replace` (все ADD) и первый документ (extract, только ADD).
|
||||
|
||||
2. **`Dockerfile` — образ не запустится: `upload/` не скопирован.**
|
||||
`COPY site /app/site` — только site/, `upload/` отсутствует.
|
||||
При старте `routes/__init__.py` делает `from upload.backend.upload_refs import ...`, `app.py` добавляет `/app` в sys.path.
|
||||
В образе `/app/upload/` нет → `ModuleNotFoundError`.
|
||||
|
||||
### 🟠 Средние
|
||||
|
||||
3. **`db/prompts.py` — `list_by_role()` запрашивает несуществующий столбец `created_by`.**
|
||||
В схеме `prompts` нет `created_by` → `sqlite3.OperationalError` → `/api/prompts/list` всегда 500.
|
||||
|
||||
4. **`routes/prompts_bp.py` — вызов несуществующих функций:**
|
||||
- `db_prompts.insert(...)` — нет (есть `save_new_version()`);
|
||||
- `db_prompts.delete(...)` — нет (есть `delete_prompt()`).
|
||||
`/api/prompts/save` и `/api/prompts/delete` падают с `AttributeError`.
|
||||
|
||||
5. **`services/llm.py`, `services/classify.py` — захардкожены `LLM_URL/LLM_KEY/LLM_MODEL` в обход `config.py`.**
|
||||
`config.py` читает из `LLM_API_URL`, а compare/classify игнорируют его. Конфигурация расщеплена на 3 блока.
|
||||
|
||||
### 🟡 XSS (frontend)
|
||||
|
||||
6. **`compare.js` — `nr.name` в таблице операций не экранирован** (`escHtml()` отсутствует).
|
||||
Имя услуги из договора (текст LLM) вставляется в innerHTML → XSS.
|
||||
|
||||
7. **`compare.js` — `sec.filename` в заголовке секции не экранирован.** Имя файла от пользователя → XSS.
|
||||
|
||||
### ⚪ Мелкие
|
||||
|
||||
8. **`llm_prompt.py`** — устаревший комментарий «Lucee API» (читает напрямую из SQLite).
|
||||
9. **`db/connection.py`** — `_pg_to_sqlite`: `gen_random_uuid()` добавляет `?` в SQL, но не добавляет значение в params (мёртвый код, но при исполнении сломает запрос).
|
||||
10. **`services/classify.py`** — комментарий «re-classify after file changes» вводит в заблуждение: `reset_classify_status()` сбрасывает только `processing`→`pending`, уже классифицированные не трогает.
|
||||
|
||||
## Итоговая таблица
|
||||
|
||||
| # | Файл | Проблема | Критичность |
|
||||
|---|---|---|---|
|
||||
| 1 | process.py, spec_events.py | UPDATE/DELETE → UNRESOLVED, частичная сверка сломана | 🔴 критический |
|
||||
| 2 | Dockerfile | upload/ не скопирован → образ не стартует | 🔴 критический |
|
||||
| 3 | db/prompts.py | created_by отсутствует → 500 на /api/prompts/list | 🟠 средний |
|
||||
| 4 | prompts_bp.py | insert()/delete() — не те имена функций → 500 | 🟠 средний |
|
||||
| 5 | llm.py, classify.py | захардкожены LLM_URL/KEY/MODEL в обход config.py | 🟠 средний |
|
||||
| 6 | compare.js | nr.name не экранирован → XSS | 🟡 XSS |
|
||||
| 7 | compare.js | sec.filename не экранирован → XSS | 🟡 XSS |
|
||||
| 8 | llm_prompt.py | устаревший комментарий | ⚪ мелкое |
|
||||
| 9 | connection.py | gen_random_uuid() мёртвый код с ошибкой | ⚪ мелкое |
|
||||
| 10 | classify.py | комментарий re-classify вводит в заблуждение | ⚪ мелкое |
|
||||
|
||||
## Статус
|
||||
|
||||
- ✅ Все 10 находок исправлены (v2.0.15, commit `e3f5581`, запушено).
|
||||
- #1 (критично) — сам: `process.py` трансляция `target_id`→`target_hash`.
|
||||
- #2–#10 (механика) — Флаш-субагент: Dockerfile, prompts CRUD, XSS, конфиг LLM, комментарии.
|
||||
- Верифицировано: `py_compile`, `node -c`, `import app` — OK.
|
||||
- Архитектурное описание от Sonnet — так и не получено (вместо него — ревью). Если нужно — отдельным запросом.
|
||||
@@ -0,0 +1,104 @@
|
||||
# Задание для Sonnet: описание архитектуры сервиса «Сверка договоров»
|
||||
|
||||
## Роль и задача
|
||||
|
||||
Ты — технический аналитик. Твоя задача — **только прочитать указанные файлы** и написать
|
||||
подробное, структурированное описание сервиса «Сверка договоров»: что это за сервис, как
|
||||
устроен, как работает каждый этап, какими методами и технологиями. **Ничего не менять и
|
||||
не создавать в коде.** Результат — один текстовый документ (markdown).
|
||||
|
||||
## Короткий контекст (чтобы ты понимал, о чём речь)
|
||||
|
||||
Сервис «Сверка договоров» — веб-приложение на **Flask + vanilla JS** (без фреймворков
|
||||
на фронте), развёрнутое на managed-кластере Nubes (Штурвал). Пользователь загружает
|
||||
договоры и допсоглашения (docx/pdf/zip), сервис:
|
||||
1. разбирает их на структурированные спецификации;
|
||||
2. автоматически классифицирует документы (тип/номер/дата/контрагент) через LLM;
|
||||
3. группирует документы по договорам;
|
||||
4. по цепочке допников сравнивает изменения спецификации (ADD/UPDATE/DELETE) через LLM;
|
||||
5. показывает историю изменений и даёт возможность задать вопрос чату по итоговой спецификации.
|
||||
|
||||
Ключевое ограничение: данные конфиденциальны, LLM — только своя (api.aillm.ru, модель
|
||||
gpt-oss-120b). Большие файлы не грузятся напрямую в кластер (шлюз рвёт тела >~64 КБ) —
|
||||
используется «ВМ-буфер»: браузер кладёт файл на ВМ (WebDAV), а бэк сам тянет его
|
||||
исходящим запросом (egress не ограничен).
|
||||
|
||||
## Что прочитать — ТОЛЬКО эти файлы (корень проекта: contracts-flask/)
|
||||
|
||||
### Ядро бэкенда (обязательно, читать все)
|
||||
- `site/app.py` — точка входа, фабрика приложения, sys.path
|
||||
- `site/config.py` — конфигурация (версия, LLM_URL/KEY/MODEL, CONVERT_SERVICE_URL, VM_UPLOAD_*)
|
||||
- `site/llm_prompt.py` — формирование промптов LLM (извлечение/сравнение/классификация)
|
||||
- `site/routes/__init__.py` — регистрация всех blueprint'ов
|
||||
- `site/routes/upload_bp.py` — загрузка файлов (multipart + sink закачки через ВМ)
|
||||
- `site/routes/pipeline_bp.py` — классификация + SSE-сравнение (/process-v2, /api/classify-batch)
|
||||
- `site/routes/api_bp.py` — API (groups, documents, supplements, sync, cleanup, spec-current, chat)
|
||||
- `site/routes/pages_bp.py` — HTML-страницы + раздача ES-модулей upload/
|
||||
- `site/routes/prompts_bp.py` — CRUD и версионирование промптов
|
||||
- `site/routes/health_bp.py` — healthcheck
|
||||
- `site/services/parse.py` — парсинг PDF (pdfplumber), DOCX (python-docx), TXT
|
||||
- `site/services/classify.py` — LLM-классификация документов (garbage-фильтры, выжимка, ThreadPool)
|
||||
- `site/services/grouping.py` — нормализация номеров и группировка по договорам
|
||||
- `site/services/process.py` — SSE-пайплайн сравнения (run_pipeline)
|
||||
- `site/services/llm.py` — вызов LLM для сравнения (call_llm)
|
||||
- `site/services/llm_client.py` — LLM-клиент (HttpxLLMClient, FakeLLMClient для тестов)
|
||||
- `site/services/metrics.py` — проверка арифметики (sum == price*qty), метрики качества
|
||||
- `site/db/connection.py` — SQLite: схема всех таблиц, WAL, thread-local соединения
|
||||
- `site/db/documents.py` — CRUD документов (статусы, классификация, elements_json)
|
||||
- `site/db/contracts.py` — договоры
|
||||
- `site/db/supplements.py` — допники (связь contract↔document)
|
||||
- `site/db/spec_events.py` — event sourcing: apply_ops (ADD/UPDATE/DELETE), reset
|
||||
- `site/db/spec_current.py` — текущее состояние спецификации договора
|
||||
- `site/db/prompts.py` — версии промптов по ролям
|
||||
|
||||
### Фронтенд (для понимания потока на стороне браузера)
|
||||
- `site/static/state.js` — центральное состояние приложения
|
||||
- `site/static/app.js` — инициализация, обработчики загрузки/классификации/сравнения
|
||||
- `site/static/files.js` — выбор файлов/папок/архивов, загрузка через ВМ-буфер
|
||||
- `site/static/groups.js` — классификация и группы на фронте
|
||||
- `site/static/compare.js` — сравнение (SSE) и рендер результатов
|
||||
- `site/templates/index.html` — разметка UI
|
||||
|
||||
### Переиспользуемый модуль загрузки через ВМ (модуль upload/)
|
||||
- `upload/README.md` — описание модуля и паттерна «ВМ-буфер + pull»
|
||||
- `upload/backend/upload_refs/blueprint.py` — blueprint POST /api/upload_refs с параметром sink
|
||||
- `upload/backend/upload_refs/safe_name.py`, `pull_file.py`, `config.py` — транспортные примитивы
|
||||
- `upload/backend/session/__init__.py` — in-memory сессия (используется drhider; сверка его не использует)
|
||||
|
||||
### Инфраструктура
|
||||
- `Dockerfile` — сборка образа
|
||||
- `requirements.txt` — зависимости
|
||||
|
||||
## Что НЕ читать и не трогать
|
||||
|
||||
- `History/` — вся история сессий, ревью, старые планы (НЕ нужно)
|
||||
- `contractor-legacy/` — замороженный Lucee-прод (не актуален)
|
||||
- `contracts-vm/`, `convert-service/`, `loadtest/`, `testgen/`, `sim/`, `dogovora/`, `hz/`, `FILES/`, `DOC/` — смежные/вспомогательные, НЕ ядро сервиса
|
||||
- `deploy/` — устаревший VM-слой (convert_server.py и т.п.), не входит в текущий managed-сервис
|
||||
- `site/services/drhider.py` — совместимость с DrHider (побочное, не ядро сверки)
|
||||
- `site/templates/drhider.html`, `architect.html`, `pipeline.html`, `ci-cd.html` — не ядро сверки
|
||||
|
||||
## Что должно быть в ответе (структура)
|
||||
|
||||
1. **Описание сервиса** — что делает, кому, какие входы/выходы (2-4 абзаца, по-простому).
|
||||
2. **Общая архитектура** — слои (фронт / бэк / БД / LLM / ВМ-буфер), как они связаны.
|
||||
Лучше — схема потока: загрузка → парсинг → классификация → группировка → сравнение → чат.
|
||||
3. **Структура проекта** — дерево `site/` (+ `upload/`) с назначением каждого файла.
|
||||
4. **Как работает каждый этап** (по пунктам, «что делает / как / где в коде»):
|
||||
- загрузка файлов (включая папки/архивы, ВМ-буфер, sink);
|
||||
- парсинг PDF/DOCX/TXT → elements (таблицы + параграфы);
|
||||
- классификация (garbage-фильтры, выжимка, LLM, параллельность);
|
||||
- группировка (нормализация номеров, виртуальные группы, __unresolved__);
|
||||
- сравнение (SSE-пайплайн, ops ADD/UPDATE/DELETE, event sourcing, spec_current);
|
||||
- чат по итоговой спецификации.
|
||||
5. **Технологии и методы** — коротко: SQLite (WAL, thread-local), SSE, ThreadPool, LLM-клиент,
|
||||
промпты (из БД + fallback), event sourcing, метрики качества, ВМ-буфер загрузки.
|
||||
6. **Модель данных** — таблицы БД и их назначение (documents, contracts, supplements,
|
||||
spec_events, spec_current, prompts).
|
||||
|
||||
## Требования к стилю
|
||||
|
||||
- Чётко, структурно, без воды и лирики.
|
||||
- Без излишней заумности — понятно не только девопсу, но и обычному разработчику/аналитику.
|
||||
- Каждый этап: «что делает → как → каким методом → где в коде (файл/функция)».
|
||||
- Ничего не менять в коде. Только текстовый документ.
|
||||
@@ -0,0 +1,101 @@
|
||||
# Задание для Sonnet (v2): описание архитектуры сервиса «Сверка договоров»
|
||||
|
||||
## Роль и задача
|
||||
|
||||
Ты — технический аналитик. Задача — **только прочитать указанные файлы** и написать
|
||||
подробное, структурированное описание сервиса «Сверка договоров»: что это, как устроено,
|
||||
как работает каждый этап, какими методами/технологиями. **Ничего не менять в коде.**
|
||||
Результат — один текстовый документ (markdown).
|
||||
|
||||
## Контекст (что уже было)
|
||||
|
||||
Ранее ты выдал **код-ревью** с 10 находками (вместо описания). Все 10 уже **исправлены**
|
||||
(v2.0.15): критичный баг `target_id` vs `target_hash` (частичная сверка), Dockerfile,
|
||||
prompts CRUD, LLM-конфиг, XSS в `compare.js`, мелочи. Учти это — читай код **после** правок,
|
||||
описывай **текущее** состояние.
|
||||
|
||||
## Сервис в двух словах
|
||||
|
||||
«Сверка договоров» — веб-приложение на **Flask + vanilla JS** (без фреймворков на фронте),
|
||||
на managed-кластере Nubes. Пользователь загружает договоры/допсоглашения (docx/pdf/zip),
|
||||
сервис: разбирает их в спецификации → классифицирует документы через LLM → группирует
|
||||
по договорам → по цепочке допников сравнивает изменения (ADD/UPDATE/DELETE) через LLM →
|
||||
показывает историю + чат по итоговой спецификации. Данные конфиденциальны, LLM — только
|
||||
своя (api.aillm.ru, gpt-oss-120b). Большие файлы грузятся через «ВМ-буфер» (браузер PUT на
|
||||
WebDAV → бэк тянет egress GET), т.к. шлюз кластера рвёт тела >~64 КБ.
|
||||
|
||||
## Что прочитать — ТОЛЬКО эти файлы (корень: contracts-flask/)
|
||||
|
||||
### Ядро бэкенда
|
||||
- `site/app.py` — точка входа, фабрика приложения, sys.path
|
||||
- `site/config.py` — конфигурация (версия, LLM, конвертер .doc, VM_UPLOAD_*)
|
||||
- `site/llm_prompt.py` — формирование промптов LLM (extract/diff/classify + fallback)
|
||||
- `site/routes/__init__.py` — регистрация blueprint'ов
|
||||
- `site/routes/upload_bp.py` — загрузка (multipart + sink закачки через ВМ)
|
||||
- `site/routes/pipeline_bp.py` — классификация + SSE-сравнение (/process-v2, /api/classify-batch)
|
||||
- `site/routes/api_bp.py` — API (groups, documents, supplements, sync, cleanup, spec-current, chat)
|
||||
- `site/routes/pages_bp.py` — HTML-страницы + раздача ES-модулей upload/
|
||||
- `site/routes/prompts_bp.py` — CRUD и версионирование промптов
|
||||
- `site/routes/health_bp.py` — healthcheck
|
||||
- `site/services/parse.py` — парсинг PDF (pdfplumber), DOCX (python-docx), TXT
|
||||
- `site/services/classify.py` — LLM-классификация (garbage-фильтры, выжимка, ThreadPool)
|
||||
- `site/services/grouping.py` — нормализация номеров и группировка по договорам
|
||||
- `site/services/process.py` — SSE-пайплайн сравнения (run_pipeline) + трансляция target_id→hash
|
||||
- `site/services/llm.py` — вызов LLM для сравнения (call_llm)
|
||||
- `site/services/llm_client.py` — LLM-клиент (HttpxLLMClient, FakeLLMClient)
|
||||
- `site/services/metrics.py` — проверка арифметики (sum == price*qty), метрики качества
|
||||
- `site/db/connection.py` — SQLite: схема, WAL, thread-local
|
||||
- `site/db/documents.py` — CRUD документов
|
||||
- `site/db/contracts.py` — договоры
|
||||
- `site/db/supplements.py` — допники (contract↔document)
|
||||
- `site/db/spec_events.py` — event sourcing: apply_ops (ADD/UPDATE/DELETE), reset
|
||||
- `site/db/spec_current.py` — текущее состояние спецификации
|
||||
- `site/db/prompts.py` — версии промптов
|
||||
|
||||
### Фронтенд
|
||||
- `site/static/state.js` — центральное состояние
|
||||
- `site/static/app.js` — инициализация, обработчики
|
||||
- `site/static/files.js` — выбор файлов/папок/архивов, загрузка через ВМ
|
||||
- `site/static/groups.js` — классификация и группы на фронте
|
||||
- `site/static/compare.js` — сравнение (SSE) и рендер
|
||||
- `site/templates/index.html` — UI
|
||||
|
||||
### Модуль upload/ (переиспользуемый)
|
||||
- `upload/README.md` — паттерн «ВМ-буфер + pull»
|
||||
- `upload/backend/upload_refs/blueprint.py` — blueprint POST /api/upload_refs с sink
|
||||
- `upload/backend/upload_refs/safe_name.py`, `pull_file.py`, `config.py`
|
||||
- `upload/backend/session/__init__.py` — in-memory сессия (drhider; сверка не использует)
|
||||
|
||||
### Инфраструктура
|
||||
- `Dockerfile`, `requirements.txt`
|
||||
|
||||
## Что НЕ читать
|
||||
|
||||
`History/`, `contractor-legacy/`, `contracts-vm/`, `convert-service/`, `loadtest/`,
|
||||
`testgen/`, `sim/`, `dogovora/`, `hz/`, `FILES/`, `DOC/`, `deploy/` (устаревший VM-слой),
|
||||
`site/services/drhider.py`, `site/templates/drhider.html|architect.html|pipeline.html|ci-cd.html`.
|
||||
|
||||
## Что должно быть в ответе (структура)
|
||||
|
||||
1. **Описание сервиса** — что делает, входы/выходы (2–4 абзаца, по-простому).
|
||||
2. **Общая архитектура** — слои (фронт/бэк/БД/LLM/ВМ-буфер) + схема потока:
|
||||
загрузка → парсинг → классификация → группировка → сравнение → чат.
|
||||
3. **Структура проекта** — дерево `site/` (+ `upload/`) с назначением каждого файла.
|
||||
4. **Как работает каждый этап** — «что делает → как → каким методом → где в коде (файл/функция)».
|
||||
5. **Технологии и методы** — SQLite (WAL, thread-local), SSE, ThreadPool, LLM-клиент,
|
||||
промпты (БД + fallback), event sourcing, метрики качества, ВМ-буфер.
|
||||
6. **Модель данных** — таблицы и назначение (documents, contracts, supplements,
|
||||
spec_events, spec_current, prompts).
|
||||
|
||||
## Дополнительно: точечная проверка фикса #1
|
||||
|
||||
Отдельно, коротко (3–5 строк): в `site/services/process.py` глянь блок трансляции
|
||||
`target_id ("r1","r2"…) → target_hash` (через `current_spec[_idx]["hash"]`). Подтверди:
|
||||
корректно ли он маппит `rN` (1-based) на индекс `current_spec`, и что при невалидном id
|
||||
операция уходит в UNRESOLVED, а не падает. Если видишь ошибку — укажи, но НЕ правь.
|
||||
|
||||
## Требования к стилю
|
||||
|
||||
- Чётко, структурно, без воды и лирики.
|
||||
- Без излишней заумности — понятно не только девопсу, но и обычному разработчику/аналитику.
|
||||
- Ничего не менять в коде. Только текстовый документ.
|
||||
Reference in New Issue
Block a user