docs: ответ Opus — дедупликация, поправки к посылкам, MVP
This commit is contained in:
@@ -45,3 +45,69 @@
|
|||||||
## Ожидаемый ответ
|
## Ожидаемый ответ
|
||||||
|
|
||||||
На каждый сценарий: **как ДОЛЖНО быть** и **как проще всего реализовать сейчас** (MVP). Приоритет: не сломать существующее поведение.
|
На каждый сценарий: **как ДОЛЖНО быть** и **как проще всего реализовать сейчас** (MVP). Приоритет: не сломать существующее поведение.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ответ Opus
|
||||||
|
|
||||||
|
### ⚠️ Поправки к посылкам
|
||||||
|
|
||||||
|
1. **«ADD всегда INSERT → дубликат»** — неверно. `spec_events.py` уже делает UPSERT по `(contract_id, name_hash)`: совпадение → UPDATE, иначе INSERT. Дублей в `spec_current` нет.
|
||||||
|
2. **«name_hash = md5(имя ‖ date_start)»** — неверно. Реально: `sha256(name.strip().lower())[:16]` — только имя. Услуги с одинаковым именем но разным периодом схлопываются.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Вопрос 1. Дедупликация документов при загрузке
|
||||||
|
|
||||||
|
**Сейчас:** `delete_by_document(contract_id, filename)` — по имени. Ошибки глотаются в `try/except: pass`. Хеша содержимого нет.
|
||||||
|
|
||||||
|
**Как ДОЛЖНО быть:**
|
||||||
|
- `sha256(original_bytes)` → `documents.content_hash`
|
||||||
|
- Идентичность в рамках договора/батча — по `content_hash`
|
||||||
|
- Сравнение по `elements_json` не нужно (нестабилен при смене парсера)
|
||||||
|
- Ошибки удаления логировать, не глотать
|
||||||
|
|
||||||
|
**MVP:**
|
||||||
|
- Колонка `content_hash` + индекс `(contract_id/batch_id, content_hash)`
|
||||||
|
- В `store_document`: если уже есть документ с таким хешем → пропустить, вернуть `{duplicate_of: doc_id}`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Вопрос 2. ADD → UPSERT
|
||||||
|
|
||||||
|
UPSERT по имени уже есть. Реальная проблема: **last-write-wins** — второй ADD молча перезаписывает первый (та же услуга, другая цена).
|
||||||
|
|
||||||
|
**Как ДОЛЖНО быть:**
|
||||||
|
- ADD = новая позиция. При коллизии ключа различать UPDATE vs реально разные позиции (одно имя, разный период)
|
||||||
|
- Ключ должен включать `date_start`/период
|
||||||
|
|
||||||
|
**MVP — вариант B (рекомендован):**
|
||||||
|
- Расширить ключ: `_hash(name + "|" + str(date_start))`
|
||||||
|
- Одна услуга в разные периоды = разные строки
|
||||||
|
- Риск: если допник меняет цену без смены `date_start`, LLM должен прислать UPDATE, а не ADD
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Вопрос 3. Краевые случаи
|
||||||
|
|
||||||
|
**a) Два допника с одинаковым содержанием, разные имена:**
|
||||||
|
Сейчас: два документа → два прогона LLM. Дублей в spec_current нет (UPSERT), но раздувается история.
|
||||||
|
→ **MVP:** дедуп по `content_hash` на загрузке (Вопрос 1).
|
||||||
|
|
||||||
|
**b) ADD X, затем DELETE X — порядок:**
|
||||||
|
Применение по `created_at` или `order_ids`. Не по хронологии допников.
|
||||||
|
→ **MVP:** сортировать по `doc_date` (классификация уже извлекает): `ORDER BY doc_date NULLS LAST, created_at`.
|
||||||
|
|
||||||
|
**c) ZIP с уже загруженными именами:**
|
||||||
|
→ **MVP:** в рамках `batch_id` отсеивать по `content_hash`. Пропускать с предупреждением, не перезаписывать молча.
|
||||||
|
|
||||||
|
**d) Два одинаковых договора в батче:**
|
||||||
|
→ **MVP:** `content_hash`-дедуп решает «одинаковые»; для «разные файлы, один номер» — флаг конфликта в UI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Сквозной MVP (что делать)
|
||||||
|
|
||||||
|
1. `documents.content_hash` + дедуп по нему на загрузке
|
||||||
|
2. Сортировка допников по `doc_date`
|
||||||
|
3. Опционально: `date_start` в `name_hash`
|
||||||
|
|||||||
Reference in New Issue
Block a user