TZ_LLM_Normalizer_v1.1.2.md

Ниже даю **ТЗ на `normalizer_v1.1.2`** с комментариями — так, чтобы его можно было и отдать Codex, и потом нормально архивировать как понятный инженерный документ.

---

# ТЗ: `normalizer_v1.1.2`

## Точечная доводка границы `heavy_analytical` ↔ `period_close_risk`

---

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

На версии `normalizer_v1.1.1` удалось очень сильно улучшить рабочие свойства normalizer’а как предроутерного слоя:

* `schema_validation_pass_rate = 100`
* `route_hint_accuracy = 100`
* `causal_flag_accuracy = 100`
* `cross_entity = 100%`
* `drilldown_explain = 100%`
* `rule_based_account_control = 100%`
* `period_close_risk = 100%`

Однако при этом появился побочный перекос в taxonomy:

* `intent_class_accuracy = 90` вместо `93.33` на `v1.1`
* `heavy_analytical = 40%`
* `high_confidence_error_rate = 3.33` вместо `0`

Комментарий:
Это означает, что версия `v1.1.1` **улучшила поведение normalizer’а как route/cause normalizer**, но при этом **слишком агрессивно начала относить heavy обзорные вопросы к `period_close_risk`**, если в формулировке есть слова про закрытие периода, отчётность или предзакрытие.

---

## 2. Что именно произошло

Проблема локализована и понятна.

### Текущий корневой дефект

В `developer prompt v1.1.1` есть правило, которое слишком жёстко задаёт:

> если вопрос относится к предзакрытию, закрытию периода, сдаче отчётности, концу месяца или рискам последнего дня, primary intent должен быть `period_close_risk`, даже если требуется обзор или приоритизация.

Комментарий:
Это правило было добавлено, чтобы вылечить потерю `period_close_risk` внутри `heavy_analytical`.
Но в текущем виде оно **перетянуло слишком много heavy overview вопросов в `period_close_risk`**.

### Дополнительный фактор

В few-shot наборе есть пример на `period_close_risk`, но **нет достаточного симметричного набора примеров**, который бы показывал модели:

* когда вопрос остаётся `heavy_analytical`,
* даже если он сформулирован “перед закрытием периода” или “перед сдачей отчётности”.

Комментарий:
То есть у модели появился сильный positive-trigger на `period_close_risk`, но нет хорошего отрицательного противовеса.

---

## 3. Цель `v1.1.2`

Сделать **узкий и безопасный patch**, который:

1. сохранит:

   * `route_hint_accuracy = 100`
   * `causal_flag_accuracy = 100`
   * `schema_validation_pass_rate = 100`
   * сильные результаты по `cross_entity`, `drilldown_explain`, `rule_based_account_control`
2. исправит:

   * taxonomy drift между `heavy_analytical` и `period_close_risk`
   * лишнюю high-confidence оценку на пограничных кейсах
3. не приведёт к деградации уже сильных зон.

Комментарий:
Это **не новый большой этап**, а именно **surgical patch**.
Менять нужно **только границу intent-class**, а не общую архитектуру normalizer’а.

---

## 4. Scope этапа

### В scope входит

* корректировка одного участка `developer prompt`;
* добавление 2 симметричных few-shot примеров для `heavy_analytical`;
* добавление 1 confidence-rule для пограничных кейсов;
* очень короткий контрольный прогон на 5 кейсах.

### В scope не входит

* изменение schema;
* изменение route logic;
* изменение cross-entity правил;
* изменение drilldown правил;
* изменение anomaly patch;
* новый большой eval-run на 30+ кейсов;
* новые данные/срезы/онтология.

Комментарий:
Данные и база сейчас не нужны.
Проблема находится **целиком в prompt/taxonomy layer**.

---

## 5. Что именно нужно исправить

---

## 5.1. Паттерн A: `heavy_analytical` ошибочно уходит в `period_close_risk`

### Симптом

На `v1.1.1` следующие кейсы получили неправильный `intent_class`:

* `NQ-002`
* `NQ-007`
* `V11-HA-004`

Во всех этих кейсах:

* route правильный: `batch_refresh_then_store`
* causal/requires нормальные
* ошибка только в том, что `intent_class` стал `period_close_risk` вместо `heavy_analytical`

### Природа ошибки

Вопросы содержат:

* контекст закрытия периода,
* июнь / предзакрытие / отчётность,
  но по сути просят:
* обзор,
* рейтинг,
* summary,
* общую картину,
* концентрацию ошибок,
* приоритизацию.

Такие вопросы должны оставаться `heavy_analytical`.

Комментарий:
`period_close_risk` — это не “любой вопрос про период”, а **специальный класс про угрозу срыва закрытия**.
Если в центре вопроса **обзор / рейтинг / агрегированный срез**, это должен быть `heavy_analytical`.

---

## 5.2. Паттерн B: high confidence на пограничном кейсе

### Симптом

В кейсе `NQ-002`:

* intent ошибочный,
* route правильный,
* confidence = `high`

### Природа ошибки

Пограничные вопросы между:

* `heavy_analytical`
* `period_close_risk`

не должны получать `high confidence`, если модель не различает класс уверенно.

Комментарий:
Даже если route совпадает, **ошибочно высокая уверенность на спорном intent — это риск для последующего управления качеством**.
Эту часть нужно приглушить.

---

# 6. Конкретные изменения для Codex

---

## 6.1. Изменить `developer prompt`

### Файл

`prompts/developer/normalizer_v1_1_2.txt`

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

Переписать правило про `period_close_risk`.

### Текущее проблемное правило

Смысл текущего правила:

> если вопрос относится к предзакрытию/закрытию/отчётности/последнему дню, primary intent = `period_close_risk`, даже если нужен обзор или приоритизация.

### Новая правильная формулировка

Вставить вместо него такой блок:

```text
If a question is explicitly about period close risk, pre-close danger, last-day closing failure, reporting deadline risk, or threats that can break the close process itself, use `period_close_risk` as the primary intent_class.

However, if the main purpose of the question is:
- ranking,
- top issues,
- overview,
- concentration of errors,
- summary,
- prioritized review list,
- company-wide analytical review,

then the primary intent_class should remain `heavy_analytical`, even if the question is phrased in the context of month-end close or reporting preparation.
```

### Дополнительное уточнение

Добавить ещё один отдельный блок:

```text
`heavy_analytical` has priority over `period_close_risk` when the question asks for:
- ranking,
- top-N,
- overview,
- summary,
- company-wide picture,
- prioritized analytical review,
even if the wording mentions closing period, reporting, or pre-close context.

Use `period_close_risk` only when the core of the question is the risk of failing or destabilizing the close process itself.
```

Комментарий:
Это главное исправление всей версии `v1.1.2`.
Ничего сильнее менять не надо.

---

## 6.2. Добавить 2 симметричных few-shot примера

### Файл

`prompts/fewshot/normalizer_fewshot_v1_1_2.txt`

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

Оставить existing few-shot на `period_close_risk`, но добавить **два примера-контрвеса**.

---

### Few-shot 1 — heavy remains heavy

```text
Q: Сделай рейтинг самых рисковых хвостов перед закрытием периода за июнь.

Expected:
{
  "intent_class": "heavy_analytical",
  "requires": {
    "needs_cross_entity_join": false,
    "needs_causal_chain": false,
    "needs_exact_object_trace": false,
    "needs_ranking": true,
    "needs_anomaly_summary": false,
    "needs_runtime_truth": false,
    "needs_period_cut": true,
    "needs_evidence": false
  },
  "expected_output_shape": "ranked_list",
  "route_hint": "batch_refresh_then_store"
}
```

### Few-shot 2 — overview remains heavy

```text
Q: Дай обзорный риск-срез перед сдачей отчетности: где максимальная концентрация ошибок.

Expected:
{
  "intent_class": "heavy_analytical",
  "requires": {
    "needs_cross_entity_join": false,
    "needs_causal_chain": false,
    "needs_exact_object_trace": false,
    "needs_ranking": true,
    "needs_anomaly_summary": true,
    "needs_runtime_truth": false,
    "needs_period_cut": true,
    "needs_evidence": false
  },
  "expected_output_shape": "anomaly_summary",
  "route_hint": "batch_refresh_then_store"
}
```

### Existing few-shot to keep

Оставить пример:

```text
Q: Перед закрытием периода что у нас может взорваться в последний день?
Expected intent_class: period_close_risk
```

Комментарий:
Эти два новых примера нужны не потому, что модель “тупая”, а потому, что в `v1.1.1` у неё был сильный пример только в одну сторону.
`v1.1.2` должен сделать границу симметричной.

---

## 6.3. Добавить confidence-ограничение

### Файл

`prompts/developer/normalizer_v1_1_2.txt`

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

Добавить явное правило:

```text
If a question is plausibly on the boundary between `heavy_analytical` and `period_close_risk`, do not assign `confidence.overall = high`.
Use `medium` unless the wording strongly and unambiguously centers on close-process failure risk rather than analytical overview.
```

Комментарий:
Это страховка от кейса `NQ-002`, где route был правильный, но модель слишком самоуверенно выбрала спорный intent.

---

# 7. Что нельзя менять

Codex **запрещено**:

1. менять schema;
2. менять route rules;
3. трогать cross-entity prompt logic;
4. трогать drilldown prompt logic;
5. трогать anomaly/store_feature_risk patch;
6. добавлять много новых few-shot;
7. переписывать весь domain prompt;
8. делать большие prompt sweep’ы;
9. делать повторные API-запросы без необходимости.

Комментарий:
`v1.1.1` очень хорош по route/cause behavior.
Наша задача — **не поломать working core ради косметики taxonomy**.

---

# 8. Контрольный прогон

## Режим

Только микро-прогон.

## Лимит

* максимум **5 API-вызовов**
* один кейс = один запрос
* без повторов
* `temperature = 0`

## Проверить только эти кейсы

1. `NQ-002`
2. `NQ-007`
3. `V11-HA-004`
4. `V11-OT-003`
5. `V11-OT-005`

Комментарий:
Этого достаточно.
Три кейса проверяют boundary heavy/period-close.
Два кейса проверяют, что `period_close_risk` мы не сломали обратно.

---

# 9. Ожидаемый результат

## Минимально acceptable

* `NQ-002` → `heavy_analytical`
* `NQ-007` → `heavy_analytical`
* `V11-HA-004` → `heavy_analytical`
* `V11-OT-003` остаётся `period_close_risk`
* `V11-OT-005` остаётся `period_close_risk`

## Дополнительно желательно

* на пограничных кейсах confidence не `high`, а `medium`

Комментарий:
Если эти 5 кейсов проходят, значит `v1.1.2` делает ровно то, что нужно, и не требует нового полного 30-case eval прямо сейчас.

---

# 10. Артефакты, которые должен выдать Codex

1. `docs/normalizer_v1_1_2_patch_notes.md`
2. `prompts/developer/normalizer_v1_1_2.txt`
3. `prompts/fewshot/normalizer_fewshot_v1_1_2.txt`
4. `reports/normalizer_v1_1_2_micro_eval.json`
5. `reports/normalizer_v1_1_2_micro_eval.md`

---

# 11. Что должно быть в patch notes

В `normalizer_v1_1_2_patch_notes.md` обязательно описать:

* какая именно проблема была в `v1.1.1`;
* почему она возникла;
* какие prompt changes внесены;
* какие few-shot added;
* почему изменения безопасны;
* сколько API-вызовов потрачено;
* были ли ретраи;
* результаты микро-прогона;
* что осталось на будущее.

Комментарий:
Это нужно именно для архива и последующего контроля версий, чтобы потом было понятно, **почему `v1.1.2` вообще появился**.

---

# 12. Приёмка этапа

Этап считается принятым, если:

1. не превышен лимит в 5 API-вызовов;
2. `NQ-002`, `NQ-007`, `V11-HA-004` получают `intent_class = heavy_analytical`;
3. `V11-OT-003` и `V11-OT-005` остаются `period_close_risk`;
4. не возвращается лишний `high confidence` на пограничные heavy/period-close кейсы;
5. изменения зафиксированы прозрачно;
6. не сломаны уже сильные route/cause части normalizer’а.

---

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

`v1.1.2` — это **не улучшение всей системы**, а **локальная шлифовка последнего taxonomy-boundary**.

По сути задача одна:

> Сохранить идеальное route/cause поведение `v1.1.1`,
> но вернуть правильное именование там, где heavy overview был ошибочно засосан в period_close_risk.

---

Если хочешь, я следующим сообщением сделаю ещё **ультра-короткую версию этого ТЗ для прямой вставки в Codex**, буквально как job prompt без пояснений.
