468 lines
13 KiB
Markdown
468 lines
13 KiB
Markdown
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 что-то там решила”.
|
||
|
||
---
|
||
|