TZ_LLM_Normalizer_v2.0.2.md

Ниже даю **ТЗ на новый вариант — `Normalizer v2.0.2`**.
Это уже не про “ещё лучше понять бухгалтерию”, а про **доводку устойчивости исполнения** после последнего жирного прогона.

---

# ТЗ: `Normalizer v2.0.2`

## Stability / No-Route / Schema Hardening

---

## 1. Контекст этапа

После перехода на `v2.0.1` система показала правильный вектор:

* `schema_validation_pass_rate = 96.15`
* `scope_in_scope_rate = 92.31`
* `clarification_required_rate = 15.38`
* `out_of_scope` отрабатывается
* нормальные in-scope вопросы в большинстве случаев проходят без лишнего clarification

Комментарий:
Это уже лучше, чем бесконечный prompt-tuning `v1.x`, потому что новая схема стала:

* устойчивее к новым формулировкам;
* лучше разделять допустимый/недопустимый контур;
* меньше душить нормальные вопросы уточнениями.

Но после жирного прогона видны три конкретные проблемы:

1. `schema_validation_pass_rate` упала с 100 до 96.15
2. `no_route_fragment_rate = 20.69` — слишком много
3. часть внутренних состояний (`fallback_type`, `execution_readiness`, `soft_assumptions`) согласованы неидеально

---

## 2. Цель этапа

Довести `v2.0.1` до более устойчивой рабочей версии `v2.0.2`, не меняя базовую архитектуру.

Цели:

* вернуть `schema_validation_pass_rate` к `100`
* разобраться с природой `no_route`
* сделать внутреннюю policy-логику согласованной
* не сломать уже хорошие свойства:

  * scope gating
  * out-of-scope filtering
  * умеренный clarification rate
  * decomposition-first подход

---

## 3. Главный принцип этапа

На этом этапе **не надо**:

* снова тюнить под конкретные 26 вопросов;
* переписывать всю prompt-логику;
* возвращаться к `v1.x`;
* пытаться “угадать всё лучше через ещё один prompt”.

Нужно:

* сделать систему **строже и честнее на уровне исполнения**;
* понять, где `no_route` правильный, а где это недонастроенный mapping;
* сделать output pipeline согласованным и стабильным.

Комментарий:
Это уже этап **операционной жёсткости**, а не “семантической магии”.

---

# 4. Что именно надо чинить

---

## 4.1. Проблема A: schema validation не 100

### Симптом

В последнем прогоне:

* `schema_validation_pass_rate = 96.15`
* кейс `BQ-001` не прошёл валидацию
* был retry (`request_count_for_case = 2`)

### Требование

Нужно вернуть:

* `schema_validation_pass_rate = 100`

### Что сделать

1. Разобрать `BQ-001` целиком:

   * raw question
   * raw model output
   * parsed object
   * validation error
   * retry output
2. Выявить точную причину:

   * field missing
   * wrong enum
   * wrong nested type
   * prompt overflow / malformed json
   * parser inconsistency
3. Исправить причину на системном уровне, а не точечно под кейс.

### Ожидаемый артефакт

Файл:
`docs/v2_0_2_schema_forensic.md`

Комментарий:
Schema loss даже в одном кейсе — это нельзя игнорировать.
Это технический риск уровня “сломается в проде в неожиданный момент”.

---

## 4.2. Проблема B: слишком много `no_route`

### Симптом

* `no_route_fragment_rate = 20.69`
* `route_distribution.no_route = 6`

### Требование

Разделить все `no_route` случаи на две категории:

### A. Legit no-route

Когда реально нельзя безопасно отправить дальше:

* fragment out-of-scope;
* fragment слишком неопределённый;
* fragment требует clarification;
* fragment семантически распознан, но не относится к исполняемому бухгалтерскому действию.

### B. Missing route mapping

Когда fragment уже in-scope и достаточно понятен, но код не умеет подобрать маршрут.

### Что сделать

1. Выгрузить все fragments с `no_route`
2. Для каждого сделать ручную классификацию:

   * `legit_no_route`
   * `missing_mapping`
3. Если это `missing_mapping`, добавить deterministic mapping rule
4. Если это `legit_no_route`, зафиксировать policy явно

### Ожидаемый артефакт

Файл:
`docs/v2_0_2_no_route_audit.md`

Комментарий:
Сейчас `no_route` — это ещё слишком “чёрный ящик”.
Нужно сделать так, чтобы каждый такой случай был объясним.

---

## 4.3. Проблема C: несогласованность execution state

### Симптом

Есть кейсы, где:

* `fallback_type = none`
* `predicted_clarification_required = false`
* но `executable_with_soft_assumptions_fragments = 0`

Например:

* `BQ-007`
* `BQ-024`

### Что это значит

Внутренняя state machine не полностью согласована.

### Требование

Согласовать следующие статусы между собой:

* `domain_relevance`
* `execution_readiness`
* `predicted_clarification_required`
* `fallback_type`
* `route_decision`
* `soft_assumption_used`

### Целевая логика

Если fragment:

* in-scope
* не clarification
* не out-of-scope
* и route выбран

то должно быть явно видно:

* либо `execution_readiness = executable`
* либо `execution_readiness = executable_with_soft_assumptions`

Не должно быть “исполняем, но readiness не поднят”.

### Ожидаемый артефакт

Файл:
`docs/v2_0_2_execution_state_machine.md`

---

# 5. Что изменить в схеме

## 5.1. Явно ввести `execution_readiness`

Если ещё не введено как обязательное поле — сделать обязательным.

```json id="mgbtkn"
{
  "execution_readiness": "executable | executable_with_soft_assumptions | needs_clarification | no_route"
}
```

## 5.2. Явно ввести `route_status`

```json id="6sdd3j"
{
  "route_status": "routed | no_route"
}
```

## 5.3. Явно ввести `no_route_reason`

Если `route_status = no_route`, обязателен reason:

```json id="y0eqbg"
{
  "no_route_reason": "out_of_scope | insufficient_specificity | missing_mapping | unsupported_fragment_type"
}
```

Комментарий:
Это очень важно.
Без этого вы будете бесконечно смотреть на `no_route` как на аморфную массу.

---

# 6. Что изменить в коде

---

## 6.1. Добавить post-normalization state resolver

Сделать отдельный кодовый слой после ответа LLM:

```text id="kcynfd"
resolve_fragment_execution_state(fragment, session_context) -> resolved_fragment
```

Он должен:

1. нормализовать readiness;
2. нормализовать route_status;
3. ставить `no_route_reason`;
4. приводить `fallback_type` в согласованное состояние.

---

## 6.2. Ввести deterministic `no_route` policy

Если fragment:

* in-scope
* и не clarification
* и есть достаточно route-critical flags

то `no_route` запрещён.
В таком случае должен выбираться маршрут.

`no_route` разрешён только если:

* fragment реально вне контура;
* fragment реально недостаточно определён;
* fragment unsupported by current route map.

---

## 6.3. Добавить trace completeness check

Сейчас у вас раньше был кейс с пустым trace view.

Нужно проверить, что для каждого run сохраняется:

* raw input
* raw model output
* parsed fragments
* resolved execution state
* final route per fragment

Если trace неполный — логировать это как системную ошибку.

---

# 7. Что изменить в prompt’ах

На этом этапе prompt менять минимально.

Нужно только:

* добавить требование, чтобы fragment-level output был полным и непротиворечивым;
* не трогать общий semantic слой без нужды.

### Добавить в developer prompt

```text id="xlg5ut"
Every in-scope fragment must produce a consistent execution state.
If a fragment is routable, mark it as executable or executable_with_soft_assumptions.
Do not leave routable fragments in an unresolved execution state.
If a fragment cannot be routed, provide an explicit no_route_reason.
```

Комментарий:
Это не semantic tuning, а дисциплина output’а.

---

# 8. Новый eval для `v2.0.2`

Нужен уже не просто набор “сложных вопросов”, а **размеченный eval по policy**.

## 8.1. Состав eval

Собрать 20 кейсов:

### Блок A — routable in-scope

8 кейсов
Ожидание:

* in-scope
* no clarification
* route selected
* no `no_route`

### Блок B — legit clarification

4 кейса
Ожидание:

* in-scope
* clarification needed

### Блок C — out-of-scope

4 кейса
Ожидание:

* out-of-scope
* no route
* fallback out_of_scope

### Блок D — borderline / soft assumptions

4 кейса
Ожидание:

* in-scope
* executable_with_soft_assumptions
* route selected

---

## 8.2. Новые метрики

Обязательно считать:

* `schema_validation_pass_rate`
* `scope_detection_accuracy`
* `route_resolution_accuracy`
* `no_route_precision`
* `false_no_route_rate`
* `execution_state_consistency_rate`
* `clarification_precision`
* `clarification_recall`

---

# 9. Целевые показатели

Минимально приемлемо:

* `schema_validation_pass_rate = 100`
* `execution_state_consistency_rate >= 95`
* `false_no_route_rate <= 10`
* `route_resolution_accuracy` заметно выше текущего
* `clarification_required_rate` не выше текущего на in-scope одноходовых кейсах

Хорошо:

* `false_no_route_rate <= 5`
* `execution_state_consistency_rate = 100`

---

# 10. Что нельзя делать

Codex запрещено:

1. снова возвращать один главный `intent_class` как основу всего;
2. снова лечить всё через few-shot под эти 26 вопросов;
3. занижать `no_route` искусственно, просто проставляя маршрут куда угодно;
4. выключать fallback/out-of-scope;
5. игнорировать единичный schema fail как “неважный”.

---

# 11. Артефакты

Codex должен выдать:

1. `docs/v2_0_2_schema_forensic.md`
2. `docs/v2_0_2_no_route_audit.md`
3. `docs/v2_0_2_execution_state_machine.md`
4. `schemas/normalized_query_v2_0_2.json`
5. `prompts/developer/normalizer_v2_0_2.txt`
6. `reports/v2_0_2_eval_plan.md`

---

# 12. Короткий смысл этапа

`v2.0.2` — это версия не про “лучше понимать язык”, а про:

* **не ломать схему**
* **не терять route без объяснения**
* **сделать внутренние состояния честными и жёсткими**
* **перестать иметь серые зоны между clarification / no-route / executable**

---

# 13. Практический итог

После `v2.0.2` вы должны получить систему, где для каждого fragment всегда понятно:

* он в контуре или нет;
* он исполним или нет;
* если не исполним — почему;
* если исполним — с какими допущениями;
* какой route выбран;
* если route не выбран — по какой строго указанной причине.

Это уже будет нормальный инженерный фундамент, а не “LLM что-то там решила”.

---

