AI СЛОЙ ДЛЯ НАРМАЛИЗАЦИИ ЗАПРОСОВ ОТ ЮЗЕРА - ГУЙ ДОЛЖЕН БЫТЬ ПОЛНОСТЬЮ РУИФИЦИРОВАН !!!



Ниже — **конкретное ТЗ для Codex** на первый этап интеграции LLM в контур бухгалтерского ассистента.

Основа решения: использовать **Responses API**, а не legacy-путь Assistants, и заставить модель возвращать **строго структурированный JSON** через structured outputs / JSON schema. API-ключ нельзя светить в client-side коде, поэтому даже для localhost-стенда запросы должны идти через локальный backend-proxy. ([OpenAI Платформа][1])

---

# ТЗ: LLM Normalizer Playground + Pre-Router Normalization Layer

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

Реализовать локальный тестовый стенд и базовый backend-слой для интеграции LLM в качестве **нормализатора человеческих бухгалтерских запросов**.

LLM на этом этапе **не отвечает за финальный бухгалтерский ответ** и **не ходит напрямую в данные**.
LLM отвечает только за:

* разбор человеческого запроса;
* выделение сущностей, периода, типа задачи и причинно-следственной формы;
* возврат строго структурированного JSON;
* подготовку нормализованного input для существующего router/orchestration слоя.

---

## 2. Бизнес-смысл этапа

Текущий deterministic router хорошо работает на каноничных и полуструктурированных вопросах, но заметно хуже справляется с длинными человеческими формулировками, особенно в cross-entity и causal explain сценариях.

Цель этого этапа — не заменить router, а поставить перед ним **semantic front-end**, который переводит живой язык пользователя в нормализованный внутренний контракт.

Новая целевая цепочка:

**Пользователь → LLM Normalizer → Normalized Query JSON → Existing Router → Existing Retrieval/Stores/Batch → Final Answer Layer**

---

## 3. Что должно получиться на выходе

После реализации должен существовать рабочий localhost-стенд, в котором можно:

* вставить API key;
* выбрать модель;
* редактировать системный / developer prompt;
* редактировать domain prompt;
* ввести человеческий бухгалтерский вопрос;
* отправить запрос в OpenAI через локальный backend;
* получить:

  * raw model output,
  * normalized JSON output,
  * parsed route hint,
  * confidence,
  * token usage,
  * latency,
  * trace/log записи;
* сохранить результат как тест-кейс для дальнейшей оценки.

---

## 4. Технологическое решение

### 4.1 API-путь

Использовать **Responses API** как основной способ интеграции. Это соответствует текущему рекомендуемому стеку OpenAI для новых интеграций. ([OpenAI Платформа][1])

### 4.2 Формат ответа модели

Использовать **structured outputs / JSON schema response format**, чтобы модель возвращала не свободный текст, а строго валидируемый JSON-объект. ([OpenAI Платформа][1])

### 4.3 Безопасность ключа

API key не должен использоваться напрямую из фронта.
Ключ должен передаваться в локальный backend-proxy и использоваться только серверной частью. В документации OpenAI прямо сказано, что API key — секрет и его нельзя светить в браузере или клиентском коде. ([OpenAI Платформа][2])

### 4.4 Целевая модель первого этапа

Для первого этапа предусмотреть параметризуемую модель, по умолчанию — `gpt-4o-mini` как fast/cheap normalizer-кандидат. Конкретный model id должен быть настраиваемым из UI, без хардкода в коде.

---

## 5. Scope этапа

### В scope входит

* localhost GUI playground;
* локальный backend-proxy для OpenAI API;
* normalizer prompt system;
* structured JSON schema;
* вызов Responses API;
* валидация JSON-ответа;
* логирование запросов/ответов;
* нормализация route hints;
* сохранение trace;
* базовый eval-mode на заранее заданном наборе вопросов.

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

* полноценный production chat;
* tool use;
* live access к 1С данным;
* финальный answer synthesis;
* agentic orchestration;
* самостоятельный выбор model toolchain;
* дообучение модели;
* интеграция в production UI Node/DC.

---

## 6. Архитектура решения

## 6.1 Общая схема

### Компоненты

1. **Frontend Playground**
2. **Local Backend Proxy**
3. **Prompt Manager**
4. **Normalizer Service**
5. **Schema Validator**
6. **Trace Logger**
7. **Optional Eval Runner**
8. **Route Hint Adapter** для передачи результата в существующий router

### Поток

1. Пользователь вводит сырой вопрос.
2. Frontend отправляет payload на local backend.
3. Backend собирает prompt + schema + user input.
4. Backend вызывает Responses API.
5. Backend получает structured JSON.
6. Backend валидирует JSON по schema.
7. Backend вычисляет route hint summary.
8. Backend возвращает UI:

   * raw response,
   * normalized JSON,
   * parse status,
   * validation status,
   * usage,
   * latency,
   * trace id.

---

## 7. Требования к GUI

Нужен простой localhost GUI. Подойдёт React/Vite/Next localhost-only интерфейс или любой быстрый web UI.

## 7.1 Обязательные блоки интерфейса

### A. Connection Panel

Поля:

* `OpenAI API Key`
* `Model ID`
* `Base URL` (опционально, по умолчанию OpenAI)
* `Temperature`
* `Max output tokens`

Кнопки:

* `Save local session config`
* `Test connection`

### B. Prompt Panel

Отдельные текстовые области:

* `System Prompt`
* `Developer / Instruction Prompt`
* `Domain Prompt`
* `Schema Notes` (опционально)
* `Few-shot examples` (опционально)

Кнопки:

* `Load preset`
* `Save preset`
* `Diff with previous`
* `Reset to default`

### C. User Query Panel

Поля:

* `Raw user question`
* `Optional period context`
* `Optional business context`
* `Optional expected route` (для eval mode)

Кнопки:

* `Normalize`
* `Normalize + Save as test case`

### D. Output Panel

Вкладки:

* `Normalized JSON`
* `Raw model output`
* `Route hint summary`
* `Validation`
* `Logs`

### E. Runtime Metrics Panel

Показывать:

* `trace_id`
* `request_started_at`
* `request_finished_at`
* `latency_ms`
* `input_tokens`
* `output_tokens`
* `total_tokens`
* `validation_status`
* `confidence`
* `schema_version`
* `prompt_version`

### F. History Panel

Список прошлых запросов:

* timestamp
* shortened question
* model
* confidence
* validation pass/fail
* route hint
* save status

---

## 8. Требования к backend

Backend должен быть локальным HTTP-сервисом.

Подойдут:

* Node.js + Express / Fastify
  или
* Python + FastAPI

Рекомендуемый вариант для скорости: **Node.js + Fastify**.

## 8.1 Обязательные endpoint’ы

### `POST /api/openai/test-connection`

Проверка, что ключ рабочий и Responses API доступен.

### `POST /api/normalize`

Основной endpoint для нормализации вопроса.

Вход:

```json
{
  "apiKey": "string",
  "model": "gpt-4o-mini",
  "temperature": 0,
  "systemPrompt": "string",
  "developerPrompt": "string",
  "domainPrompt": "string",
  "schemaVersion": "v1",
  "userQuestion": "string",
  "context": {
    "period_hint": "2020-06",
    "business_context": "optional"
  }
}
```

Выход:

```json
{
  "trace_id": "string",
  "ok": true,
  "normalized": { ... },
  "route_hint_summary": { ... },
  "raw_model_output": "string or object",
  "validation": {
    "passed": true,
    "errors": []
  },
  "usage": {
    "input_tokens": 0,
    "output_tokens": 0,
    "total_tokens": 0
  },
  "latency_ms": 0,
  "prompt_version": "normalizer_v1",
  "schema_version": "v1"
}
```

### `POST /api/eval/run`

Запуск серии нормализаций на тестовом наборе.

### `GET /api/history`

История запросов.

### `GET /api/history/:trace_id`

Получение полного trace.

### `POST /api/presets/save`

Сохранение набора prompt’ов / конфигурации.

### `GET /api/presets`

Получение доступных prompt presets.

---

## 9. Центральная сущность: Normalized Query JSON

Это ключевой контракт между LLM и вашим deterministic router.

## 9.1 Schema version

Первая версия:

* `schema_version = "normalized_query_v1"`

## 9.2 Обязательные поля

```json
{
  "schema_version": "normalized_query_v1",
  "user_question_raw": "string",
  "normalized_question": "string",
  "intent_class": "heavy_analytical | cross_entity | drilldown_explain | rule_based_account_control | anomaly_probe | period_close_risk | ambiguous_human_query | simple_factual",
  "business_problem_type": "string",
  "domain_entities": ["string"],
  "accounts_mentioned": ["string"],
  "documents_mentioned": ["string"],
  "registers_mentioned": ["string"],
  "period_scope": {
    "type": "explicit | inferred | missing",
    "value": "string | null",
    "confidence": "high | medium | low"
  },
  "requires": {
    "needs_cross_entity_join": true,
    "needs_causal_chain": true,
    "needs_exact_object_trace": false,
    "needs_ranking": false,
    "needs_anomaly_summary": false,
    "needs_runtime_truth": false,
    "needs_period_cut": true,
    "needs_evidence": true
  },
  "expected_output_shape": "ranked_list | evidence_chain | anomaly_summary | point_answer | reconciliation_report | prioritized_review_list",
  "route_hint": "store_canonical | store_feature_risk | hybrid_store_plus_live | live_mcp_drilldown | batch_refresh_then_store",
  "ambiguities": [
    {
      "field": "period_scope",
      "reason": "month not specified",
      "severity": "medium"
    }
  ],
  "confidence": {
    "overall": "high | medium | low",
    "intent_class": "high | medium | low",
    "route_hint": "high | medium | low"
  }
}
```

---

## 10. Семанические требования к normalizer

LLM normalizer обязан:

### 10.1 Не отвечать на бухгалтерский вопрос по сути

Она не должна возвращать:

* “вероятно, проблема в 62 счёте”
* “скорее всего, не хватает оплаты”

Она должна возвращать **нормализованную интерпретацию**, а не бизнес-ответ.

### 10.2 Выделять причинно-следственную форму

Если вопрос содержит конструкции типа:

* “не бьётся”
* “не собралось в цепочку”
* “разложи по документам, оплатам, закрывающим”
* “чем подтверждается”
* “почему висит хвост”
* “не видно прихода под реализацию”

LLM должна поднимать:

* `needs_cross_entity_join = true`
* `needs_causal_chain = true`
* часто `needs_evidence = true`

### 10.3 Отдельно распознавать exact object trace

Если вопрос про:

* конкретный документ,
* конкретную проводку,
* конкретную строку,
* конкретный объект,
* конкретный номер / ref / line / posting

Тогда поднимать:

* `needs_exact_object_trace = true`

### 10.4 Различать множественный explain и точечный drilldown

Если вопрос:

* “по нескольким материалам покажи, почему зависли”
* “по каким кейсам это происходит”
* “по каким поставщикам не бьётся”

Это **не** exact drilldown.
Это множественный causal explain → чаще `hybrid_store_plus_live`.

### 10.5 Не путать risk-лексику с risk-route

Если в вопросе есть слова:

* “риск”
* “проблема”
* “аномалия”
* “опасный”
  но одновременно есть document/payment/posting chain semantics,
  то normalizer не должен автоматически относить это к `store_feature_risk`.
  Он должен сохранять causal cross-entity приоритет.

---

## 11. Prompt system

Нужна управляемая prompt-архитектура.

## 11.1 Структура prompt’ов

Разделить prompt минимум на 3 уровня:

### `system_prompt`

Общие жесткие правила:

* ты не отвечаешь на бухгалтерский вопрос;
* ты возвращаешь только JSON;
* ты обязан следовать schema;
* ты не выдумываешь период, если его нельзя разумно вывести;
* ты явно помечаешь ambiguity.

### `developer_prompt`

Правила нормализации:

* классификация типов вопросов;
* различение heavy/cross-entity/drilldown;
* правила выбора route_hint;
* правила confidence;
* правила causal semantics.

### `domain_prompt`

Предметная специфика:

* словарь бухгалтерских формулировок;
* названия счетов;
* доменные сущности;
* типовые паттерны “хвост”, “не бьётся”, “акт сверки”, “закрывающие”, “реализация без оплаты”, “продажа раньше прихода”, “ошибка даты”, “97 счёт”, “ОС”, “банковская выписка” и т.д.

## 11.2 Few-shot examples

Добавить отдельный блок примеров:

* raw question
* expected normalized JSON fragment

Минимум 10–15 примеров на старт.

---

## 12. Route Hint Adapter

Нужен слой, который переводит normalized JSON в input существующего router.

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

На основе `normalized_query_v1` строить объект:

```json
{
  "intent_class": "cross_entity",
  "decision_flags": {
    "needs_cross_entity_join": true,
    "needs_causal_chain": true,
    "needs_exact_object_trace": false,
    "needs_ranking": false,
    "needs_anomaly_summary": false,
    "needs_runtime_truth": false
  },
  "route_hint": "hybrid_store_plus_live",
  "confidence": "medium",
  "entities": { ... },
  "period_scope": { ... }
}
```

## 12.2 Поведение

Этот adapter пока только готовит payload и показывает его в UI.
Интеграция в боевой router может быть следующей фазой.

---

## 13. Логирование и traceability

Каждая нормализация должна логироваться.

## 13.1 Что сохранять

* `trace_id`
* timestamp
* model
* prompt_version
* schema_version
* raw user question
* context
* raw request payload
* raw model response
* parsed normalized JSON
* validation result
* route_hint
* confidence
* token usage
* latency
* optional expected route
* optional eval label

## 13.2 Формат хранения

Хранить локально в JSONL или SQLite.

Рекомендуемый вариант:

* `data/normalizer_traces/*.json`
  или
* `data/normalizer.sqlite`

---

## 14. Eval mode

Нужен режим оценки качества normalizer.

## 14.1 Источник eval-набора

Собрать eval corpus из:

* каноничных benchmark-вопросов;
* creative stress вопросов;
* реальных человеческих фраз из диалогов.

## 14.2 Формат тест-кейса

```json
{
  "case_id": "NQ-001",
  "raw_question": "Где у нас не бьются взаиморасчёты по поставщикам...",
  "expected": {
    "intent_class": "cross_entity",
    "route_hint": "hybrid_store_plus_live",
    "requires": {
      "needs_cross_entity_join": true,
      "needs_causal_chain": true
    },
    "accounts_mentioned": ["60"],
    "expected_output_shape": "reconciliation_report"
  }
}
```

## 14.3 Метрики eval

Считать:

* `intent_class_accuracy`
* `route_hint_accuracy`
* `causal_flag_accuracy`
* `period_scope_accuracy`
* `entity_extraction_accuracy`
* `schema_validation_pass_rate`
* `high_confidence_error_rate`

---

## 15. Валидация structured output

## 15.1 Обязательное правило

Если модель вернула невалидный JSON:

* backend не должен silently repair;
* backend должен помечать `validation.passed = false`;
* UI должен показывать ошибку;
* запись должна логироваться.

## 15.2 Допустимый soft-recovery

Разрешён только один controlled retry:

* при invalid JSON
* с дополнительной server-side инструкцией “return valid JSON strictly matching schema”

Но не больше 1 повтора на запрос.

---

## 16. Security requirements

### 16.1 Обязательные требования

* ключ не хранить в localStorage в открытом виде;
* ключ не отправлять в сторонние домены;
* ключ не логировать в trace;
* ключ не возвращать во frontend response;
* backend должен редактировать чувствительные поля из логов.

### 16.2 Допустимый режим для localhost

Разрешается:

* временно держать ключ в памяти backend-процесса;
* или вводить ключ вручную на сессию без персистентного хранения.

---

## 17. Нефункциональные требования

* UI должен быть пригоден для localhost-тестов без production hardening.
* Нормализация одного запроса должна быть достаточно быстрой для интерактивной работы.
* Код должен быть модульным, чтобы потом normalizer можно было вынести из playground в основной backend.
* Все schema и prompts должны быть версионируемыми.
* Архитектура должна позволять заменить модель без переписывания всего пайплайна.

---

## 18. Предлагаемая структура проекта

```text
llm_normalizer/
  frontend/
    src/
      components/
      pages/
      api/
      state/
    package.json

  backend/
    src/
      server.ts
      routes/
        normalize.ts
        presets.ts
        history.ts
        eval.ts
        testConnection.ts
      services/
        openaiResponsesClient.ts
        normalizerService.ts
        promptBuilder.ts
        schemaValidator.ts
        routeHintAdapter.ts
        traceLogger.ts
      schemas/
        normalized_query_v1.json
      prompts/
        system/
        developer/
        domain/
        fewshot/
      storage/
      types/
    package.json

  data/
    presets/
    traces/
    eval_cases/

  docs/
    README.md
    API.md
    SCHEMA.md
    PROMPTS.md
```

---

## 19. Acceptance criteria

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

### Функционально

* localhost GUI запускается;
* можно ввести ключ, модель и вопрос;
* backend успешно вызывает Responses API; ([OpenAI Платформа][1])
* модель возвращает structured JSON по schema; ([OpenAI Платформа][1])
* JSON проходит валидацию минимум в 90% тестовых кейсов;
* сохраняются traces;
* route hint summary отображается в UI;
* есть история запросов;
* есть eval-mode на локальном наборе кейсов.

### Архитектурно

* frontend не ходит напрямую в OpenAI API с ключом; ([OpenAI Платформа][2])
* prompts версионируются;
* schema версионируется;
* normalizer отделён от router;
* код модульный и пригоден к дальнейшей интеграции.

### Качественно

На стартовом eval-наборе целевые ориентиры:

* `schema_validation_pass_rate >= 90%`
* `intent_class_accuracy >= 80%`
* `route_hint_accuracy >= 75%`
* `causal_flag_accuracy >= 80%`
* `high_confidence_error_rate <= 10%`

---

## 20. Порядок реализации

### Этап 1

Поднять backend-proxy и test connection.

### Этап 2

Сделать GUI с input/output блоками.

### Этап 3

Сделать prompt manager и schema loader.

### Этап 4

Сделать `POST /api/normalize` и structured JSON parsing.

### Этап 5

Сделать route hint adapter и validation panel.

### Этап 6

Сделать history + trace logging.

### Этап 7

Сделать eval mode и загрузку тестовых кейсов.

### Этап 8

Подготовить README и пример prompt presets.

---

## 21. Что Codex должен сгенерировать

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

1. Полностью рабочий localhost-проект.
2. Frontend + backend.
3. JSON schema `normalized_query_v1`.
4. Стартовые prompt-файлы:

   * system
   * developer
   * domain
   * fewshot
5. Примеры test cases.
6. README с инструкцией запуска.
7. Пример `.env.example`.
8. Пример trace storage.
9. Минимальные тесты:

   * schema validation
   * route hint adapter
   * normalize endpoint
   * prompt builder

---

## 22. Ключевое архитектурное правило

Самое важное правило этого этапа:

**LLM — это semantic front-end, а не исполнитель бизнес-логики.**

LLM должна:

* нормализовать,
* структурировать,
* классифицировать,
* подсказывать route hint.

LLM не должна:

* сама принимать окончательное orchestration-решение;
* сама лезть в данные;
* сама становиться вашим retrieval engine.

---

Если хочешь, следующим сообщением я дам **сразу вторую часть ТЗ**: уже не про playground, а про **встраивание normalizer’а в существующий router/orchestration pipeline**.

[1]: https://platform.openai.com/docs/api-reference/responses/list?ref=test-ippon.ghost.io&utm_source=chatgpt.com "Responses | OpenAI API Reference"
[2]: https://platform.openai.com/docs/api-reference/chat/create%23chat-create-temperature?utm_source=chatgpt.com "Chat Completions | OpenAI API Reference"
