# GENERAL_QA / SUMMARY Workflow

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

| Поле | Значение |
|---|---|
| `domain` | `GENERAL` |
| `intent` | `GENERAL_QA` |
| `subintent` | `SUMMARY` |
| `workflow_id` | `v2.general_qa.summary` |
| `source` | `workflow.v2.general_summary` |

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

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

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

### 1) `RequireRagSessionStep`

Шаг проверяет, что есть активная RAG-сессия с проиндексированной документацией. Если сессии нет, workflow сразу возвращает ответ о недостатке опоры и ставит `missing_rag_session` в gate-решение. Это защищает от псевдо-генерации без данных.

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

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

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

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

### 2) `ResolveRetrievalPlanStep`

Шаг строит retrieval-план для общего QA-summary сценария. План включает профиль `general_qa_grounded_summary`, слои и фильтры, релевантные широкому запросу по документации. Параметры плана пишутся в trace для диагностики политики.

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

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

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

| Параметр | Как формируется |
|---|---|
| `context.retrieval_plan` | `self._resolver.resolve(context.route)` |
| `process.v2.retrieval_policy.retrieval_plan_resolved` | Лог `profile/layers/limit/filters` |

### 3) `FetchRagRowsStep`

Шаг запускает retrieval через `V2RagRetrievalAdapter` по нормализованному запросу и плану. Адаптер объединяет seed-документы по hints и основную retrieval-выдачу. Так в `retrieved_rows` попадают как точные, так и семантически близкие источники.

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

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

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

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

### 4) `PrepareCandidateRowsStep`

Шаг выполняет post-processing retrieval-строк: metadata lookup, merge и seed от target hints. Он формирует `context.rows` как итоговый набор кандидатов для summary-ranking. Одновременно пишет расширенный retrieval-trace с источниками кандидатов.

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

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

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

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

### 5) `BuildSummaryEvidenceStep`

Шаг строит shortlist summary-документов через `DocsEvidenceAssembler.assemble_summaries`. На этом этапе формируются оценка релевантности и причины совпадения для каждого документа. Результат нужен и для gate, и для финального ответа.

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

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

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

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

### 6) `ApplySummaryEvidenceGateStep`

Шаг проверяет, достаточно ли качества и объёма summary-evidence для grounded-ответа. Если gate не пройден, следующий шаг вернёт `gate_decision.message` без LLM-генерации. Это ограничивает риск неуверенных ответов.

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

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

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

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

### 7) `GenerateGeneralSummaryAnswerStep`

Шаг финализирует ответ по одной из трёх веток: gate-message, детерминированный режим, или LLM по prompt. При LLM-ветке формируется `prompt_input` из запроса и списка опорных документов, затем вызывается `llm.generate`. На выходе всегда ставится `answer_generated_payload` с режимом и длиной ответа.

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

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

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

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

