docs: итоги сессии — decoupling + дедупликация + защита
This commit is contained in:
@@ -0,0 +1,85 @@
|
|||||||
|
# Сессия 2026-06-28 — Итоги
|
||||||
|
|
||||||
|
## Decoupling пайплайна (Ф1-Ф4)
|
||||||
|
|
||||||
|
### Ф1 — Контракты + upload decouple
|
||||||
|
- `deploy/contracts.py` — 5 dataclass'ов: ParseResult, ClassifyResult, GroupingResult, BatchGroupingResult, CompareOp
|
||||||
|
- `deploy/services/upload.py` — выделены `parse_multipart()` и `store_document()`
|
||||||
|
- `handle_upload` теперь тонкий HTTP-handler
|
||||||
|
|
||||||
|
### Ф2 — LLM Client DI
|
||||||
|
- `deploy/services/llm_client.py` — протокол `LLMClient.complete(prompt)->str` + `HttpxLLMClient` + `FakeLLMClient`
|
||||||
|
- `deploy/services/llm.py` — `call_llm(..., llm_client=None)` — DI с обратной совместимостью
|
||||||
|
- `deploy/services/classify.py` — `_call_llm_classify(..., llm_client=None)` + `classify_batch(..., llm_client=None, repo=None)`
|
||||||
|
|
||||||
|
### Ф3 — Repository facade
|
||||||
|
- `deploy/repository.py` — Protocol `Repository`, `PgRepository`, `MemRepository` (17 методов)
|
||||||
|
|
||||||
|
### Ф4 — pytest
|
||||||
|
- `deploy/conftest.py` — фикстуры: `fake_llm`, `mem_repo`, `sample_docx_bytes`
|
||||||
|
- `deploy/tests/unit/test_contracts.py` — 13 тестов (contracts, garbage, multipart)
|
||||||
|
- `deploy/tests/unit/test_llm_repo.py` — 9 тестов (FakeLLM, MemRepository)
|
||||||
|
- `deploy/tests/unit/test_classify_batch.py` — 4 теста (garbage×2, LLM, no_pending)
|
||||||
|
- **26/26 PASS**
|
||||||
|
|
||||||
|
### Баги (ревью Opus)
|
||||||
|
- #1 Двойная сериализация JSON в `PgRepository.set_document_parsed` — исправлено
|
||||||
|
- #2 `db_docs` → `_db` в `classify_batch` — исправлено (6 вызовов)
|
||||||
|
- #3 `ParseResult.from_row` element_count=0 — исправлено
|
||||||
|
- #4 `GroupingResult.unresolved` → `BatchGroupingResult` — исправлено
|
||||||
|
- Protocol расширен: +7 методов
|
||||||
|
- `build_classify_prompt`: try/except вокруг `db.prompts.get_active()`
|
||||||
|
- `MemRepository.set_classification`: +`classify_raw`, `classify_input`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Дедупликация + защита от careless user
|
||||||
|
|
||||||
|
### content_hash
|
||||||
|
- `db/documents.py` — `insert()` принимает `content_hash`, `get_by_hash()` для проверки
|
||||||
|
- `services/upload.py` — `store_document()` считает `sha256` и пропускает дубликаты
|
||||||
|
- `convert_server.py` — ALTER TABLE `content_hash`
|
||||||
|
|
||||||
|
### Сортировка по doc_date
|
||||||
|
- `services/process.py` — `supps.sort(key=lambda s: (doc_date, created_at))`
|
||||||
|
|
||||||
|
### date_start в name_hash
|
||||||
|
- `db/spec_events.py` — `_hash(name, date_start)` — хеш включает период, разные периоды = разные строки
|
||||||
|
|
||||||
|
### ZIP-защита
|
||||||
|
- `services/unzip.py` — лимит 500 файлов, 500 MB, проверка ratio (100x), санитизация имён (cp437→utf8, path-traversal)
|
||||||
|
|
||||||
|
### Статус «не распознан»
|
||||||
|
- `services/upload.py` — supplement НЕ создаётся при ошибке парсинга, `warning` при пустом результате
|
||||||
|
- `try/except: pass` → `logging.warning`
|
||||||
|
|
||||||
|
### UNRESOLVED вместо тихих no-op
|
||||||
|
- `db/spec_events.py` — `ADD` без имени → UNRESOLVED, `UPDATE/DELETE` с пустым `target_hash` → UNRESOLVED, неизвестный `action` → UNRESOLVED
|
||||||
|
- `_log_unresolved()` — общая функция
|
||||||
|
|
||||||
|
### Нормализация чисел и дат
|
||||||
|
- `services/metrics.py` — `_to_decimal`: пробелы → удалить, `,` → `.`
|
||||||
|
- `services/metrics.py` — `normalize_date()`: DD.MM.YYYY → YYYY-MM-DD
|
||||||
|
- `db/spec_events.py` — `_hash` применяет `normalize_date`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Архитектура check.kube5s.ru
|
||||||
|
- ВМ: порт 8777, БД `contracts_check`, код `~/contracts-check/`
|
||||||
|
- Nginx: SSL (letsencrypt), CORS, proxy headers
|
||||||
|
- Managed Flask: `contracts-flask.git`, `site/` + `Dockerfile`
|
||||||
|
- VM_API = `https://check.kube5s.ru`
|
||||||
|
- Полная изоляция от прода (contracts.kube5s.ru)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Файлы History
|
||||||
|
- `architecture.md` — архитектура Flask-стека
|
||||||
|
- `session-01-init-2026-06-27.md` — создание стека
|
||||||
|
- `opus-decoupling-plan-2026-06-28.md` — задание Opus
|
||||||
|
- `opus-decoupling-review-2026-06-28.md` — рецензия DeepSeek
|
||||||
|
- `opus-dialog-2026-06-28.md` — полный диалог DeepSeek↔Opus
|
||||||
|
- `opus-review-request-2026-06-28.md` — запрос ревью
|
||||||
|
- `opus-review-2-request-2026-06-28.md` — повторное ревью
|
||||||
|
- `opus-review-response-2026-06-28.md` — результат ревью
|
||||||
|
- `opus-dedup-2026-06-28.md` — дедупликация + засады (15 пунктов)
|
||||||
Reference in New Issue
Block a user