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

25 KiB
Raw Blame History

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_chainGigaChatAnswerer.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)

{
  "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)

{
  "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) для локального репозитория:
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.

  • Прогнать тесткейсы:
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 как в логах артефактов:
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/.