Files
contracts/History/llm-analysis/opus-answer.md
T

106 lines
5.9 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.
# Ответ Opus 4.8 на вопросы по архитектуре
**Дата:** 2026-06-23
---
## Вопрос 1: Параллельные вызовы LLM
**Ключевой вопрос — допники кумулятивны или независимы.**
- Если допник №3 может менять услугу, добавленную допником №2 — цепочка семантически последовательна, распараллелить нельзя.
- Если каждый допник трогает свой непересекающийся набор услуг — параллелить можно.
**Рекомендация:** оставить последовательным. Реальный выигрыш:
- Фаза 1 (параллельно) — textify + подготовка контекста (CPU/IO, дёшево)
- Фаза 2 (последовательно) — LLM+apply по цепочке
- Или: более быстрая модель / меньше токенов / стриминг прогресса
При параллельном apply_events seq поедет в гонку — ещё одна причина не параллелить.
---
## Вопрос 2: Атомарный seq без гонки
**Рекомендация (по предпочтению):**
1. Таблица-счётчик `contract_seq(contract_id PK, last_seq)` + `UPDATE ... SET last_seq=last_seq+1 WHERE contract_id=? RETURNING last_seq`. Атомарно, без DDL, без гонок.
2. Advisory lock: `SELECT pg_advisory_xact_lock(contract_id)` в начале транзакции, затем MAX+1. Минимальная правка.
`INSERT ... SELECT MAX+1` не атомарен. `CREATE SEQUENCE` на контракт — DDL-мусор.
---
## Вопрос 3: Стабильность name_hash
**Рекомендация (комбинация):**
- В промпт подавать spec_current с готовыми идентификаторами (hash). LLM для UPDATE/DELETE выбирает из готового списка, не сочиняет имя.
- Хэш считать только на сервере из нормализованного имени: Unicode NFKC → схлопнуть пробелы → lower → trim.
- Fuzzy (`similarity()`) только как fallback-страховка, не основной механизм.
---
## Вопрос 4: Имена в UPDATE/DELETE в UI
**Рекомендация: вариант 2 — обогащать в Python перед SSE.**
- spec_current — источник истины, имя для любого target_hash известно.
- LLM вообще не должна возвращать имена для UPDATE/DELETE — только идентификатор.
- Вариант 1 (LLM возвращает имена) — лишние токены + риск рассинхрона.
- Вариант 3 (JS) — лишние round-trip'ы.
---
## Вопрос 5: Мульти-юзеры и рост данных
1. `prompts` одна таблица + `user_id TEXT` — норм для 10-10000 юзеров. Индекс `(user_id)`. Партиционирование преждевременно.
2. `spec_events` 10K строк — мизер для PG. Партиционирование ближе к 10M+ строк.
3. `BYTEA` 30MB — спокойно в PostgreSQL (TOAST). Вынести в S3 когда объём начнёт раздувать бэкапы.
---
## Сквозная связь
В3 и В4 решаются одним сдвигом — давать LLM spec_current с готовыми ID и считать/резолвить всё на сервере. Это убирает и нестабильные хэши, и пустые ячейки в UI.
**Уточнение Opus:** допники у вас кумулятивные (могут менять то, что добавили предыдущие) или независимые?
---
## Уточнения (23.06.2026)
### 1. Advisory lock в CFML
**Да, безопасен.** `pg_advisory_xact_lock` — транзакционный: снимается автоматически при завершении транзакции (и commit, и rollback).
Условия в Lucee:
- Лок и INSERT в одном `<cftransaction>` — одно соединение, одна транзакция
- `pg_advisory_xact_lock` требует быть внутри транзакции — `<cftransaction>` гарантирует
Ключ: `pg_advisory_xact_lock(hashtext(contract_id))`. Коллизии hashtext безвредны — два контракта просто сериализуются.
### 2. NFKC-нормализация — в PostgreSQL
Делать в PostgreSQL, в том же выражении что считает хэш. Python не дублировать.
NFKC НЕ объединяет разные тире. Нужна связка в одном выражении:
```sql
lower(trim(regexp_replace(translate(normalize(name, NFKC), '–—‑−«»""', '------""'), '\s+', ' ', 'g')))
```
Всё одним выражением — для INSERT-хэша и для lookup.
### 3. Формат spec_current в промпте с готовыми ID
Давать LLM компактный список с коротким суррогатным id (`r1, r2...`):
```json
{"current_spec": [
{"id": "r1", "name": "Аренда стойко-места...", "price": 15000, "qty": 2, "sum": 30000, "date_start": "2025-01-01"}
]}
```
Инструкция в промпте:
- UPDATE: `{"op":"UPDATE","target_id":"r1","new_values":{...}}`
- DELETE: `{"op":"DELETE","target_id":"r2"}`
- ADD: `{"op":"ADD","new_row":{...}}` (без id)
LLM не видит хэши — только выбирает из готового набора. Сервер маппит `id → name_hash`. Устойчивее, меньше токенов, нет шанса опечатки в md5.