фиксирую состояние

This commit is contained in:
2026-04-07 21:41:27 +03:00
parent bc29d51a29
commit 8fb76bb331
56 changed files with 7011 additions and 316 deletions
@@ -2,31 +2,44 @@
## 1. Архитектура
Текущий `V2IntentRouter` состоит из следующих компонентов:
Текущий `V2IntentRouter` реализован как **LLM-first router**.
Deterministic-слой не выбирает маршрут по умолчанию и используется только для:
- preprocessing
- validation ответа LLM
- fallback, если LLM не ответил или вернул невалидный маршрут
Актуальные компоненты:
- `router.py`
Главная точка входа и оркестратор.
Главная точка входа и оркестратор пайплайна.
- `modules/normalizer.py`
Нормализация текста запроса в `normalized_query`.
- `modules/target_terms.py`
Извлечение `target_terms`, `endpoint_paths`, `matched_aliases`, `alias_docs`.
Извлечение retrieval-oriented `target_terms`, `endpoint_paths`, `matched_aliases`, `alias_docs`.
- `modules/anchors.py`
Извлечение `anchors` и вспомогательных marker-сигналов.
Извлечение `anchors` и marker-сигналов для fallback и downstream retrieval.
- `routers/docs_subintent_resolver.py`
Определение `subintent`.
- `routers/deterministic.py`
Детерминированное определение `routing_domain`, `intent`, `subintent`, `confidence`, `routing_mode`, `llm_router_used`, `reason_short`.
- `routers/route_catalog.py`
Каталог допустимых маршрутов (`allowed_routes`).
- `routers/llm.py`
LLM-based определение `routing_domain`, `intent`, `subintent`, `confidence`, `reason_short`.
Основной 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.
Prompt-контракт для LLM-router.
## 2. Контракт
@@ -53,7 +66,6 @@
`V2RouteAnchors`:
- `entity_names: list[str]`
- `terms: list[str]`
- `file_names: list[str]`
- `endpoint_paths: list[str]`
- `target_doc_hints: list[str]`
@@ -78,35 +90,61 @@
- `SUMMARY`
- `FIND_FILES`
### Поддерживаемые маршруты
### Допустимые маршруты
- `GENERAL / GENERAL_QA / SUMMARY`
- `DOCS / DOC_EXPLAIN / SUMMARY`
- `DOCS / DOC_EXPLAIN / FIND_FILES`
## 4. Флоу обработки запроса
Эти маршруты централизованно заданы в `routers/route_catalog.py`.
## 4. Актуальный флоу
Пайплайн обработки запроса:
1. `router.py` принимает `user_query`.
2. `modules/normalizer.py` строит `normalized_query`.
3. `modules/target_terms.py` извлекает ключевые термы и alias-based сигналы.
4. `modules/anchors.py` строит `anchors` и marker-сигналы.
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/deterministic.py` пытается определить маршрут детерминированно.
7. Если deterministic route найден, он сразу возвращается.
8. Если deterministic route не найден, `router.py` вызывает `routers/llm.py`.
9. Если LLM вернул валидный маршрут, собирается `V2RouteResult` с `routing_mode="llm_assisted"`.
10. Если LLM недоступен или не вернул валидный маршрут, используется fallback:
`GENERAL / GENERAL_QA / SUMMARY` с `routing_mode="llm_fallback"`.
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, anchors extractor, deterministic router и при необходимости LLM router.
Последовательно вызывает:
- normalizer
- target terms extractor
- anchor extractor
- LLM router
- validator
- confidence adjuster
- fallback router
- Вход
`user_query: str`
@@ -117,7 +155,7 @@
### `modules/normalizer.py`
- Задача
Привести запрос к стабильной форме для дальнейшего анализа.
Привести запрос к стабильной форме для анализа.
- Как решает
Схлопывает лишние пробелы через `" ".join(...split())`.
@@ -131,14 +169,29 @@
### `modules/target_terms.py`
- Задача
Выделить ключевые термы и retrieval-сигналы из запроса.
Построить **чистое retrieval-поле** `target_terms`.
- Как решает
Использует:
- regex для path/entity-like фрагментов
- список stop-words
- alias rules с фразами и каноническими термами
- эвристику для `/health`
Использует позитивную модель отбора и включает в `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`
@@ -153,117 +206,141 @@
### `modules/anchors.py`
- Задача
Построить полный набор `anchors` и doc-oriented marker-сигналов.
Построить `anchors` и marker-сигналы, не смешивая их с `target_terms`.
- Как решает
Использует:
- regex для `entity_names` и `file_names`
- словари marker-фраз:
- file markers
- architecture markers
- logic markers
- domain markers
- endpoint markers
- map `endpoint -> target_doc_hint`
- alias docs из `TargetTermsAnalysis`
Извлекает:
- `entity_names` из PascalCase-like токенов
- `file_names` только по жёстким правилам:
- `*.md`, `*.yaml`, `*.yml`, `*.json`
- `docs/...`, `doc/...`, `documentation/...`
- `endpoint_paths` из `TargetTermsAnalysis`
- `target_doc_hints` из alias docs, endpoint map и marker-сигналов
- Вход
- `normalized_query: str`
- `TargetTermsAnalysis`
- Выход
`AnchorAnalysis`:
- `anchors`
Marker-сигналы живут отдельно:
- `file_markers`
- `architecture_markers`
- `logic_markers`
- `domain_markers`
- `endpoint_markers`
### `routers/docs_subintent_resolver.py`
- Задача
Определить `subintent`.
- Как решает
Эвристика:
- если есть `file_markers` -> `FIND_FILES`
- если есть doc-signals (`endpoint_paths`, `endpoint_markers`, `architecture_markers`, `logic_markers`, `domain_markers`, `target_doc_hints`) -> `SUMMARY`
- иначе `None`
- Вход
`QueryFeatures`
- `normalized_query: str`
- `TargetTermsAnalysis`
- Выход
`subintent: str | None`
`AnchorAnalysis`
### `routers/deterministic.py`
### `routers/route_catalog.py`
- Задача
Детерминированно определить маршрут без LLM там, где это возможно.
Держать один источник истины для допустимых маршрутов.
- Как решает
Использует:
- `DocsSubintentResolver`
- проверку conflicting doc anchors
- список general markers
Правила:
- `FIND_FILES` -> `DOCS / DOC_EXPLAIN / FIND_FILES`
- `subintent != None` и нет конфликта doc-signals -> `DOCS / DOC_EXPLAIN / SUMMARY`
- general marker -> `GENERAL / GENERAL_QA / SUMMARY`
- Вход
- `user_query: str`
- `QueryFeatures`
- `anchors: V2RouteAnchors`
- Выход
`V2RouteResult | None`
Возвращает:
- список `allowed_routes` для payload LLM
- проверку допустимости комбинации `routing_domain + intent + subintent`
### `routers/llm.py`
- Задача
Определить маршрут через LLM, если deterministic routing не дал результата.
Выбрать маршрут через LLM как основной селектор.
- Как решает
Формирует JSON payload из:
- `user_query`
- `normalized_query`
- `target_terms`
- `anchors`
- списка допустимых маршрутов
- `allowed_routes`
Затем:
- вызывает LLM
- парсит JSON
- валидирует маршрут по whitelist
- нормализует `confidence`
- возвращает сырой candidate route без deterministic business-routing
- Вход
- `user_query: str`
- `normalized_query: str`
- `target_terms: list[str]`
- `anchors: dict`
- Выход
`dict | None`:
`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`
### `routers/prompts.yml`
## 6. Ключевые инварианты
- Задача
Задать LLM-router формальный контракт ответа.
- Как решает
Описывает допустимые маршруты и требует вернуть только JSON.
- Вход
Payload от `routers/llm.py`
- Выход
Структурированный JSON-ответ LLM
- 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 недоступен или вернул невалидный маршрут.