Initial import NDC_1C

This commit is contained in:
2026-03-26 10:38:25 +03:00
commit a162d77ef7
2943 changed files with 3615871 additions and 0 deletions
+585
View File
@@ -0,0 +1,585 @@
TZ_LLM_Normalizer_v2.md
Да, всё, картина понятна.
И по тому, что ты сейчас написал, **ТЗ уже можно делать нормально**, без новых срезов 1С.
Главное, что я из твоего сообщения беру как опору:
* **стабильная база у вас — `v1.1.2`**;
* `v1.1.2.1` показал, что бесконечный prompt-tuning — тупик;
* **допустимый контур** у вас определяется не “всей бухгалтерией мира”, а так:
* вопросы **по текущему предприятию**,
* вопросы **по его данным**,
* вопросы **по сущностям вашей онтологии / учётного контура**;
* **недопустимый контур**:
* абстрактная бухгалтерия,
* законы, формы, “как вообще по ФСБУ/НК РФ”,
* вопросы не про текущее предприятие,
* вопросы не про данные этой базы;
* если вопрос не по контуру — **в 1С не идём**;
* если пользователь пишет “потоком”, система должна:
* разложить это на куски,
* отфильтровать мусор,
* оставить только валидные task fragments,
* уже из них собрать человекочитаемый ответ.
Это уже достаточно, чтобы зафиксировать **правильное ТЗ на `Normalizer v2 / Decomposition-first Pre-Router`**.
---
# ТЗ: `Normalizer v2`
## Decomposition-first pre-router для бухгалтерического ассистента
---
## 1. Цель этапа
Перестроить текущий LLM-normalizer из схемы:
**сырой вопрос → один intent_class → один route_hint**
в более устойчивую схему:
**сырой вопрос → decomposition → semantic fragments → route-critical flags → deterministic routing in code**
Цель:
* перестать бесконечно дотюнивать prompt под каждую новую пачку вопросов;
* устойчиво обрабатывать:
* свободную человеческую речь,
* длинные сообщения,
* смешанные запросы,
* multi-intent сообщения,
* “поток сознания” пользователя;
* не допускать поход в бухгалтерский контур для вопросов вне допустимой области.
Комментарий:
Это уже не “ещё один prompt patch”, а **смена архитектуры нормализации**.
---
## 2. Главная идея новой схемы
LLM больше **не должна пытаться угадать один главный класс вопроса** как основу всей логики.
Вместо этого LLM должна:
1. определить, относится ли сообщение к допустимому бухгалтерскому контуру;
2. разложить сообщение на один или несколько **task fragments**;
3. для каждого фрагмента вернуть:
* смысловые признаки,
* флаги,
* границы уверенности,
* domain relevance;
4. передать это в код;
5. код уже сам решает:
* пускать ли это в 1С-контур;
* какому маршруту это соответствует;
* нужно ли разбивать ответ на несколько частей;
* нужно ли вернуть fallback/уточнение.
Комментарий:
То есть LLM здесь — **semantic parser**,
а не “магический final decision engine”.
---
## 3. Что именно было не так в `v1.x`
На `v1.x` одна модель одновременно пыталась:
* понять intent;
* понять causal;
* понять route;
* понять period;
* понять output shape;
* выбрать один taxonomy label.
На стабильном типизированном наборе это работало хорошо.
На новых 30 вопросах всё поплыло:
* `intent_class_accuracy = 70`
* `route_hint_accuracy = 80`
* `causal_flag_accuracy = 60`
Комментарий:
Это не значит, что LLM плохая.
Это значит, что **схема слишком хрупкая**:
она слишком зависит от того, как именно человек сформулировал вопрос.
---
## 4. Новая архитектура v2
---
## 4.1. Вход
Входом является **сырое сообщение пользователя**, которое может быть:
* коротким;
* длинным;
* многочастным;
* неструктурированным;
* частично бухгалтерским, частично нет;
* содержать:
* вопрос,
* сомнение,
* гипотезу,
* просьбу проверить,
* комментарий,
* мусор,
* лирическое отступление.
---
## 4.2. Выход LLM
LLM должна возвращать **не один итоговый intent**, а structured decomposition.
### Ключевая идея
На выходе нужен объект вида:
```json
{
"schema_version": "normalized_query_v2",
"message_in_scope": true,
"scope_confidence": "high",
"fragments": [...],
"discarded_fragments": [...],
"global_notes": {...}
}
```
---
## 4.3. Фрагменты
Каждый фрагмент — это самостоятельный кандидат на задачу.
Пример:
```json
{
"fragment_id": "F1",
"raw_fragment_text": "по поставщикам не бьются взаиморасчеты",
"normalized_fragment_text": "Проверка расхождений по взаиморасчетам с поставщиками",
"domain_relevance": "in_scope",
"business_scope": "company_specific_accounting",
"entity_hints": ["supplier", "settlements", "payments", "documents"],
"account_hints": ["60"],
"time_scope": {
"type": "explicit",
"value": "2020-06",
"confidence": "medium"
},
"flags": {
"has_multi_entity_scope": true,
"asks_for_chain_explanation": true,
"asks_for_ranking_or_top": false,
"asks_for_period_summary": false,
"asks_for_rule_check": false,
"asks_for_anomaly_scan": false,
"asks_for_exact_object_trace": false,
"asks_for_evidence": true
},
"candidate_labels": ["cross_entity"],
"confidence": "medium"
}
```
Комментарий:
Фрагмент — это не “ответ”, а **единица смыслового разбора**.
---
# 5. Главный принцип: domain gating
Это критично.
## 5.1. Что считается допустимым контуром
Допустимыми считаются только вопросы:
1. про **текущее предприятие**;
2. про **его данные**;
3. про сущности и связи внутри вашей онтологии/учётного контура;
4. про:
* документы,
* проводки,
* взаиморасчёты,
* остатки,
* хвосты,
* закрытие периода,
* аномалии,
* правила учёта,
* признаки ошибок,
* проблемные связи в конкретной базе.
Комментарий:
То есть допустимость определяется **не общей темой “бухгалтерия”**, а связью с **данными и онтологией текущего предприятия**.
## 5.2. Что считается недопустимым контуром
Недопустимыми считаются:
* общие вопросы по бухгалтерии;
* законы, кодексы, формы, инструкции “вообще”;
* абстрактные вопросы без опоры на данные предприятия;
* вопросы не про текущее предприятие;
* бытовой трёп;
* оффтоп;
* всё, что не маппится в ваш учётный контур.
## 5.3. Обязательное правило
Если фрагмент вне контура:
* он **не должен** идти в 1С / retrieval / analytics pipeline;
* по нему возвращается safe fallback.
---
# 6. Что должно происходить с “потоком сознания”
Если пользователь пишет длинно и хаотично, система не должна пытаться сделать вид, что это один аккуратный вопрос.
Она должна:
1. разбить сообщение на фрагменты;
2. определить для каждого:
* это task fragment или шум;
* in-scope или out-of-scope;
* требует ли похода в контур;
3. собрать **valid task set**;
4. выполнить только допустимые части;
5. на выходе сделать один человекочитаемый ответ, где:
* каждая допустимая часть обработана;
* недопустимые части вежливо отбиты.
Комментарий:
Да, конечный ответ должен быть **связанным по диалекту**, а не “пять сухих буллетов из ада”.
Но это уже задача **response composer**, а не нормализатора.
---
# 7. Новый output contract: `normalized_query_v2`
---
## 7.1. Верхний уровень
```json
{
"schema_version": "normalized_query_v2",
"user_message_raw": "string",
"message_in_scope": true,
"scope_confidence": "high | medium | low",
"contains_multiple_tasks": true,
"fragments": [],
"discarded_fragments": [],
"global_notes": {
"needs_clarification": false,
"clarification_reason": null
}
}
```
---
## 7.2. Поля fragment-level
Для каждого фрагмента:
```json
{
"fragment_id": "F1",
"raw_fragment_text": "string",
"normalized_fragment_text": "string",
"domain_relevance": "in_scope | out_of_scope | unclear",
"business_scope": "company_specific_accounting | generic_accounting | offtopic | unclear",
"entity_hints": ["string"],
"account_hints": ["string"],
"document_hints": ["string"],
"register_hints": ["string"],
"time_scope": {
"type": "explicit | inferred | missing",
"value": "string | null",
"confidence": "high | medium | low"
},
"flags": {
"has_multi_entity_scope": true,
"asks_for_chain_explanation": true,
"asks_for_ranking_or_top": false,
"asks_for_period_summary": false,
"asks_for_rule_check": false,
"asks_for_anomaly_scan": false,
"asks_for_exact_object_trace": false,
"asks_for_evidence": false,
"mentions_period_close_context": false
},
"candidate_labels": ["cross_entity", "period_close_risk"],
"confidence": "high | medium | low"
}
```
Комментарий:
Обрати внимание:
здесь **нет обязательного одного `intent_class`**.
Есть `candidate_labels` и `flags`.
Это ключевое изменение v2.
---
# 8. Маршрутизация больше не выбирается LLM напрямую
## 8.1. Что делает LLM
LLM только возвращает decomposition + flags.
## 8.2. Что делает код
Код детерминированно выбирает route на основе flags.
### Пример логики
* если `asks_for_exact_object_trace = true` → `live_mcp_drilldown`
* если `asks_for_ranking_or_top = true` или `asks_for_period_summary = true` → `batch_refresh_then_store`
* если `has_multi_entity_scope = true` и `asks_for_chain_explanation = true` → `hybrid_store_plus_live`
* если `asks_for_rule_check = true` и нет causal chain → `store_feature_risk`
* если `asks_for_anomaly_scan = true` и нет heavy/causal признаков → `store_feature_risk`
* если fragment out-of-scope → no-route / fallback
Комментарий:
Это главный способ уйти от бесконечной гонки за “идеальным prompt”.
---
# 9. Fallback policy
Нужно формализовать три типа fallback.
## 9.1. Out-of-scope fallback
Если вопрос не относится к данным текущего предприятия и его учётному контуру:
Пример ответа:
> Я работаю только с данными и бухгалтерическим контуром текущей компании.
> Вопрос не относится к данным этого предприятия или не попадает в доступную предметную область.
## 9.2. Clarification fallback
Если вопрос в контуре, но недостаточно определён:
Пример ответа:
> Могу проверить это в контуре компании, но нужно уточнить период, документ, счёт или участок, который нужно смотреть.
## 9.3. Partial fallback
Если сообщение смешанное:
* часть in-scope,
* часть out-of-scope.
Пример:
> Проверю часть запроса, которая относится к данным компании.
> Остальная часть выходит за пределы доступного бухгалтерического контура.
Комментарий:
Формулировки должны быть:
* профессиональные,
* не сухие как бетон,
* без “писечки-попочки”,
* но и без канцелярита.
---
# 10. Что делать с сообщением, если там несколько задач
Нужно в коде реализовать `message execution planner`.
## 10.1. Что он делает
* получает fragments;
* выбрасывает out-of-scope;
* группирует in-scope fragments;
* решает:
* это один aggregated response,
* или серия mini-answers;
* передаёт дальше route decisions по каждому фрагменту.
## 10.2. Важное правило
Если пользователь навалил 4 задачи в одном сообщении,
система **не должна** насильно сводить это к одному intent.
---
# 11. Что нужно реализовать в GUI / playground
Текущую GUI можно использовать, но её надо расширить под v2.
Нужны новые вкладки:
### A. Fragment View
Показывает:
* сколько фрагментов выделено;
* тексты фрагментов;
* какие отброшены;
* какие in-scope.
### B. Scope View
Показывает:
* global in-scope / out-of-scope;
* business_scope;
* clarification need.
### C. Flags View
Показывает по каждому фрагменту:
* `has_multi_entity_scope`
* `asks_for_chain_explanation`
* `asks_for_ranking_or_top`
* `asks_for_period_summary`
* `asks_for_rule_check`
* `asks_for_anomaly_scan`
* `asks_for_exact_object_trace`
* `asks_for_evidence`
### D. Route Simulation
Показывает уже не LLM hint, а **что выбрал deterministic code**.
---
# 12. Какие данные нужны для реализации
## Для написания ТЗ — достаточно текущего контекста
Новых срезов базы не нужно.
## Для реализации желательно иметь
1. `normalizer_v1.1.2` prompt set
2. calibration set
3. новая challenge-30
4. 2–3 живых примера длинного “потока”
5. текущие fallback-тексты, если уже есть
Комментарий:
То есть не данные предприятия нужны, а **данные по самому языковому поведению пользователя**.
---
# 13. Новый eval подход
Нужно перестать мерить всё только через `intent_class_accuracy`.
## Основные метрики v2
* `schema_validation_pass_rate`
* `scope_detection_accuracy`
* `fragment_split_accuracy`
* `out_of_scope_filter_accuracy`
* `route_flag_accuracy`
* `route_decision_accuracy`
* `false_cross_entity_activation_rate`
* `false_causal_activation_rate`
* `multi-intent_handling_accuracy`
Комментарий:
Вот это уже будет реальная метрика устойчивости.
---
# 14. Что нельзя делать
Codex запрещено:
1. снова пытаться лечить всё одним prompt patch;
2. сохранять один mandatory `intent_class` как ядро всей логики;
3. отправлять out-of-scope вопросы в бухгалтерский контур;
4. смешивать decomposition и final user answer в один слой;
5. делать дорогие массовые прогоны на старте.
---
# 15. Ограничения по бюджету
Этап проектировать экономно.
## Допустимый режим
* сначала сделать spec + schema + deterministic router rules;
* потом сделать маленький pilot eval;
* не жечь API на sweep’ы.
Комментарий:
Сейчас самый умный путь — **архитектурное переустройство**, а не дорогая перестрелка запросами.
---
# 16. Артефакты, которые должен выдать Codex
1. `docs/normalizer_v2_spec.md`
2. `schemas/normalized_query_v2.json`
3. `docs/domain_scope_policy.md`
4. `docs/fallback_policy.md`
5. `docs/fragment_execution_policy.md`
6. `prompts/developer/normalizer_v2.txt`
7. `prompts/fewshot/normalizer_v2.txt`
8. `reports/v2_pilot_eval_plan.md`
---
# 17. Ключевой смысл этапа
Нужно уйти от философии:
> “давайте подгоним модель под очередные 30 вопросов”
к философии:
> “давайте сделаем такую схему, где новые 30 вопросов не ломают систему, потому что система опирается не на один магический ярлык, а на decomposition + flags + deterministic routing.”
---
# 18. Очень короткий вывод
Сейчас **не надо** пытаться добить новую тридцатку patch’ем.
Сейчас надо:
* зафиксировать `v1.1.2` как стабильный baseline;
* новую тридцатку считать challenge set;
* перейти к `Normalizer v2`, где:
* multi-label,
* fragment decomposition,
* scope filter,
* deterministic routing after LLM.
---
Если хочешь, следующим сообщением я могу дать тебе **ещё более жёсткую короткую версию этого ТЗ для Codex**, уже как job prompt без пояснительного мяса.