# DOC_EXPLAIN / SUMMARY Workflow

## Контракт сабинтента

| Поле | Значение |
|---|---|
| `domain` | `DOCS` |
| `intent` | `DOC_EXPLAIN` |
| `subintent` | `SUMMARY` |
| `workflow_id` | `v2.docs_explain.summary` |
| `source` | `workflow.v2.summary` |

## Диаграмма флоу

```mermaid
flowchart TD
    A["RequireRagSessionStep"] --> B["ResolveRetrievalPlanStep"]
    B --> C["FetchRagRowsStep"]
    C --> D["PrepareCandidateRowsStep"]
    D --> E["BuildSummaryEvidenceStep"]
    E --> F["ApplySummaryEvidenceGateStep"]
    F --> G["GenerateSummaryAnswerStep"]
```

## Шаги процесса

### 1) `RequireRagSessionStep`

Шаг валидирует наличие активной RAG-сессии до retrieval. Если `rag_session_id` отсутствует, workflow завершает обработку и записывает сообщение об отсутствии опоры. Это предотвращает запуск последующих шагов без индекса проекта.

**Входные параметры**

| Параметр | Откуда берётся | Описание |
|---|---|---|
| `context.rag_session_id` | `V2Process` -> `DocExplainSummaryContext` | Идентификатор RAG-сессии |
| `self._missing_message` | Конфигурация в `graph.py` | Ответ при отсутствии сессии |
| `self._missing_gate` | Конфиг шага | Опциональный gate для раннего выхода |

**Выходные параметры**

| Параметр | Как формируется |
|---|---|
| `context.answer` | Заполняется `missing_message` при пустом `rag_session_id` |
| `context.gate_decision` | Заполняется, если передан `missing_gate` |
| `context.answer_generated_payload` | Формируется как `{"answer_mode", "answer_length"}` при раннем ответе |

### 2) `ResolveRetrievalPlanStep`

Шаг вызывает policy-резолвер и строит `RetrievalPlan` для doc-summary сценария. Профиль и фильтры зависят от сигналов route: endpoint/architecture/logic/domain. Результат сохраняется в контекст и логируется в trace.

**Входные параметры**

| Параметр | Откуда берётся | Описание |
|---|---|---|
| `context.route` | `intent_router` | Route с `target_terms` и `anchors` |
| `self._resolver` | DI из `graph.py` | Реализация `RetrievalPlanResolver` |
| `context.answer` | Предыдущие шаги | При наличии ответа шаг пропускается |

**Выходные параметры**

| Параметр | Как формируется |
|---|---|
| `context.retrieval_plan` | `self._resolver.resolve(context.route)` |
| `process.v2.retrieval_policy.retrieval_plan_resolved` | Лог профиля, слоёв, лимита и фильтров |

### 3) `FetchRagRowsStep`

Шаг выполняет retrieval по сформированному плану. Внутри адаптера объединяются seed-результаты по `target_doc_hints` и основной retrieval. Это даёт более устойчивую выдачу как по явным hints, так и по семантическому совпадению.

**Входные параметры**

| Параметр | Откуда берётся | Описание |
|---|---|---|
| `context.rag_session_id` | Контекст workflow | Сессия для поиска |
| `context.route.normalized_query` | Route | Нормализованный запрос |
| `context.retrieval_plan` | `ResolveRetrievalPlanStep` | План retrieval |

**Выходные параметры**

| Параметр | Как формируется |
|---|---|
| `context.retrieved_rows` | `await rag_adapter.fetch_rows(...)` |

### 4) `PrepareCandidateRowsStep`

Шаг обогащает найденные строки через metadata lookup и подмешивает кандидатов из `target_doc_hints`. На выходе формируется `context.rows`, который уже используется для построения summary-evidence. Шаг также пишет подробные retrieval-логи для анализа ранжирования.

**Входные параметры**

| Параметр | Откуда берётся | Описание |
|---|---|---|
| `context.retrieved_rows` | `FetchRagRowsStep` | Строки retrieval |
| `context.route` | Route | Сигналы hints/aliases/terms |
| `self._builder` | `CandidateRowsBuilder()` | Логика merge и metadata поиска |

**Выходные параметры**

| Параметр | Как формируется |
|---|---|
| `context.rows` | `prepared.rows` из `CandidateRowsBuilder.build(...)` |
| `process.v2.rag_retrieval.rag_rows_fetched` | Лог выборки и источников кандидатов |

### 5) `BuildSummaryEvidenceStep`

Шаг преобразует candidate rows в список `RetrievedSummary` через `DocsEvidenceAssembler.assemble_summaries`. Здесь формируется shortlist документов с оценками и причинами совпадения, который затем проверяется gate. Дополнительно логируются ranking-разборы.

**Входные параметры**

| Параметр | Откуда берётся | Описание |
|---|---|---|
| `context.rows` | `PrepareCandidateRowsStep` | Подготовленные кандидаты |
| `context.route` | Route | Сигналы для ranking |
| `self._assembler` | DI из `graph.py` | Сборщик summary evidence |

**Выходные параметры**

| Параметр | Как формируется |
|---|---|
| `context.documents` | `assemble_summaries(context.rows, context.route)` |
| `process.v2.evidence.evidence_assembled` | Лог `document_count` и списка путей |

### 6) `ApplySummaryEvidenceGateStep`

Шаг оценивает достаточность и надёжность собранного summary-evidence. Решение gate влияет на дальнейшее формирование ответа: либо прямой отказ/ограничение, либо генерация summary. Результат логируется в pipeline.

**Входные параметры**

| Параметр | Откуда берётся | Описание |
|---|---|---|
| `context.route` | Route | Контекст запроса |
| `context.documents` | `BuildSummaryEvidenceStep` | Кандидаты summary |
| `self._gate` | DI из `graph.py` | Правила валидации evidence |

**Выходные параметры**

| Параметр | Как формируется |
|---|---|
| `context.gate_decision` | `self._gate.check_summaries(context.route, context.documents)` |
| `process.v2.pipeline.evidence_gate_checked` | Лог `passed/reason/answer_mode` |

### 7) `GenerateSummaryAnswerStep`

Шаг формирует финальный ответ: gate-message, детерминированный ответ или LLM-генерация. При LLM-ветке собирается `prompt_input` из пользовательского запроса, route-сигналов и найденных summary-блоков. Итог всегда записывается в `context.answer` и сопровождается `answer_generated_payload`.

**Входные параметры**

| Параметр | Откуда берётся | Описание |
|---|---|---|
| `context.documents` | `BuildSummaryEvidenceStep` | Опорные summary-документы |
| `context.gate_decision` | `ApplySummaryEvidenceGateStep` | Режим допуска к генерации |
| `context.workflow_llm_enabled` | `V2Process` | Флаг LLM/детерминизм |
| `context.prompt_name` | `V2Process` | Имя prompt-шаблона |
| `self._llm` | DI из `graph.py` | LLM-сервис |

**Выходные параметры**

| Параметр | Как формируется |
|---|---|
| `context.prompt_input` | Склеивается из route + shortlist документов |
| `context.answer` | Gate-message / deterministic summary / результат `llm.generate(...)` |
| `context.answer_generated_payload` | `{"answer_mode", "answer_length"}` по выбранной ветке |

