TZ_Assistant_Mode_vNext.md ## Переход от route-plan ответа к нормальному диалоговому ответу по данным базы --- ## 1. Цель этапа Превратить текущий Assistant Mode из технического интерфейса, который показывает: * decomposition, * route plan, * trace, * sandbox-пояснения, в рабочий диалоговый режим, который: * принимает вопрос пользователя; * прогоняет его через normalizer/decomposition; * выполняет реальный retrieval/query по маршруту; * получает фактические данные; * собирает **человекочитаемый ответ на русском языке**; * показывает debug только как дополнительный раскрываемый слой. --- ## 2. Текущее состояние Сейчас Assistant Mode работает как **planner/debug shell**: **что уже есть:** * ввод вопроса; * decomposition / route selection; * trace id; * debug drawer; * session-based диалоговая оболочка. **что отсутствует:** * реальный factual retrieval по данным базы; * route-specific execution с возвратом нормальных данных; * финальный answer composition по результатам retrieval; * русскоязычный user-facing reply policy; * разделение user-answer и technical-debug. Комментарий: Сейчас пользователь получает “planned routes / sandbox retrieval mode / trace”, а должен получать ответ по сути вопроса. --- ## 3. Новая целевая архитектура Нужен полный контур: **User message → Normalizer → Execution Planner → Route-specific Retrieval → Result Normalization → Final Answer Composer → Assistant Reply** --- ## 4. Что должно происходить после сообщения пользователя ## 4.1. Шаг 1 — Normalization Оставляем текущий `normalizer_v2.x`: * scope filter; * fragments; * execution readiness; * route status; * clarification/out-of-scope detection. ## 4.2. Шаг 2 — Execution planning Для каждого исполнимого fragment: * определить маршрут; * собрать execution plan; * передать fragment в route-specific executor. ## 4.3. Шаг 3 — Real retrieval / query Вместо sandbox plan нужно выполнять **реальный вызов backend data layer**: * к 1С / хранилищу / API / query handlers; * получать фактические данные; * нормализовать их в общий result schema. ## 4.4. Шаг 4 — Final answer composition На основе: * user message, * fragments, * data results, * fallback state нужно собрать **один нормальный ответ для пользователя**. --- # 5. Главный принцип нового режима ## Пользователь не должен видеть: * planned routes; * store canonical selected; * sandbox retrieval mode; * raw trace explanation; * транслит; * служебные внутренние названия маршрутов. ## Пользователь должен видеть: * нормальный русский ответ; * если данных нет — понятное объяснение; * если нужен clarification — уточняющий вопрос; * если часть вопроса вне контура — вежливое ограничение; * при желании — кнопку “Показать технический разбор”. --- # 6. Новый слой: Route-specific factual retrieval --- ## 6.1. Для каждого route нужен executor Нужно реализовать route executors как отдельные backend handlers. ### A. `hybrid_store_plus_live` Для causal / cross-entity / chain queries: * искать связанные сущности; * возвращать: * контрагент/объект, * документы, * оплаты, * проводки, * подтверждающие связи, * признаки разрыва цепочки. ### B. `store_feature_risk` Для anomaly / rule-check / suspicious scan: * искать проблемные объекты/записи; * возвращать: * сущность, * причина попадания, * риск-признак, * релевантные поля. ### C. `batch_refresh_then_store` Для overview / ranking / top / period risk: * возвращать агрегаты; * топы; * summary blocks; * приоритеты проверки; * зоны риска. ### D. `store_canonical` Для canonical factual path: * выдавать прямую фактическую информацию по каноническому учётному представлению. ### E. `live_mcp_drilldown` (если уже подключён) Для точечного drilldown: * конкретный документ; * конкретная проводка; * конкретный объект; * source-of-record. --- ## 6.2. Все executors должны возвращать unified result schema Нужен единый формат результата, чтобы answer composer не зависел от route руками. Примерно так: ```json { "fragment_id": "F1", "route": "hybrid_store_plus_live", "status": "ok | empty | partial | error", "result_type": "list | summary | object | chain | ranking", "items": [], "summary": {}, "evidence": [], "errors": [] } ``` --- # 7. Новый слой: Result normalization Перед финальной генерацией ответа retrieval-результаты нужно привести в нормализованный вид. Нужен слой: * `normalize_retrieval_result(fragment, raw_backend_result)` Задача: * привести все маршруты к общему формату; * отдать composer понятный и предсказуемый payload. --- # 8. Новый слой: Final Answer Composer Это ключевой этап. ## 8.1. Что он получает На вход: * `user_message` * `normalized_query_v2_x` * `resolved_fragments` * `retrieval_results` * `fallback state` * `session context` ## 8.2. Что он должен делать Собирать **один связный user-facing ответ**. ### Если один fragment Отвечать прямо по вопросу. ### Если несколько fragments Собирать один ответ с понятной структурой: * сначала общий вывод; * потом блоки по подзадачам; * без ощущения, что это 5 несвязанных кусков. ### Если часть вопроса не выполнима Отвечать по доступной части и мягко пояснять ограничения. --- ## 8.3. Ответ должен быть на русском Жёсткое требование: * никакого транслита; * никаких англоязычных route names в тексте; * никаких внутренних служебных формулировок. --- ## 8.4. Ответ не должен быть “сухой отладкой” Нельзя выводить пользователю: * `planned routes` * `trace` * `store canonical selected` * `fallback_type=...` Это всё — только в debug drawer. --- # 9. Типы user-facing ответов --- ## 9.1. Normal factual reply Когда данные найдены. Пример: * перечисление поставщиков с хвостами; * список проблемных объектов; * summary по рискам; * ranking. ## 9.2. Empty result reply Когда запрос корректен, но ничего не найдено. Пример: > По текущим данным явных проблемных записей по этому условию не найдено. ## 9.3. Partial reply Когда по части вопроса данные найдены, по части — нет. ## 9.4. Clarification reply Когда нужно уточнение. ## 9.5. Out-of-scope reply Когда вопрос не про базу компании. ## 9.6. Backend error reply Когда retrieval сломался технически. Пример: > Не удалось получить данные из контура. Попробуйте повторить запрос или уточнить формулировку. --- # 10. Нужно разделить два уровня ответа --- ## 10.1. User Reply То, что человек видит в чате. ## 10.2. Debug Payload То, что раскрывается по кнопке: * fragments * execution readiness * route status * no_route_reason * retrieval status * trace id Это должно жить отдельно. --- # 11. Что изменить в GUI --- ## 11.1. Assistant message bubble В ответе ассистента должен отображаться: * нормальный текст; * опционально таблица/список результатов, если уместно; * без технического мусора. ## 11.2. Debug toggle Кнопка: * `Показать разбор` или * `Технические детали` Внутри: * decomposition; * routes; * fragments; * trace id; * backend status; * fallback state. ## 11.3. Loading states Во время выполнения: * `Разбираю запрос` * `Ищу данные` * `Собираю ответ` --- # 12. Что изменить в backend API Нужен нормальный assistant endpoint, который возвращает уже готовый user reply. ## Пример `POST /api/assistant/message` ### Request ```json { "session_id": "...", "message": "...", "mode": "assistant", "period_hint": "...", "business_context": "..." } ``` ### Response ```json { "ok": true, "assistant_reply": "человекочитаемый ответ", "reply_type": "factual | clarification | out_of_scope | partial | empty | error", "debug": { "trace_id": "...", "fragments": [...], "routes": [...], "retrieval_status": [...] } } ``` --- # 13. Что логировать Для каждого сообщения логировать: * `session_id` * `message_id` * `user_message` * `normalizer_output` * `execution_plan` * `retrieval_calls` * `retrieval_results_raw` * `retrieval_results_normalized` * `assistant_reply` * `reply_type` * `trace_id` Это пригодится потом для field-based hardening. --- # 14. Минимальный MVP этого этапа Чтобы не расползтись, можно сделать MVP так: ### Этап MVP-1 Сделать хотя бы 2 полноценных route executor: * `hybrid_store_plus_live` * `store_feature_risk` И уже через них показать нормальный user-facing reply. ### Этап MVP-2 Добавить: * `batch_refresh_then_store` * `store_canonical` ### Этап MVP-3 Дополировать: * partial replies * better formatting * richer debug drawer --- # 15. Формат ответа ассистента Ответ должен быть: * на русском; * коротким, но содержательным; * с понятной привязкой к вопросу; * без внутренних названий маршрутов. ### Пример плохого ответа > Planned routes: store canonical. Sandbox mode. Trace... ### Пример допустимого ответа > По текущему запросу найдены проблемные хвосты по нескольким поставщикам. > Основные расхождения связаны с оплатами без корректного закрытия и неполной связкой документов. > Ниже показаны контрагенты и проблемные цепочки. --- # 16. Что НЕ делать на этом этапе Не надо сейчас: * строить суперсложную агентную систему; * делать память на долгий диалог; * пытаться решить все типы route сразу идеально; * снова уходить в synthetic eval hardening; * показывать пользователю внутреннюю кухню как основной результат. --- # 17. Критерии приёмки Этап считается принятым, если: 1. Assistant Mode возвращает **русский человекочитаемый ответ**, а не route plan; 2. debug остаётся доступен, но вынесен отдельно; 3. хотя бы для 2 route’ов работает полный factual loop: * question → retrieval → factual reply; 4. out-of-scope / clarification / partial fallback корректно отображаются в чате; 5. decomposition mode не сломан; 6. транслит и технический мусор исчезли из основного ответа. --- # 18. Артефакты, которые должен выдать Codex 1. `docs/assistant_mode_vnext_spec.md` 2. `docs/route_executor_contracts.md` 3. `docs/final_answer_composer_spec.md` 4. backend endpoint для assistant reply 5. unified retrieval result schema 6. GUI update для user-facing reply + debug drawer 7. `docs/known_limits_current_routes.md` --- # 19. Короткий итог Следующий шаг — это уже **не normalizer**, а: **сделать так, чтобы ассистент после нормализации реально ходил за фактами и отвечал человеку по сути, а не показывал внутренний план работы.** Если хочешь, я следующим сообщением сделаю ещё **короткую жёсткую версию этого ТЗ для вставки в Codex**.