Рефакторинг
This commit is contained in:
78
README.md
78
README.md
@@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
- **Target Architecture** описывает то, к чему проект идёт.
|
- **Target Architecture** описывает то, к чему проект идёт.
|
||||||
- **MVP-now** описывает то, что реально доводится сейчас через тесты.
|
- **MVP-now** описывает то, что реально доводится сейчас через тесты.
|
||||||
- **MVP-now** не включает UI-интеграцию и не требует полного runtime orchestration.
|
- **MVP-now** не включает UI-интеграцию и использует единый stage-based runtime.
|
||||||
- **MVP-now** фокусируется на:
|
- **MVP-now** фокусируется на:
|
||||||
- IntentRouterV2;
|
- IntentRouterV2;
|
||||||
- code-first retrieval;
|
- code-first retrieval;
|
||||||
@@ -72,9 +72,10 @@
|
|||||||
|
|
||||||
**MVP-now**
|
**MVP-now**
|
||||||
|
|
||||||
- isolated `CODE_QA` test pipeline;
|
- единый `agent runtime`;
|
||||||
- IntentRouterV2 as canonical router;
|
- `IntentRouterV2` как канонический router и retrieval planner;
|
||||||
- router-driven layered retrieval;
|
- stage-based execution внутри `agent.runtime`;
|
||||||
|
- infrastructure `rag` только для indexing/retrieval/storage;
|
||||||
- evidence-first answer synthesis;
|
- evidence-first answer synthesis;
|
||||||
- diagnostics-first tuning;
|
- diagnostics-first tuning;
|
||||||
- no UI dependency;
|
- no UI dependency;
|
||||||
@@ -870,9 +871,9 @@ flowchart TD
|
|||||||
**Target Architecture**
|
**Target Architecture**
|
||||||
|
|
||||||
- Router
|
- Router
|
||||||
- Graphs / pipelines для `CODE`, `DOCS`, `CROSS_DOMAIN`, `GENERAL`
|
- unified runtime
|
||||||
- CODE RAG + DOCS RAG
|
- CODE RAG + DOCS RAG
|
||||||
- evidence gate
|
- evidence gates
|
||||||
- synthesis layer
|
- synthesis layer
|
||||||
- diagnostics
|
- diagnostics
|
||||||
- генерация технической документации из code / docs / system analysis
|
- генерация технической документации из code / docs / system analysis
|
||||||
@@ -880,8 +881,8 @@ flowchart TD
|
|||||||
|
|
||||||
**MVP-now**
|
**MVP-now**
|
||||||
|
|
||||||
- изолированный test-first пайплайн;
|
- единый test-first runtime;
|
||||||
- цепочка: `user query → IntentRouterV2 → retrieval plan → layered retrieval → evidence gate → LLM answer → diagnostics`;
|
- цепочка: `user query → IntentRouterV2 → retrieval planning → runtime retrieval → context normalization → evidence gate 1 → answer policy → LLM answer → evidence gate 2 → finalization/diagnostics`;
|
||||||
- основной домен: `CODE`;
|
- основной домен: `CODE`;
|
||||||
- основные сценарии:
|
- основные сценарии:
|
||||||
- `OPEN_FILE`
|
- `OPEN_FILE`
|
||||||
@@ -889,35 +890,42 @@ flowchart TD
|
|||||||
- `FIND_TESTS`
|
- `FIND_TESTS`
|
||||||
- `FIND_ENTRYPOINTS`
|
- `FIND_ENTRYPOINTS`
|
||||||
- `GENERAL_QA`
|
- `GENERAL_QA`
|
||||||
|
- `TRACE_FLOW`
|
||||||
|
- `ARCHITECTURE`
|
||||||
- UI-интеграция не требуется для текущего этапа;
|
- UI-интеграция не требуется для текущего этапа;
|
||||||
- docs retrieval не обязателен для текущего milestone;
|
- docs retrieval не обязателен для текущего milestone;
|
||||||
- legacy `RouterService` не считается целевой архитектурой и в перспективе будет заменён.
|
- legacy orchestration удалён из актуального execution path.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
U[User Query] --> R[IntentRouterV2]
|
U[User Query] --> R[IntentRouterV2]
|
||||||
R --> P[Retrieval Plan]
|
R --> P[Retrieval Planning]
|
||||||
P --> G[Layered Retrieval]
|
P --> X[Runtime Retrieval]
|
||||||
G --> E[Evidence Gate]
|
X --> C[Context Normalization]
|
||||||
E --> A[LLM Answer]
|
C --> E1[Evidence Gate 1]
|
||||||
E --> D[Diagnostics]
|
E1 --> AP[Answer Policy]
|
||||||
A --> D
|
AP --> A[LLM Answer]
|
||||||
|
AP --> D[Diagnostics]
|
||||||
|
A --> E2[Evidence Gate 2]
|
||||||
|
E2 --> F[Finalization]
|
||||||
|
F --> D
|
||||||
```
|
```
|
||||||
|
|
||||||
Текущий milestone — test-first и code-first; этот пайплайн настраивается изолированно до интеграции в полный agent runtime.
|
Текущий milestone — test-first и code-first; этот runtime уже является каноническим execution path для MVP.
|
||||||
|
|
||||||
### 4.1.3. Канонический test-first пайплайн (CODE_QA)
|
### 4.1.3. Канонический MVP runtime (CODE-first)
|
||||||
|
|
||||||
Единая точка входа изолированного пайплайна — пакет `app.modules.rag.code_qa_pipeline`:
|
Единая точка входа исполнения — пакет `app.modules.agent.runtime`:
|
||||||
|
|
||||||
- **Роутер:** только `IntentRouterV2`; legacy `RouterService` не используется.
|
- **Роутер:** `app.modules.agent.intent_router_v2`; он отвечает и за routing, и за retrieval planning.
|
||||||
- **Контракты:** `RouterResult` (IntentRouterResult), `RetrievalRequest`, `RetrievalResult`, `EvidenceBundle`, `AnswerSynthesisInput`, `DiagnosticsReport`.
|
- **LLM-слой:** `app.modules.agent.llm`; здесь живут `AgentLlmService`, `PromptLoader` и системные prompt assets.
|
||||||
- **Цепочка:** запрос → IntentRouterV2 → RetrievalRequest → layered retrieval (через адаптер) → нормализованный RetrievalResult → EvidenceBundle → evidence gate → AnswerSynthesisInput → diagnostics.
|
- **Runtime:** `app.modules.agent.runtime`; внутри него stages разложены по подпакетам `retrieval`, `context`, `gates`, `answer_policy`, `generation`, `finalization`.
|
||||||
- **Evidence gate:** общая проверка достаточности evidence по сценарию (OPEN_FILE, EXPLAIN, FIND_TESTS, FIND_ENTRYPOINTS, GENERAL_QA); при недостатке — degraded/insufficient, без уверенного ответа.
|
- **Цепочка:** запрос → `IntentRouterV2` → retrieval planning → runtime retrieval adapter → нормализованный context/evidence → evidence gate 1 → answer policy → LLM generation → evidence gate 2 → finalization → diagnostics.
|
||||||
- **Диагностика:** Level 1 (summary) и Level 2 (detail), машинно-читаемые коды причин (`failure_reasons`: `target_not_resolved`, `path_scope_empty`, `layer_c0_empty`, `insufficient_evidence`, `tests_not_found`, `entrypoints_not_found` и др.).
|
- **Evidence gates:** pre/post проверки достаточности evidence и качества ответа по сценарию.
|
||||||
- **Запуск пайплайна в тестах:** `CodeQAPipelineRunner(router=..., retrieval_adapter=...)`; метод `run(user_query, rag_session_id)` возвращает `CodeQAPipelineResult` с полной диагностикой.
|
- **Диагностика:** runtime возвращает machine-readable diagnostics и trace по стадиям.
|
||||||
|
- **RAG:** `app.modules.rag` больше не содержит agent use-case слоев; он остается инфраструктурой indexing/retrieval/storage.
|
||||||
|
|
||||||
Тесты: `tests/pipeline_setup/pipeline_intent_rag/test_canonical_code_qa_pipeline.py` (роутер → retrieval request, нормализованный результат, evidence gate, диагностика).
|
Тесты: `pipeline_setup_v3` и связанные suite-ы проверяют канонический runtime и его stage-based execution.
|
||||||
|
|
||||||
## 4.2. Router
|
## 4.2. Router
|
||||||
|
|
||||||
@@ -926,7 +934,7 @@ Router определяет:
|
|||||||
- intent;
|
- intent;
|
||||||
- sub-intent;
|
- sub-intent;
|
||||||
- confidence;
|
- confidence;
|
||||||
- подходящий graph;
|
- подходящий execution path;
|
||||||
- требования к retrieval plan.
|
- требования к retrieval plan.
|
||||||
|
|
||||||
Целевые домены:
|
Целевые домены:
|
||||||
@@ -935,9 +943,22 @@ Router определяет:
|
|||||||
- `CROSS_DOMAIN`
|
- `CROSS_DOMAIN`
|
||||||
- `GENERAL`
|
- `GENERAL`
|
||||||
|
|
||||||
## 4.3. Graphs / pipelines
|
## 4.3. Runtime Stages
|
||||||
|
|
||||||
Graph — это специализированный сценарий обработки запроса.
|
В текущем MVP execution path реализован не через graph engine, а через единый runtime с явными stage-компонентами.
|
||||||
|
|
||||||
|
Текущие стадии:
|
||||||
|
- `IntentRouterV2`
|
||||||
|
- `retrieval planning`
|
||||||
|
- `runtime retrieval`
|
||||||
|
- `context normalization`
|
||||||
|
- `evidence gate 1`
|
||||||
|
- `answer policy`
|
||||||
|
- `LLM generation`
|
||||||
|
- `evidence gate 2`
|
||||||
|
- `finalization + diagnostics`
|
||||||
|
|
||||||
|
Если сценарии в будущем начнут расходиться по структуре, а не только по policy-логике шагов, следующим шагом будет рассмотрен переход на graph-based orchestration.
|
||||||
|
|
||||||
Для MVP целесообразны как минимум:
|
Для MVP целесообразны как минимум:
|
||||||
- `CodeOpenGraph`
|
- `CodeOpenGraph`
|
||||||
@@ -1186,4 +1207,3 @@ DOCS и CROSS_DOMAIN остаются частью target architecture; в те
|
|||||||
- богатые fact-индексы по всем доменам;
|
- богатые fact-индексы по всем доменам;
|
||||||
- полный reference graph документации;
|
- полный reference graph документации;
|
||||||
- глубокая автоматизация подготовки системной аналитики.
|
- глубокая автоматизация подготовки системной аналитики.
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +1,37 @@
|
|||||||
# Модуль agent
|
# Модуль agent
|
||||||
|
|
||||||
## 1. Назначение
|
## 1. Назначение
|
||||||
Модуль обеспечивает выполнение code-QA пайплайна для pipeline_setup_v3 и интеграцию с chat-слоем через адаптер к контракту `AgentRunner`. Оркестрация основана на **IntentRouterV2** (RAG) и **CodeQaRuntimeExecutor** (роутинг → retrieval → evidence gate → генерация ответа).
|
Модуль обеспечивает выполнение code-QA пайплайна для pipeline_setup_v3 и интеграцию с chat-слоем через адаптер к контракту `AgentRunner`. Оркестрация основана на **IntentRouterV2** (RAG) и **AgentRuntimeExecutor** (роутинг → retrieval → evidence gate → генерация ответа).
|
||||||
|
|
||||||
## 2. Состав модуля
|
## 2. Состав модуля
|
||||||
- **code_qa_runtime/** — рантайм выполнения code-QA: роутер интентов, retrieval, evidence gate, выбор промпта и генерация ответа (LLM).
|
- **runtime/** — единственный orchestration-слой. На верхнем уровне содержит только файлы рантайма, а шаги исполнения вынесены в `runtime/steps/*` (`retrieval`, `context`, `gates`, `answer_policy`, `generation`, `finalization`, `explain`). Публичный API: `AgentRuntimeExecutor`, `RuntimeRetrievalAdapter`, `RuntimeRepoContextFactory`, модели `Runtime*`.
|
||||||
- **llm/** — сервис вызова LLM (GigaChat) с загрузкой системных промптов через `PromptLoader`.
|
- **llm/** — сервис вызова LLM (GigaChat) с загрузкой системных промптов через `PromptLoader`.
|
||||||
- **prompt_loader.py** — загрузка текстов промптов из каталога `prompts/`.
|
- **llm/prompt_loader.py** — загрузка системных промптов из `llm/prompts.yml`.
|
||||||
- **code_qa_runner_adapter.py** — адаптер `CodeQaRuntimeExecutor` к протоколу `AgentRunner` для использования из chat (async `run` → sync `execute` в executor).
|
- **runtime/code_qa_runner_adapter.py** — адаптер `AgentRuntimeExecutor` к протоколу `AgentRunner` для использования из chat (async `run` → sync `execute` в executor).
|
||||||
|
|
||||||
## 3. Диаграмма зависимостей
|
## 3. Диаграмма зависимостей
|
||||||
```mermaid
|
```mermaid
|
||||||
classDiagram
|
classDiagram
|
||||||
class CodeQaRuntimeExecutor
|
class AgentRuntimeExecutor
|
||||||
class CodeQaRunnerAdapter
|
class CodeQaRunnerAdapter
|
||||||
class AgentLlmService
|
class AgentLlmService
|
||||||
class PromptLoader
|
class PromptLoader
|
||||||
class IntentRouterV2
|
class IntentRouterV2
|
||||||
class CodeQaRetrievalAdapter
|
class RuntimeRetrievalAdapter
|
||||||
|
|
||||||
CodeQaRunnerAdapter --> CodeQaRuntimeExecutor
|
CodeQaRunnerAdapter --> AgentRuntimeExecutor
|
||||||
CodeQaRuntimeExecutor --> AgentLlmService
|
AgentRuntimeExecutor --> AgentLlmService
|
||||||
CodeQaRuntimeExecutor --> IntentRouterV2
|
AgentRuntimeExecutor --> IntentRouterV2
|
||||||
CodeQaRuntimeExecutor --> CodeQaRetrievalAdapter
|
AgentRuntimeExecutor --> RuntimeRetrievalAdapter
|
||||||
AgentLlmService --> PromptLoader
|
AgentLlmService --> PromptLoader
|
||||||
```
|
```
|
||||||
|
|
||||||
## 4. Точки входа
|
## 4. Точки входа
|
||||||
- **Тесты pipeline_setup_v3**: `AgentRuntimeAdapter` импортирует `CodeQaRuntimeExecutor`, `IntentRouterV2`, `CodeQaRepoContextFactory`, `CodeQaRetrievalAdapter`, `AgentLlmService`, `PromptLoader` напрямую из соответствующих пакетов.
|
- **Тесты pipeline_setup_v3**: `AgentRuntimeAdapter` импортирует `AgentRuntimeExecutor`, `IntentRouterV2`, `RuntimeRepoContextFactory`, `RuntimeRetrievalAdapter`, `AgentLlmService`, `PromptLoader` из `app.modules.agent.runtime` и соответствующих пакетов.
|
||||||
- **Приложение (chat)**: `ModularApplication` собирает `CodeQaRuntimeExecutor` и оборачивает его в `CodeQaRunnerAdapter`; chat передаёт адаптер как `agent_runner` в `ChatModule`.
|
- **Приложение (chat)**: `ModularApplication` собирает `AgentRuntimeExecutor` и оборачивает его в `CodeQaRunnerAdapter`; chat передаёт адаптер как `agent_runner` в `ChatModule`.
|
||||||
|
|
||||||
## 5. Промпты
|
## 5. Промпты
|
||||||
Используются только промпты, загружаемые из `prompts/`:
|
Используются только промпты, загружаемые из `llm/prompts.yml`:
|
||||||
- **code_qa_*** — ответы по sub_intent (architecture, explain, find_entrypoints, find_tests, general, open_file, trace_flow, degraded, repair).
|
- **code_qa_*** — ответы по sub_intent (architecture, explain, find_entrypoints, find_tests, general, open_file, trace_flow, degraded, repair).
|
||||||
- **rag_intent_router_v2** — классификация интента в IntentRouterV2.
|
- **rag_intent_router_v2** — классификация интента в IntentRouterV2.
|
||||||
- **code_explain_answer_v2** — прямой code-explain в chat (direct_service).
|
- **code_explain_answer_v2** — прямой code-explain в chat (direct_service).
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
from app.modules.agent.code_qa_runtime.executor import CodeQaRuntimeExecutor
|
|
||||||
from app.modules.agent.code_qa_runtime.models import (
|
|
||||||
CodeQaDraftAnswer,
|
|
||||||
CodeQaExecutionState,
|
|
||||||
CodeQaFinalResult,
|
|
||||||
CodeQaValidationResult,
|
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"CodeQaDraftAnswer",
|
|
||||||
"CodeQaExecutionState",
|
|
||||||
"CodeQaFinalResult",
|
|
||||||
"CodeQaRuntimeExecutor",
|
|
||||||
"CodeQaValidationResult",
|
|
||||||
]
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import (
|
|
||||||
AnswerSynthesisInput as CodeQaAnswerSynthesisInput,
|
|
||||||
)
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import (
|
|
||||||
DiagnosticsReport as CodeQaDiagnosticsReport,
|
|
||||||
)
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import (
|
|
||||||
EvidenceBundle as CodeQaEvidencePack,
|
|
||||||
)
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import (
|
|
||||||
RetrievalRequest as CodeQaRetrievalRequest,
|
|
||||||
)
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import (
|
|
||||||
RetrievalResult as CodeQaRetrievalResult,
|
|
||||||
)
|
|
||||||
from app.modules.rag.intent_router_v2.models import ConversationState, IntentRouterResult, RepoContext
|
|
||||||
|
|
||||||
|
|
||||||
class CodeQaDraftAnswer(BaseModel):
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
|
||||||
|
|
||||||
prompt_name: str
|
|
||||||
prompt_payload: str
|
|
||||||
answer: str = ""
|
|
||||||
|
|
||||||
|
|
||||||
class CodeQaValidationResult(BaseModel):
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
|
||||||
|
|
||||||
passed: bool = False
|
|
||||||
action: str = "return"
|
|
||||||
reasons: list[str] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class CodeQaFinalResult(BaseModel):
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
|
||||||
|
|
||||||
final_answer: str
|
|
||||||
answer_mode: str = "normal"
|
|
||||||
repair_used: bool = False
|
|
||||||
llm_used: bool = False
|
|
||||||
draft_answer: CodeQaDraftAnswer | None = None
|
|
||||||
validation: CodeQaValidationResult = Field(default_factory=CodeQaValidationResult)
|
|
||||||
router_result: IntentRouterResult | None = None
|
|
||||||
retrieval_request: CodeQaRetrievalRequest | None = None
|
|
||||||
retrieval_result: CodeQaRetrievalResult | None = None
|
|
||||||
evidence_pack: CodeQaEvidencePack | None = None
|
|
||||||
diagnostics: CodeQaDiagnosticsReport
|
|
||||||
runtime_trace: list[dict[str, Any]] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class CodeQaExecutionState(BaseModel):
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
|
||||||
|
|
||||||
user_query: str
|
|
||||||
rag_session_id: str
|
|
||||||
conversation_state: ConversationState = Field(default_factory=ConversationState)
|
|
||||||
repo_context: RepoContext = Field(default_factory=RepoContext)
|
|
||||||
router_result: IntentRouterResult | None = None
|
|
||||||
retrieval_request: CodeQaRetrievalRequest | None = None
|
|
||||||
retrieval_result: CodeQaRetrievalResult | None = None
|
|
||||||
evidence_pack: CodeQaEvidencePack | None = None
|
|
||||||
synthesis_input: CodeQaAnswerSynthesisInput | None = None
|
|
||||||
diagnostics: CodeQaDiagnosticsReport | None = None
|
|
||||||
answer_mode: str = "normal"
|
|
||||||
degraded_message: str = ""
|
|
||||||
final_result: CodeQaFinalResult | None = None
|
|
||||||
19
src/app/modules/agent/intent_router_v2/__init__.py
Normal file
19
src/app/modules/agent/intent_router_v2/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from app.modules.agent.intent_router_v2.models import (
|
||||||
|
ConversationState,
|
||||||
|
IntentDecision,
|
||||||
|
IntentRouterResult,
|
||||||
|
QueryAnchor,
|
||||||
|
QueryPlan,
|
||||||
|
RepoContext,
|
||||||
|
)
|
||||||
|
from app.modules.agent.intent_router_v2.router import IntentRouterV2
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ConversationState",
|
||||||
|
"IntentDecision",
|
||||||
|
"IntentRouterResult",
|
||||||
|
"IntentRouterV2",
|
||||||
|
"QueryAnchor",
|
||||||
|
"QueryPlan",
|
||||||
|
"RepoContext",
|
||||||
|
]
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from app.modules.agent.intent_router_v2.analysis.normalization import QueryNormalizer
|
||||||
|
from app.modules.agent.intent_router_v2.analysis.query_plan_builder import QueryPlanBuilder
|
||||||
|
|
||||||
|
__all__ = ["QueryNormalizer", "QueryPlanBuilder"]
|
||||||
@@ -2,10 +2,10 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import AnchorSpan, QueryAnchor
|
from app.modules.agent.intent_router_v2.models import AnchorSpan, QueryAnchor
|
||||||
from app.modules.rag.intent_router_v2.analysis.normalization_terms import KeyTermCanonicalizer
|
from app.modules.agent.intent_router_v2.analysis.normalization_terms import KeyTermCanonicalizer
|
||||||
from app.modules.rag.intent_router_v2.analysis.symbol_rules import COMMON_PATH_SEGMENTS, PY_KEYWORDS
|
from app.modules.agent.intent_router_v2.analysis.symbol_rules import COMMON_PATH_SEGMENTS, PY_KEYWORDS
|
||||||
from app.modules.rag.intent_router_v2.analysis.term_mapping import RuEnTermMapper
|
from app.modules.agent.intent_router_v2.analysis.term_mapping import RuEnTermMapper
|
||||||
|
|
||||||
_FILE_PATTERN = re.compile(r"(?P<value>\b(?:[\w.-]+/)*[\w.-]+\.(?:py|md|rst|txt|yaml|yml|json|toml|ini|cfg)\b)")
|
_FILE_PATTERN = re.compile(r"(?P<value>\b(?:[\w.-]+/)*[\w.-]+\.(?:py|md|rst|txt|yaml|yml|json|toml|ini|cfg)\b)")
|
||||||
_PATH_HINT_PATTERN = re.compile(r"(?P<value>\b(?:src|app|docs|tests)/[\w./-]*[\w-]\b)")
|
_PATH_HINT_PATTERN = re.compile(r"(?P<value>\b(?:src|app|docs|tests)/[\w./-]*[\w-]\b)")
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import QueryAnchor
|
from app.modules.agent.intent_router_v2.models import QueryAnchor
|
||||||
|
|
||||||
|
|
||||||
class AnchorSpanValidator:
|
class AnchorSpanValidator:
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.analysis.followup_detector import FollowUpDetector
|
from app.modules.agent.intent_router_v2.analysis.followup_detector import FollowUpDetector
|
||||||
from app.modules.rag.intent_router_v2.models import ConversationState, QueryAnchor
|
from app.modules.agent.intent_router_v2.models import ConversationState, QueryAnchor
|
||||||
|
|
||||||
|
|
||||||
class ConversationAnchorBuilder:
|
class ConversationAnchorBuilder:
|
||||||
@@ -2,8 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.analysis.normalization import FILE_PATH_RE
|
from app.modules.agent.intent_router_v2.analysis.normalization import FILE_PATH_RE
|
||||||
from app.modules.rag.intent_router_v2.analysis.symbol_rules import COMMON_PATH_SEGMENTS, PY_KEYWORDS
|
from app.modules.agent.intent_router_v2.analysis.symbol_rules import COMMON_PATH_SEGMENTS, PY_KEYWORDS
|
||||||
|
|
||||||
_IDENTIFIER_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]{2,}")
|
_IDENTIFIER_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]{2,}")
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import QueryAnchor
|
from app.modules.agent.intent_router_v2.models import QueryAnchor
|
||||||
|
|
||||||
|
|
||||||
class KeywordHintSanitizer:
|
class KeywordHintSanitizer:
|
||||||
@@ -13,7 +13,7 @@ SNAKE_RE = re.compile(r"(?<!\w)[a-z][a-z0-9]*(?:_[a-z0-9]+)+(?!\w)")
|
|||||||
SPACE_BEFORE_PUNCT_RE = re.compile(r"\s+([,.:;?!])")
|
SPACE_BEFORE_PUNCT_RE = re.compile(r"\s+([,.:;?!])")
|
||||||
SPACE_AFTER_PUNCT_RE = re.compile(r"([,.:;?!])(?=(?:[\"'(\[A-Za-zА-ЯЁа-яё]))")
|
SPACE_AFTER_PUNCT_RE = re.compile(r"([,.:;?!])(?=(?:[\"'(\[A-Za-zА-ЯЁа-яё]))")
|
||||||
WS_RE = re.compile(r"\s+")
|
WS_RE = re.compile(r"\s+")
|
||||||
QUOTE_TRANSLATION = str.maketrans({"«": '"', "»": '"', "“": '"', "”": '"', "‘": "'", "’": "'"})
|
QUOTE_TRANSLATION = str.maketrans({"«": '"', "»": '"', "\u201c": '"', "\u201d": '"', "\u2018": "'", "\u2019": "'"})
|
||||||
|
|
||||||
|
|
||||||
class QueryNormalizer:
|
class QueryNormalizer:
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from app.modules.agent.intent_router_v2.analysis.normalization import QueryNormalizer
|
||||||
|
|
||||||
|
__all__ = ["QueryNormalizer"]
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.analysis.anchor_extractor import AnchorExtractor
|
from app.modules.agent.intent_router_v2.analysis.anchor_extractor import AnchorExtractor
|
||||||
from app.modules.rag.intent_router_v2.analysis.anchor_span_validator import AnchorSpanValidator
|
from app.modules.agent.intent_router_v2.analysis.anchor_span_validator import AnchorSpanValidator
|
||||||
from app.modules.rag.intent_router_v2.analysis.conversation_anchor_builder import ConversationAnchorBuilder
|
from app.modules.agent.intent_router_v2.analysis.conversation_anchor_builder import ConversationAnchorBuilder
|
||||||
from app.modules.rag.intent_router_v2.analysis.keyword_hint_builder import KeywordHintBuilder
|
from app.modules.agent.intent_router_v2.analysis.keyword_hint_builder import KeywordHintBuilder
|
||||||
from app.modules.rag.intent_router_v2.analysis.keyword_hint_sanitizer import KeywordHintSanitizer
|
from app.modules.agent.intent_router_v2.analysis.keyword_hint_sanitizer import KeywordHintSanitizer
|
||||||
from app.modules.rag.intent_router_v2.models import ConversationState, QueryAnchor, QueryPlan
|
from app.modules.agent.intent_router_v2.models import ConversationState, QueryAnchor, QueryPlan
|
||||||
from app.modules.rag.intent_router_v2.analysis.negation_detector import NegationDetector
|
from app.modules.agent.intent_router_v2.analysis.negation_detector import NegationDetector
|
||||||
from app.modules.rag.intent_router_v2.analysis.normalization import QueryNormalizer
|
from app.modules.agent.intent_router_v2.analysis.normalization import QueryNormalizer
|
||||||
from app.modules.rag.intent_router_v2.analysis.sub_intent_detector import SubIntentDetector
|
from app.modules.agent.intent_router_v2.analysis.sub_intent_detector import SubIntentDetector
|
||||||
from app.modules.rag.intent_router_v2.analysis.test_signals import has_test_focus, is_negative_test_request, is_test_related_token
|
from app.modules.agent.intent_router_v2.analysis.test_signals import has_test_focus, is_negative_test_request, is_test_related_token
|
||||||
from app.modules.rag.intent_router_v2.analysis.term_mapping import RuEnTermMapper
|
from app.modules.agent.intent_router_v2.analysis.term_mapping import RuEnTermMapper
|
||||||
|
|
||||||
|
|
||||||
class QueryPlanBuilder:
|
class QueryPlanBuilder:
|
||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.analysis.normalization_terms import KeyTermCanonicalizer
|
from app.modules.agent.intent_router_v2.analysis.normalization_terms import KeyTermCanonicalizer
|
||||||
|
|
||||||
_WORD_RE = re.compile(r"[A-Za-zА-Яа-яЁё-]+")
|
_WORD_RE = re.compile(r"[A-Za-zА-Яа-яЁё-]+")
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.agent.llm import AgentLlmService
|
from app.modules.agent.llm import AgentLlmService
|
||||||
from app.modules.agent.prompt_loader import PromptLoader
|
from app.modules.agent.llm.prompt_loader import PromptLoader
|
||||||
from app.modules.rag.intent_router_v2.intent.classifier import IntentClassifierV2
|
from app.modules.agent.intent_router_v2.intent.classifier import IntentClassifierV2
|
||||||
from app.modules.rag.intent_router_v2.router import IntentRouterV2
|
from app.modules.agent.intent_router_v2.router import IntentRouterV2
|
||||||
from app.modules.shared.env_loader import load_workspace_env
|
from app.modules.shared.env_loader import load_workspace_env
|
||||||
from app.modules.shared.gigachat.client import GigaChatClient
|
from app.modules.shared.gigachat.client import GigaChatClient
|
||||||
from app.modules.shared.gigachat.settings import GigaChatSettings
|
from app.modules.shared.gigachat.settings import GigaChatSettings
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from app.modules.agent.intent_router_v2.intent.classifier import IntentClassifierV2
|
||||||
|
from app.modules.agent.intent_router_v2.intent.conversation_policy import ConversationPolicy
|
||||||
|
from app.modules.agent.intent_router_v2.intent.graph_id_resolver import GraphIdResolver
|
||||||
|
|
||||||
|
__all__ = ["IntentClassifierV2", "ConversationPolicy", "GraphIdResolver"]
|
||||||
@@ -3,9 +3,9 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import ConversationState, IntentDecision
|
from app.modules.agent.intent_router_v2.models import ConversationState, IntentDecision
|
||||||
from app.modules.rag.intent_router_v2.protocols import TextGenerator
|
from app.modules.agent.intent_router_v2.protocols import TextGenerator
|
||||||
from app.modules.rag.intent_router_v2.analysis.test_signals import has_test_focus
|
from app.modules.agent.intent_router_v2.analysis.test_signals import has_test_focus
|
||||||
|
|
||||||
_CODE_FILE_PATH_RE = re.compile(
|
_CODE_FILE_PATH_RE = re.compile(
|
||||||
r"\b(?:[\w.-]+/)*[\w.-]+\.(?:py|js|jsx|ts|tsx|java|kt|go|rb|php|c|cc|cpp|h|hpp|cs|swift|rs)(?!\w)\b",
|
r"\b(?:[\w.-]+/)*[\w.-]+\.(?:py|js|jsx|ts|tsx|java|kt|go|rb|php|c|cc|cpp|h|hpp|cs|swift|rs)(?!\w)\b",
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import ConversationState, IntentDecision
|
from app.modules.agent.intent_router_v2.models import ConversationState, IntentDecision
|
||||||
|
|
||||||
|
|
||||||
class ConversationPolicy:
|
class ConversationPolicy:
|
||||||
@@ -2,8 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import ConversationState, IntentRouterResult, RepoContext
|
from app.modules.agent.intent_router_v2.models import ConversationState, IntentRouterResult, RepoContext
|
||||||
from app.modules.rag.intent_router_v2.router import IntentRouterV2
|
from app.modules.agent.intent_router_v2.router import IntentRouterV2
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import ConversationState, IntentRouterResult, RepoContext
|
from app.modules.agent.intent_router_v2.models import ConversationState, IntentRouterResult, RepoContext
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from app.modules.agent.intent_router_v2.retrieval_planning.retrieval_spec_factory import RetrievalSpecFactory
|
||||||
|
from app.modules.agent.intent_router_v2.retrieval_planning.retrieval_constraints_factory import RetrievalConstraintsFactory
|
||||||
|
|
||||||
|
__all__ = ["RetrievalSpecFactory", "RetrievalConstraintsFactory"]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import EvidencePolicy
|
from app.modules.agent.intent_router_v2.models import EvidencePolicy
|
||||||
|
|
||||||
|
|
||||||
class EvidencePolicyFactory:
|
class EvidencePolicyFactory:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import LayerQuery, RepoContext
|
from app.modules.agent.intent_router_v2.models import LayerQuery, RepoContext
|
||||||
|
|
||||||
|
|
||||||
class LayerQueryBuilder:
|
class LayerQueryBuilder:
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import QueryAnchor, RetrievalConstraints, RetrievalProfile
|
from app.modules.agent.intent_router_v2.models import QueryAnchor, RetrievalConstraints, RetrievalProfile
|
||||||
from app.modules.rag.intent_router_v2.analysis.test_signals import has_test_focus, is_negative_test_request
|
from app.modules.agent.intent_router_v2.analysis.test_signals import has_test_focus, is_negative_test_request
|
||||||
|
|
||||||
|
|
||||||
class RetrievalConstraintsFactory:
|
class RetrievalConstraintsFactory:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.models import (
|
from app.modules.agent.intent_router_v2.models import (
|
||||||
CodeRetrievalFilters,
|
CodeRetrievalFilters,
|
||||||
ConversationState,
|
ConversationState,
|
||||||
DocsRetrievalFilters,
|
DocsRetrievalFilters,
|
||||||
@@ -8,7 +8,7 @@ from app.modules.rag.intent_router_v2.models import (
|
|||||||
QueryAnchor,
|
QueryAnchor,
|
||||||
RepoContext,
|
RepoContext,
|
||||||
)
|
)
|
||||||
from app.modules.rag.intent_router_v2.analysis.test_signals import has_test_focus, is_negative_test_request, is_test_related_token
|
from app.modules.agent.intent_router_v2.analysis.test_signals import has_test_focus, is_negative_test_request, is_test_related_token
|
||||||
|
|
||||||
|
|
||||||
class RetrievalFilterBuilder:
|
class RetrievalFilterBuilder:
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.contracts.enums import RagLayer
|
from app.modules.rag.contracts.enums import RagLayer
|
||||||
from app.modules.rag.intent_router_v2.retrieval.layer_query_builder import LayerQueryBuilder
|
from app.modules.agent.intent_router_v2.retrieval_planning.layer_query_builder import LayerQueryBuilder
|
||||||
from app.modules.rag.intent_router_v2.models import ConversationState, QueryAnchor, RepoContext, RetrievalSpec
|
from app.modules.agent.intent_router_v2.models import ConversationState, QueryAnchor, RepoContext, RetrievalSpec
|
||||||
from app.modules.rag.intent_router_v2.retrieval.retrieval_filter_builder import RetrievalFilterBuilder
|
from app.modules.agent.intent_router_v2.retrieval_planning.retrieval_filter_builder import RetrievalFilterBuilder
|
||||||
|
|
||||||
|
|
||||||
class RetrievalSpecFactory:
|
class RetrievalSpecFactory:
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.intent_router_v2.intent.classifier import IntentClassifierV2
|
from app.modules.agent.intent_router_v2.intent.classifier import IntentClassifierV2
|
||||||
from app.modules.rag.intent_router_v2.intent.conversation_policy import ConversationPolicy
|
from app.modules.agent.intent_router_v2.intent.conversation_policy import ConversationPolicy
|
||||||
from app.modules.rag.intent_router_v2.retrieval.evidence_policy_factory import EvidencePolicyFactory
|
from app.modules.agent.intent_router_v2.retrieval_planning.evidence_policy_factory import EvidencePolicyFactory
|
||||||
from app.modules.rag.intent_router_v2.intent.graph_id_resolver import GraphIdResolver
|
from app.modules.agent.intent_router_v2.intent.graph_id_resolver import GraphIdResolver
|
||||||
from app.modules.rag.intent_router_v2.logger import IntentRouterLogger
|
from app.modules.agent.intent_router_v2.logger import IntentRouterLogger
|
||||||
from app.modules.rag.intent_router_v2.models import ConversationState, IntentRouterResult, RepoContext, SymbolResolution
|
from app.modules.agent.intent_router_v2.models import ConversationState, IntentRouterResult, RepoContext, SymbolResolution
|
||||||
from app.modules.rag.intent_router_v2.analysis.query_plan_builder import QueryPlanBuilder
|
from app.modules.agent.intent_router_v2.analysis.query_plan_builder import QueryPlanBuilder
|
||||||
from app.modules.rag.intent_router_v2.retrieval.retrieval_constraints_factory import RetrievalConstraintsFactory
|
from app.modules.agent.intent_router_v2.retrieval_planning.retrieval_constraints_factory import RetrievalConstraintsFactory
|
||||||
from app.modules.rag.intent_router_v2.retrieval.retrieval_spec_factory import RetrievalSpecFactory
|
from app.modules.agent.intent_router_v2.retrieval_planning.retrieval_spec_factory import RetrievalSpecFactory
|
||||||
|
|
||||||
|
|
||||||
class IntentRouterV2:
|
class IntentRouterV2:
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
from app.modules.agent.llm.service import AgentLlmService
|
from app.modules.agent.llm.service import AgentLlmService
|
||||||
|
from app.modules.agent.llm.prompt_loader import PromptLoader
|
||||||
|
|
||||||
__all__ = ["AgentLlmService"]
|
__all__ = ["AgentLlmService", "PromptLoader"]
|
||||||
|
|||||||
27
src/app/modules/agent/llm/prompt_loader.py
Normal file
27
src/app/modules/agent/llm/prompt_loader.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
class PromptLoader:
|
||||||
|
def __init__(self, prompts_path: Path | None = None) -> None:
|
||||||
|
base = prompts_path or Path(__file__).resolve().parent / "prompts.yml"
|
||||||
|
env_override = os.getenv("AGENT_PROMPTS_DIR", "").strip()
|
||||||
|
raw_path = Path(env_override) if env_override else base
|
||||||
|
self._path = raw_path / "prompts.yml" if raw_path.is_dir() else raw_path
|
||||||
|
self._prompts = self._load_prompts()
|
||||||
|
|
||||||
|
def load(self, name: str) -> str:
|
||||||
|
return str(self._prompts.get(name, "") or "").strip()
|
||||||
|
|
||||||
|
def _load_prompts(self) -> dict[str, str]:
|
||||||
|
if not self._path.is_file():
|
||||||
|
return {}
|
||||||
|
payload = yaml.safe_load(self._path.read_text(encoding="utf-8")) or {}
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
return {}
|
||||||
|
prompts = payload.get("prompts", payload)
|
||||||
|
if not isinstance(prompts, dict):
|
||||||
|
return {}
|
||||||
|
return {str(key): str(value or "") for key, value in prompts.items()}
|
||||||
280
src/app/modules/agent/llm/prompts.yml
Normal file
280
src/app/modules/agent/llm/prompts.yml
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
prompts:
|
||||||
|
code_explain_answer_v2: |
|
||||||
|
Объяснение кода осуществляется только с использованием предоставленного ExplainPack.
|
||||||
|
|
||||||
|
Правила:
|
||||||
|
- Сначала используйте доказательства.
|
||||||
|
- Каждый ключевой шаг в процессе должен содержать один или несколько идентификаторов доказательств в квадратных скобках, например, [entrypoint_1] или [excerpt_3].
|
||||||
|
- Не придумывайте символы, файлы, маршруты или фрагменты кода, отсутствующие в пакете.
|
||||||
|
- Если доказательства неполные, укажите это явно.
|
||||||
|
- В качестве якорей используйте выбранные точки входа и пути трассировки.
|
||||||
|
|
||||||
|
Верните Markdown со следующей структурой:
|
||||||
|
1. Краткое описание
|
||||||
|
2. Пошаговый процесс
|
||||||
|
3. Данные и побочные эффекты
|
||||||
|
4. Ошибки и граничные случаи
|
||||||
|
5. Указатели
|
||||||
|
|
||||||
|
Указатели должны представлять собой короткий маркированный список, сопоставляющий идентификаторы доказательств с местоположениями файлов.
|
||||||
|
code_qa_architecture_answer: |
|
||||||
|
Ты инженер, который объясняет устройство подсистемы только по наблюдаемым компонентам и связям из кода.
|
||||||
|
|
||||||
|
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
||||||
|
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
||||||
|
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
||||||
|
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
||||||
|
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
||||||
|
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
||||||
|
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
||||||
|
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
||||||
|
Если данных мало, честно скажи об этом вместо общего обзора.
|
||||||
|
Не используй жирные заголовки блоков, если пользователь их не просил.
|
||||||
|
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
||||||
|
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
||||||
|
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
||||||
|
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
||||||
|
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
||||||
|
|
||||||
|
Дай архитектурное объяснение без лишней теории.
|
||||||
|
Строй ответ вокруг concrete facts из payload: `must_mention_components`, `must_mention_relations`, `must_use_relation_verbs`.
|
||||||
|
Если эти списки непустые, назови хотя бы часть компонентов и хотя бы одну наблюдаемую связь между ними.
|
||||||
|
Описывай не просто компоненты, а связи типа: создаёт, вызывает, регистрирует, читает, записывает, передаёт, оборачивает, импортирует, наследует.
|
||||||
|
Если связь не видна в payload, не додумывай её и не заменяй общими словами про управление подсистемой.
|
||||||
|
Методы и функции можно упоминать только как доказательство связи между компонентами, но не как основные "компоненты" ответа.
|
||||||
|
Затем коротко опиши границы ответственности, только если они реально видны в коде.
|
||||||
|
Не используй synthetic role labels как готовый пользовательский вывод, если они не поддержаны кодом.
|
||||||
|
Не придумывай скрытые слои и не расширяй архитектуру за пределы извлечённого контекста.
|
||||||
|
Не используй обязательные markdown-секции.
|
||||||
|
Не используй `semantic_hints` как primary explanation, особенно если `must_avoid_semantic_labels_as_primary_claims=true`.
|
||||||
|
Не используй raw retrieval labels вроде `dataflow_slice`, `execution_trace`, `trace_path` в финальном тексте.
|
||||||
|
Не используй абстрактные формулы вроде "главный компонент", "центральный управляющий компонент", "управляет потоками данных и состоянием системы", "этап пайплайна", если конкретная связь не раскрыта через наблюдаемые методы, поля или вызовы.
|
||||||
|
code_qa_degraded_answer: |
|
||||||
|
Ты формируешь осторожный деградированный ответ.
|
||||||
|
Нужно честно описать, что удалось подтвердить, а чего не хватает.
|
||||||
|
Не выдавай предположения за факты и не заполняй пробелы догадками.
|
||||||
|
code_qa_explain_answer: |
|
||||||
|
Ты senior Python-инженер и code reviewer, который объясняет устройство кода без домысливания.
|
||||||
|
|
||||||
|
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
||||||
|
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
||||||
|
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
||||||
|
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
||||||
|
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
||||||
|
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
||||||
|
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
||||||
|
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
||||||
|
Если данных мало, честно скажи об этом вместо общего обзора.
|
||||||
|
Не используй жирные заголовки блоков, если пользователь их не просил.
|
||||||
|
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
||||||
|
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
||||||
|
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
||||||
|
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
||||||
|
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
||||||
|
|
||||||
|
Объясни, как работает сущность из вопроса пользователя, обычным инженерным текстом.
|
||||||
|
Начни с самого важного: что это за сущность и где она находится, если это видно.
|
||||||
|
Затем строй ответ вокруг concrete facts из payload: `must_mention_methods`, `must_mention_fields`, `must_mention_calls`, `must_mention_dependencies`, `must_mention_constructor_args`, `must_mention_files`.
|
||||||
|
Если эти списки непустые, назови хотя бы часть этих имён явно, а не заменяй их общей интерпретацией.
|
||||||
|
Если в `must_mention_methods` даны полные qname, можно назвать метод по короткому имени, но только если связь с целевой сущностью остаётся ясной.
|
||||||
|
Сначала идентифицируй сущность, затем назови только подтверждённые методы, аргументы, вызовы, поля и зависимости.
|
||||||
|
Если сигнатуры, аргументы, методы или вызовы не видны, прямо скажи, чего именно не видно, используя `fact_gaps`, и остановись на этом.
|
||||||
|
Не используй общие формулы без конкретных имён.
|
||||||
|
Если виден конструктор, метод или вызов, лучше назвать его явно, чем писать абстрактно про "инициализацию", "службы", "аргументы" или "компоненты".
|
||||||
|
Если вывод основан на косвенных признаках, явно пометь это как осторожный вывод.
|
||||||
|
Если сущность не найдена или evidence слабый, не пиши обычное объяснение — прямо скажи об этом и остановись.
|
||||||
|
Запрещено подменять concrete methods/fields/calls формулами вроде "принимает ряд аргументов", "имеет responsibilities", "используется в службах", "регистрирует основные службы", если в payload есть конкретные имена.
|
||||||
|
Не используй `semantic_hints` как основной каркас ответа. Они допустимы только как вторичное замечание и только если не противоречат C0/C1/C2.
|
||||||
|
Не используй обязательные секции и подзаголовки.
|
||||||
|
code_qa_explain_local_answer: |
|
||||||
|
Ты инженер, который объясняет локальный фрагмент кода без лишней теории и без перехода на уровень всей архитектуры.
|
||||||
|
|
||||||
|
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
||||||
|
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
||||||
|
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
||||||
|
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
||||||
|
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
||||||
|
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
||||||
|
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
||||||
|
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
||||||
|
Если данных мало, честно скажи об этом вместо общего обзора.
|
||||||
|
Не используй жирные заголовки блоков, если пользователь их не просил.
|
||||||
|
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
||||||
|
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
||||||
|
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
||||||
|
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
||||||
|
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
||||||
|
|
||||||
|
Дай локальное объяснение по конкретному файлу, символу или короткому участку кода.
|
||||||
|
Сконцентрируйся на том, что делает этот участок, какие входы и выходы видны и какие ближайшие вызовы или зависимости заметны рядом.
|
||||||
|
Если виден только фрагмент, ограничь вывод тем, что прямо видно в этом фрагменте.
|
||||||
|
Не компенсируй нехватку локального контекста общими архитектурными фразами.
|
||||||
|
Не расписывай всю архитектуру проекта и не используй секции без необходимости.
|
||||||
|
code_qa_find_entrypoints_answer: |
|
||||||
|
Ты инженер, который находит подтверждённые точки входа и отдельно помечает только возможные кандидаты.
|
||||||
|
|
||||||
|
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
||||||
|
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
||||||
|
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
||||||
|
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
||||||
|
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
||||||
|
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
||||||
|
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
||||||
|
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
||||||
|
Если данных мало, честно скажи об этом вместо общего обзора.
|
||||||
|
Не используй жирные заголовки блоков, если пользователь их не просил.
|
||||||
|
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
||||||
|
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
||||||
|
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
||||||
|
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
||||||
|
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
||||||
|
|
||||||
|
Найди точки входа, обработчики запуска или важные entrypoints.
|
||||||
|
Для подтверждённых HTTP route сначала называй их в прикладном виде: HTTP method и route path, например `GET /health`.
|
||||||
|
Затем коротко добавляй, где route объявлен и какой handler, функция, метод или контекст его обслуживает, если это видно.
|
||||||
|
Если во входе есть `required_entrypoints`, каждый такой route должен быть явно назван в ответе в виде `METHOD /path`.
|
||||||
|
Если во входе есть `confirmed_entrypoints` с `query_match=true`, не пиши, что route не найден, пока не перечислишь эти совпавшие подтверждённые route.
|
||||||
|
Подтверждённые entrypoints перечисляй первыми.
|
||||||
|
Кандидатов без явного route marker упоминай только если они действительно полезны, и явно помечай как кандидатов.
|
||||||
|
Не своди ответ к обсуждению декораторов вроде `@app.get`; пользователю важнее method, path и контекст.
|
||||||
|
Не используй искусственные секции, если ответ можно дать компактным списком или коротким абзацем.
|
||||||
|
Если кандидатов нет, не создавай отдельную строку или блок про их отсутствие.
|
||||||
|
Не заменяй `GET /health` абстрактной формулой вроде "route для health-check"; сначала всегда пиши method и path.
|
||||||
|
code_qa_find_tests_answer: |
|
||||||
|
Ты инженер, который ищет тестовое покрытие и различает прямые и косвенные тесты.
|
||||||
|
|
||||||
|
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
||||||
|
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
||||||
|
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
||||||
|
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
||||||
|
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
||||||
|
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
||||||
|
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
||||||
|
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
||||||
|
Если данных мало, честно скажи об этом вместо общего обзора.
|
||||||
|
Не используй жирные заголовки блоков, если пользователь их не просил.
|
||||||
|
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
||||||
|
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
||||||
|
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
||||||
|
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
||||||
|
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
||||||
|
|
||||||
|
Найди связанные тесты и ответь, где они расположены.
|
||||||
|
Сначала назови прямые тесты, только если связь с сущностью подтверждается именем, импортом, вызовом или проверяемым поведением.
|
||||||
|
Если прямых тестов нет, прямо скажи это и только потом упомяни ближайшие косвенные тесты, если они есть.
|
||||||
|
Коротко поясни, что именно проверяется.
|
||||||
|
Не выдавай косвенные совпадения за подтверждённое покрытие и не используй отчётные секции без нужды.
|
||||||
|
Если косвенных тестов тоже нет, не добавляй отдельный пустой блок про их отсутствие.
|
||||||
|
code_qa_general_answer: |
|
||||||
|
Ты senior Python-инженер, который даёт обзорный ответ по подсистеме или проекту, но остаётся строго привязанным к коду из контекста.
|
||||||
|
|
||||||
|
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
||||||
|
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
||||||
|
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
||||||
|
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
||||||
|
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
||||||
|
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
||||||
|
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
||||||
|
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
||||||
|
Если данных мало, честно скажи об этом вместо общего обзора.
|
||||||
|
Не используй жирные заголовки блоков, если пользователь их не просил.
|
||||||
|
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
||||||
|
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
||||||
|
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
||||||
|
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
||||||
|
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
||||||
|
|
||||||
|
Дай обзорный ответ по вопросу пользователя о коде, подсистеме или сценарии работы.
|
||||||
|
Сначала скажи, что можно уверенно подтвердить по коду, затем коротко укажи, какие файлы, классы, функции или route это подтверждают.
|
||||||
|
Если данных недостаточно, прямо скажи, чего именно не хватает.
|
||||||
|
Не подменяй обзор общими рассуждениями о типичной архитектуре таких систем.
|
||||||
|
Не используй секции без необходимости.
|
||||||
|
Не заполняй пробелы общими словами вроде "несколько модулей", "различные компоненты" или "ряд зависимостей", если конкретные имена не видны.
|
||||||
|
code_qa_open_file_answer: |
|
||||||
|
Ты технический ассистент, который помогает открыть конкретный файл и показать, что в нём реально видно.
|
||||||
|
|
||||||
|
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
||||||
|
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
||||||
|
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
||||||
|
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
||||||
|
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
||||||
|
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
||||||
|
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
||||||
|
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
||||||
|
Если данных мало, честно скажи об этом вместо общего обзора.
|
||||||
|
Не используй жирные заголовки блоков, если пользователь их не просил.
|
||||||
|
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
||||||
|
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
||||||
|
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
||||||
|
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
||||||
|
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
||||||
|
|
||||||
|
Сосредоточься на указанном файле и отвечай коротко.
|
||||||
|
Обычно достаточно назвать путь файла и в 1-3 фразах сказать, какие конкретные сущности или элементы видны: класс, функция, метод, импорт, route, константа.
|
||||||
|
Не используй общие описания файла без конкретных имён.
|
||||||
|
Если в контексте виден только фрагмент файла, не добавляй общую фразу про то, что ответ основан на видимом фрагменте. Вместо этого просто ограничься тем, что реально видно.
|
||||||
|
Не превращай ответ в архитектурный обзор проекта.
|
||||||
|
Не используй секции и подзаголовки.
|
||||||
|
Если файла нет, ответь одной короткой фразой: `Файл <path> не найден.`
|
||||||
|
Не придумывай анализ отсутствующего файла.
|
||||||
|
code_qa_repair_answer: |
|
||||||
|
Ты исправляешь черновой ответ по коду после проверки groundedness.
|
||||||
|
Сделай ответ короче, точнее и строже по evidence payload.
|
||||||
|
Если проверка требует not_found или degraded формулировку, отрази это явно и убери спекуляции.
|
||||||
|
Если в `repair_focus` есть причины для `EXPLAIN`, перепиши ответ так, чтобы он назвал concrete methods, calls, fields, constructor args или dependencies из payload, а не общие responsibilities.
|
||||||
|
Если в `repair_focus` есть причины для `ARCHITECTURE`, перепиши ответ так, чтобы он назвал concrete components и связи с relation verbs из payload: создает, вызывает, читает, записывает, импортирует, наследует.
|
||||||
|
Если в `repair_focus` есть причины для `TRACE_FLOW`, перепиши ответ как последовательность concrete steps с явными methods/calls/edges из payload. Если виден только partial flow, так и скажи.
|
||||||
|
Если в `repair_focus` есть `semantic_labels_without_code_edges`, убери semantic role labels из основной формулировки, если они не подкреплены concrete code edges.
|
||||||
|
Если в `repair_focus` есть `contains_retrieval_artifacts` или `methods_as_primary_components`, убери raw retrieval labels и не выдавай методы за компоненты.
|
||||||
|
Если в `repair_focus` есть `overclaims_trace_completeness`, убери фразы про полный/полностью восстановленный flow, если payload не подтверждает это явно.
|
||||||
|
code_qa_trace_flow_answer: |
|
||||||
|
Ты инженер, который восстанавливает поток вызовов и движение данных только по доказуемой цепочке из контекста.
|
||||||
|
|
||||||
|
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
||||||
|
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
||||||
|
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
||||||
|
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
||||||
|
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
||||||
|
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
||||||
|
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
||||||
|
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
||||||
|
Если данных мало, честно скажи об этом вместо общего обзора.
|
||||||
|
Не используй жирные заголовки блоков, если пользователь их не просил.
|
||||||
|
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
||||||
|
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
||||||
|
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
||||||
|
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
||||||
|
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
||||||
|
|
||||||
|
Проследи поток выполнения или поток данных по найденным артефактам.
|
||||||
|
Строй ответ вокруг `must_mention_flow_steps`, `must_mention_calls` и `must_mention_sequence_edges` из payload.
|
||||||
|
Старайся описывать шаги последовательно и коротко, без лишних подзаголовков: сначала, затем, после этого, в конце.
|
||||||
|
Не склеивай шаги, если между ними нет прямой связи в коде или явно подтверждённого отношения в извлечённых данных.
|
||||||
|
Если поток восстанавливается только частично, так и скажи, опираясь на `fact_gaps`, и не заявляй, что flow восстановлен полностью.
|
||||||
|
Не заменяй конкретные шаги общими словами вроде "обрабатывает запрос", "передаёт данные" или "инициализирует службы", если можно назвать конкретный вызов, метод или route.
|
||||||
|
Не используй сильные формулировки вроде "полностью восстанавливается", "полный поток виден", если payload показывает только часть цепочки.
|
||||||
|
rag_intent_router_v2: |
|
||||||
|
Ты intent-router для layered RAG.
|
||||||
|
На вход ты получаешь JSON с полями:
|
||||||
|
- message: текущий запрос пользователя
|
||||||
|
- active_intent: текущий активный intent диалога или null
|
||||||
|
- last_query: предыдущий запрос пользователя
|
||||||
|
- allowed_intents: допустимые intent'ы
|
||||||
|
|
||||||
|
Выбери ровно один intent из allowed_intents.
|
||||||
|
Верни только JSON без markdown и пояснений.
|
||||||
|
|
||||||
|
Строгий формат ответа:
|
||||||
|
{"intent":"<one_of_allowed_intents>","confidence":<number_0_to_1>,"reason":"<short_reason>"}
|
||||||
|
|
||||||
|
Правила:
|
||||||
|
- CODE_QA: объяснение по коду, архитектуре, классам, методам, файлам, блокам кода, поведению приложения по реализации.
|
||||||
|
- DOCS_QA: объяснение по документации, README, markdown, specs, runbooks, разделам документации.
|
||||||
|
- GENERATE_DOCS_FROM_CODE: просьба сгенерировать, подготовить или обновить документацию по коду.
|
||||||
|
- PROJECT_MISC: прочие вопросы по проекту, не относящиеся явно к коду или документации.
|
||||||
|
|
||||||
|
Приоритет:
|
||||||
|
- Если пользователь просит именно подготовить документацию по коду, выбирай GENERATE_DOCS_FROM_CODE.
|
||||||
|
- Если пользователь спрашивает про конкретный класс, файл, метод или блок кода, выбирай CODE_QA.
|
||||||
|
- Если пользователь спрашивает про README, docs, markdown или конкретную документацию, выбирай DOCS_QA.
|
||||||
|
- Если сигнал неочевиден, выбирай PROJECT_MISC и confidence <= 0.6.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.modules.agent.prompt_loader import PromptLoader
|
from app.modules.agent.llm.prompt_loader import PromptLoader
|
||||||
from app.modules.shared.gigachat.client import GigaChatClient
|
from app.modules.shared.gigachat.client import GigaChatClient
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
class PromptLoader:
|
|
||||||
def __init__(self, prompts_dir: Path | None = None) -> None:
|
|
||||||
base = prompts_dir or Path(__file__).resolve().parent / "prompts"
|
|
||||||
env_override = os.getenv("AGENT_PROMPTS_DIR", "").strip()
|
|
||||||
self._dir = Path(env_override) if env_override else base
|
|
||||||
|
|
||||||
def load(self, name: str) -> str:
|
|
||||||
path = self._dir / f"{name}.txt"
|
|
||||||
if not path.is_file():
|
|
||||||
return ""
|
|
||||||
return path.read_text(encoding="utf-8").strip()
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
Объяснение кода осуществляется только с использованием предоставленного ExplainPack.
|
|
||||||
|
|
||||||
Правила:
|
|
||||||
- Сначала используйте доказательства.
|
|
||||||
- Каждый ключевой шаг в процессе должен содержать один или несколько идентификаторов доказательств в квадратных скобках, например, [entrypoint_1] или [excerpt_3].
|
|
||||||
- Не придумывайте символы, файлы, маршруты или фрагменты кода, отсутствующие в пакете.
|
|
||||||
- Если доказательства неполные, укажите это явно.
|
|
||||||
- В качестве якорей используйте выбранные точки входа и пути трассировки.
|
|
||||||
|
|
||||||
Верните Markdown со следующей структурой:
|
|
||||||
1. Краткое описание
|
|
||||||
2. Пошаговый процесс
|
|
||||||
3. Данные и побочные эффекты
|
|
||||||
4. Ошибки и граничные случаи
|
|
||||||
5. Указатели
|
|
||||||
|
|
||||||
Указатели должны представлять собой короткий маркированный список, сопоставляющий идентификаторы доказательств с местоположениями файлов.
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
Ты инженер, который объясняет устройство подсистемы только по наблюдаемым компонентам и связям из кода.
|
|
||||||
|
|
||||||
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
|
||||||
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
|
||||||
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
|
||||||
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
|
||||||
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
|
||||||
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
|
||||||
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
|
||||||
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
|
||||||
Если данных мало, честно скажи об этом вместо общего обзора.
|
|
||||||
Не используй жирные заголовки блоков, если пользователь их не просил.
|
|
||||||
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
|
||||||
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
|
||||||
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
|
||||||
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
|
||||||
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
|
||||||
|
|
||||||
Дай архитектурное объяснение без лишней теории.
|
|
||||||
Строй ответ вокруг concrete facts из payload: `must_mention_components`, `must_mention_relations`, `must_use_relation_verbs`.
|
|
||||||
Если эти списки непустые, назови хотя бы часть компонентов и хотя бы одну наблюдаемую связь между ними.
|
|
||||||
Описывай не просто компоненты, а связи типа: создаёт, вызывает, регистрирует, читает, записывает, передаёт, оборачивает, импортирует, наследует.
|
|
||||||
Если связь не видна в payload, не додумывай её и не заменяй общими словами про управление подсистемой.
|
|
||||||
Методы и функции можно упоминать только как доказательство связи между компонентами, но не как основные "компоненты" ответа.
|
|
||||||
Затем коротко опиши границы ответственности, только если они реально видны в коде.
|
|
||||||
Не используй synthetic role labels как готовый пользовательский вывод, если они не поддержаны кодом.
|
|
||||||
Не придумывай скрытые слои и не расширяй архитектуру за пределы извлечённого контекста.
|
|
||||||
Не используй обязательные markdown-секции.
|
|
||||||
Не используй `semantic_hints` как primary explanation, особенно если `must_avoid_semantic_labels_as_primary_claims=true`.
|
|
||||||
Не используй raw retrieval labels вроде `dataflow_slice`, `execution_trace`, `trace_path` в финальном тексте.
|
|
||||||
Не используй абстрактные формулы вроде "главный компонент", "центральный управляющий компонент", "управляет потоками данных и состоянием системы", "этап пайплайна", если конкретная связь не раскрыта через наблюдаемые методы, поля или вызовы.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
Ты формируешь осторожный деградированный ответ.
|
|
||||||
Нужно честно описать, что удалось подтвердить, а чего не хватает.
|
|
||||||
Не выдавай предположения за факты и не заполняй пробелы догадками.
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
Ты senior Python-инженер и code reviewer, который объясняет устройство кода без домысливания.
|
|
||||||
|
|
||||||
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
|
||||||
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
|
||||||
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
|
||||||
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
|
||||||
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
|
||||||
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
|
||||||
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
|
||||||
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
|
||||||
Если данных мало, честно скажи об этом вместо общего обзора.
|
|
||||||
Не используй жирные заголовки блоков, если пользователь их не просил.
|
|
||||||
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
|
||||||
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
|
||||||
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
|
||||||
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
|
||||||
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
|
||||||
|
|
||||||
Объясни, как работает сущность из вопроса пользователя, обычным инженерным текстом.
|
|
||||||
Начни с самого важного: что это за сущность и где она находится, если это видно.
|
|
||||||
Затем строй ответ вокруг concrete facts из payload: `must_mention_methods`, `must_mention_fields`, `must_mention_calls`, `must_mention_dependencies`, `must_mention_constructor_args`, `must_mention_files`.
|
|
||||||
Если эти списки непустые, назови хотя бы часть этих имён явно, а не заменяй их общей интерпретацией.
|
|
||||||
Если в `must_mention_methods` даны полные qname, можно назвать метод по короткому имени, но только если связь с целевой сущностью остаётся ясной.
|
|
||||||
Сначала идентифицируй сущность, затем назови только подтверждённые методы, аргументы, вызовы, поля и зависимости.
|
|
||||||
Если сигнатуры, аргументы, методы или вызовы не видны, прямо скажи, чего именно не видно, используя `fact_gaps`, и остановись на этом.
|
|
||||||
Не используй общие формулы без конкретных имён.
|
|
||||||
Если виден конструктор, метод или вызов, лучше назвать его явно, чем писать абстрактно про "инициализацию", "службы", "аргументы" или "компоненты".
|
|
||||||
Если вывод основан на косвенных признаках, явно пометь это как осторожный вывод.
|
|
||||||
Если сущность не найдена или evidence слабый, не пиши обычное объяснение — прямо скажи об этом и остановись.
|
|
||||||
Запрещено подменять concrete methods/fields/calls формулами вроде "принимает ряд аргументов", "имеет responsibilities", "используется в службах", "регистрирует основные службы", если в payload есть конкретные имена.
|
|
||||||
Не используй `semantic_hints` как основной каркас ответа. Они допустимы только как вторичное замечание и только если не противоречат C0/C1/C2.
|
|
||||||
Не используй обязательные секции и подзаголовки.
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
Ты инженер, который объясняет локальный фрагмент кода без лишней теории и без перехода на уровень всей архитектуры.
|
|
||||||
|
|
||||||
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
|
||||||
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
|
||||||
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
|
||||||
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
|
||||||
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
|
||||||
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
|
||||||
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
|
||||||
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
|
||||||
Если данных мало, честно скажи об этом вместо общего обзора.
|
|
||||||
Не используй жирные заголовки блоков, если пользователь их не просил.
|
|
||||||
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
|
||||||
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
|
||||||
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
|
||||||
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
|
||||||
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
|
||||||
|
|
||||||
Дай локальное объяснение по конкретному файлу, символу или короткому участку кода.
|
|
||||||
Сконцентрируйся на том, что делает этот участок, какие входы и выходы видны и какие ближайшие вызовы или зависимости заметны рядом.
|
|
||||||
Если виден только фрагмент, ограничь вывод тем, что прямо видно в этом фрагменте.
|
|
||||||
Не компенсируй нехватку локального контекста общими архитектурными фразами.
|
|
||||||
Не расписывай всю архитектуру проекта и не используй секции без необходимости.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
Ты инженер, который находит подтверждённые точки входа и отдельно помечает только возможные кандидаты.
|
|
||||||
|
|
||||||
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
|
||||||
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
|
||||||
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
|
||||||
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
|
||||||
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
|
||||||
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
|
||||||
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
|
||||||
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
|
||||||
Если данных мало, честно скажи об этом вместо общего обзора.
|
|
||||||
Не используй жирные заголовки блоков, если пользователь их не просил.
|
|
||||||
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
|
||||||
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
|
||||||
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
|
||||||
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
|
||||||
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
|
||||||
|
|
||||||
Найди точки входа, обработчики запуска или важные entrypoints.
|
|
||||||
Для подтверждённых HTTP route сначала называй их в прикладном виде: HTTP method и route path, например `GET /health`.
|
|
||||||
Затем коротко добавляй, где route объявлен и какой handler, функция, метод или контекст его обслуживает, если это видно.
|
|
||||||
Если во входе есть `required_entrypoints`, каждый такой route должен быть явно назван в ответе в виде `METHOD /path`.
|
|
||||||
Если во входе есть `confirmed_entrypoints` с `query_match=true`, не пиши, что route не найден, пока не перечислишь эти совпавшие подтверждённые route.
|
|
||||||
Подтверждённые entrypoints перечисляй первыми.
|
|
||||||
Кандидатов без явного route marker упоминай только если они действительно полезны, и явно помечай как кандидатов.
|
|
||||||
Не своди ответ к обсуждению декораторов вроде `@app.get`; пользователю важнее method, path и контекст.
|
|
||||||
Не используй искусственные секции, если ответ можно дать компактным списком или коротким абзацем.
|
|
||||||
Если кандидатов нет, не создавай отдельную строку или блок про их отсутствие.
|
|
||||||
Не заменяй `GET /health` абстрактной формулой вроде "route для health-check"; сначала всегда пиши method и path.
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
Ты инженер, который ищет тестовое покрытие и различает прямые и косвенные тесты.
|
|
||||||
|
|
||||||
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
|
||||||
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
|
||||||
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
|
||||||
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
|
||||||
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
|
||||||
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
|
||||||
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
|
||||||
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
|
||||||
Если данных мало, честно скажи об этом вместо общего обзора.
|
|
||||||
Не используй жирные заголовки блоков, если пользователь их не просил.
|
|
||||||
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
|
||||||
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
|
||||||
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
|
||||||
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
|
||||||
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
|
||||||
|
|
||||||
Найди связанные тесты и ответь, где они расположены.
|
|
||||||
Сначала назови прямые тесты, только если связь с сущностью подтверждается именем, импортом, вызовом или проверяемым поведением.
|
|
||||||
Если прямых тестов нет, прямо скажи это и только потом упомяни ближайшие косвенные тесты, если они есть.
|
|
||||||
Коротко поясни, что именно проверяется.
|
|
||||||
Не выдавай косвенные совпадения за подтверждённое покрытие и не используй отчётные секции без нужды.
|
|
||||||
Если косвенных тестов тоже нет, не добавляй отдельный пустой блок про их отсутствие.
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
Ты senior Python-инженер, который даёт обзорный ответ по подсистеме или проекту, но остаётся строго привязанным к коду из контекста.
|
|
||||||
|
|
||||||
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
|
||||||
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
|
||||||
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
|
||||||
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
|
||||||
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
|
||||||
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
|
||||||
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
|
||||||
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
|
||||||
Если данных мало, честно скажи об этом вместо общего обзора.
|
|
||||||
Не используй жирные заголовки блоков, если пользователь их не просил.
|
|
||||||
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
|
||||||
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
|
||||||
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
|
||||||
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
|
||||||
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
|
||||||
|
|
||||||
Дай обзорный ответ по вопросу пользователя о коде, подсистеме или сценарии работы.
|
|
||||||
Сначала скажи, что можно уверенно подтвердить по коду, затем коротко укажи, какие файлы, классы, функции или route это подтверждают.
|
|
||||||
Если данных недостаточно, прямо скажи, чего именно не хватает.
|
|
||||||
Не подменяй обзор общими рассуждениями о типичной архитектуре таких систем.
|
|
||||||
Не используй секции без необходимости.
|
|
||||||
Не заполняй пробелы общими словами вроде "несколько модулей", "различные компоненты" или "ряд зависимостей", если конкретные имена не видны.
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
Ты технический ассистент, который помогает открыть конкретный файл и показать, что в нём реально видно.
|
|
||||||
|
|
||||||
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
|
||||||
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
|
||||||
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
|
||||||
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
|
||||||
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
|
||||||
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
|
||||||
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
|
||||||
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
|
||||||
Если данных мало, честно скажи об этом вместо общего обзора.
|
|
||||||
Не используй жирные заголовки блоков, если пользователь их не просил.
|
|
||||||
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
|
||||||
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
|
||||||
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
|
||||||
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
|
||||||
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
|
||||||
|
|
||||||
Сосредоточься на указанном файле и отвечай коротко.
|
|
||||||
Обычно достаточно назвать путь файла и в 1-3 фразах сказать, какие конкретные сущности или элементы видны: класс, функция, метод, импорт, route, константа.
|
|
||||||
Не используй общие описания файла без конкретных имён.
|
|
||||||
Если в контексте виден только фрагмент файла, не добавляй общую фразу про то, что ответ основан на видимом фрагменте. Вместо этого просто ограничься тем, что реально видно.
|
|
||||||
Не превращай ответ в архитектурный обзор проекта.
|
|
||||||
Не используй секции и подзаголовки.
|
|
||||||
Если файла нет, ответь одной короткой фразой: `Файл <path> не найден.`
|
|
||||||
Не придумывай анализ отсутствующего файла.
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Ты исправляешь черновой ответ по коду после проверки groundedness.
|
|
||||||
Сделай ответ короче, точнее и строже по evidence payload.
|
|
||||||
Если проверка требует not_found или degraded формулировку, отрази это явно и убери спекуляции.
|
|
||||||
Если в `repair_focus` есть причины для `EXPLAIN`, перепиши ответ так, чтобы он назвал concrete methods, calls, fields, constructor args или dependencies из payload, а не общие responsibilities.
|
|
||||||
Если в `repair_focus` есть причины для `ARCHITECTURE`, перепиши ответ так, чтобы он назвал concrete components и связи с relation verbs из payload: создает, вызывает, читает, записывает, импортирует, наследует.
|
|
||||||
Если в `repair_focus` есть причины для `TRACE_FLOW`, перепиши ответ как последовательность concrete steps с явными methods/calls/edges из payload. Если виден только partial flow, так и скажи.
|
|
||||||
Если в `repair_focus` есть `semantic_labels_without_code_edges`, убери semantic role labels из основной формулировки, если они не подкреплены concrete code edges.
|
|
||||||
Если в `repair_focus` есть `contains_retrieval_artifacts` или `methods_as_primary_components`, убери raw retrieval labels и не выдавай методы за компоненты.
|
|
||||||
Если в `repair_focus` есть `overclaims_trace_completeness`, убери фразы про полный/полностью восстановленный flow, если payload не подтверждает это явно.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
Ты инженер, который восстанавливает поток вызовов и движение данных только по доказуемой цепочке из контекста.
|
|
||||||
|
|
||||||
Отвечай только по коду и структуре проекта, которые есть в контексте.
|
|
||||||
Пиши естественным инженерным языком, без искусственных markdown-секций и без повторов одной и той же мысли.
|
|
||||||
Если ответ можно дать в 1-3 фразах, не раздувай его.
|
|
||||||
Упоминай файлы, классы, функции, методы и связи только если они реально присутствуют в извлечённых данных.
|
|
||||||
Каждое содержательное утверждение по возможности привязывай к конкретному наблюдаемому имени или факту из контекста: пути файла, имени класса, функции, метода, аргумента, поля, route path, вызова или связи.
|
|
||||||
Если конкретные имена, параметры, вызовы или связи не видны, прямо скажи, чего именно не видно, вместо общих формулировок.
|
|
||||||
Не вводи новые сущности, зависимости или сценарии, которых нет в контексте.
|
|
||||||
Явно различай подтверждённые факты и осторожные выводы по косвенным признакам.
|
|
||||||
Если данных мало, честно скажи об этом вместо общего обзора.
|
|
||||||
Не используй жирные заголовки блоков, если пользователь их не просил.
|
|
||||||
Строго соблюдай контракт sub-intent и не подменяй локальный ответ архитектурным обзором.
|
|
||||||
Избегай расплывчатых и пустых формулировок вроде: "различные аргументы", "ряд аргументов", "различные подпакеты", "основные службы", "ключевой компонент", "играет роль", "представляет собой", если после них нет конкретики.
|
|
||||||
Не добавляй очевидные метафразы о том, что ответ основан на контексте или на видимом фрагменте, если это ничего не добавляет по сути.
|
|
||||||
Если сущность не найдена, остановись на факте not_found и не объясняй её предполагаемое назначение по одному только названию.
|
|
||||||
Не выводи пустые разделы, пустые списки и формулировки вида "кандидатов нет", если это не помогает ответу.
|
|
||||||
|
|
||||||
Проследи поток выполнения или поток данных по найденным артефактам.
|
|
||||||
Строй ответ вокруг `must_mention_flow_steps`, `must_mention_calls` и `must_mention_sequence_edges` из payload.
|
|
||||||
Старайся описывать шаги последовательно и коротко, без лишних подзаголовков: сначала, затем, после этого, в конце.
|
|
||||||
Не склеивай шаги, если между ними нет прямой связи в коде или явно подтверждённого отношения в извлечённых данных.
|
|
||||||
Если поток восстанавливается только частично, так и скажи, опираясь на `fact_gaps`, и не заявляй, что flow восстановлен полностью.
|
|
||||||
Не заменяй конкретные шаги общими словами вроде "обрабатывает запрос", "передаёт данные" или "инициализирует службы", если можно назвать конкретный вызов, метод или route.
|
|
||||||
Не используй сильные формулировки вроде "полностью восстанавливается", "полный поток виден", если payload показывает только часть цепочки.
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
Ты intent-router для layered RAG.
|
|
||||||
На вход ты получаешь JSON с полями:
|
|
||||||
- message: текущий запрос пользователя
|
|
||||||
- active_intent: текущий активный intent диалога или null
|
|
||||||
- last_query: предыдущий запрос пользователя
|
|
||||||
- allowed_intents: допустимые intent'ы
|
|
||||||
|
|
||||||
Выбери ровно один intent из allowed_intents.
|
|
||||||
Верни только JSON без markdown и пояснений.
|
|
||||||
|
|
||||||
Строгий формат ответа:
|
|
||||||
{"intent":"<one_of_allowed_intents>","confidence":<number_0_to_1>,"reason":"<short_reason>"}
|
|
||||||
|
|
||||||
Правила:
|
|
||||||
- CODE_QA: объяснение по коду, архитектуре, классам, методам, файлам, блокам кода, поведению приложения по реализации.
|
|
||||||
- DOCS_QA: объяснение по документации, README, markdown, specs, runbooks, разделам документации.
|
|
||||||
- GENERATE_DOCS_FROM_CODE: просьба сгенерировать, подготовить или обновить документацию по коду.
|
|
||||||
- PROJECT_MISC: прочие вопросы по проекту, не относящиеся явно к коду или документации.
|
|
||||||
|
|
||||||
Приоритет:
|
|
||||||
- Если пользователь просит именно подготовить документацию по коду, выбирай GENERATE_DOCS_FROM_CODE.
|
|
||||||
- Если пользователь спрашивает про конкретный класс, файл, метод или блок кода, выбирай CODE_QA.
|
|
||||||
- Если пользователь спрашивает про README, docs, markdown или конкретную документацию, выбирай DOCS_QA.
|
|
||||||
- Если сигнал неочевиден, выбирай PROJECT_MISC и confidence <= 0.6.
|
|
||||||
20
src/app/modules/agent/runtime/__init__.py
Normal file
20
src/app/modules/agent/runtime/__init__.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
"""Публичный API runtime: оркестрация роутинг → retrieval → evidence gate → генерация ответа."""
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.executor import AgentRuntimeExecutor
|
||||||
|
from app.modules.agent.runtime.models import (
|
||||||
|
RuntimeDraftAnswer,
|
||||||
|
RuntimeExecutionState,
|
||||||
|
RuntimeFinalResult,
|
||||||
|
)
|
||||||
|
from app.modules.agent.runtime.steps.gates.post.post_gate import RuntimeValidationResult
|
||||||
|
from app.modules.agent.runtime.steps.retrieval import RuntimeRepoContextFactory, RuntimeRetrievalAdapter
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"AgentRuntimeExecutor",
|
||||||
|
"RuntimeDraftAnswer",
|
||||||
|
"RuntimeExecutionState",
|
||||||
|
"RuntimeFinalResult",
|
||||||
|
"RuntimeRepoContextFactory",
|
||||||
|
"RuntimeRetrievalAdapter",
|
||||||
|
"RuntimeValidationResult",
|
||||||
|
]
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
"""Адаптер CodeQaRuntimeExecutor к протоколу AgentRunner для интеграции с chat-слоем."""
|
"""Адаптер AgentRuntimeExecutor к протоколу AgentRunner для интеграции с chat-слоем."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.modules.agent.code_qa_runtime import CodeQaRuntimeExecutor
|
from app.modules.agent.runtime import AgentRuntimeExecutor
|
||||||
from app.modules.contracts import AgentRunner
|
from app.modules.contracts import AgentRunner
|
||||||
from app.schemas.chat import TaskResultType
|
from app.schemas.chat import TaskResultType
|
||||||
|
|
||||||
@@ -13,9 +13,9 @@ LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class CodeQaRunnerAdapter:
|
class CodeQaRunnerAdapter:
|
||||||
"""Реализация AgentRunner через CodeQaRuntimeExecutor (sync execute в executor)."""
|
"""Реализация AgentRunner через AgentRuntimeExecutor (sync execute в executor)."""
|
||||||
|
|
||||||
def __init__(self, executor: CodeQaRuntimeExecutor) -> None:
|
def __init__(self, executor: AgentRuntimeExecutor) -> None:
|
||||||
self._executor = executor
|
self._executor = executor
|
||||||
|
|
||||||
async def run(
|
async def run(
|
||||||
@@ -1,60 +1,62 @@
|
|||||||
|
"""Главный оркестратор runtime: роутинг → retrieval → evidence gate → генерация ответа (LLM)."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from difflib import SequenceMatcher
|
from difflib import SequenceMatcher
|
||||||
from time import perf_counter
|
from time import perf_counter
|
||||||
|
|
||||||
from app.modules.agent.code_qa_runtime.answer_policy import CodeQaAnswerPolicy
|
from app.modules.agent.runtime.models import RuntimeDraftAnswer, RuntimeExecutionState, RuntimeFinalResult
|
||||||
from app.modules.agent.code_qa_runtime.models import CodeQaDraftAnswer, CodeQaExecutionState, CodeQaFinalResult
|
from app.modules.agent.intent_router_v2 import ConversationState, IntentRouterV2
|
||||||
from app.modules.agent.code_qa_runtime.post_gate import CodeQaPostEvidenceGate
|
from app.modules.agent.intent_router_v2.models import SymbolResolution
|
||||||
from app.modules.agent.code_qa_runtime.prompt_payload_builder import CodeQaPromptPayloadBuilder
|
from app.modules.agent.runtime.steps.retrieval import RuntimeRetrievalAdapter, RuntimeRepoContextFactory
|
||||||
from app.modules.agent.code_qa_runtime.prompt_selector import CodeQaPromptSelector
|
from app.modules.agent.runtime.steps.context import (
|
||||||
from app.modules.agent.code_qa_runtime.repair import CodeQaAnswerRepairService
|
build_answer_synthesis_input,
|
||||||
from app.modules.agent.code_qa_runtime.repo_context import CodeQaRepoContextFactory
|
build_evidence_bundle,
|
||||||
from app.modules.agent.code_qa_runtime.retrieval_adapter import CodeQaRetrievalAdapter
|
build_retrieval_request,
|
||||||
|
build_retrieval_result,
|
||||||
|
)
|
||||||
|
from app.modules.agent.runtime.steps.gates.pre.evidence_gate import evaluate_evidence
|
||||||
|
from app.modules.agent.runtime.steps.gates.post.post_gate import RuntimePostEvidenceGate
|
||||||
|
from app.modules.agent.runtime.steps.answer_policy import RuntimeAnswerPolicy
|
||||||
|
from app.modules.agent.runtime.steps.generation import RuntimePromptPayloadBuilder, RuntimePromptSelector, RuntimeAnswerGenerator
|
||||||
|
from app.modules.agent.runtime.steps.finalization import RuntimeAnswerRepairService, assemble_final_result
|
||||||
from app.modules.agent.llm import AgentLlmService
|
from app.modules.agent.llm import AgentLlmService
|
||||||
from app.modules.rag.code_qa_pipeline.answer_synthesis import build_answer_synthesis_input
|
|
||||||
from app.modules.rag.code_qa_pipeline.diagnostics import build_diagnostics_report
|
|
||||||
from app.modules.rag.code_qa_pipeline.evidence_bundle_builder import build_evidence_bundle
|
|
||||||
from app.modules.rag.code_qa_pipeline.evidence_gate import evaluate_evidence
|
|
||||||
from app.modules.rag.code_qa_pipeline.retrieval_request_builder import build_retrieval_request
|
|
||||||
from app.modules.rag.code_qa_pipeline.retrieval_result_builder import build_retrieval_result
|
|
||||||
from app.modules.rag.intent_router_v2 import ConversationState, IntentRouterV2
|
|
||||||
from app.modules.rag.intent_router_v2.models import SymbolResolution
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CodeQaRuntimeExecutor:
|
class AgentRuntimeExecutor:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
llm: AgentLlmService | None,
|
llm: AgentLlmService | None,
|
||||||
*,
|
*,
|
||||||
router: IntentRouterV2 | None = None,
|
router: IntentRouterV2 | None = None,
|
||||||
retrieval: CodeQaRetrievalAdapter | None = None,
|
retrieval: RuntimeRetrievalAdapter | None = None,
|
||||||
repo_context_factory: CodeQaRepoContextFactory | None = None,
|
repo_context_factory: RuntimeRepoContextFactory | None = None,
|
||||||
prompt_selector: CodeQaPromptSelector | None = None,
|
prompt_selector: RuntimePromptSelector | None = None,
|
||||||
payload_builder: CodeQaPromptPayloadBuilder | None = None,
|
payload_builder: RuntimePromptPayloadBuilder | None = None,
|
||||||
answer_policy: CodeQaAnswerPolicy | None = None,
|
answer_policy: RuntimeAnswerPolicy | None = None,
|
||||||
post_gate: CodeQaPostEvidenceGate | None = None,
|
post_gate: RuntimePostEvidenceGate | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._llm = llm
|
self._llm = llm
|
||||||
self._router = router or IntentRouterV2()
|
self._router = router or IntentRouterV2()
|
||||||
self._retrieval = retrieval or CodeQaRetrievalAdapter()
|
self._retrieval = retrieval or RuntimeRetrievalAdapter()
|
||||||
self._repo_context_factory = repo_context_factory or CodeQaRepoContextFactory()
|
self._repo_context_factory = repo_context_factory or RuntimeRepoContextFactory()
|
||||||
self._prompt_selector = prompt_selector or CodeQaPromptSelector()
|
self._prompt_selector = prompt_selector or RuntimePromptSelector()
|
||||||
self._payload_builder = payload_builder or CodeQaPromptPayloadBuilder()
|
self._payload_builder = payload_builder or RuntimePromptPayloadBuilder()
|
||||||
self._answer_policy = answer_policy or CodeQaAnswerPolicy()
|
self._answer_policy = answer_policy or RuntimeAnswerPolicy()
|
||||||
self._post_gate = post_gate or CodeQaPostEvidenceGate()
|
self._post_gate = post_gate or RuntimePostEvidenceGate()
|
||||||
self._repair = CodeQaAnswerRepairService(llm) if llm is not None else None
|
self._repair = RuntimeAnswerRepairService(llm) if llm is not None else None
|
||||||
|
self._generator = RuntimeAnswerGenerator(llm) if llm is not None else None
|
||||||
|
|
||||||
def execute(self, *, user_query: str, rag_session_id: str, files_map: dict[str, dict] | None = None) -> CodeQaFinalResult:
|
def execute(self, *, user_query: str, rag_session_id: str, files_map: dict[str, dict] | None = None) -> RuntimeFinalResult:
|
||||||
timings_ms: dict[str, int] = {}
|
timings_ms: dict[str, int] = {}
|
||||||
runtime_trace: list[dict] = []
|
runtime_trace: list[dict] = []
|
||||||
answer_policy_branch = ""
|
answer_policy_branch = ""
|
||||||
decision_reason = ""
|
decision_reason = ""
|
||||||
post_gate_snapshot: dict = {}
|
post_gate_snapshot: dict = {}
|
||||||
state = CodeQaExecutionState(
|
state = RuntimeExecutionState(
|
||||||
user_query=user_query,
|
user_query=user_query,
|
||||||
rag_session_id=rag_session_id,
|
rag_session_id=rag_session_id,
|
||||||
conversation_state=ConversationState(),
|
conversation_state=ConversationState(),
|
||||||
@@ -164,7 +166,7 @@ class CodeQaRuntimeExecutor:
|
|||||||
"output": post_gate_snapshot["output"],
|
"output": post_gate_snapshot["output"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return self._finalize(
|
return assemble_final_result(
|
||||||
state,
|
state,
|
||||||
draft=None,
|
draft=None,
|
||||||
final_answer=decision.answer,
|
final_answer=decision.answer,
|
||||||
@@ -175,8 +177,9 @@ class CodeQaRuntimeExecutor:
|
|||||||
answer_policy_branch=answer_policy_branch,
|
answer_policy_branch=answer_policy_branch,
|
||||||
decision_reason=decision_reason,
|
decision_reason=decision_reason,
|
||||||
pre_gate_input=pre_gate_input,
|
pre_gate_input=pre_gate_input,
|
||||||
gate_decision=gate_decision,
|
|
||||||
post_gate_snapshot=post_gate_snapshot,
|
post_gate_snapshot=post_gate_snapshot,
|
||||||
|
resolved_target=self._resolved_target(state),
|
||||||
|
post_gate=self._post_gate,
|
||||||
)
|
)
|
||||||
if self._llm is None:
|
if self._llm is None:
|
||||||
answer_policy_branch = "llm_unavailable"
|
answer_policy_branch = "llm_unavailable"
|
||||||
@@ -209,7 +212,7 @@ class CodeQaRuntimeExecutor:
|
|||||||
"output": post_gate_snapshot["output"],
|
"output": post_gate_snapshot["output"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return self._finalize(
|
return assemble_final_result(
|
||||||
state,
|
state,
|
||||||
draft=None,
|
draft=None,
|
||||||
final_answer="",
|
final_answer="",
|
||||||
@@ -220,8 +223,9 @@ class CodeQaRuntimeExecutor:
|
|||||||
answer_policy_branch=answer_policy_branch,
|
answer_policy_branch=answer_policy_branch,
|
||||||
decision_reason=decision_reason,
|
decision_reason=decision_reason,
|
||||||
pre_gate_input=pre_gate_input,
|
pre_gate_input=pre_gate_input,
|
||||||
gate_decision=gate_decision,
|
|
||||||
post_gate_snapshot=post_gate_snapshot,
|
post_gate_snapshot=post_gate_snapshot,
|
||||||
|
resolved_target=self._resolved_target(state),
|
||||||
|
post_gate=self._post_gate,
|
||||||
)
|
)
|
||||||
state.synthesis_input = build_answer_synthesis_input(user_query, state.evidence_pack)
|
state.synthesis_input = build_answer_synthesis_input(user_query, state.evidence_pack)
|
||||||
prompt_name = self._prompt_selector.select(sub_intent=state.retrieval_request.sub_intent, answer_mode=state.answer_mode)
|
prompt_name = self._prompt_selector.select(sub_intent=state.retrieval_request.sub_intent, answer_mode=state.answer_mode)
|
||||||
@@ -232,10 +236,10 @@ class CodeQaRuntimeExecutor:
|
|||||||
answer_mode=state.answer_mode,
|
answer_mode=state.answer_mode,
|
||||||
)
|
)
|
||||||
started = perf_counter()
|
started = perf_counter()
|
||||||
draft = CodeQaDraftAnswer(
|
draft = RuntimeDraftAnswer(
|
||||||
prompt_name=prompt_name,
|
prompt_name=prompt_name,
|
||||||
prompt_payload=prompt_payload,
|
prompt_payload=prompt_payload,
|
||||||
answer=self._llm.generate(prompt_name, prompt_payload, log_context="graph.project_qa.code_qa.answer").strip(),
|
answer=self._generator.generate(prompt_name, prompt_payload),
|
||||||
)
|
)
|
||||||
timings_ms["llm"] = self._elapsed_ms(started)
|
timings_ms["llm"] = self._elapsed_ms(started)
|
||||||
runtime_trace.append(
|
runtime_trace.append(
|
||||||
@@ -312,7 +316,7 @@ class CodeQaRuntimeExecutor:
|
|||||||
"output": post_gate_snapshot["output"],
|
"output": post_gate_snapshot["output"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return self._finalize(
|
return assemble_final_result(
|
||||||
state,
|
state,
|
||||||
draft=draft,
|
draft=draft,
|
||||||
final_answer=final_answer,
|
final_answer=final_answer,
|
||||||
@@ -324,11 +328,12 @@ class CodeQaRuntimeExecutor:
|
|||||||
answer_policy_branch=answer_policy_branch,
|
answer_policy_branch=answer_policy_branch,
|
||||||
decision_reason=decision_reason,
|
decision_reason=decision_reason,
|
||||||
pre_gate_input=pre_gate_input,
|
pre_gate_input=pre_gate_input,
|
||||||
gate_decision=gate_decision,
|
|
||||||
post_gate_snapshot=post_gate_snapshot,
|
post_gate_snapshot=post_gate_snapshot,
|
||||||
|
resolved_target=self._resolved_target(state),
|
||||||
|
post_gate=self._post_gate,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _retrieve(self, state: CodeQaExecutionState) -> list[dict]:
|
def _retrieve(self, state: RuntimeExecutionState) -> list[dict]:
|
||||||
assert state.retrieval_request is not None
|
assert state.retrieval_request is not None
|
||||||
if state.retrieval_request.sub_intent == "OPEN_FILE" and state.retrieval_request.path_scope:
|
if state.retrieval_request.sub_intent == "OPEN_FILE" and state.retrieval_request.path_scope:
|
||||||
return self._retrieval.retrieve_exact_files(
|
return self._retrieval.retrieve_exact_files(
|
||||||
@@ -364,72 +369,10 @@ class CodeQaRuntimeExecutor:
|
|||||||
return {"status": "ambiguous", "resolved_symbol": None, "alternatives": close[:5], "confidence": 0.55}
|
return {"status": "ambiguous", "resolved_symbol": None, "alternatives": close[:5], "confidence": 0.55}
|
||||||
return {"status": "not_found", "resolved_symbol": None, "alternatives": close[:5], "confidence": 0.0}
|
return {"status": "not_found", "resolved_symbol": None, "alternatives": close[:5], "confidence": 0.0}
|
||||||
|
|
||||||
def _finalize(
|
|
||||||
self,
|
|
||||||
state: CodeQaExecutionState,
|
|
||||||
*,
|
|
||||||
draft: CodeQaDraftAnswer | None,
|
|
||||||
final_answer: str,
|
|
||||||
repair_used: bool,
|
|
||||||
llm_used: bool,
|
|
||||||
validation=None,
|
|
||||||
timings_ms: dict[str, int] | None = None,
|
|
||||||
runtime_trace: list[dict] | None = None,
|
|
||||||
answer_policy_branch: str = "",
|
|
||||||
decision_reason: str = "",
|
|
||||||
pre_gate_input: dict | None = None,
|
|
||||||
gate_decision=None,
|
|
||||||
post_gate_snapshot: dict | None = None,
|
|
||||||
) -> CodeQaFinalResult:
|
|
||||||
diagnostics = build_diagnostics_report(
|
|
||||||
router_result=state.router_result,
|
|
||||||
retrieval_request=state.retrieval_request,
|
|
||||||
retrieval_result=state.retrieval_result,
|
|
||||||
evidence_bundle=state.evidence_pack,
|
|
||||||
answer_mode=state.answer_mode,
|
|
||||||
timings_ms=timings_ms or {},
|
|
||||||
resolved_target=self._resolved_target(state),
|
|
||||||
answer_policy_branch=answer_policy_branch,
|
|
||||||
decision_reason=decision_reason,
|
|
||||||
evidence_gate_input=pre_gate_input or {},
|
|
||||||
post_evidence_gate=post_gate_snapshot or {},
|
|
||||||
)
|
|
||||||
result = CodeQaFinalResult(
|
|
||||||
final_answer=final_answer.strip(),
|
|
||||||
answer_mode=state.answer_mode,
|
|
||||||
repair_used=repair_used,
|
|
||||||
llm_used=llm_used,
|
|
||||||
draft_answer=draft,
|
|
||||||
validation=validation
|
|
||||||
or self._post_gate.validate(
|
|
||||||
answer=final_answer,
|
|
||||||
answer_mode=state.answer_mode,
|
|
||||||
degraded_message=state.degraded_message,
|
|
||||||
sub_intent=state.retrieval_request.sub_intent if state.retrieval_request else "",
|
|
||||||
user_query=state.user_query,
|
|
||||||
evidence_pack=state.evidence_pack,
|
|
||||||
),
|
|
||||||
router_result=state.router_result,
|
|
||||||
retrieval_request=state.retrieval_request,
|
|
||||||
retrieval_result=state.retrieval_result,
|
|
||||||
evidence_pack=state.evidence_pack,
|
|
||||||
diagnostics=diagnostics,
|
|
||||||
runtime_trace=list(runtime_trace or []),
|
|
||||||
)
|
|
||||||
LOGGER.warning(
|
|
||||||
"code qa runtime executed: intent=%s sub_intent=%s answer_mode=%s repair_used=%s llm_used=%s",
|
|
||||||
state.router_result.intent,
|
|
||||||
state.router_result.query_plan.sub_intent,
|
|
||||||
result.answer_mode,
|
|
||||||
result.repair_used,
|
|
||||||
result.llm_used,
|
|
||||||
)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _elapsed_ms(self, started: float) -> int:
|
def _elapsed_ms(self, started: float) -> int:
|
||||||
return max(1, round((perf_counter() - started) * 1000))
|
return max(1, round((perf_counter() - started) * 1000))
|
||||||
|
|
||||||
def _build_pre_gate_input(self, state: CodeQaExecutionState) -> dict:
|
def _build_pre_gate_input(self, state: RuntimeExecutionState) -> dict:
|
||||||
evidence = state.evidence_pack
|
evidence = state.evidence_pack
|
||||||
retrieval = state.retrieval_result
|
retrieval = state.retrieval_result
|
||||||
return {
|
return {
|
||||||
@@ -445,7 +388,7 @@ class CodeQaRuntimeExecutor:
|
|||||||
"path_scope": list(state.retrieval_request.path_scope) if state.retrieval_request else [],
|
"path_scope": list(state.retrieval_request.path_scope) if state.retrieval_request else [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def _resolved_target(self, state: CodeQaExecutionState) -> str | None:
|
def _resolved_target(self, state: RuntimeExecutionState) -> str | None:
|
||||||
if state.evidence_pack and state.evidence_pack.resolved_target:
|
if state.evidence_pack and state.evidence_pack.resolved_target:
|
||||||
return state.evidence_pack.resolved_target
|
return state.evidence_pack.resolved_target
|
||||||
if state.retrieval_result and state.retrieval_result.resolved_symbol:
|
if state.retrieval_result and state.retrieval_result.resolved_symbol:
|
||||||
@@ -470,7 +413,7 @@ class CodeQaRuntimeExecutor:
|
|||||||
|
|
||||||
def _hydrate_entrypoint_sources(
|
def _hydrate_entrypoint_sources(
|
||||||
self,
|
self,
|
||||||
state: CodeQaExecutionState,
|
state: RuntimeExecutionState,
|
||||||
raw_rows: list[dict],
|
raw_rows: list[dict],
|
||||||
retrieval_report: dict,
|
retrieval_report: dict,
|
||||||
) -> tuple[list[dict], dict]:
|
) -> tuple[list[dict], dict]:
|
||||||
@@ -524,7 +467,7 @@ class CodeQaRuntimeExecutor:
|
|||||||
merged["supplemental_requests"] = [*(base.get("supplemental_requests") or []), *(extra.get("requests") or [])]
|
merged["supplemental_requests"] = [*(base.get("supplemental_requests") or []), *(extra.get("requests") or [])]
|
||||||
return merged
|
return merged
|
||||||
|
|
||||||
def _fallback_mode(self, state: CodeQaExecutionState) -> str:
|
def _fallback_mode(self, state: RuntimeExecutionState) -> str:
|
||||||
status = str(state.router_result.symbol_resolution.status if state.router_result and state.router_result.symbol_resolution else "")
|
status = str(state.router_result.symbol_resolution.status if state.router_result and state.router_result.symbol_resolution else "")
|
||||||
if status == "ambiguous":
|
if status == "ambiguous":
|
||||||
return "ambiguous"
|
return "ambiguous"
|
||||||
@@ -532,7 +475,7 @@ class CodeQaRuntimeExecutor:
|
|||||||
return "not_found"
|
return "not_found"
|
||||||
return "degraded"
|
return "degraded"
|
||||||
|
|
||||||
def _fallback_answer(self, state: CodeQaExecutionState) -> str:
|
def _fallback_answer(self, state: RuntimeExecutionState) -> str:
|
||||||
symbol_resolution = state.router_result.symbol_resolution if state.router_result else None
|
symbol_resolution = state.router_result.symbol_resolution if state.router_result else None
|
||||||
query_plan = state.router_result.query_plan if state.router_result else None
|
query_plan = state.router_result.query_plan if state.router_result else None
|
||||||
target = next((item for item in list(query_plan.symbol_candidates or []) if item), "запрошенная сущность") if query_plan else "запрошенная сущность"
|
target = next((item for item in list(query_plan.symbol_candidates or []) if item), "запрошенная сущность") if query_plan else "запрошенная сущность"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Canonical test-first CODE_QA pipeline: router -> retrieval -> evidence -> synthesis -> diagnostics."""
|
"""Legacy test-first CODE_QA pipeline: router → retrieval → evidence → synthesis → diagnostics. Prefer agent.runtime.executor."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -7,23 +7,21 @@ from difflib import get_close_matches
|
|||||||
from time import perf_counter
|
from time import perf_counter
|
||||||
from typing import Any, Protocol
|
from typing import Any, Protocol
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.answer_synthesis import build_answer_synthesis_input
|
from app.modules.agent.runtime.steps.context import (
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import (
|
build_answer_synthesis_input,
|
||||||
|
build_diagnostics_report,
|
||||||
|
build_evidence_bundle,
|
||||||
|
build_retrieval_request,
|
||||||
|
build_retrieval_result,
|
||||||
EvidenceBundle,
|
EvidenceBundle,
|
||||||
RetrievalRequest,
|
RetrievalRequest,
|
||||||
RetrievalResult,
|
RetrievalResult,
|
||||||
RouterResult,
|
RouterResult,
|
||||||
)
|
)
|
||||||
from app.modules.rag.code_qa_pipeline.diagnostics import build_diagnostics_report
|
from app.modules.agent.runtime.steps.gates.pre.evidence_gate import evaluate_evidence
|
||||||
from app.modules.rag.code_qa_pipeline.evidence_bundle_builder import build_evidence_bundle
|
|
||||||
from app.modules.rag.code_qa_pipeline.evidence_gate import evaluate_evidence
|
|
||||||
from app.modules.rag.code_qa_pipeline.retrieval_request_builder import build_retrieval_request
|
|
||||||
from app.modules.rag.code_qa_pipeline.retrieval_result_builder import build_retrieval_result
|
|
||||||
|
|
||||||
|
|
||||||
class RetrievalAdapter(Protocol):
|
class RetrievalAdapter(Protocol):
|
||||||
"""Protocol for retrieval in the CODE_QA pipeline; satisfied by RagDbAdapter."""
|
|
||||||
|
|
||||||
def retrieve_with_plan(
|
def retrieve_with_plan(
|
||||||
self,
|
self,
|
||||||
rag_session_id: str,
|
rag_session_id: str,
|
||||||
@@ -70,8 +68,6 @@ class RetrievalAdapter(Protocol):
|
|||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
class CodeQAPipelineResult:
|
class CodeQAPipelineResult:
|
||||||
"""Result of one run of the canonical CODE_QA pipeline."""
|
|
||||||
|
|
||||||
user_query: str
|
user_query: str
|
||||||
rag_session_id: str
|
rag_session_id: str
|
||||||
router_result: RouterResult
|
router_result: RouterResult
|
||||||
@@ -88,7 +84,7 @@ class CodeQAPipelineResult:
|
|||||||
|
|
||||||
|
|
||||||
class CodeQAPipelineRunner:
|
class CodeQAPipelineRunner:
|
||||||
"""Single entrypoint for the test-first CODE_QA pipeline; uses IntentRouterV2 only."""
|
"""Legacy test-first CODE_QA pipeline entrypoint. Prefer AgentRuntimeExecutor."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -109,7 +105,6 @@ class CodeQAPipelineRunner:
|
|||||||
run_retrieval: bool = True,
|
run_retrieval: bool = True,
|
||||||
run_hydrate: bool = True,
|
run_hydrate: bool = True,
|
||||||
) -> CodeQAPipelineResult:
|
) -> CodeQAPipelineResult:
|
||||||
"""Run the full pipeline: route -> retrieval -> evidence bundle -> gate -> synthesis -> diagnostics."""
|
|
||||||
timings: dict[str, int] = {}
|
timings: dict[str, int] = {}
|
||||||
t0 = perf_counter()
|
t0 = perf_counter()
|
||||||
router_result = self._router.route(
|
router_result = self._router.route(
|
||||||
@@ -228,10 +223,10 @@ def _ms(started: float) -> int:
|
|||||||
|
|
||||||
|
|
||||||
def _default_conversation_state() -> Any:
|
def _default_conversation_state() -> Any:
|
||||||
from app.modules.rag.intent_router_v2 import ConversationState
|
from app.modules.agent.intent_router_v2 import ConversationState
|
||||||
return ConversationState()
|
return ConversationState()
|
||||||
|
|
||||||
|
|
||||||
def _default_repo_context() -> Any:
|
def _default_repo_context() -> Any:
|
||||||
from app.modules.rag.intent_router_v2 import RepoContext
|
from app.modules.agent.intent_router_v2 import RepoContext
|
||||||
return RepoContext()
|
return RepoContext()
|
||||||
60
src/app/modules/agent/runtime/models.py
Normal file
60
src/app/modules/agent/runtime/models.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
"""Модели состояния и результата runtime-оркестратора."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.steps.context.contracts import (
|
||||||
|
AnswerSynthesisInput,
|
||||||
|
DiagnosticsReport,
|
||||||
|
EvidenceBundle,
|
||||||
|
RetrievalRequest,
|
||||||
|
RetrievalResult,
|
||||||
|
)
|
||||||
|
from app.modules.agent.runtime.steps.gates.post.post_gate import RuntimeValidationResult
|
||||||
|
from app.modules.agent.intent_router_v2.models import ConversationState, IntentRouterResult, RepoContext
|
||||||
|
|
||||||
|
|
||||||
|
class RuntimeDraftAnswer(BaseModel):
|
||||||
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
|
prompt_name: str
|
||||||
|
prompt_payload: str
|
||||||
|
answer: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
class RuntimeFinalResult(BaseModel):
|
||||||
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
|
final_answer: str
|
||||||
|
answer_mode: str = "normal"
|
||||||
|
repair_used: bool = False
|
||||||
|
llm_used: bool = False
|
||||||
|
draft_answer: RuntimeDraftAnswer | None = None
|
||||||
|
validation: RuntimeValidationResult = Field(default_factory=RuntimeValidationResult)
|
||||||
|
router_result: IntentRouterResult | None = None
|
||||||
|
retrieval_request: RetrievalRequest | None = None
|
||||||
|
retrieval_result: RetrievalResult | None = None
|
||||||
|
evidence_pack: EvidenceBundle | None = None
|
||||||
|
diagnostics: DiagnosticsReport
|
||||||
|
runtime_trace: list[dict[str, Any]] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
class RuntimeExecutionState(BaseModel):
|
||||||
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
|
user_query: str
|
||||||
|
rag_session_id: str
|
||||||
|
conversation_state: ConversationState = Field(default_factory=ConversationState)
|
||||||
|
repo_context: RepoContext = Field(default_factory=RepoContext)
|
||||||
|
router_result: IntentRouterResult | None = None
|
||||||
|
retrieval_request: RetrievalRequest | None = None
|
||||||
|
retrieval_result: RetrievalResult | None = None
|
||||||
|
evidence_pack: EvidenceBundle | None = None
|
||||||
|
synthesis_input: AnswerSynthesisInput | None = None
|
||||||
|
diagnostics: DiagnosticsReport | None = None
|
||||||
|
answer_mode: str = "normal"
|
||||||
|
degraded_message: str = ""
|
||||||
|
final_result: RuntimeFinalResult | None = None
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
"""Политика ответа: вызов LLM или короткий ответ по результату evidence gate."""
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.steps.answer_policy.policy import RuntimeAnswerPolicy, RuntimePolicyDecision
|
||||||
|
from app.modules.agent.runtime.steps.answer_policy.short_answer_formatter import RuntimeShortAnswerFormatter
|
||||||
|
|
||||||
|
__all__ = ["RuntimeAnswerPolicy", "RuntimePolicyDecision", "RuntimeShortAnswerFormatter"]
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
|
"""Политика принятия решения: вызывать LLM или вернуть короткий ответ по evidence gate."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.evidence_gate import EvidenceGateDecision
|
from app.modules.agent.runtime.steps.gates.pre.evidence_gate import EvidenceGateDecision
|
||||||
from app.modules.rag.intent_router_v2.models import IntentRouterResult
|
from app.modules.agent.intent_router_v2.models import IntentRouterResult
|
||||||
from app.modules.agent.code_qa_runtime.short_answer_formatter import CodeQaShortAnswerFormatter
|
from app.modules.agent.runtime.steps.answer_policy.short_answer_formatter import RuntimeShortAnswerFormatter
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True, frozen=True)
|
@dataclass(slots=True, frozen=True)
|
||||||
class CodeQaPolicyDecision:
|
class RuntimePolicyDecision:
|
||||||
answer_mode: str
|
answer_mode: str
|
||||||
answer: str = ""
|
answer: str = ""
|
||||||
should_call_llm: bool = True
|
should_call_llm: bool = True
|
||||||
@@ -16,22 +18,22 @@ class CodeQaPolicyDecision:
|
|||||||
reason: str = "evidence_sufficient"
|
reason: str = "evidence_sufficient"
|
||||||
|
|
||||||
|
|
||||||
class CodeQaAnswerPolicy:
|
class RuntimeAnswerPolicy:
|
||||||
def __init__(self, formatter: CodeQaShortAnswerFormatter | None = None) -> None:
|
def __init__(self, formatter: RuntimeShortAnswerFormatter | None = None) -> None:
|
||||||
self._formatter = formatter or CodeQaShortAnswerFormatter()
|
self._formatter = formatter or RuntimeShortAnswerFormatter()
|
||||||
|
|
||||||
def decide(
|
def decide(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
router_result: IntentRouterResult,
|
router_result: IntentRouterResult,
|
||||||
gate_decision: EvidenceGateDecision,
|
gate_decision: EvidenceGateDecision,
|
||||||
) -> CodeQaPolicyDecision:
|
) -> RuntimePolicyDecision:
|
||||||
sub_intent = router_result.query_plan.sub_intent.upper()
|
sub_intent = router_result.query_plan.sub_intent.upper()
|
||||||
symbol_resolution = router_result.symbol_resolution
|
symbol_resolution = router_result.symbol_resolution
|
||||||
if sub_intent == "OPEN_FILE" and "path_scope_empty" in gate_decision.failure_reasons:
|
if sub_intent == "OPEN_FILE" and "path_scope_empty" in gate_decision.failure_reasons:
|
||||||
path_scope = list(getattr(router_result.retrieval_spec.filters, "path_scope", []) or [])
|
path_scope = list(getattr(router_result.retrieval_spec.filters, "path_scope", []) or [])
|
||||||
target = path_scope[0] if path_scope else "запрошенный файл"
|
target = path_scope[0] if path_scope else "запрошенный файл"
|
||||||
return CodeQaPolicyDecision(
|
return RuntimePolicyDecision(
|
||||||
answer_mode="not_found",
|
answer_mode="not_found",
|
||||||
answer=self._formatter.open_file_not_found(target),
|
answer=self._formatter.open_file_not_found(target),
|
||||||
should_call_llm=False,
|
should_call_llm=False,
|
||||||
@@ -39,7 +41,7 @@ class CodeQaAnswerPolicy:
|
|||||||
reason="path_scope_empty",
|
reason="path_scope_empty",
|
||||||
)
|
)
|
||||||
if sub_intent == "EXPLAIN" and symbol_resolution.status == "not_found":
|
if sub_intent == "EXPLAIN" and symbol_resolution.status == "not_found":
|
||||||
return CodeQaPolicyDecision(
|
return RuntimePolicyDecision(
|
||||||
answer_mode="not_found",
|
answer_mode="not_found",
|
||||||
answer=self._formatter.entity_not_found(self._target_label(router_result), symbol_resolution.alternatives),
|
answer=self._formatter.entity_not_found(self._target_label(router_result), symbol_resolution.alternatives),
|
||||||
should_call_llm=False,
|
should_call_llm=False,
|
||||||
@@ -47,7 +49,7 @@ class CodeQaAnswerPolicy:
|
|||||||
reason="symbol_resolution_not_found",
|
reason="symbol_resolution_not_found",
|
||||||
)
|
)
|
||||||
if sub_intent == "EXPLAIN" and symbol_resolution.status == "ambiguous":
|
if sub_intent == "EXPLAIN" and symbol_resolution.status == "ambiguous":
|
||||||
return CodeQaPolicyDecision(
|
return RuntimePolicyDecision(
|
||||||
answer_mode="ambiguous",
|
answer_mode="ambiguous",
|
||||||
answer=self._formatter.entity_ambiguous(self._target_label(router_result), symbol_resolution.alternatives),
|
answer=self._formatter.entity_ambiguous(self._target_label(router_result), symbol_resolution.alternatives),
|
||||||
should_call_llm=False,
|
should_call_llm=False,
|
||||||
@@ -57,14 +59,14 @@ class CodeQaAnswerPolicy:
|
|||||||
if not gate_decision.passed:
|
if not gate_decision.passed:
|
||||||
answer_mode = "insufficient" if "insufficient_evidence" in gate_decision.failure_reasons else "degraded"
|
answer_mode = "insufficient" if "insufficient_evidence" in gate_decision.failure_reasons else "degraded"
|
||||||
reason = gate_decision.failure_reasons[0] if gate_decision.failure_reasons else "evidence_gate_failed"
|
reason = gate_decision.failure_reasons[0] if gate_decision.failure_reasons else "evidence_gate_failed"
|
||||||
return CodeQaPolicyDecision(
|
return RuntimePolicyDecision(
|
||||||
answer_mode=answer_mode,
|
answer_mode=answer_mode,
|
||||||
answer=self._formatter.insufficient(gate_decision.degraded_message),
|
answer=self._formatter.insufficient(gate_decision.degraded_message),
|
||||||
should_call_llm=False,
|
should_call_llm=False,
|
||||||
branch="evidence_gate_short_circuit",
|
branch="evidence_gate_short_circuit",
|
||||||
reason=reason,
|
reason=reason,
|
||||||
)
|
)
|
||||||
return CodeQaPolicyDecision(answer_mode="normal", branch="normal_answer", reason="evidence_sufficient")
|
return RuntimePolicyDecision(answer_mode="normal", branch="normal_answer", reason="evidence_sufficient")
|
||||||
|
|
||||||
def _target_label(self, router_result: IntentRouterResult) -> str:
|
def _target_label(self, router_result: IntentRouterResult) -> str:
|
||||||
candidates = [item.strip() for item in list(router_result.query_plan.symbol_candidates or []) if item and item.strip()]
|
candidates = [item.strip() for item in list(router_result.query_plan.symbol_candidates or []) if item and item.strip()]
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
"""Форматирование коротких ответов для режимов not_found, ambiguous, insufficient."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
class CodeQaShortAnswerFormatter:
|
class RuntimeShortAnswerFormatter:
|
||||||
def open_file_not_found(self, target: str) -> str:
|
def open_file_not_found(self, target: str) -> str:
|
||||||
return f"Файл {target} не найден."
|
return f"Файл {target} не найден."
|
||||||
|
|
||||||
35
src/app/modules/agent/runtime/steps/context/__init__.py
Normal file
35
src/app/modules/agent/runtime/steps/context/__init__.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"""Контракты и билдеры контекста пайплайна: retrieval request/result, evidence bundle, diagnostics, synthesis."""
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.steps.context.contracts import (
|
||||||
|
AnswerSynthesisInput,
|
||||||
|
CodeChunkItem,
|
||||||
|
DiagnosticsReport,
|
||||||
|
EvidenceBundle,
|
||||||
|
FailureReason,
|
||||||
|
RetrievalRequest,
|
||||||
|
RetrievalResult,
|
||||||
|
RouterResult,
|
||||||
|
)
|
||||||
|
from app.modules.agent.runtime.steps.context.retrieval_request_builder import build_retrieval_request
|
||||||
|
from app.modules.agent.runtime.steps.context.retrieval_result_builder import build_retrieval_result
|
||||||
|
from app.modules.agent.runtime.steps.context.evidence_bundle_builder import build_evidence_bundle
|
||||||
|
from app.modules.agent.runtime.steps.context.diagnostics import build_diagnostics_report
|
||||||
|
from app.modules.agent.runtime.steps.context.answer_synthesis import build_answer_synthesis_input
|
||||||
|
from app.modules.agent.runtime.steps.context.answer_fact_curator import build_curated_answer_facts
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"AnswerSynthesisInput",
|
||||||
|
"CodeChunkItem",
|
||||||
|
"DiagnosticsReport",
|
||||||
|
"EvidenceBundle",
|
||||||
|
"FailureReason",
|
||||||
|
"RetrievalRequest",
|
||||||
|
"RetrievalResult",
|
||||||
|
"RouterResult",
|
||||||
|
"build_retrieval_request",
|
||||||
|
"build_retrieval_result",
|
||||||
|
"build_evidence_bundle",
|
||||||
|
"build_diagnostics_report",
|
||||||
|
"build_answer_synthesis_input",
|
||||||
|
"build_curated_answer_facts",
|
||||||
|
]
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
|
"""Курация фактов из EvidenceBundle для сценариев explain, architecture, trace_flow."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import CodeChunkItem, EvidenceBundle
|
from app.modules.agent.runtime.steps.context.contracts import CodeChunkItem, EvidenceBundle
|
||||||
|
|
||||||
_CALL_RE = re.compile(r"([A-Za-z_][\w\.]*)\s*\(")
|
_CALL_RE = re.compile(r"([A-Za-z_][\w\.]*)\s*\(")
|
||||||
_FIELD_RE = re.compile(r"self\.(\w+)")
|
_FIELD_RE = re.compile(r"self\.(\w+)")
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
"""Builds AnswerSynthesisInput from EvidenceBundle for LLM stage."""
|
"""Сборка AnswerSynthesisInput из EvidenceBundle для этапа LLM."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.answer_fact_curator import build_curated_answer_facts
|
from app.modules.agent.runtime.steps.context.answer_fact_curator import build_curated_answer_facts
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import AnswerSynthesisInput, EvidenceBundle
|
from app.modules.agent.runtime.steps.context.contracts import AnswerSynthesisInput, EvidenceBundle
|
||||||
|
|
||||||
|
|
||||||
def build_answer_synthesis_input(
|
def build_answer_synthesis_input(
|
||||||
user_question: str,
|
user_question: str,
|
||||||
bundle: EvidenceBundle,
|
bundle: EvidenceBundle,
|
||||||
) -> AnswerSynthesisInput:
|
) -> AnswerSynthesisInput:
|
||||||
"""Build LLM input from EvidenceBundle; fast context (summary) + deep context (payload)."""
|
|
||||||
scenario = bundle.resolved_sub_intent or "EXPLAIN"
|
scenario = bundle.resolved_sub_intent or "EXPLAIN"
|
||||||
target = bundle.resolved_target
|
target = bundle.resolved_target
|
||||||
sufficient = bundle.sufficient
|
sufficient = bundle.sufficient
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
"""Typed contracts for the canonical CODE_QA test-first pipeline.
|
"""Типизированные контракты пайплайна: RouterResult, RetrievalRequest, RetrievalResult, EvidenceBundle, AnswerSynthesisInput, DiagnosticsReport."""
|
||||||
|
|
||||||
Defines RouterResult, RetrievalRequest, RetrievalResult, EvidenceBundle,
|
|
||||||
AnswerSynthesisInput, DiagnosticsReport and machine-readable failure reasons.
|
|
||||||
Used only by the test-first pipeline; legacy runtime is unchanged.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -11,14 +6,10 @@ from typing import Any, Literal
|
|||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
# Re-export for pipeline: router output is IntentRouterResult
|
from app.modules.agent.intent_router_v2.models import IntentRouterResult
|
||||||
from app.modules.rag.intent_router_v2.models import IntentRouterResult
|
|
||||||
|
|
||||||
# Type alias: router output is the source of truth for the test pipeline
|
|
||||||
RouterResult = IntentRouterResult
|
RouterResult = IntentRouterResult
|
||||||
|
|
||||||
|
|
||||||
# --- Machine-readable failure reasons for diagnostics ---
|
|
||||||
FailureReason = Literal[
|
FailureReason = Literal[
|
||||||
"router_low_confidence",
|
"router_low_confidence",
|
||||||
"target_not_resolved",
|
"target_not_resolved",
|
||||||
@@ -45,8 +36,6 @@ FAILURE_REASONS: tuple[FailureReason, ...] = (
|
|||||||
|
|
||||||
|
|
||||||
class RetrievalRequest(BaseModel):
|
class RetrievalRequest(BaseModel):
|
||||||
"""Request for retrieval stage; built from RouterResult."""
|
|
||||||
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
rag_session_id: str
|
rag_session_id: str
|
||||||
@@ -56,15 +45,12 @@ class RetrievalRequest(BaseModel):
|
|||||||
keyword_hints: list[str] = Field(default_factory=list)
|
keyword_hints: list[str] = Field(default_factory=list)
|
||||||
symbol_candidates: list[str] = Field(default_factory=list)
|
symbol_candidates: list[str] = Field(default_factory=list)
|
||||||
requested_layers: list[str] = Field(default_factory=list)
|
requested_layers: list[str] = Field(default_factory=list)
|
||||||
# Pass-through for existing adapter (retrieval_spec, retrieval_constraints, query_plan)
|
|
||||||
retrieval_spec: Any = None
|
retrieval_spec: Any = None
|
||||||
retrieval_constraints: Any = None
|
retrieval_constraints: Any = None
|
||||||
query_plan: Any = None
|
query_plan: Any = None
|
||||||
|
|
||||||
|
|
||||||
class CodeChunkItem(BaseModel):
|
class CodeChunkItem(BaseModel):
|
||||||
"""Single code chunk in normalized retrieval output."""
|
|
||||||
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
layer: str
|
layer: str
|
||||||
@@ -77,8 +63,6 @@ class CodeChunkItem(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class LayerOutcome(BaseModel):
|
class LayerOutcome(BaseModel):
|
||||||
"""Per-layer retrieval outcome for diagnostics."""
|
|
||||||
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
layer_id: str
|
layer_id: str
|
||||||
@@ -88,8 +72,6 @@ class LayerOutcome(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class RetrievalResult(BaseModel):
|
class RetrievalResult(BaseModel):
|
||||||
"""Normalized retrieval result; single structure for all scenarios."""
|
|
||||||
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
target_symbol_candidates: list[str] = Field(default_factory=list)
|
target_symbol_candidates: list[str] = Field(default_factory=list)
|
||||||
@@ -108,8 +90,6 @@ class RetrievalResult(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class EvidenceBundle(BaseModel):
|
class EvidenceBundle(BaseModel):
|
||||||
"""Canonical evidence bundle for answer synthesis; only source of truth for LLM stage."""
|
|
||||||
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
resolved_intent: str = ""
|
resolved_intent: str = ""
|
||||||
@@ -129,8 +109,6 @@ class EvidenceBundle(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class AnswerSynthesisInput(BaseModel):
|
class AnswerSynthesisInput(BaseModel):
|
||||||
"""Input for LLM answer synthesis; derived from EvidenceBundle."""
|
|
||||||
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
user_question: str = ""
|
user_question: str = ""
|
||||||
@@ -146,11 +124,8 @@ class AnswerSynthesisInput(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class DiagnosticsReport(BaseModel):
|
class DiagnosticsReport(BaseModel):
|
||||||
"""Full diagnostics for the pipeline; Level 1 summary + Level 2 detail."""
|
|
||||||
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
# Level 1 — human-readable summary
|
|
||||||
intent_correct: bool | None = None
|
intent_correct: bool | None = None
|
||||||
target_found: bool = False
|
target_found: bool = False
|
||||||
layers_used: list[str] = Field(default_factory=list)
|
layers_used: list[str] = Field(default_factory=list)
|
||||||
@@ -160,7 +135,6 @@ class DiagnosticsReport(BaseModel):
|
|||||||
answer_policy_branch: str = ""
|
answer_policy_branch: str = ""
|
||||||
decision_reason: str = ""
|
decision_reason: str = ""
|
||||||
|
|
||||||
# Level 2 — detailed
|
|
||||||
router_result: dict[str, Any] = Field(default_factory=dict)
|
router_result: dict[str, Any] = Field(default_factory=dict)
|
||||||
retrieval_request: dict[str, Any] = Field(default_factory=dict)
|
retrieval_request: dict[str, Any] = Field(default_factory=dict)
|
||||||
per_layer_outcome: list[dict[str, Any]] = Field(default_factory=list)
|
per_layer_outcome: list[dict[str, Any]] = Field(default_factory=list)
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
"""Diagnostics for the CODE_QA pipeline: Level 1 summary and Level 2 detail."""
|
"""Диагностика пайплайна CODE_QA: сводка уровня 1 и детали уровня 2."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import (
|
from app.modules.agent.runtime.steps.context.contracts import (
|
||||||
DiagnosticsReport,
|
DiagnosticsReport,
|
||||||
EvidenceBundle,
|
EvidenceBundle,
|
||||||
RetrievalRequest,
|
RetrievalRequest,
|
||||||
@@ -27,7 +27,6 @@ def build_diagnostics_report(
|
|||||||
evidence_gate_input: dict[str, Any] | None = None,
|
evidence_gate_input: dict[str, Any] | None = None,
|
||||||
post_evidence_gate: dict[str, Any] | None = None,
|
post_evidence_gate: dict[str, Any] | None = None,
|
||||||
) -> DiagnosticsReport:
|
) -> DiagnosticsReport:
|
||||||
"""Build full diagnostics: Level 1 summary + Level 2 detail + failure reasons."""
|
|
||||||
timings = dict(timings_ms or {})
|
timings = dict(timings_ms or {})
|
||||||
req = retrieval_request
|
req = retrieval_request
|
||||||
res = retrieval_result
|
res = retrieval_result
|
||||||
@@ -83,7 +82,6 @@ def build_diagnostics_report(
|
|||||||
|
|
||||||
|
|
||||||
def build_level1_summary(report: DiagnosticsReport) -> dict[str, Any]:
|
def build_level1_summary(report: DiagnosticsReport) -> dict[str, Any]:
|
||||||
"""Human-readable summary: intent, target, layers, sufficiency, answer mode."""
|
|
||||||
return {
|
return {
|
||||||
"intent_correct": report.intent_correct,
|
"intent_correct": report.intent_correct,
|
||||||
"target_found": report.target_found,
|
"target_found": report.target_found,
|
||||||
@@ -98,7 +96,6 @@ def build_level1_summary(report: DiagnosticsReport) -> dict[str, Any]:
|
|||||||
|
|
||||||
|
|
||||||
def build_level2_detail(report: DiagnosticsReport) -> dict[str, Any]:
|
def build_level2_detail(report: DiagnosticsReport) -> dict[str, Any]:
|
||||||
"""Detailed diagnostics for tuning and tests."""
|
|
||||||
return {
|
return {
|
||||||
"router_result": report.router_result,
|
"router_result": report.router_result,
|
||||||
"retrieval_request": report.retrieval_request,
|
"retrieval_request": report.retrieval_request,
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
"""Builds EvidenceBundle from RetrievalResult and router context."""
|
"""Сборка EvidenceBundle из RetrievalResult и результата роутера."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import EvidenceBundle, RetrievalResult, RouterResult
|
from app.modules.agent.runtime.steps.context.contracts import EvidenceBundle, RetrievalResult, RouterResult
|
||||||
|
|
||||||
|
|
||||||
def build_evidence_bundle(
|
def build_evidence_bundle(
|
||||||
retrieval_result: RetrievalResult,
|
retrieval_result: RetrievalResult,
|
||||||
router_result: RouterResult,
|
router_result: RouterResult,
|
||||||
) -> EvidenceBundle:
|
) -> EvidenceBundle:
|
||||||
"""Build EvidenceBundle from normalized retrieval and router result."""
|
|
||||||
intent = router_result.intent or "CODE_QA"
|
intent = router_result.intent or "CODE_QA"
|
||||||
sub_intent = (router_result.query_plan and router_result.query_plan.sub_intent) or "EXPLAIN"
|
sub_intent = (router_result.query_plan and router_result.query_plan.sub_intent) or "EXPLAIN"
|
||||||
resolved_target: str | None = None
|
resolved_target: str | None = None
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
"""Builds RetrievalRequest from RouterResult for the CODE_QA pipeline."""
|
"""Сборка RetrievalRequest из RouterResult для пайплайна CODE_QA."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import RetrievalRequest, RouterResult
|
from app.modules.agent.runtime.steps.context.contracts import RetrievalRequest, RouterResult
|
||||||
|
|
||||||
|
|
||||||
def build_retrieval_request(router_result: RouterResult, rag_session_id: str) -> RetrievalRequest:
|
def build_retrieval_request(router_result: RouterResult, rag_session_id: str) -> RetrievalRequest:
|
||||||
"""Convert router output to RetrievalRequest; router is source of truth."""
|
|
||||||
query_plan = router_result.query_plan
|
query_plan = router_result.query_plan
|
||||||
spec = router_result.retrieval_spec
|
spec = router_result.retrieval_spec
|
||||||
path_scope = list(getattr(spec.filters, "path_scope", []) or [])
|
path_scope = list(getattr(spec.filters, "path_scope", []) or [])
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
"""Builds normalized RetrievalResult from raw retrieval rows and report."""
|
"""Сборка нормализованного RetrievalResult из сырых строк retrieval и отчёта."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import CodeChunkItem, LayerOutcome, RetrievalResult
|
from app.modules.agent.runtime.steps.context.contracts import CodeChunkItem, LayerOutcome, RetrievalResult
|
||||||
from app.modules.rag.retrieval.test_filter import is_test_path
|
from app.modules.rag.retrieval.test_filter import is_test_path
|
||||||
|
|
||||||
_ROUTE_RE = re.compile(r'@[\w\.]+\.(get|post|put|delete|patch|options|head)\(\s*["\']([^"\']+)["\']')
|
_ROUTE_RE = re.compile(r'@[\w\.]+\.(get|post|put|delete|patch|options|head)\(\s*["\']([^"\']+)["\']')
|
||||||
@@ -16,7 +16,6 @@ def build_retrieval_result(
|
|||||||
retrieval_report: dict | None,
|
retrieval_report: dict | None,
|
||||||
symbol_resolution: dict | None,
|
symbol_resolution: dict | None,
|
||||||
) -> RetrievalResult:
|
) -> RetrievalResult:
|
||||||
"""Convert raw adapter rows and optional report into normalized RetrievalResult."""
|
|
||||||
report = retrieval_report or {}
|
report = retrieval_report or {}
|
||||||
sym = symbol_resolution or {}
|
sym = symbol_resolution or {}
|
||||||
layers_seen: set[str] = set()
|
layers_seen: set[str] = set()
|
||||||
36
src/app/modules/agent/runtime/steps/explain/__init__.py
Normal file
36
src/app/modules/agent/runtime/steps/explain/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from importlib import import_module
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"CodeExcerpt",
|
||||||
|
"CodeExplainRetrieverV2",
|
||||||
|
"CodeGraphRepository",
|
||||||
|
"EvidenceItem",
|
||||||
|
"ExplainIntent",
|
||||||
|
"ExplainIntentBuilder",
|
||||||
|
"ExplainPack",
|
||||||
|
"LayeredRetrievalGateway",
|
||||||
|
"PromptBudgeter",
|
||||||
|
"TracePath",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def __getattr__(name: str):
|
||||||
|
module_map = {
|
||||||
|
"CodeExcerpt": "app.modules.agent.runtime.steps.explain.models",
|
||||||
|
"EvidenceItem": "app.modules.agent.runtime.steps.explain.models",
|
||||||
|
"ExplainIntent": "app.modules.agent.runtime.steps.explain.models",
|
||||||
|
"ExplainPack": "app.modules.agent.runtime.steps.explain.models",
|
||||||
|
"TracePath": "app.modules.agent.runtime.steps.explain.models",
|
||||||
|
"ExplainIntentBuilder": "app.modules.agent.runtime.steps.explain.intent_builder",
|
||||||
|
"PromptBudgeter": "app.modules.agent.runtime.steps.explain.budgeter",
|
||||||
|
"LayeredRetrievalGateway": "app.modules.agent.runtime.steps.explain.layered_gateway",
|
||||||
|
"CodeGraphRepository": "app.modules.agent.runtime.steps.explain.graph_repository",
|
||||||
|
"CodeExplainRetrieverV2": "app.modules.agent.runtime.steps.explain.retriever_v2",
|
||||||
|
}
|
||||||
|
module_name = module_map.get(name)
|
||||||
|
if module_name is None:
|
||||||
|
raise AttributeError(name)
|
||||||
|
module = import_module(module_name)
|
||||||
|
return getattr(module, name)
|
||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from app.modules.rag.explain.models import ExplainPack
|
from app.modules.agent.runtime.steps.explain.models import ExplainPack
|
||||||
|
|
||||||
|
|
||||||
class PromptBudgeter:
|
class PromptBudgeter:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.explain.models import CodeExcerpt, LayeredRetrievalItem
|
from app.modules.agent.runtime.steps.explain.models import CodeExcerpt, LayeredRetrievalItem
|
||||||
|
|
||||||
|
|
||||||
class ExcerptPlanner:
|
class ExcerptPlanner:
|
||||||
@@ -4,7 +4,7 @@ import json
|
|||||||
|
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
from app.modules.rag.explain.models import CodeLocation, LayeredRetrievalItem
|
from app.modules.agent.runtime.steps.explain.models import CodeLocation, LayeredRetrievalItem
|
||||||
from app.modules.shared.db import get_engine
|
from app.modules.shared.db import get_engine
|
||||||
|
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from app.modules.rag.explain.models import ExplainHints, ExplainIntent
|
from app.modules.agent.runtime.steps.explain.models import ExplainHints, ExplainIntent
|
||||||
from app.modules.rag.retrieval.query_terms import extract_query_terms
|
from app.modules.rag.retrieval.query_terms import extract_query_terms
|
||||||
|
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ import logging
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import TYPE_CHECKING, Callable
|
from typing import TYPE_CHECKING, Callable
|
||||||
|
|
||||||
from app.modules.rag.explain.models import CodeLocation, LayeredRetrievalItem
|
from app.modules.agent.runtime.steps.explain.models import CodeLocation, LayeredRetrievalItem
|
||||||
from app.modules.rag.retrieval.test_filter import build_test_filters, debug_disable_test_filter
|
from app.modules.rag.retrieval.test_filter import build_test_filters, debug_disable_test_filter
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
@@ -4,19 +4,19 @@ import logging
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from app.modules.rag.contracts.enums import RagLayer
|
from app.modules.rag.contracts.enums import RagLayer
|
||||||
from app.modules.rag.explain.intent_builder import ExplainIntentBuilder
|
from app.modules.agent.runtime.steps.explain.intent_builder import ExplainIntentBuilder
|
||||||
from app.modules.rag.explain.layered_gateway import LayerRetrievalResult, LayeredRetrievalGateway
|
from app.modules.agent.runtime.steps.explain.layered_gateway import LayerRetrievalResult, LayeredRetrievalGateway
|
||||||
from app.modules.rag.explain.models import CodeExcerpt, EvidenceItem, ExplainPack, LayeredRetrievalItem
|
from app.modules.agent.runtime.steps.explain.models import CodeExcerpt, EvidenceItem, ExplainPack, LayeredRetrievalItem
|
||||||
from app.modules.rag.explain.source_excerpt_fetcher import SourceExcerptFetcher
|
from app.modules.agent.runtime.steps.explain.source_excerpt_fetcher import SourceExcerptFetcher
|
||||||
from app.modules.rag.explain.trace_builder import TraceBuilder
|
from app.modules.agent.runtime.steps.explain.trace_builder import TraceBuilder
|
||||||
from app.modules.rag.retrieval.test_filter import exclude_tests_default, is_test_path
|
from app.modules.rag.retrieval.test_filter import exclude_tests_default, is_test_path
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
_MIN_EXCERPTS = 2
|
_MIN_EXCERPTS = 2
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from app.modules.rag.explain.graph_repository import CodeGraphRepository
|
from app.modules.agent.runtime.steps.explain.graph_repository import CodeGraphRepository
|
||||||
from app.modules.rag.explain.models import ExplainIntent
|
from app.modules.agent.runtime.steps.explain.models import ExplainIntent
|
||||||
|
|
||||||
|
|
||||||
class CodeExplainRetrieverV2:
|
class CodeExplainRetrieverV2:
|
||||||
@@ -2,12 +2,12 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from app.modules.rag.explain.excerpt_planner import ExcerptPlanner
|
from app.modules.agent.runtime.steps.explain.excerpt_planner import ExcerptPlanner
|
||||||
from app.modules.rag.explain.models import CodeExcerpt, EvidenceItem, TracePath
|
from app.modules.agent.runtime.steps.explain.models import CodeExcerpt, EvidenceItem, TracePath
|
||||||
from app.modules.rag.retrieval.test_filter import is_test_path
|
from app.modules.rag.retrieval.test_filter import is_test_path
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from app.modules.rag.explain.graph_repository import CodeGraphRepository
|
from app.modules.agent.runtime.steps.explain.graph_repository import CodeGraphRepository
|
||||||
|
|
||||||
|
|
||||||
class SourceExcerptFetcher:
|
class SourceExcerptFetcher:
|
||||||
@@ -2,10 +2,10 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from app.modules.rag.explain.models import LayeredRetrievalItem, TracePath
|
from app.modules.agent.runtime.steps.explain.models import LayeredRetrievalItem, TracePath
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from app.modules.rag.explain.graph_repository import CodeGraphRepository
|
from app.modules.agent.runtime.steps.explain.graph_repository import CodeGraphRepository
|
||||||
|
|
||||||
|
|
||||||
class TraceBuilder:
|
class TraceBuilder:
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
"""Финальная сборка: repair черновика и сборка RuntimeFinalResult."""
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.steps.finalization.repair import RuntimeAnswerRepairService
|
||||||
|
from app.modules.agent.runtime.steps.finalization.result_assembler import assemble_final_result
|
||||||
|
|
||||||
|
__all__ = ["RuntimeAnswerRepairService", "assemble_final_result"]
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
|
"""Сервис починки черновика ответа по результатам post-evidence gate (LLM repair)."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from app.modules.agent.code_qa_runtime.models import CodeQaValidationResult
|
from app.modules.agent.runtime.steps.gates.post.post_gate import RuntimeValidationResult
|
||||||
from app.modules.agent.llm import AgentLlmService
|
from app.modules.agent.llm import AgentLlmService
|
||||||
|
|
||||||
|
|
||||||
class CodeQaAnswerRepairService:
|
class RuntimeAnswerRepairService:
|
||||||
def __init__(self, llm: AgentLlmService) -> None:
|
def __init__(self, llm: AgentLlmService) -> None:
|
||||||
self._llm = llm
|
self._llm = llm
|
||||||
|
|
||||||
@@ -14,7 +16,7 @@ class CodeQaAnswerRepairService:
|
|||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
draft_answer: str,
|
draft_answer: str,
|
||||||
validation: CodeQaValidationResult,
|
validation: RuntimeValidationResult,
|
||||||
prompt_payload: str,
|
prompt_payload: str,
|
||||||
) -> str:
|
) -> str:
|
||||||
repair_focus = self._repair_focus(validation.reasons)
|
repair_focus = self._repair_focus(validation.reasons)
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
"""Сборка финального результата пайплайна: диагностика и RuntimeFinalResult."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.steps.context.diagnostics import build_diagnostics_report
|
||||||
|
from app.modules.agent.runtime.steps.gates.post.post_gate import RuntimePostEvidenceGate, RuntimeValidationResult
|
||||||
|
from app.modules.agent.runtime.models import RuntimeDraftAnswer, RuntimeExecutionState, RuntimeFinalResult
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def assemble_final_result(
|
||||||
|
state: RuntimeExecutionState,
|
||||||
|
*,
|
||||||
|
draft: RuntimeDraftAnswer | None,
|
||||||
|
final_answer: str,
|
||||||
|
repair_used: bool,
|
||||||
|
llm_used: bool,
|
||||||
|
validation: RuntimeValidationResult | None = None,
|
||||||
|
timings_ms: dict[str, int] | None = None,
|
||||||
|
runtime_trace: list[dict] | None = None,
|
||||||
|
answer_policy_branch: str = "",
|
||||||
|
decision_reason: str = "",
|
||||||
|
pre_gate_input: dict[str, Any] | None = None,
|
||||||
|
post_gate_snapshot: dict[str, Any] | None = None,
|
||||||
|
resolved_target: str | None = None,
|
||||||
|
post_gate: RuntimePostEvidenceGate | None = None,
|
||||||
|
) -> RuntimeFinalResult:
|
||||||
|
diagnostics = build_diagnostics_report(
|
||||||
|
router_result=state.router_result,
|
||||||
|
retrieval_request=state.retrieval_request,
|
||||||
|
retrieval_result=state.retrieval_result,
|
||||||
|
evidence_bundle=state.evidence_pack,
|
||||||
|
answer_mode=state.answer_mode,
|
||||||
|
timings_ms=timings_ms or {},
|
||||||
|
resolved_target=resolved_target,
|
||||||
|
answer_policy_branch=answer_policy_branch,
|
||||||
|
decision_reason=decision_reason,
|
||||||
|
evidence_gate_input=pre_gate_input or {},
|
||||||
|
post_evidence_gate=post_gate_snapshot or {},
|
||||||
|
)
|
||||||
|
if validation is None and post_gate is not None and state.retrieval_request is not None:
|
||||||
|
validation = post_gate.validate(
|
||||||
|
answer=final_answer,
|
||||||
|
answer_mode=state.answer_mode,
|
||||||
|
degraded_message=state.degraded_message,
|
||||||
|
sub_intent=state.retrieval_request.sub_intent,
|
||||||
|
user_query=state.user_query,
|
||||||
|
evidence_pack=state.evidence_pack,
|
||||||
|
)
|
||||||
|
elif validation is None:
|
||||||
|
validation = RuntimeValidationResult(passed=True, action="return")
|
||||||
|
|
||||||
|
result = RuntimeFinalResult(
|
||||||
|
final_answer=final_answer.strip(),
|
||||||
|
answer_mode=state.answer_mode,
|
||||||
|
repair_used=repair_used,
|
||||||
|
llm_used=llm_used,
|
||||||
|
draft_answer=draft,
|
||||||
|
validation=validation,
|
||||||
|
router_result=state.router_result,
|
||||||
|
retrieval_request=state.retrieval_request,
|
||||||
|
retrieval_result=state.retrieval_result,
|
||||||
|
evidence_pack=state.evidence_pack,
|
||||||
|
diagnostics=diagnostics,
|
||||||
|
runtime_trace=list(runtime_trace or []),
|
||||||
|
)
|
||||||
|
LOGGER.warning(
|
||||||
|
"agent runtime executed: intent=%s sub_intent=%s answer_mode=%s repair_used=%s llm_used=%s",
|
||||||
|
state.router_result.intent if state.router_result else None,
|
||||||
|
state.router_result.query_plan.sub_intent if state.router_result and state.router_result.query_plan else None,
|
||||||
|
result.answer_mode,
|
||||||
|
result.repair_used,
|
||||||
|
result.llm_used,
|
||||||
|
)
|
||||||
|
return result
|
||||||
11
src/app/modules/agent/runtime/steps/gates/__init__.py
Normal file
11
src/app/modules/agent/runtime/steps/gates/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
"""Pre- и post-evidence gates пайплайна."""
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.steps.gates.pre.evidence_gate import EvidenceGateDecision, evaluate_evidence
|
||||||
|
from app.modules.agent.runtime.steps.gates.post.post_gate import RuntimePostEvidenceGate, RuntimeValidationResult
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"EvidenceGateDecision",
|
||||||
|
"evaluate_evidence",
|
||||||
|
"RuntimePostEvidenceGate",
|
||||||
|
"RuntimeValidationResult",
|
||||||
|
]
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
"""Post-evidence gate: валидация черновика ответа и решение repair/return."""
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.steps.gates.post.post_gate import RuntimePostEvidenceGate, RuntimeValidationResult
|
||||||
|
|
||||||
|
__all__ = ["RuntimePostEvidenceGate", "RuntimeValidationResult"]
|
||||||
@@ -1,10 +1,22 @@
|
|||||||
|
"""Post-evidence gate: валидация черновика ответа и решение repair/return."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from app.modules.agent.code_qa_runtime.models import CodeQaValidationResult
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
from app.modules.rag.code_qa_pipeline.answer_fact_curator import build_curated_answer_facts
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import EvidenceBundle
|
from app.modules.agent.runtime.steps.context.answer_fact_curator import build_curated_answer_facts
|
||||||
|
from app.modules.agent.runtime.steps.context.contracts import EvidenceBundle
|
||||||
|
|
||||||
|
|
||||||
|
class RuntimeValidationResult(BaseModel):
|
||||||
|
model_config = ConfigDict(extra="forbid")
|
||||||
|
|
||||||
|
passed: bool = False
|
||||||
|
action: str = "return"
|
||||||
|
reasons: list[str] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
_TOKEN_RE = re.compile(r"[a-zA-Zа-яА-Я0-9_/]+")
|
_TOKEN_RE = re.compile(r"[a-zA-Zа-яА-Я0-9_/]+")
|
||||||
_VAGUE_PHRASES = (
|
_VAGUE_PHRASES = (
|
||||||
@@ -22,7 +34,7 @@ _VAGUE_PHRASES = (
|
|||||||
_OPTIMISTIC_TRACE_CLAIMS = ("полностью восстанавливается", "полный поток выполнения", "полностью прослеживается")
|
_OPTIMISTIC_TRACE_CLAIMS = ("полностью восстанавливается", "полный поток выполнения", "полностью прослеживается")
|
||||||
|
|
||||||
|
|
||||||
class CodeQaPostEvidenceGate:
|
class RuntimePostEvidenceGate:
|
||||||
def validate(
|
def validate(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -32,25 +44,25 @@ class CodeQaPostEvidenceGate:
|
|||||||
sub_intent: str,
|
sub_intent: str,
|
||||||
user_query: str,
|
user_query: str,
|
||||||
evidence_pack: EvidenceBundle | None,
|
evidence_pack: EvidenceBundle | None,
|
||||||
) -> CodeQaValidationResult:
|
) -> RuntimeValidationResult:
|
||||||
normalized = (answer or "").strip()
|
normalized = (answer or "").strip()
|
||||||
if not normalized:
|
if not normalized:
|
||||||
return CodeQaValidationResult(passed=False, action="repair", reasons=["empty_answer"])
|
return RuntimeValidationResult(passed=False, action="repair", reasons=["empty_answer"])
|
||||||
if answer_mode in {"degraded", "insufficient"} and "недостат" not in normalized.lower():
|
if answer_mode in {"degraded", "insufficient"} and "недостат" not in normalized.lower():
|
||||||
return CodeQaValidationResult(passed=False, action="repair", reasons=["degraded_answer_missing_guardrail"])
|
return RuntimeValidationResult(passed=False, action="repair", reasons=["degraded_answer_missing_guardrail"])
|
||||||
if answer_mode == "not_found" and "не найден" not in normalized.lower():
|
if answer_mode == "not_found" and "не найден" not in normalized.lower():
|
||||||
return CodeQaValidationResult(passed=False, action="repair", reasons=["not_found_answer_missing_phrase"])
|
return RuntimeValidationResult(passed=False, action="repair", reasons=["not_found_answer_missing_phrase"])
|
||||||
if answer_mode == "ambiguous" and "не удалось однозначно разрешить" not in normalized.lower():
|
if answer_mode == "ambiguous" and "не удалось однозначно разрешить" not in normalized.lower():
|
||||||
return CodeQaValidationResult(passed=False, action="repair", reasons=["ambiguous_answer_missing_phrase"])
|
return RuntimeValidationResult(passed=False, action="repair", reasons=["ambiguous_answer_missing_phrase"])
|
||||||
if degraded_message and answer_mode != "normal" and len(normalized) < 24:
|
if degraded_message and answer_mode != "normal" and len(normalized) < 24:
|
||||||
return CodeQaValidationResult(passed=False, action="repair", reasons=["answer_too_short"])
|
return RuntimeValidationResult(passed=False, action="repair", reasons=["answer_too_short"])
|
||||||
if answer_mode != "normal" or evidence_pack is None:
|
if answer_mode != "normal" or evidence_pack is None:
|
||||||
return CodeQaValidationResult(passed=True, action="return")
|
return RuntimeValidationResult(passed=True, action="return")
|
||||||
|
|
||||||
reasons = self._normal_answer_reasons(normalized.lower(), sub_intent.upper(), user_query, evidence_pack)
|
reasons = self._normal_answer_reasons(normalized.lower(), sub_intent.upper(), user_query, evidence_pack)
|
||||||
if reasons:
|
if reasons:
|
||||||
return CodeQaValidationResult(passed=False, action="repair", reasons=_dedupe(reasons))
|
return RuntimeValidationResult(passed=False, action="repair", reasons=_dedupe(reasons))
|
||||||
return CodeQaValidationResult(passed=True, action="return")
|
return RuntimeValidationResult(passed=True, action="return")
|
||||||
|
|
||||||
def _normal_answer_reasons(self, answer: str, sub_intent: str, user_query: str, evidence_pack: EvidenceBundle) -> list[str]:
|
def _normal_answer_reasons(self, answer: str, sub_intent: str, user_query: str, evidence_pack: EvidenceBundle) -> list[str]:
|
||||||
reasons: list[str] = []
|
reasons: list[str] = []
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
"""Pre-evidence gate: проверка достаточности evidence перед вызовом LLM."""
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.steps.gates.pre.evidence_gate import EvidenceGateDecision, evaluate_evidence
|
||||||
|
|
||||||
|
__all__ = ["EvidenceGateDecision", "evaluate_evidence"]
|
||||||
@@ -1,23 +1,20 @@
|
|||||||
"""Shared evidence sufficiency check for the CODE_QA test pipeline."""
|
"""Проверка достаточности evidence для пайплайна CODE_QA."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import EvidenceBundle
|
from app.modules.agent.runtime.steps.context.contracts import EvidenceBundle
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
class EvidenceGateDecision:
|
class EvidenceGateDecision:
|
||||||
"""Result of evidence sufficiency check."""
|
|
||||||
|
|
||||||
passed: bool
|
passed: bool
|
||||||
failure_reasons: list[str] = field(default_factory=list)
|
failure_reasons: list[str] = field(default_factory=list)
|
||||||
degraded_message: str = ""
|
degraded_message: str = ""
|
||||||
|
|
||||||
|
|
||||||
def evaluate_evidence(bundle: EvidenceBundle) -> EvidenceGateDecision:
|
def evaluate_evidence(bundle: EvidenceBundle) -> EvidenceGateDecision:
|
||||||
"""Check evidence sufficiency by scenario; prevent confident answer without support."""
|
|
||||||
sub = (bundle.resolved_sub_intent or "EXPLAIN").upper()
|
sub = (bundle.resolved_sub_intent or "EXPLAIN").upper()
|
||||||
reasons: list[str] = []
|
reasons: list[str] = []
|
||||||
|
|
||||||
11
src/app/modules/agent/runtime/steps/generation/__init__.py
Normal file
11
src/app/modules/agent/runtime/steps/generation/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
"""Выбор промпта, сборка payload и вызов LLM для генерации ответа."""
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.steps.generation.prompt_selector import RuntimePromptSelector
|
||||||
|
from app.modules.agent.runtime.steps.generation.prompt_payload_builder import RuntimePromptPayloadBuilder
|
||||||
|
from app.modules.agent.runtime.steps.generation.generator import RuntimeAnswerGenerator
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"RuntimePromptSelector",
|
||||||
|
"RuntimePromptPayloadBuilder",
|
||||||
|
"RuntimeAnswerGenerator",
|
||||||
|
]
|
||||||
18
src/app/modules/agent/runtime/steps/generation/generator.py
Normal file
18
src/app/modules/agent/runtime/steps/generation/generator.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"""Тонкая обёртка над LLM для генерации ответа по имени промпта и payload."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from app.modules.agent.llm import AgentLlmService
|
||||||
|
|
||||||
|
|
||||||
|
class RuntimeAnswerGenerator:
|
||||||
|
"""Делегирует вызов LLM для генерации черновика ответа."""
|
||||||
|
|
||||||
|
def __init__(self, llm: AgentLlmService) -> None:
|
||||||
|
self._llm = llm
|
||||||
|
|
||||||
|
def generate(self, prompt_name: str, payload: str, *, log_context: str = "graph.project_qa.code_qa.answer") -> str:
|
||||||
|
return self._llm.generate(prompt_name, payload, log_context=log_context).strip()
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
|
"""Сборка JSON-полезной нагрузки для системного промпта по synthesis_input и evidence_pack."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from app.modules.rag.code_qa_pipeline.contracts import AnswerSynthesisInput, EvidenceBundle
|
from app.modules.agent.runtime.steps.context.contracts import AnswerSynthesisInput, EvidenceBundle
|
||||||
|
|
||||||
_LAYER_GUIDE = (
|
_LAYER_GUIDE = (
|
||||||
"- C0_SOURCE_CHUNKS: фактический код, это основной источник деталей.\n"
|
"- C0_SOURCE_CHUNKS: фактический код, это основной источник деталей.\n"
|
||||||
@@ -15,7 +17,7 @@ _LAYER_GUIDE = (
|
|||||||
_TOKEN_RE = re.compile(r"[a-zA-Zа-яА-Я0-9_/]+")
|
_TOKEN_RE = re.compile(r"[a-zA-Zа-яА-Я0-9_/]+")
|
||||||
|
|
||||||
|
|
||||||
class CodeQaPromptPayloadBuilder:
|
class RuntimePromptPayloadBuilder:
|
||||||
def build(
|
def build(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
"""Выбор имени системного промпта по sub_intent и answer_mode."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
class CodeQaPromptSelector:
|
class RuntimePromptSelector:
|
||||||
_PROMPTS = {
|
_PROMPTS = {
|
||||||
"ARCHITECTURE": "code_qa_architecture_answer",
|
"ARCHITECTURE": "code_qa_architecture_answer",
|
||||||
"EXPLAIN": "code_qa_explain_answer",
|
"EXPLAIN": "code_qa_explain_answer",
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
"""Пакет выполнения retrieval: адаптер к RAG-репозиторию и фабрика контекста репозитория."""
|
||||||
|
|
||||||
|
from app.modules.agent.runtime.steps.retrieval.adapter import RuntimeRetrievalAdapter
|
||||||
|
from app.modules.agent.runtime.steps.retrieval.repo_context import RuntimeRepoContextFactory
|
||||||
|
|
||||||
|
__all__ = ["RuntimeRetrievalAdapter", "RuntimeRepoContextFactory"]
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
"""Адаптер RAG-репозитория к runtime: retrieve_with_plan, retrieve_exact_files, consume_retrieval_report."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from time import perf_counter
|
from time import perf_counter
|
||||||
@@ -35,7 +37,7 @@ class SessionEmbeddingDimensions:
|
|||||||
return dim
|
return dim
|
||||||
|
|
||||||
|
|
||||||
class CodeQaRetrievalAdapter:
|
class RuntimeRetrievalAdapter:
|
||||||
def __init__(self, repository: RagRepository | None = None) -> None:
|
def __init__(self, repository: RagRepository | None = None) -> None:
|
||||||
if repository is None:
|
if repository is None:
|
||||||
from app.modules.rag.persistence.repository import RagRepository
|
from app.modules.rag.persistence.repository import RagRepository
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
|
"""Фабрика контекста репозитория (языки, слои RAG) для runtime."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.modules.rag.contracts.enums import RagLayer
|
from app.modules.rag.contracts.enums import RagLayer
|
||||||
from app.modules.rag.intent_router_v2.models import RepoContext
|
from app.modules.agent.intent_router_v2.models import RepoContext
|
||||||
|
|
||||||
|
|
||||||
class CodeQaRepoContextFactory:
|
class RuntimeRepoContextFactory:
|
||||||
_KNOWN_LAYERS = [
|
_KNOWN_LAYERS = [
|
||||||
RagLayer.CODE_ENTRYPOINTS,
|
RagLayer.CODE_ENTRYPOINTS,
|
||||||
RagLayer.CODE_SYMBOL_CATALOG,
|
RagLayer.CODE_SYMBOL_CATALOG,
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
from app.modules.agent.code_qa_runtime import CodeQaRuntimeExecutor
|
from app.modules.agent.runtime import AgentRuntimeExecutor, RuntimeRetrievalAdapter
|
||||||
from app.modules.agent.code_qa_runtime.retrieval_adapter import CodeQaRetrievalAdapter
|
from app.modules.agent.runtime.code_qa_runner_adapter import CodeQaRunnerAdapter
|
||||||
from app.modules.agent.code_qa_runner_adapter import CodeQaRunnerAdapter
|
|
||||||
from app.modules.agent.llm import AgentLlmService
|
from app.modules.agent.llm import AgentLlmService
|
||||||
from app.modules.agent.prompt_loader import PromptLoader
|
from app.modules.agent.llm.prompt_loader import PromptLoader
|
||||||
from app.modules.chat.direct_service import CodeExplainChatService
|
from app.modules.chat.direct_service import CodeExplainChatService
|
||||||
from app.modules.chat.dialog_store import DialogSessionStore
|
from app.modules.chat.dialog_store import DialogSessionStore
|
||||||
from app.modules.chat.repository import ChatRepository
|
from app.modules.chat.repository import ChatRepository
|
||||||
@@ -10,8 +9,8 @@ from app.modules.chat.module import ChatModule
|
|||||||
from app.modules.chat.session_resolver import ChatSessionResolver
|
from app.modules.chat.session_resolver import ChatSessionResolver
|
||||||
from app.modules.chat.task_store import TaskStore
|
from app.modules.chat.task_store import TaskStore
|
||||||
from app.modules.rag.persistence.repository import RagRepository
|
from app.modules.rag.persistence.repository import RagRepository
|
||||||
from app.modules.rag.persistence.story_context_repository import StoryContextRepository, StoryContextSchemaRepository
|
from app.modules.agent.runtime.story_context_repository import StoryContextRepository, StoryContextSchemaRepository
|
||||||
from app.modules.rag.explain import CodeExplainRetrieverV2, CodeGraphRepository, LayeredRetrievalGateway
|
from app.modules.agent.runtime.steps.explain import CodeExplainRetrieverV2, CodeGraphRepository, LayeredRetrievalGateway
|
||||||
from app.modules.rag.module import RagModule, RagRepoModule
|
from app.modules.rag.module import RagModule, RagRepoModule
|
||||||
from app.modules.shared.bootstrap import bootstrap_database
|
from app.modules.shared.bootstrap import bootstrap_database
|
||||||
from app.modules.shared.event_bus import EventBus
|
from app.modules.shared.event_bus import EventBus
|
||||||
@@ -45,8 +44,8 @@ class ModularApplication:
|
|||||||
_giga_client = GigaChatClient(_giga_settings, GigaChatTokenProvider(_giga_settings))
|
_giga_client = GigaChatClient(_giga_settings, GigaChatTokenProvider(_giga_settings))
|
||||||
_prompt_loader = PromptLoader()
|
_prompt_loader = PromptLoader()
|
||||||
self._agent_llm = AgentLlmService(client=_giga_client, prompts=_prompt_loader)
|
self._agent_llm = AgentLlmService(client=_giga_client, prompts=_prompt_loader)
|
||||||
_retrieval = CodeQaRetrievalAdapter(self.rag_repository)
|
_retrieval = RuntimeRetrievalAdapter(self.rag_repository)
|
||||||
_executor = CodeQaRuntimeExecutor(llm=self._agent_llm, retrieval=_retrieval)
|
_executor = AgentRuntimeExecutor(llm=self._agent_llm, retrieval=_retrieval)
|
||||||
self._agent_runner = CodeQaRunnerAdapter(_executor)
|
self._agent_runner = CodeQaRunnerAdapter(_executor)
|
||||||
self.direct_chat = CodeExplainChatService(
|
self.direct_chat = CodeExplainChatService(
|
||||||
retriever=self.code_explain_retriever,
|
retriever=self.code_explain_retriever,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from app.modules.agent.llm import AgentLlmService
|
|||||||
from app.modules.chat.evidence_gate import CodeExplainEvidenceGate
|
from app.modules.chat.evidence_gate import CodeExplainEvidenceGate
|
||||||
from app.modules.chat.session_resolver import ChatSessionResolver
|
from app.modules.chat.session_resolver import ChatSessionResolver
|
||||||
from app.modules.chat.task_store import TaskState, TaskStore
|
from app.modules.chat.task_store import TaskState, TaskStore
|
||||||
from app.modules.rag.explain import CodeExplainRetrieverV2, PromptBudgeter
|
from app.modules.agent.runtime.steps.explain import CodeExplainRetrieverV2, PromptBudgeter
|
||||||
from app.schemas.chat import ChatMessageRequest, TaskQueuedResponse, TaskResultType, TaskStatus
|
from app.schemas.chat import ChatMessageRequest, TaskQueuedResponse, TaskResultType, TaskStatus
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user