270 lines
7.1 KiB
Markdown
270 lines
7.1 KiB
Markdown
# V2IntentRouter Architecture
|
||
|
||
## 1. Архитектура
|
||
|
||
Текущий `V2IntentRouter` состоит из следующих компонентов:
|
||
|
||
- `router.py`
|
||
Главная точка входа и оркестратор.
|
||
|
||
- `modules/normalizer.py`
|
||
Нормализация текста запроса в `normalized_query`.
|
||
|
||
- `modules/target_terms.py`
|
||
Извлечение `target_terms`, `endpoint_paths`, `matched_aliases`, `alias_docs`.
|
||
|
||
- `modules/anchors.py`
|
||
Извлечение `anchors` и вспомогательных marker-сигналов.
|
||
|
||
- `routers/docs_subintent_resolver.py`
|
||
Определение `subintent`.
|
||
|
||
- `routers/deterministic.py`
|
||
Детерминированное определение `routing_domain`, `intent`, `subintent`, `confidence`, `routing_mode`, `llm_router_used`, `reason_short`.
|
||
|
||
- `routers/llm.py`
|
||
LLM-based определение `routing_domain`, `intent`, `subintent`, `confidence`, `reason_short`.
|
||
|
||
- `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]`
|
||
- `terms: 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`
|
||
|
||
## 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-сигналы.
|
||
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"`.
|
||
|
||
## 5. Компоненты по флоу
|
||
|
||
### `router.py`
|
||
|
||
- Задача
|
||
Собрать весь процесс роутинга в одной входной точке.
|
||
|
||
- Как решает
|
||
Последовательно вызывает normalizer, target terms extractor, anchors extractor, deterministic router и при необходимости LLM router.
|
||
|
||
- Вход
|
||
`user_query: str`
|
||
|
||
- Выход
|
||
`V2RouteResult`
|
||
|
||
### `modules/normalizer.py`
|
||
|
||
- Задача
|
||
Привести запрос к стабильной форме для дальнейшего анализа.
|
||
|
||
- Как решает
|
||
Схлопывает лишние пробелы через `" ".join(...split())`.
|
||
|
||
- Вход
|
||
`user_query: str`
|
||
|
||
- Выход
|
||
`normalized_query: str`
|
||
|
||
### `modules/target_terms.py`
|
||
|
||
- Задача
|
||
Выделить ключевые термы и retrieval-сигналы из запроса.
|
||
|
||
- Как решает
|
||
Использует:
|
||
- regex для path/entity-like фрагментов
|
||
- список stop-words
|
||
- alias rules с фразами и каноническими термами
|
||
- эвристику для `/health`
|
||
|
||
- Вход
|
||
`normalized_query: str`
|
||
|
||
- Выход
|
||
`TargetTermsAnalysis`:
|
||
- `target_terms`
|
||
- `endpoint_paths`
|
||
- `matched_aliases`
|
||
- `alias_docs`
|
||
|
||
### `modules/anchors.py`
|
||
|
||
- Задача
|
||
Построить полный набор `anchors` и doc-oriented marker-сигналов.
|
||
|
||
- Как решает
|
||
Использует:
|
||
- regex для `entity_names` и `file_names`
|
||
- словари marker-фраз:
|
||
- file markers
|
||
- architecture markers
|
||
- logic markers
|
||
- domain markers
|
||
- endpoint markers
|
||
- map `endpoint -> target_doc_hint`
|
||
- alias docs из `TargetTermsAnalysis`
|
||
|
||
- Вход
|
||
- `normalized_query: str`
|
||
- `TargetTermsAnalysis`
|
||
|
||
- Выход
|
||
`AnchorAnalysis`:
|
||
- `anchors`
|
||
- `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`
|
||
|
||
- Выход
|
||
`subintent: str | None`
|
||
|
||
### `routers/deterministic.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`
|
||
|
||
### `routers/llm.py`
|
||
|
||
- Задача
|
||
Определить маршрут через LLM, если deterministic routing не дал результата.
|
||
|
||
- Как решает
|
||
Формирует JSON payload из:
|
||
- `user_query`
|
||
- `normalized_query`
|
||
- `target_terms`
|
||
- `anchors`
|
||
- списка допустимых маршрутов
|
||
|
||
Затем:
|
||
- вызывает LLM
|
||
- парсит JSON
|
||
- валидирует маршрут по whitelist
|
||
- нормализует `confidence`
|
||
|
||
- Вход
|
||
- `user_query: str`
|
||
- `normalized_query: str`
|
||
- `target_terms: list[str]`
|
||
- `anchors: dict`
|
||
|
||
- Выход
|
||
`dict | None`:
|
||
- `routing_domain`
|
||
- `intent`
|
||
- `subintent`
|
||
- `confidence`
|
||
- `reason_short`
|
||
|
||
### `routers/prompts.yml`
|
||
|
||
- Задача
|
||
Задать LLM-router формальный контракт ответа.
|
||
|
||
- Как решает
|
||
Описывает допустимые маршруты и требует вернуть только JSON.
|
||
|
||
- Вход
|
||
Payload от `routers/llm.py`
|
||
|
||
- Выход
|
||
Структурированный JSON-ответ LLM
|