11 KiB
V2RetrievalPolicyResolver Architecture
1. Роль компонента
V2RetrievalPolicyResolver это deterministic bridge между V2IntentRouter и docs-RAG retrieval.
Компонент работает поверх уже готового V2RouteResult и не делает повторную интерпретацию пользовательского текста:
- не вызывает LLM;
- не меняет
intentиsubintent; - не ранжирует документы;
- не собирает evidence.
Его задача: собрать один RetrievalPlan с полями:
profilelayerslimitfilters
2. Зависимости
Актуальная реализация опирается на:
src/app/core/agent/processes/v2/retrieval/policy_resolver.pysrc/app/core/agent/processes/v2/anchor_signals.pysrc/app/core/agent/processes/v2/models.pysrc/app/core/rag/contracts/enums.pysrc/app/core/agent/processes/v2/retrieval/v2_rag_adapter.pysrc/app/core/rag/retrieval/session_retriever.pysrc/app/core/rag/persistence/repository.pysrc/app/core/rag/persistence/query_repository.pysrc/app/core/rag/persistence/retrieval_statement_builder.py
3. Входной контракт
Resolver использует:
route.intentroute.subintentroute.anchors.entity_namesroute.anchors.file_namesroute.anchors.endpoint_pathsroute.anchors.target_doc_hintsroute.anchors.matched_aliasesroute.anchors.process_domainroute.anchors.process_subdomain
route.target_terms в текущей реализации profile/filter branching не влияет.
4. Верхнеуровневый branching
resolve(route) имеет три ветки:
GENERAL_QA->general_qa_grounded_summaryFIND_FILES->file_lookup- иначе -> docs summary branch
Инварианты:
GENERAL_QAвсегда остаётся general profile;FIND_FILESвсегда остаётсяfile_lookup;- resolver всегда возвращает один валидный
RetrievalPlan.
5. Внутренняя декомпозиция
Текущая реализация разбита на два helper-класса.
_AnchorTermCollector
Собирает термы для prefer_like_patterns.
Источники:
- basename из
target_doc_hints endpoint_pathsfile_namesentity_namesmatched_aliasesprocess_domainprocess_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_endpointARCHITECTURE->docs_summary_architectureLOGIC_FLOW->docs_summary_logic_flowDOMAIN_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_hintsmetadata.domain, если естьprocess_domainmetadata.subdomain, если естьprocess_subdomainprefer_path_prefixesprefer_like_patterns
Дополнительно:
- если есть
API_ENDPOINTsignal, добавляется hardpath_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_hintsmetadata.domain, если естьprocess_domainmetadata.subdomain, если естьprocess_subdomainpath_prefixesprefer_path_prefixesprefer_like_patterns
path_prefixes для FIND_FILES выбираются по приоритету:
- директории из
target_doc_hints - директории из
file_names, если путь начинается сdocs/ - signal-based fallback:
- API ->
["docs/api/", "docs/"] - ARCHITECTURE ->
["docs/architecture/", "docs/"] - LOGIC_FLOW ->
["docs/logic/", "docs/"] - DOMAIN_ENTITY ->
["docs/domains/", "docs/"]
- API ->
- 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_prefixesmetadata.domainmetadata.subdomain
Soft preferences:
prefer_path_prefixesprefer_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_prefixesexclude_path_prefixesexclude_like_patternsprefer_path_prefixesprefer_like_patternsprefer_non_testsmetadata_domainизfilters["metadata.domain"]metadata_subdomainизfilters["metadata.subdomain"]
RetrievalStatementBuilder.build_retrieve(...) добавляет SQL predicates:
lower(metadata_json->>'domain') = :metadata_domainlower(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.