Files
agent/rag_structure_report.md
2026-03-12 16:55:23 +03:00

324 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 0) TL;DR (1015 строк)
- В `intent_router_v2` графы задаются как строковые `graph_id`: `CodeQAGraph` и `DocsQAGraph` (`app/modules/rag/intent_router_v2/intent/graph_id_resolver.py:4`, класс `GraphIdResolver.resolve`).
- Точка входа роутера: `IntentRouterV2.route(...)` (`app/modules/rag/intent_router_v2/router.py:35`, класс `IntentRouterV2`).
- Отдельные классы/функции с именами `CodeQAGraph` и `DocsQAGraph` в `app/` не найдены (поиск по коду).
- В тестовом CLI-пайплайне выполнение идет через `IntentRouterRagPipelineRunner.run_case(...)` (`tests/pipeline_intent_rag/helpers/pipeline_runner.py:45`) и `PipelineRuntime.run_*` (`tests/pipeline_intent_rag/helpers/runtime.py:31`).
- Контракт роутера возвращается моделью `IntentRouterResult` (`app/modules/rag/intent_router_v2/models.py:139`) и включает `intent`, `retrieval_profile`, `query_plan`, `retrieval_spec`, `retrieval_constraints`, `symbol_resolution`, `evidence_policy`.
- `sub_intent`, `symbol_kind_hint`, `symbol_candidates`, `path_hints`, `doc_scope_hints` находятся внутри `query_plan` (`app/modules/rag/intent_router_v2/models.py:43`).
- Топ-слои retrieval задаются фабрикой `RetrievalSpecFactory` (`app/modules/rag/intent_router_v2/retrieval/retrieval_spec_factory.py:9`).
- Реально индексируемые/используемые слои: `C0..C3`, `D1..D4` (`app/modules/rag/indexing/code/pipeline.py:37`, `app/modules/rag/indexing/docs/pipeline.py:24`, `app/modules/rag/intent_router_v2/retrieval/retrieval_spec_factory.py:10`).
- В enum есть `C4..C6`, но build/retrieval-код для них в `app/modules/rag` (кроме `README` и `enums.py`) не найден (`app/modules/rag/contracts/enums.py:13`).
- Слой `F0` в `app/` и `tests/` не найден (поиск по коду).
- Ранжирование retrieval: `lexical_rank -> test_penalty -> layer_rank -> distance` (`app/modules/rag/persistence/retrieval_statement_builder.py:59`).
- В `pipeline_intent_rag` evidence gate отсутствует; в `chat` есть `CodeExplainEvidenceGate(min_excerpts=2)` (`app/modules/chat/evidence_gate.py:15`).
## 1) High-level pipeline (диаграмма текстом)
### CodeQAGraph
`Router -> Plan -> Retrieval per layer -> Merge/Rank -> Evidence gate -> LLM prompt builder`
- Router: `IntentRouterV2.route` (`app/modules/rag/intent_router_v2/router.py:35`, класс `IntentRouterV2`).
Определяет `intent`, `graph_id`, `query_plan`, `retrieval_spec`, `retrieval_constraints`, `symbol_resolution`.
- Plan: `QueryPlanBuilder.build` (`app/modules/rag/intent_router_v2/analysis/query_plan_builder.py:50`, класс `QueryPlanBuilder`).
Извлекает anchors, `sub_intent`, symbol/path/doc hints.
- Retrieval per layer: `RetrievalSpecFactory.build` (`app/modules/rag/intent_router_v2/retrieval/retrieval_spec_factory.py:74`) + `RagDbAdapter.retrieve` (`tests/pipeline_intent_rag/helpers/rag_db_adapter.py:41`).
Для `CODE_QA` выбираются `C*` слои; выполняется запрос в БД с фильтрами.
- Merge/Rank: `RetrievalStatementBuilder.build_retrieve` (`app/modules/rag/persistence/retrieval_statement_builder.py:9`).
Все выбранные слои читаются одним SQL и сортируются по `lexical_rank`, `test_penalty`, `layer_rank`, `distance`.
- Evidence gate: в цепочке `tests/pipeline_intent_rag` не найден.
Для отдельного code-explain чата есть `CodeExplainEvidenceGate.evaluate` (`app/modules/chat/evidence_gate.py:19`).
- LLM prompt builder: в режиме `full_chain``GigaChatAnswerer.answer`/`_format_context` (`tests/pipeline_intent_rag/helpers/llm_answerer.py:15`, `:27`).
Формируется prompt из вопроса и первых RAG-строк.
### DocsQAGraph
`Router -> Plan -> Retrieval per layer -> Merge/Rank -> Evidence gate -> LLM prompt builder`
- Router: `IntentRouterV2.route` (`app/modules/rag/intent_router_v2/router.py:35`).
Для `DOCS_QA` ставит `retrieval_profile="docs"` (`app/modules/rag/intent_router_v2/router.py:112`).
- Plan: `QueryPlanBuilder.build` (`app/modules/rag/intent_router_v2/analysis/query_plan_builder.py:50`).
Для `DOCS_QA` принудительно `sub_intent="EXPLAIN"` (`app/modules/rag/intent_router_v2/analysis/query_plan_builder.py:78`).
- Retrieval per layer: `RetrievalSpecFactory.build` (`app/modules/rag/intent_router_v2/retrieval/retrieval_spec_factory.py:74`) + `RagDbAdapter.retrieve` (`tests/pipeline_intent_rag/helpers/rag_db_adapter.py:41`).
Для docs выбираются `D*` слои; при `path_scope` используется docs-scoped набор (`app/modules/rag/intent_router_v2/retrieval/retrieval_spec_factory.py:97`).
- Merge/Rank: `RetrievalStatementBuilder.build_retrieve` (`app/modules/rag/persistence/retrieval_statement_builder.py:9`).
Механизм ранжирования тот же, что и для code.
- Evidence gate: для docs в `pipeline_intent_rag` не найден.
- LLM prompt builder: в `full_chain` тот же `GigaChatAnswerer` (`tests/pipeline_intent_rag/helpers/llm_answerer.py:15`).
## 2) Router contract (фактическое API)
### Пример JSON (code)
```json
{
"intent": "CODE_QA",
"sub_intent": "EXPLAIN",
"retrieval_profile": "code",
"symbol_kind_hint": "class",
"symbol_candidates": ["Context"],
"path_hints": [],
"path_scope": [],
"doc_scope_hints": [],
"layers": ["C1_SYMBOL_CATALOG", "C0_SOURCE_CHUNKS", "C2_DEPENDENCY_GRAPH"],
"retrieval_constraints": {
"include_globs": ["src/**"],
"exclude_globs": ["tests/**", "**/test_*.py", "**/*_test.py"],
"prefer_globs": [],
"test_file_globs": [],
"test_symbol_patterns": [],
"max_candidates": 20,
"fuzzy_symbol_search": {"enabled": true, "max_distance": 2, "top_k": 5}
},
"symbol_resolution": {
"status": "pending",
"resolved_symbol": null,
"alternatives": ["Context"],
"confidence": 0.0
}
}
```
### Пример JSON (docs)
```json
{
"intent": "DOCS_QA",
"sub_intent": "EXPLAIN",
"retrieval_profile": "docs",
"symbol_kind_hint": "unknown",
"symbol_candidates": [],
"path_hints": ["README_DEPLOY.md"],
"path_scope": ["README_DEPLOY.md"],
"doc_scope_hints": ["README_DEPLOY.md", "README*", "docs/**", "**/*.md"],
"layers": ["D3_SECTION_INDEX", "D2_FACT_INDEX"],
"retrieval_constraints": {
"include_globs": ["README_DEPLOY.md", "docs/**", "README*", "**/*.md"],
"exclude_globs": [".venv/**", "node_modules/**", "**/*.bin", "**/*.png", "**/*.jpg"],
"prefer_globs": [],
"test_file_globs": [],
"test_symbol_patterns": [],
"max_candidates": 20,
"fuzzy_symbol_search": {"enabled": true, "max_distance": 2, "top_k": 5}
},
"symbol_resolution": {
"status": "not_requested",
"resolved_symbol": null,
"alternatives": [],
"confidence": 0.0
}
}
```
### Поля контракта
- `intent`, `retrieval_profile`: формируются в `IntentRouterV2.route` (`app/modules/rag/intent_router_v2/router.py:35`).
- `sub_intent`, `symbol_kind_hint`, `symbol_candidates`, `path_hints`, `doc_scope_hints`: внутри `QueryPlan` (`app/modules/rag/intent_router_v2/models.py:43`), заполняются в `QueryPlanBuilder.build` (`app/modules/rag/intent_router_v2/analysis/query_plan_builder.py:50`).
- `path_scope`: находится в `retrieval_spec.filters.path_scope`; строится `RetrievalFilterBuilder._path_scope` (`app/modules/rag/intent_router_v2/retrieval/retrieval_filter_builder.py:63`).
- `layers`: отдельного top-level поля в `IntentRouterResult` нет; фактический источник — `retrieval_spec.layer_queries[].layer_id` (`app/modules/rag/intent_router_v2/models.py:121`, `:125`).
- `retrieval_constraints`: модель `RetrievalConstraints` (`app/modules/rag/intent_router_v2/models.py:67`), заполняется в `RetrievalConstraintsFactory.build` (`app/modules/rag/intent_router_v2/retrieval/retrieval_constraints_factory.py:22`).
- `symbol_resolution`: модель `SymbolResolution` (`app/modules/rag/intent_router_v2/models.py:79`), первичный статус задает `_initial_symbol_resolution` (`app/modules/rag/intent_router_v2/router.py:93`).
### `symbol_resolution` статусы и переходы
- Допустимые статусы: `not_requested`, `pending`, `resolved`, `ambiguous`, `not_found` (`app/modules/rag/intent_router_v2/models.py:15`).
- Начальное состояние: `IntentRouterV2._initial_symbol_resolution` (`app/modules/rag/intent_router_v2/router.py:93`).
- Изменение статуса в тестовом router+rag пайплайне: `_resolve_symbol` (`tests/pipeline_intent_rag/helpers/pipeline_runner.py:69`), где `pending` переходит в `resolved`/`ambiguous`/`not_found`.
### Где определены типы/схемы и где валидация
- Типы/схемы: `QueryPlan`, `RetrievalConstraints`, `SymbolResolution`, `RetrievalSpec`, `IntentRouterResult` в `app/modules/rag/intent_router_v2/models.py:43`, `:67`, `:79`, `:121`, `:139`.
- Валидация: pydantic-модели с `ConfigDict(extra="forbid")` (например `app/modules/rag/intent_router_v2/models.py:44`, `:68`, `:80`, `:122`, `:140`), плюс валидаторы confidence (`app/modules/rag/intent_router_v2/models.py:37`, `:208`).
## 3) Layer inventory (что реально индексируется и как)
### 3.1 Code layers
#### C0_SOURCE_CHUNKS
- Назначение: фрагменты исходного кода для цитирования и lexical fallback.
- Единица индексации: chunk.
- Источник данных: AST-разбор (`ast.parse`) с fallback на оконную нарезку (`app/modules/rag/indexing/code/code_text/chunker.py:17`).
- Схема записи:
- Базовые поля документа: `layer`, `repo_id`, `commit_sha`, `path`, `title`, `text`, `span_start/end`, `metadata`, `embedding` (`app/modules/rag/contracts/documents.py:62`).
- C0 metadata: `chunk_index`, `chunk_type`, `module_or_unit`, `is_test`, `artifact_type` (`app/modules/rag/indexing/code/code_text/document_builder.py:17`).
- Колонки БД: `rag_chunks` + `layer/lang/repo_id/commit_sha/title/metadata_json/span_*...` (`app/modules/rag/persistence/schema_repository.py:47`, `:112`).
- Где строится индекс: `CodeIndexingPipeline.index_file` (`app/modules/rag/indexing/code/pipeline.py:37`) + `CodeTextDocumentBuilder.build` (`app/modules/rag/indexing/code/code_text/document_builder.py:9`).
- Где retrieval: `RagDbAdapter.retrieve` (`tests/pipeline_intent_rag/helpers/rag_db_adapter.py:41`) -> `RagRepository.retrieve|retrieve_lexical_code` (`app/modules/rag/persistence/repository.py:62`, `:87`).
- Как ранжируется: `build_retrieve` и `build_lexical_code` (`app/modules/rag/persistence/retrieval_statement_builder.py:9`, `:64`).
#### C1_SYMBOL_CATALOG
- Назначение: каталог символов (class/function/method/import aliases).
- Единица индексации: symbol.
- Источник данных: Python AST visitor (`app/modules/rag/indexing/code/symbols/extractor.py:32`).
- Схема записи:
- C1 metadata: `symbol_id`, `qname`, `kind`, `signature`, `decorators_or_annotations`, `docstring_or_javadoc`, `parent_symbol_id`, `package_or_module`, `is_entry_candidate`, `is_test`, `lang_payload`, `artifact_type` (`app/modules/rag/indexing/code/symbols/document_builder.py:20`).
- Где строится индекс: `SymbolExtractor.extract` (`app/modules/rag/indexing/code/symbols/extractor.py:24`) + `SymbolDocumentBuilder.build` (`app/modules/rag/indexing/code/symbols/document_builder.py:9`).
- Где retrieval: тот же общий путь SQL retrieval (`app/modules/rag/persistence/retrieval_statement_builder.py:9`).
- Как ранжируется: общий ranking SQL (`app/modules/rag/persistence/retrieval_statement_builder.py:31`, `:39`, `:59`).
#### C2_DEPENDENCY_GRAPH
- Назначение: связи между символами.
- Единица индексации: edge.
- Источник данных: AST visitor по классам/функциям/import/call (`app/modules/rag/indexing/code/edges/extractor.py:33`).
- Схема записи:
- C2 metadata: `edge_id`, `edge_type`, `src_symbol_id`, `src_qname`, `dst_symbol_id`, `dst_ref`, `resolution`, `is_test`, `lang_payload`, `artifact_type` (`app/modules/rag/indexing/code/edges/document_builder.py:18`).
- Где строится индекс: `EdgeExtractor.extract` (`app/modules/rag/indexing/code/edges/extractor.py:24`) + `EdgeDocumentBuilder.build` (`app/modules/rag/indexing/code/edges/document_builder.py:9`).
- Где retrieval: общий SQL retrieval (`app/modules/rag/persistence/retrieval_statement_builder.py:9`).
- Как ранжируется: общий ranking SQL (`app/modules/rag/persistence/retrieval_statement_builder.py:59`).
Отдельно по `C2_DEPENDENCY_GRAPH`:
- Поддерживаемые `edge_type`: `inherits`, `imports`, `calls` (`app/modules/rag/indexing/code/edges/extractor.py:43`, `:58`, `:72`).
- `dst_ref`: собирается из AST-узла имени/атрибута (`app/modules/rag/indexing/code/edges/extractor.py:107`).
- `dst_symbol_id`: ищется в `qname_map` (`app/modules/rag/indexing/code/edges/extractor.py:89`).
- `resolution`: `"resolved"` если `dst_symbol_id` найден, иначе `"partial"` (`app/modules/rag/indexing/code/edges/extractor.py:102`).
#### C3_ENTRYPOINTS
- Назначение: точки входа (HTTP/CLI).
- Единица индексации: entrypoint.
- Источник данных: эвристики по decorators символов (`FastAPI`, `Flask`, `Typer/Click`) (`app/modules/rag/indexing/code/entrypoints/registry.py:23`).
- Схема записи:
- C3 metadata: `entry_id`, `entry_type`, `framework`, `route_or_command`, `handler_symbol_id`, `is_test`, `lang_payload`, `artifact_type` (`app/modules/rag/indexing/code/entrypoints/document_builder.py:17`).
- Где строится индекс: detectors `FastApiEntrypointDetector.detect`, `FlaskEntrypointDetector.detect`, `TyperClickEntrypointDetector.detect` (`app/modules/rag/indexing/code/entrypoints/fastapi_detector.py:11`, `flask_detector.py:9`, `typer_click_detector.py:9`).
- Где retrieval: общий SQL retrieval (`app/modules/rag/persistence/retrieval_statement_builder.py:9`).
- Как ранжируется: общий ranking SQL (`app/modules/rag/persistence/retrieval_statement_builder.py:59`), приоритет слоя `C3` в `layer_rank` = 0 (`app/modules/rag/persistence/retrieval_statement_builder.py:41`).
### 3.2 Docs layers
#### D1_MODULE_CATALOG
- Назначение: карточка модуля документации.
- Единица: документ на файл (если есть `id/type/domain` во frontmatter).
- Схема: `module_id`, `type`, `domain`, `status`, `version`, `tags`, `owners`, `links`, `source_path`, `summary_text`, `doc_kind` и поля связей (`calls_api`, `called_by`, ... ) (`app/modules/rag/indexing/docs/document_builder.py:10`).
- Build: `DocsIndexingPipeline.index_file` (`app/modules/rag/indexing/docs/pipeline.py:24`) -> `DocsDocumentBuilder.build_module_catalog` (`app/modules/rag/indexing/docs/document_builder.py:10`).
- Retrieval: общий SQL retrieval (`app/modules/rag/persistence/retrieval_statement_builder.py:9`).
#### D2_FACT_INDEX
- Назначение: атомарные факты.
- Единица: fact.
- Схема: `fact_id`, `subject_id`, `predicate`, `object`, `object_ref`, `anchor`, `tags`, `source_path` (`app/modules/rag/indexing/docs/document_builder.py:86`).
- Build: `DocsIndexingPipeline._extract_facts/_facts_from_table/_facts_from_lists` (`app/modules/rag/indexing/docs/pipeline.py:55`, `:76`, `:118`) -> `build_fact` (`app/modules/rag/indexing/docs/document_builder.py:86`).
- Retrieval: общий SQL retrieval (`app/modules/rag/persistence/retrieval_statement_builder.py:9`).
#### D3_SECTION_INDEX
- Назначение: секции документа.
- Единица: section chunk.
- Схема: `module_id`, `type`, `domain`, `tags`, `section_path`, `section_title`, `order`, `doc_kind`, `source_path`, `artifact_type` (`app/modules/rag/indexing/docs/document_builder.py:42`).
- Build: `MarkdownDocChunker.chunk` (`app/modules/rag/indexing/docs/chunkers/markdown_chunker.py:20`) -> `DocsDocumentBuilder.build_section` (`app/modules/rag/indexing/docs/document_builder.py:42`).
- Retrieval: общий SQL retrieval (`app/modules/rag/persistence/retrieval_statement_builder.py:9`).
#### D4_POLICY_INDEX
- Назначение: policy-документы.
- Единица: policy doc.
- Схема: `policy_id`, `applies_to`, `rules`, `default_behaviors`, `doc_kind`, `section_path`, `source_path` (`app/modules/rag/indexing/docs/document_builder.py:64`).
- Build: только если `frontmatter.type == "policy"` (`app/modules/rag/indexing/docs/pipeline.py:36`) -> `build_policy` (`app/modules/rag/indexing/docs/document_builder.py:64`).
- Retrieval: общий SQL retrieval (`app/modules/rag/persistence/retrieval_statement_builder.py:9`).
## 4) Scoping & filtering (самое важное)
- `repo_id`/workspace scope при индексации:
- `RagService._resolve_repo_id` берет `project_id` из `rag_session`; fallback — сам `rag_session_id` (`app/modules/rag/services/rag_service.py:161`).
- `repo_id` пишется в документы/metadata (`app/modules/rag/services/rag_service.py:147`).
- Retrieval scope: SQL всегда фильтрует по `rag_session_id = :sid` (`app/modules/rag/persistence/retrieval_statement_builder.py:23`).
- `include_globs`, `exclude_globs`, `prefer_globs`:
- Формируются роутером в `RetrievalConstraintsFactory.build` (`app/modules/rag/intent_router_v2/retrieval/retrieval_constraints_factory.py:22`).
- В `RagDbAdapter.retrieve` применяются `include_globs`/`exclude_globs` через перевод в prefixes/LIKE (`tests/pipeline_intent_rag/helpers/rag_db_adapter.py:46`).
- Использование `prefer_globs` в retrieval-коде не найдено; найдено только формирование/тесты.
- `path_scope`:
- Формируется в `RetrievalFilterBuilder._path_scope` (`app/modules/rag/intent_router_v2/retrieval/retrieval_filter_builder.py:63`).
- В retrieval используется как prefixes: сначала точный scope, затем parent prefix, затем без scope (`tests/pipeline_intent_rag/helpers/rag_db_adapter.py:98`).
- До/после ранжирования:
- Path/layer/test exclude фильтры попадают в `WHERE` до `ORDER BY` (`app/modules/rag/persistence/retrieval_statement_builder.py:23`, `:58`, `:59`).
- `tests/**` penalty:
- Penalty вычисляется как `CASE WHEN lower(path) LIKE ... THEN 1 ELSE 0 END` при `prefer_non_tests=True` (`app/modules/rag/persistence/retrieval_statement_builder.py:176`).
- В `RagDbAdapter` `prefer_non_tests` зависит от `test_policy` (`tests/pipeline_intent_rag/helpers/rag_db_adapter.py:72`).
## 5) Merge policy (как собираются rag_rows)
- Top-K на слой задается в `retrieval_spec.layer_queries` (`app/modules/rag/intent_router_v2/models.py:125`) и заполняется `RetrievalSpecFactory` (`app/modules/rag/intent_router_v2/retrieval/retrieval_spec_factory.py:10`).
- Фактический SQL limit в `pipeline_intent_rag`: `limit = max(top_k)` по всем слоям, не отдельный budget на слой (`tests/pipeline_intent_rag/helpers/rag_db_adapter.py:56`).
- Смешивание слоев: один SQL по списку слоев `layer = ANY(:layers)` + общий `ORDER BY` (`app/modules/rag/persistence/retrieval_statement_builder.py:29`, `:59`).
- Политика merge: interleave/concat как отдельные алгоритмы не найдены; порядок задает SQL sorting.
- Дедупликация по `blob_sha`/`symbol_id` для `rag_rows` в `pipeline_intent_rag` не найдена.
- Общий budget:
- retrieval rows ограничены `LIMIT :lim` (`app/modules/rag/persistence/retrieval_statement_builder.py:60`);
- в `full_chain` в prompt уходит максимум 6 `rag_rows`, каждый `content` режется до 1200 символов (`tests/pipeline_intent_rag/helpers/llm_answerer.py:31`, `:34`).
## 6) Prompt builder / evidence packaging
- Формат передачи `rag_rows` в LLM (`pipeline_intent_rag/full_chain`):
- `GigaChatAnswerer._format_context` делает блоки `- path | title\\ncontent` (`tests/pipeline_intent_rag/helpers/llm_answerer.py:35`),
- затем `answer` добавляет вопрос+контекст в `user_prompt` (`tests/pipeline_intent_rag/helpers/llm_answerer.py:17`).
- Evidence gate:
- в `pipeline_intent_rag` gate не найден;
- в direct code-explain есть `CodeExplainEvidenceGate(min_excerpts=2)` (`app/modules/chat/evidence_gate.py:16`), применяется в `CodeExplainChatService.handle_message` (`app/modules/chat/direct_service.py:46`).
- Шаблоны промптов:
- code explain: `app/modules/agent/prompts/code_explain_answer_v2.txt:1` (используется в `app/modules/chat/direct_service.py:50`, `app/modules/agent/engine/graphs/project_qa_step_graphs.py:169`);
- project/docs QA: `app/modules/agent/prompts/project_answer.txt:1` (используется в `app/modules/agent/engine/graphs/project_qa_graph.py:36`);
- general: `app/modules/agent/prompts/general_answer.txt:1` (используется в `app/modules/agent/engine/graphs/base_graph.py:62`).
## 7) Repro commands
- Построить новый индекс (новая `rag_session`) для локального репозитория:
```bash
python3 -m tests.pipeline_intent_rag reindex --repo-path /abs/path/to/repo
# optional:
python3 -m tests.pipeline_intent_rag reindex --repo-path /abs/path/to/repo --project-id my-project
```
Источник: `tests/pipeline_intent_rag/cli.py:143`, `:123`; README `tests/pipeline_intent_rag/README.md:54`.
- Прогнать тесткейсы:
```bash
python3 -m tests.pipeline_intent_rag run --mode router_only
python3 -m tests.pipeline_intent_rag run --mode router_rag --rag-session-id <uuid>
python3 -m tests.pipeline_intent_rag run --mode full_chain --rag-session-id <uuid>
```
Источник: `tests/pipeline_intent_rag/cli.py:135`, `:138`; README `tests/pipeline_intent_rag/README.md:49`.
- Получить `rag_rows` как в логах артефактов:
```bash
python3 -m tests.pipeline_intent_rag run --mode router_rag --rag-session-id <uuid> --test-name rag_probe
ls -t tests/artifacts/rag_probe_*.jsonl | head -n 1
tail -n 50 "$(ls -t tests/artifacts/rag_probe_*.jsonl | head -n 1)"
```
`rag_rows` сериализуются в JSONL через `PipelineResult.to_record` (`tests/pipeline_intent_rag/helpers/models.py:31`) и `ArtifactWriter` (`tests/pipeline_intent_rag/helpers/artifact_writer.py:20`), имя файла `<test_name>_<YYYYMMDD_HHMMSS>.jsonl` (`tests/pipeline_intent_rag/helpers/artifact_writer.py:16`).
## 8) Appendix: Code pointers
- Entrypoints:
- Router API: `app/modules/rag/intent_router_v2/router.py:35` (`IntentRouterV2.route`).
- Graph-id map: `app/modules/rag/intent_router_v2/intent/graph_id_resolver.py:4` (`GraphIdResolver`).
- CLI: `tests/pipeline_intent_rag/__main__.py:1`, `tests/pipeline_intent_rag/cli.py:178` (`main`).
- Ключевые классы:
- `IntentRouterV2`: `app/modules/rag/intent_router_v2/router.py:14`.
- `RagService`: `app/modules/rag/services/rag_service.py:20`.
- `CodeIndexingPipeline`: `app/modules/rag/indexing/code/pipeline.py:19`.
- `DocsIndexingPipeline`: `app/modules/rag/indexing/docs/pipeline.py:14`.
- `RagDbAdapter`: `tests/pipeline_intent_rag/helpers/rag_db_adapter.py:36`.
- `RetrievalStatementBuilder`: `app/modules/rag/persistence/retrieval_statement_builder.py:8`.
- Конфиги/переменные окружения:
- Test env: `tests/pipeline_intent_rag/.env.test:3`.
- Env loader: `tests/pipeline_intent_rag/helpers/env_bootstrap.py:13`.
- Run config keys: `tests/pipeline_intent_rag/helpers/pipeline_config.py:30`.
- Форматы данных (pydantic/dataclasses):
- Router schema: `app/modules/rag/intent_router_v2/models.py:43`, `:139`.
- RAG document dataclass: `app/modules/rag/contracts/documents.py:29`.
- Pipeline result JSONL model: `tests/pipeline_intent_rag/helpers/models.py:18`.
- Not found:
- Классы/функции `CodeQAGraph`, `DocsQAGraph` как реальные graph-реализации в `app/` не найдены (строки встречаются как `graph_id` map).
- `F0` layer не найден в `app/` и `tests/`.