Initial import NDC_1C
This commit is contained in:
@@ -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 без пояснительного мяса.
|
||||
Reference in New Issue
Block a user