фиксирую состояние
This commit is contained in:
@@ -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 недоступен или вернул невалидный маршрут.
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
# V2RetrievalPolicyResolver Architecture
|
||||
|
||||
## 1. Роль компонента
|
||||
|
||||
`V2RetrievalPolicyResolver` это deterministic bridge между `V2IntentRouter` и docs-RAG retrieval.
|
||||
|
||||
Компонент работает поверх уже готового `V2RouteResult` и не делает повторную интерпретацию пользовательского текста:
|
||||
|
||||
- не вызывает LLM;
|
||||
- не меняет `intent` и `subintent`;
|
||||
- не ранжирует документы;
|
||||
- не собирает evidence.
|
||||
|
||||
Его задача: собрать один `RetrievalPlan` с полями:
|
||||
|
||||
- `profile`
|
||||
- `layers`
|
||||
- `limit`
|
||||
- `filters`
|
||||
|
||||
## 2. Зависимости
|
||||
|
||||
Актуальная реализация опирается на:
|
||||
|
||||
- `src/app/core/agent/processes/v2/retrieval/policy_resolver.py`
|
||||
- `src/app/core/agent/processes/v2/anchor_signals.py`
|
||||
- `src/app/core/agent/processes/v2/models.py`
|
||||
- `src/app/core/rag/contracts/enums.py`
|
||||
- `src/app/core/agent/processes/v2/retrieval/v2_rag_adapter.py`
|
||||
- `src/app/core/rag/retrieval/session_retriever.py`
|
||||
- `src/app/core/rag/persistence/repository.py`
|
||||
- `src/app/core/rag/persistence/query_repository.py`
|
||||
- `src/app/core/rag/persistence/retrieval_statement_builder.py`
|
||||
|
||||
## 3. Входной контракт
|
||||
|
||||
Resolver использует:
|
||||
|
||||
- `route.intent`
|
||||
- `route.subintent`
|
||||
- `route.anchors.entity_names`
|
||||
- `route.anchors.file_names`
|
||||
- `route.anchors.endpoint_paths`
|
||||
- `route.anchors.target_doc_hints`
|
||||
- `route.anchors.matched_aliases`
|
||||
- `route.anchors.process_domain`
|
||||
- `route.anchors.process_subdomain`
|
||||
|
||||
`route.target_terms` в текущей реализации profile/filter branching не влияет.
|
||||
|
||||
## 4. Верхнеуровневый branching
|
||||
|
||||
`resolve(route)` имеет три ветки:
|
||||
|
||||
1. `GENERAL_QA` -> `general_qa_grounded_summary`
|
||||
2. `FIND_FILES` -> `file_lookup`
|
||||
3. иначе -> docs summary branch
|
||||
|
||||
Инварианты:
|
||||
|
||||
- `GENERAL_QA` всегда остаётся general profile;
|
||||
- `FIND_FILES` всегда остаётся `file_lookup`;
|
||||
- resolver всегда возвращает один валидный `RetrievalPlan`.
|
||||
|
||||
## 5. Внутренняя декомпозиция
|
||||
|
||||
Текущая реализация разбита на два helper-класса.
|
||||
|
||||
### `_AnchorTermCollector`
|
||||
|
||||
Собирает термы для `prefer_like_patterns`.
|
||||
|
||||
Источники:
|
||||
|
||||
- basename из `target_doc_hints`
|
||||
- `endpoint_paths`
|
||||
- `file_names`
|
||||
- `entity_names`
|
||||
- `matched_aliases`
|
||||
- `process_domain`
|
||||
- `process_subdomain`
|
||||
|
||||
Все значения нормализуются в lower-case и превращаются в SQL-like patterns вида `"%term%"`.
|
||||
|
||||
Для `FIND_FILES` действует отдельное правило:
|
||||
|
||||
- если есть `target_doc_hints`, `prefer_like_patterns` строится только по basename hints;
|
||||
- иначе используется общий набор collected terms.
|
||||
|
||||
### `_RouteFilterBuilder`
|
||||
|
||||
Собирает `filters` для трёх веток:
|
||||
|
||||
- `general_filters(route)`
|
||||
- `summary_filters(route)`
|
||||
- `find_files_filters(route)`
|
||||
|
||||
Дополнительно содержит path selection:
|
||||
|
||||
- `_summary_prefixes(route)`
|
||||
- `_find_files_prefixes(route)`
|
||||
- `_find_files_prefer_prefixes(route)`
|
||||
|
||||
## 6. Signal detection
|
||||
|
||||
Summary profile и часть path preferences зависят от `anchor_signal_types(route)`.
|
||||
|
||||
Сигналы вычисляются так:
|
||||
|
||||
- `FIND_FILES`
|
||||
- если `route.subintent == FIND_FILES`
|
||||
- `API_ENDPOINT`
|
||||
- если есть `endpoint_paths`
|
||||
- или в `target_doc_hints` / `file_names` / `matched_aliases` встречаются маркеры `"/api/"`, `"api"`, `"endpoint"`
|
||||
- `ARCHITECTURE`
|
||||
- если в `target_doc_hints` / `file_names` / `matched_aliases` встречаются `"/architecture/"`, `"architecture"`, `"arch"`
|
||||
- `LOGIC_FLOW`
|
||||
- если в `target_doc_hints` / `file_names` / `matched_aliases` встречаются `"/logic/"`, `"logic"`, `"workflow"`, `"flow"`, `"process"`
|
||||
- `DOMAIN_ENTITY`
|
||||
- если есть `entity_names`
|
||||
- или в `target_doc_hints` / `file_names` / `matched_aliases` встречаются `"/domains/"`, `"domain"`, `"entity"`, `"component"`
|
||||
|
||||
Важно:
|
||||
|
||||
- `process_domain` и `process_subdomain` сейчас **не участвуют** в signal detection;
|
||||
- они влияют только на filters и `prefer_like_patterns`.
|
||||
|
||||
## 7. Summary profile selection
|
||||
|
||||
Метод `_summary_profile(route)` использует:
|
||||
|
||||
- `meaningful = anchor_signal_types(route) - {FIND_FILES}`
|
||||
|
||||
Правило:
|
||||
|
||||
- если meaningful signal не ровно один -> `docs_summary_generic`
|
||||
- если ровно один:
|
||||
- `API_ENDPOINT` -> `docs_summary_api_endpoint`
|
||||
- `ARCHITECTURE` -> `docs_summary_architecture`
|
||||
- `LOGIC_FLOW` -> `docs_summary_logic_flow`
|
||||
- `DOMAIN_ENTITY` -> `docs_summary_domain_entity`
|
||||
|
||||
Следствие:
|
||||
|
||||
- конфликт API + architecture -> generic;
|
||||
- API + entity -> generic;
|
||||
- weak/no signals -> generic.
|
||||
|
||||
## 8. Profiles, layers, limits
|
||||
|
||||
### `general_qa_grounded_summary`
|
||||
|
||||
- condition: `route.intent == GENERAL_QA`
|
||||
- layers: `[D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS]`
|
||||
- limit: `8`
|
||||
|
||||
### `file_lookup`
|
||||
|
||||
- condition: `route.subintent == FIND_FILES`
|
||||
- layers: `[D1_DOCUMENT_CATALOG, D3_ENTITY_CATALOG]`
|
||||
- limit: `12`
|
||||
|
||||
### `docs_summary_api_endpoint`
|
||||
|
||||
- layers: `[D1_DOCUMENT_CATALOG, D2_FACT_INDEX, D0_DOC_CHUNKS]`
|
||||
- limit: `8`
|
||||
|
||||
### `docs_summary_logic_flow`
|
||||
|
||||
- layers: `[D4_WORKFLOW_INDEX, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS]`
|
||||
- limit: `8`
|
||||
|
||||
### `docs_summary_domain_entity`
|
||||
|
||||
- layers: `[D3_ENTITY_CATALOG, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS]`
|
||||
- limit: `8`
|
||||
|
||||
### `docs_summary_architecture`
|
||||
|
||||
- layers: `[D1_DOCUMENT_CATALOG, D5_RELATION_GRAPH, D0_DOC_CHUNKS]`
|
||||
- limit: `8`
|
||||
|
||||
### `docs_summary_generic`
|
||||
|
||||
- layers: `[D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS]`
|
||||
- limit: `8`
|
||||
|
||||
## 9. Filters by branch
|
||||
|
||||
### General branch
|
||||
|
||||
`general_filters(route)` возвращает:
|
||||
|
||||
- `prefer_path_prefixes = ["docs/architecture/", "docs/"]`
|
||||
- `prefer_like_patterns = ["%readme.md%", "%overview%"]`
|
||||
- `target_doc_hints = list(route.anchors.target_doc_hints)`
|
||||
|
||||
Это обзорный, но не узкий plan: hard `path_prefixes` здесь нет.
|
||||
|
||||
### Summary branch
|
||||
|
||||
`summary_filters(route)` всегда включает:
|
||||
|
||||
- `target_doc_hints`
|
||||
- `metadata.domain`, если есть `process_domain`
|
||||
- `metadata.subdomain`, если есть `process_subdomain`
|
||||
- `prefer_path_prefixes`
|
||||
- `prefer_like_patterns`
|
||||
|
||||
Дополнительно:
|
||||
|
||||
- если есть `API_ENDPOINT` signal, добавляется hard `path_prefixes = ["docs/api/", "docs/"]`
|
||||
|
||||
`prefer_path_prefixes` для summary:
|
||||
|
||||
- API -> `["docs/api/", "docs/"]`
|
||||
- ARCHITECTURE -> `["docs/architecture/", "docs/"]`
|
||||
- LOGIC_FLOW -> `["docs/logic/", "docs/architecture/", "docs/"]`
|
||||
- DOMAIN_ENTITY -> `["docs/domains/", "docs/", "docs/api/"]`
|
||||
- empty signals -> `["docs/"]`
|
||||
|
||||
Если сигналов несколько, prefixes объединяются и dedupe-ятся с сохранением порядка.
|
||||
|
||||
### FIND_FILES branch
|
||||
|
||||
`find_files_filters(route)` всегда включает:
|
||||
|
||||
- `target_doc_hints`
|
||||
- `metadata.domain`, если есть `process_domain`
|
||||
- `metadata.subdomain`, если есть `process_subdomain`
|
||||
- `path_prefixes`
|
||||
- `prefer_path_prefixes`
|
||||
- `prefer_like_patterns`
|
||||
|
||||
`path_prefixes` для `FIND_FILES` выбираются по приоритету:
|
||||
|
||||
1. директории из `target_doc_hints`
|
||||
2. директории из `file_names`, если путь начинается с `docs/`
|
||||
3. signal-based fallback:
|
||||
- API -> `["docs/api/", "docs/"]`
|
||||
- ARCHITECTURE -> `["docs/architecture/", "docs/"]`
|
||||
- LOGIC_FLOW -> `["docs/logic/", "docs/"]`
|
||||
- DOMAIN_ENTITY -> `["docs/domains/", "docs/"]`
|
||||
4. default -> `["docs/"]`
|
||||
|
||||
`prefer_path_prefixes` для `FIND_FILES`:
|
||||
|
||||
- начинается с `path_prefixes`
|
||||
- если есть `process_domain` или `process_subdomain`, дополнительно добавляет:
|
||||
- `"docs/domains/"`
|
||||
- `"docs/logic/"`
|
||||
|
||||
## 10. Hard и soft сигналы в текущей реализации
|
||||
|
||||
В терминах текущего кода:
|
||||
|
||||
Hard-ish / narrowing filters:
|
||||
|
||||
- `path_prefixes`
|
||||
- `metadata.domain`
|
||||
- `metadata.subdomain`
|
||||
|
||||
Soft preferences:
|
||||
|
||||
- `prefer_path_prefixes`
|
||||
- `prefer_like_patterns`
|
||||
|
||||
Отдельно:
|
||||
|
||||
- `target_doc_hints` всегда сохраняются в `RetrievalPlan.filters`, но **не маппятся напрямую** в `RagRepository.retrieve(...)` как SQL hard filter.
|
||||
|
||||
То есть сейчас `target_doc_hints` это не прямой DB filter, а downstream anchor для других шагов пайплайна и для deterministic exact-doc seeding logic.
|
||||
|
||||
## 11. Интеграция с retrieval stack
|
||||
|
||||
Следующий слой после resolver теперь исполняет plan не напрямую в `V2Process`, а через `V2RagRetrievalAdapter`.
|
||||
|
||||
`V2RagRetrievalAdapter.fetch_rows(...)` использует `RetrievalPlan` так:
|
||||
|
||||
- читает `filters["target_doc_hints"]` из самого плана;
|
||||
- делает exact-path seed через `retrieve_exact_files(...)`;
|
||||
- для missing hints делает substring fallback через `retrieve_chunks_by_path_substrings(...)`;
|
||||
- затем делает обычный semantic retrieve через `RagSessionRetriever.retrieve(...)`;
|
||||
- объединяет exact / substring / semantic rows через dedupe merge.
|
||||
|
||||
Это важный сдвиг: execution strategy теперь зависит от **контракта `RetrievalPlan`**, а не от скрытой route-specific логики внутри `V2Process`.
|
||||
|
||||
`RagSessionRetriever._map_filters()` прокидывает в `RagRepository.retrieve(...)`:
|
||||
|
||||
- `path_prefixes`
|
||||
- `exclude_path_prefixes`
|
||||
- `exclude_like_patterns`
|
||||
- `prefer_path_prefixes`
|
||||
- `prefer_like_patterns`
|
||||
- `prefer_non_tests`
|
||||
- `metadata_domain` из `filters["metadata.domain"]`
|
||||
- `metadata_subdomain` из `filters["metadata.subdomain"]`
|
||||
|
||||
`RetrievalStatementBuilder.build_retrieve(...)` добавляет SQL predicates:
|
||||
|
||||
- `lower(metadata_json->>'domain') = :metadata_domain`
|
||||
- `lower(metadata_json->>'subdomain') = :metadata_subdomain`
|
||||
|
||||
Таким образом:
|
||||
|
||||
- `process_domain/process_subdomain` реально участвуют в retrieval query;
|
||||
- `target_doc_hints` реально участвуют в retrieval execution strategy на уровне adapter;
|
||||
- `V2RetrievalPolicyResolver` определяет plan contract, а следующий шаг исполняет этот contract более буквально.
|
||||
|
||||
## 12. Актуальные ограничения
|
||||
|
||||
- Логика полностью deterministic.
|
||||
- `target_terms` сейчас не участвуют в branching resolver.
|
||||
- `process_domain/process_subdomain` не влияют на summary profile selection.
|
||||
- API signal добавляет `path_prefixes` даже в generic summary, если среди конфликтующих сигналов присутствует API.
|
||||
- `target_doc_hints` не являются прямым SQL filter внутри обычного `retrieve`, но используются adapter-уровнем для exact-path / substring seeding до semantic retrieval.
|
||||
Reference in New Issue
Block a user