# V2IntentRouter Architecture ## 1. Архитектура Текущий `V2IntentRouter` реализован как **LLM-first router**. Deterministic-слой не выбирает маршрут по умолчанию и используется только для: - preprocessing - validation ответа LLM - fallback, если LLM не ответил или вернул невалидный маршрут Актуальные компоненты: - `router.py` Главная точка входа и оркестратор пайплайна. - `modules/normalizer.py` Нормализация текста запроса в `normalized_query`. - `modules/target_terms.py` Извлечение retrieval-oriented `target_terms`, `endpoint_paths`, `matched_aliases`, `alias_docs`. - `modules/anchors.py` Извлечение `anchors` и marker-сигналов для fallback и downstream retrieval. - `routers/route_catalog.py` Каталог допустимых маршрутов (`allowed_routes`). - `routers/llm.py` Основной LLM-router. Получает нормализованный запрос, `target_terms`, `anchors` и список допустимых маршрутов. - `routers/validator.py` Deterministic validator для enum-значений, комбинации маршрута и базовой нормализации `confidence`. - `routers/confidence.py` Пост-обработка confidence после ответа LLM. - `routers/fallback.py` Fallback-маршрутизация, если LLM не ответил или ответ не прошёл validator. - `routers/prompts.yml` Prompt-контракт для LLM-router. ## 2. Контракт ### Вход - `user_query: str` ### Выход `V2RouteResult`: - `routing_domain: str` - `intent: str` - `subintent: str` - `user_query: str` - `normalized_query: str` - `target_terms: list[str]` - `anchors: V2RouteAnchors` - `confidence: float` - `routing_mode: str` - `llm_router_used: bool` - `reason_short: str` `V2RouteAnchors`: - `entity_names: list[str]` - `file_names: list[str]` - `endpoint_paths: list[str]` - `target_doc_hints: list[str]` - `matched_aliases: list[str]` - `process_domain: str | None` - `process_subdomain: str | None` ## 3. Поддерживаемые домены, интенты и сабинтенты ### Домены - `DOCS` - `GENERAL` ### Интенты - `DOC_EXPLAIN` - `GENERAL_QA` ### Сабинтенты - `SUMMARY` - `FIND_FILES` ### Допустимые маршруты - `GENERAL / GENERAL_QA / SUMMARY` - `DOCS / DOC_EXPLAIN / SUMMARY` - `DOCS / DOC_EXPLAIN / FIND_FILES` Эти маршруты централизованно заданы в `routers/route_catalog.py`. ## 4. Актуальный флоу Пайплайн обработки запроса: 1. `router.py` принимает `user_query`. 2. `modules/normalizer.py` строит `normalized_query`. 3. `modules/target_terms.py` извлекает: - `target_terms` - `endpoint_paths` - `matched_aliases` - `alias_docs` 4. `modules/anchors.py` строит: - `anchors` - `file_markers` - `architecture_markers` - `logic_markers` - `domain_markers` - `endpoint_markers` 5. `router.py` собирает `QueryFeatures`. 6. `routers/llm.py` вызывается как **основной селектор маршрута**. 7. `routers/validator.py` проверяет: - что значения входят в допустимые enum - что комбинация маршрута разрешена - что `confidence` можно привести к `float` 8. `routers/confidence.py` корректирует confidence на основе силы сигналов. 9. Если ответ LLM валиден, возвращается `V2RouteResult` с `routing_mode="llm_default"`. 10. Если LLM не ответил, вернул сломанный JSON или невалидный маршрут, `routers/fallback.py` строит fallback route: - `FIND_FILES`, если есть `file_markers` - `DOCS / DOC_EXPLAIN / SUMMARY`, если есть docs-oriented anchors - иначе `GENERAL / GENERAL_QA / SUMMARY` ## 5. Компоненты по флоу ### `router.py` - Задача Оркестрировать полный routing pipeline. - Как решает Последовательно вызывает: - normalizer - target terms extractor - anchor extractor - LLM router - validator - confidence adjuster - fallback router - Вход `user_query: str` - Выход `V2RouteResult` ### `modules/normalizer.py` - Задача Привести запрос к стабильной форме для анализа. - Как решает Схлопывает лишние пробелы через `" ".join(...split())`. - Вход `user_query: str` - Выход `normalized_query: str` ### `modules/target_terms.py` - Задача Построить **чистое retrieval-поле** `target_terms`. - Как решает Использует позитивную модель отбора и включает в `target_terms` только: - endpoint paths - identifier-like tokens - alias canonical terms - domain terms Исключаются: - question words - intent words - filler/noisy words - marker words - короткие токены `< 3`, если это не endpoint или alias - битые path-like токены Дополнительно: - lowercase - trim punctuation по краям - dedupe - ограничение до `7` элементов - приоритет: endpoints → identifiers → aliases → domain terms - Вход `normalized_query: str` - Выход `TargetTermsAnalysis`: - `target_terms` - `endpoint_paths` - `matched_aliases` - `alias_docs` ### `modules/anchors.py` - Задача Построить `anchors` и marker-сигналы, не смешивая их с `target_terms`. - Как решает Извлекает: - `entity_names` из PascalCase-like токенов - `file_names` только по жёстким правилам: - `*.md`, `*.yaml`, `*.yml`, `*.json` - `docs/...`, `doc/...`, `documentation/...` - `endpoint_paths` из `TargetTermsAnalysis` - `target_doc_hints` из alias docs, endpoint map и marker-сигналов Marker-сигналы живут отдельно: - `file_markers` - `architecture_markers` - `logic_markers` - `domain_markers` - `endpoint_markers` - Вход - `normalized_query: str` - `TargetTermsAnalysis` - Выход `AnchorAnalysis` ### `routers/route_catalog.py` - Задача Держать один источник истины для допустимых маршрутов. - Как решает Возвращает: - список `allowed_routes` для payload LLM - проверку допустимости комбинации `routing_domain + intent + subintent` ### `routers/llm.py` - Задача Выбрать маршрут через LLM как основной селектор. - Как решает Формирует JSON payload из: - `normalized_query` - `target_terms` - `anchors` - `allowed_routes` Затем: - вызывает LLM - парсит JSON - возвращает сырой candidate route без deterministic business-routing - Вход - `normalized_query: str` - `target_terms: list[str]` - `anchors: dict` - Выход `dict | None` ### `routers/validator.py` - Задача Deterministic validation ответа LLM. - Как решает Проверяет: - что `routing_domain`, `intent`, `subintent` заполнены - что комбинация маршрута входит в `route_catalog` - что `confidence` можно привести к числу - Вход `dict | None` - Выход Валидированный `dict | None` ### `routers/confidence.py` - Задача Сделать confidence осмысленным после ответа LLM. - Как решает Корректирует confidence: - `-0.1`, если нет strong anchors - `-0.1`, если запрос короткий или vague - `+0.05`, если есть явный signal (`file_markers`, `endpoint_paths`, `endpoint_markers`) - затем clamp в диапазон `0.0..1.0` - Вход - `confidence: float` - `QueryFeatures` - Выход `confidence: float` ### `routers/fallback.py` - Задача Построить deterministic fallback, если LLM невалиден. - Как решает Правила: - есть `file_markers` → `DOCS / DOC_EXPLAIN / FIND_FILES` - есть docs-signals (`endpoint_paths`, `target_doc_hints`, `matched_aliases`, marker groups) → `DOCS / DOC_EXPLAIN / SUMMARY` - иначе → `GENERAL / GENERAL_QA / SUMMARY` - Вход - `user_query: str` - `QueryFeatures` - `anchors: V2RouteAnchors` - `llm_attempted: bool` - Выход `V2RouteResult` ### `routers/prompts.yml` - Задача Задать LLM-router контракт ответа и guidance по confidence. - Как решает Ограничивает модель только `allowed_routes` и требует JSON с полями: - `routing_domain` - `intent` - `subintent` - `confidence` - `reason_short` ## 6. Ключевые инварианты - LLM является default router. - Deterministic-слой не принимает основной routing decision. - `target_terms` содержат только retrieval-useful terms. - `anchors` не содержат `terms`. - `/health` и другие endpoint paths не должны попадать в `file_names`, если это не файл с расширением. - `file_names` содержат только реальные file/doc paths. - Fallback используется только если LLM недоступен или вернул невалидный маршрут.