# ГЛАВА 20. ПАТТЕРНЫ ПРОЕКТИРОВАНИЯ LLM-ПРИЛОЖЕНИЙ

---

В 1994 году «Банда четырёх» (Gang of Four) выпустила книгу *Design Patterns*. Она не изобрела паттерны — паттерны уже существовали в коде тысяч проектов. Книга дала им **имена**. Когда у решения есть имя, инженеры перестают объяснять его заново на каждом совещании. «Используем Observer» — и комната понимает. Не потому что Observer сложен, а потому что имя заменяет десять минут рисования на доске.

LLM-инженерия к 2026 году находится в похожей точке. В [главах 6](06_prompt_is_a_protocol.md)–[19](19_fine_tuning_and_post_training.md) книги десятки паттернов — от структуры промпта до агентной архитектуры — разбросаны по контексту, в котором они впервые понадобились. Chain-of-Thought объяснялся в [главе 9](09_multistep_reasoning.md), ReAct — в [главе 10](10_agent_not_chat.md), CoVe — в [главе 13](13_anti_hallucination_loop.md). Каждый раз паттерн раскрывался «изнутри» — механизм, формулы, когда работает и почему. Но инженеру, проектирующему новую систему, нужен другой вид: **каталог**. Одно место, где видны все паттерны, их назначение и критерии выбора.

Эта глава — каталог. Она не повторяет механику (за ней — в исходную главу), а даёт обзорную карту и добавляет паттерны, которые ранее не были формализованы: **Router**, **Human-in-the-Loop**, **Fallback Chain**, **Retrieval-Gated Generation**, **MapReduce**.

Формат каждого паттерна: **Имя → Проблема → Решение → Когда использовать → Когда НЕ использовать → Ссылка на детальное объяснение**.

---

## 20.1. Каталог паттернов: обзорная таблица

Двадцать паттернов, отсортированных от атомарных (уровень одного вызова) к составным (уровень системы):

| # | Паттерн | Проблема | Решение (одно предложение) | Глава |
|---|---------|---------|---------------------------|-------|
| 1 | **Chain-of-Thought** | Модель ошибается в рассуждениях | «Думай шаг за шагом» выводит промежуточную логику | Гл. 9 |
| 2 | **Constrained Decoding** | Выход должен соответствовать схеме | JSON Schema / grammar-based generation ограничивает формат | Гл. 6 |
| 3 | **Semantic Exoskeleton** | Секции промпта интерферируют | XML-теги разделяют блоки промпта | Гл. 7 |
| 4 | **Self-Consistency** | Один ответ может быть ошибочным | Генерируй N ответов, голосуй за консенсус | Гл. 8 |
| 5 | **Best-of-N** | Качество варьируется между генерациями | Генерируй N, оцени судьёй, выбери лучший | Гл. 8 |
| 6 | **Generator-Verifier** | Один вызов LLM может галлюцинировать | Второй вызов/проверка валидирует выход | Гл. 13 |
| 7 | **CoVe** | Факты требуют верификации | Генерируй → сформулируй вопросы → ответь независимо → исправь | Гл. 13 |
| 8 | **RAG** | Модель не знает доменную информацию | Найди контекст → дополни промпт → генерируй | Гл. 12 |
| 9 | **Self-RAG** | Не каждый запрос требует retrieval | Модель сама решает, когда извлекать | Гл. 12 |
| 10 | **GraphRAG** | Нужно рассуждение по связям между сущностями | Графовый retrieval с entity relationships | Гл. 12 |
| 11 | **Retrieval-Gated Generation** | Модель должна отвечать только при наличии evidence | Проверь confidence retrieval перед генерацией | **Новый** |
| 12 | **Router** | Разные задачи требуют разных моделей/промптов | Классификатор маршрутизирует вход к нужному обработчику | **Новый** |
| 13 | **Planner-Executor** | Сложная задача требует декомпозиции | Планировщик создаёт план, исполнитель выполняет шаги | Гл. 9, 10 |
| 14 | **ReAct** | Агенту нужны и рассуждение, и действие | Чередуй Thought / Action / Observation | Гл. 10 |
| 15 | **Reflexion** | Агент повторяет одни и те же ошибки | Агент рефлексирует над неудачами и корректирует стратегию | Гл. 10 |
| 16 | **Multi-Agent** | Задача требует нескольких специализированных ролей | Оркестратор делегирует агентам-специалистам | Гл. 10 |
| 17 | **MapReduce** | Вход слишком велик для одного context window | Разбей → обработай фрагменты → объедини результаты | **Новый** |
| 18 | **Human-in-the-Loop** | Некоторые решения требуют одобрения человека | Автоматизируй безопасный путь, блокируй рискованный | **Новый** |
| 19 | **Fallback Chain** | Основная модель/промпт может отказать | Цепочка от основного к запасному варианту | **Новый** |
| 20 | **ColPali** | Document retrieval теряет визуальный контекст | Visual embeddings из скриншотов страниц | Гл. 18 |
> Паттерны в таблице пронумерованы для ссылок внутри главы; нумерация не означает приоритет.

Далее — детальные карточки. Паттерны, подробно разобранные ранее (Self-Consistency, Best-of-N, CoVe, ReAct, Reflexion, Multi-Agent, RAG, Self-RAG, GraphRAG), даны в сжатой форме со ссылкой на главу. Новые паттерны (Router, Human-in-the-Loop, Fallback Chain, Retrieval-Gated Generation, MapReduce) раскрыты полностью.

---

## 20.2. Router

### Проблема

Ваша система обрабатывает разнородные входы: простые справочные вопросы, сложный анализ, генерация кода, работа с изображениями. Один промпт и одна модель не подходят для всех случаев — frontier-модель избыточна для FAQ, а лёгкая модель не справится с многошаговым рассуждением. Без маршрутизации вы платите за overkill на простых задачах или получаете провал на сложных.

### Решение

Лёгкий классификатор на входе определяет тип задачи и направляет запрос к подходящему обработчику:

```
Input → Router (быстрый классификатор)
           ├── Handler A: frontier model + полный промпт (сложный анализ)
           ├── Handler B: лёгкая модель (FAQ, справки)
           ├── Handler C: rule-based (шаблонные ответы)
           └── Handler D: специализированный агент (код, данные)
```

### Реализация

Три подхода к построению Router, от простого к сложному:

**1. Rule-based Router** — ключевые слова, regex, длина запроса. Самый предсказуемый вариант: если условия можно описать правилами, LLM не нужен. Маршрутизация по ключевым словам, длине запроса, наличию вложений.

**2. Embedding-based Router** — embed запрос, найти ближайший кластер. Для каждого маршрута формулируется текстовое описание (например, «написать код, исправить баг, рефакторинг» для code-handler). Запрос embed-ится той же моделью, cosine similarity определяет лучший маршрут. Плюс: не требует вызова LLM на каждый запрос. Минус: нужен набор описаний маршрутов.

**3. LLM-based Router** — дешёвая модель классифицирует запрос в одну из категорий. Самый гибкий, но самый дорогой из трёх. Оправдан, когда категории сложно описать правилами или embedding'ами.

> **Промпт для генерации Router:**
> *Напиши три варианта Router на Python с OpenAI SDK: (1) rule-based — маршрутизация по ключевым словам и regex, (2) embedding-based — cosine similarity между запросом и описаниями маршрутов через `text-embedding-3-small`, (3) LLM-based — дешёвая модель (mini/nano-tier) классифицирует запрос в JSON `{"category": "..."}` через `response_format={"type": "json_object"}`. Категории: code, analysis, faq, creative. Каждый вариант — отдельная функция `route(query: str) -> str`. Добавь type hints и обработку ошибок.*

### Когда использовать

- Система обслуживает несколько типов задач с разными требованиями к качеству, стоимости или latency.
- Вы хотите снизить inference-расходы, отправляя простые запросы на дешёвую модель.
- Разные задачи требуют разных tool sets или системных промптов.

### Когда НЕ использовать

- Вся система решает одну задачу — один промпт справляется.
- Объём запросов мал, и экономия от маршрутизации не окупает сложность.

### Связь с другими паттернами

Router часто стоит *перед* другими паттернами: Router → Planner-Executor для сложных задач, Router → RAG для вопросов с доменной спецификой, Router → rule-based для шаблонных ответов. В [главе 24 (§24.9)](24_landscape_2026.md) dynamic model routing упоминается как тренд inference-экономики; здесь паттерн формализован как переиспользуемый компонент.

---

## 20.3. Planner-Executor (с Verifier)

### Проблема

Задача из нескольких шагов, где каждый шаг может провалиться, а результат предыдущего определяет следующий.

### Решение

Три роли: **Planner** формирует план, **Executor** выполняет каждый шаг, **Verifier** проверяет результат. Оркестратор управляет циклом.

```
[Цель] → Planner → [План: шаг 1, 2, ..., N]
              ↓
         Executor (шаг 1) → Verifier → OK? → Executor (шаг 2) → ...
              ↑                           ↓
              └──── Replan ◄──── FAIL ◄───┘
```

**Подробное объяснение:**
- Декомпозиция и DAG-планирование → [глава 9 (§9.2–9.3)](09_multistep_reasoning.md)
- ReAct loop и Plan-and-Execute → [глава 10 (§10.2)](10_agent_not_chat.md)
- Минимальный контур Planner/Executor/Verifier/Orchestrator → [глава 23 (§23.4)](23_getting_started.md)
- Generator-Verifier → [глава 13 (§13.1)](13_anti_hallucination_loop.md)

### Варианты

| Вариант | Особенность | Глава |
|---------|------------|-------|
| DAG-планирование | Шаги могут выполняться параллельно | Гл. 9, §9.3 |
| ReAct | Мысль → действие → наблюдение в цикле | Гл. 10, §10.2 |
| Plan-and-Execute | Полный план вперёд → последовательное исполнение | Гл. 10, §10.2 |
| Mode-Architect | Архитектор задаёт стратегию, исполнитель реализует | Гл. 9, §9.4 |

### Когда использовать

- Задача требует нескольких шагов, и ошибка на одном шаге ломает весь результат.
- Нужна возможность перепланирования при неожиданном результате.

### Когда НЕ использовать

- Single-shot Q&A, где Chain-of-Thought достаточен.
- Задача не допускает задержку, вносимую циклом plan → execute → verify.

---

## 20.4. Generator-Verifier (Critic)

### Проблема

Один вызов LLM порождает fluent, но фактически неточный текст. Модель оптимизирована на гладкость, а не на достоверность.

### Решение

Принцип «два набора глаз»: Generator создаёт кандидат, Verifier проверяет. Пилот и второй пилот в кабине (подробная аналогия — [глава 13, §13.1](13_anti_hallucination_loop.md)).

**Варианты:**

| Вариант | Механизм | Глава |
|---------|---------|-------|
| Два вызова одной модели | Разные системные промпты (generate vs critique) | Гл. 13, §13.1 |
| Разные модели | Generator = creative, Verifier = analytical | Гл. 13, §13.1 |
| CoVe | Generate → plan questions → answer independently → revise | Гл. 13, §13.2 |
| LLM-as-Judge | Модель оценивает выход по rubric | Гл. 14 |
| Код/тесты как Verifier | Linter, unit tests, schema validation | Гл. 13, §13.3 |

### Когда использовать

- High-stakes выходы: медицина, финансы, юридические документы, клиентские коммуникации.
- Задачи с объективно проверяемыми фактами.

### Когда НЕ использовать

- Low-stakes, high-throughput задачи, где latency двойного вызова неприемлема.
- Творческая генерация, где «правильного ответа» нет.

---

## 20.5. Human-in-the-Loop

### Проблема

Некоторые действия слишком рискованны для полной автоматизации: финансовые транзакции, удаление данных, отправка сообщений клиентам, изменение production-инфраструктуры. Даже лучший агент с Verifier может ошибиться. Цена ошибки — не «плохой ответ в чате», а деньги, репутация, безопасность.

### Решение

Автоматизируй безопасный путь, блокируй рискованный до подтверждения человеком. Три режима:

**1. Confirmation Gate** — агент формирует действие, но не выполняет до явного OK. Компонент `ConfirmationGate` хранит список high-risk инструментов (`delete_record`, `send_email`, `execute_payment`, `deploy`) и пороги (например, `amount > 10_000`). Если действие попадает под gate — выполнение блокируется до подтверждения человека. При отказе — возвращается `{"status": "blocked", "reason": "human rejected"}`.

> **Промпт для генерации Confirmation Gate:**
> *Напиши класс `ConfirmationGate` на Python: метод `should_gate(tool_name, args)` проверяет, нужно ли одобрение (по списку high-risk инструментов и порогам в args). Метод `request_approval(tool_name, args)` запрашивает подтверждение (через CLI для прототипа, через webhook/API для production). Функция `agent_step(tool_name, args, gate)` — обёртка: если gate сработал и человек отклонил, возвращает блокировку; иначе выполняет инструмент. Добавь поддержку async-подтверждения через очередь.*

**2. Review Queue** — агент выполняет задачу, результат попадает в очередь на ревью перед доставкой. Паттерн описан в [главе 17 (§17.7)](17_observability_and_operations.md) в контексте наблюдаемости: оператор просматривает выходы, ловит систематические ошибки, настраивает пороги.

**3. Escalation** — агент сам определяет неуверенность и маршрутизирует к человеку. Механизм: после генерации оценивается confidence (через logprobs, self-rating или внешний классификатор). Если confidence ниже порога (например, 0.7) — результат уходит человеку как черновик, а не как финальный ответ. Ключевой момент: передавайте черновик вместе с эскалацией, чтобы человек не начинал с нуля.

> **Промпт для генерации Escalation-обёртки:**
> *Напиши функцию `agent_with_escalation(query, confidence_threshold=0.7)` на Python: генерирует ответ через LLM, оценивает confidence (logprobs или self-rating), при низкой уверенности возвращает `{"status": "escalated", "draft": ...}`, иначе `{"status": "completed", "result": ...}`. Покажи два варианта оценки confidence: через logprobs API и через повторный LLM-вызов с rubric.*

### Критерии для решения «нужен ли gate»

Три вопроса:

| Критерий | Вопрос | Примеры |
|----------|--------|---------|
| **Необратимость** | Можно ли отменить действие? | DELETE без бэкапа, отправка email — нельзя |
| **Цена ошибки** | Финансовые, репутационные, safety-последствия? | Платёж клиенту, юрид. документ — высокая цена |
| **Уверенность** | Достаточно ли информации у агента? | Неоднозначный запрос, противоречивые данные — нет |

**Правило:** если ответ на любой из трёх вопросов — «да, это рискованно» — ставьте gate.

### Когда использовать

- Действия с реальными последствиями (деньги, данные, инфраструктура).
- Домены с регуляторными требованиями (финансы, медицина).
- Начальный период внедрения, пока не накоплена статистика ошибок.

### Когда НЕ использовать

- Полностью обратимые, низкорискованные действия — gate убьёт throughput.
- Задачи с latency-требованиями, где ожидание человека неприемлемо.

---

## 20.6. Fallback Chain

### Проблема

Production-система не может позволить себе ответ «сервис недоступен». Основная модель может отказать: rate limit, timeout, content filter, неожиданный формат ответа. Основной промпт может не работать для edge-case. Один сбой не должен ронять весь пайплайн.

### Решение

Упорядоченная цепочка стратегий от оптимальной к минимально приемлемой:

```
Primary:   frontier model + полный промпт
    ↓ fail
Fallback 1: та же модель + упрощённый промпт
    ↓ fail
Fallback 2: лёгкая модель (GPT-5.4-mini, Claude Haiku 4.5)
    ↓ fail
Fallback 3: кэшированный ответ / статический default
    ↓ fail
Error:     структурированный отказ с объяснением
```

### Реализация

Логика проста: упорядоченный список шагов (`FallbackStep`), каждый со своим `name` и `execute`-функцией. Функция `fallback_chain` проходит по цепочке: первый успешный результат возвращается с указанием источника (`{"status": "ok", "source": step.name}`). Если все шаги провалились — возвращается структурированная ошибка со всеми накопленными errors. Типичная цепочка: frontier-модель с полным промптом → та же модель с упрощённым → лёгкая модель → кэш.

> **Промпт для генерации Fallback Chain:**
> *Напиши на Python класс `FallbackChain` с упорядоченным списком шагов (dataclass `FallbackStep` с `name: str` и `execute: Callable[[str], str]`). Метод `run(query)` последовательно пробует каждый шаг с try/except, накапливает ошибки, возвращает первый успешный результат с `source`. Добавь логирование каждого fallback-перехода и подсчёт метрик. Пример цепочки: Claude Opus 4.6 (full prompt) → Claude Opus 4.6 (simplified) → Claude Haiku 4.5 (simplified) → cache lookup. Каждый уровень должен возвращать ответ в одном формате.*

Важный нюанс: каждый уровень должен возвращать результат в **одном и том же формате**. Downstream-код не должен знать, пришёл ответ от frontier-модели или из кэша. Это обеспечивает Constrained Decoding (§20.9) на каждом уровне.

### Мониторинг

Fallback Chain — один из паттернов, который *требует* наблюдаемости ([глава 17](17_observability_and_operations.md)). Если система регулярно проваливается на fallback 2-3, это сигнал: основной промпт нужно чинить, а не мириться с деградацией.

### Когда использовать

- Production-системы, где uptime критичен.
- Multi-provider setup, где один API-провайдер может быть недоступен.
- Задачи с постоянным потоком запросов (customer support, data pipelines).

### Когда НЕ использовать

- Development и evaluation: вам *нужно* видеть все ошибки, а не маскировать их fallback'ами.
- Задачи, где качество fallback-ответа хуже, чем отсутствие ответа.

---

## 20.7. Retrieval-Gated Generation

### Проблема

Классический RAG извлекает документы и генерирует ответ, даже если извлечённый контекст нерелевантен. Результат: модель галлюцинирует, опираясь на слабый контекст, — хуже, чем если бы просто сказала «не знаю». Проблема особенно остра в fact-critical доменах: медицинские справочники, юридические базы, финансовая аналитика.

### Решение

Добавь **gate** между retrieval и generation. Если confidence retrieval ниже порога — не генерируй, а верни «недостаточно информации» или запроси уточнение.

```
Query → Retriever → [top-K документов, scores]
                        ↓
                     Gate: score > threshold?
                     ├── YES → Generator (с контекстом) → Ответ
                     └── NO  → "Недостаточно информации для ответа"
```

### Реализация

Логика: retriever возвращает top-K документов со score. Фильтруем по `score_threshold`. Если количество релевантных документов меньше `min_relevant_docs` — возвращаем `{"status": "insufficient_evidence"}` вместо генерации. Иначе — передаём контекст в generator и возвращаем `{"status": "answered", "answer": ..., "sources": N}`.

> **Промпт для генерации Retrieval-Gated Generation:**
> *Напиши функцию `retrieval_gated_generate(query, retriever, generator, score_threshold=0.75, min_relevant_docs=1)` на Python. Retriever имеет метод `.search(query, top_k)`, возвращающий список `RetrievalResult(text, score)`. Фильтруй по порогу, при недостаточном evidence возвращай отказ с top_score для диагностики. Иначе объединяй тексты релевантных документов как контекст для generator. Добавь type hints и логирование.*

### Настройка порогов

Два параметра: `score_threshold` и `min_relevant_docs`. Правильные значения зависят от домена и embeddings. Подход:

1. Соберите eval-набор: 50+ запросов с known-answer + 50+ запросов без ответа в базе.
2. Постройте precision-recall кривую для разных порогов.
3. Выберите порог, при котором recall приемлем (не отклоняет слишком много хороших запросов), а precision высока (не пропускает «пустые» контексты).

### Связь с Self-RAG

Self-RAG ([глава 12, §12.7](12_rag.md)) — более сложный вариант: модель *сама* решает, нужен ли retrieval, и *сама* оценивает, полезен ли извлечённый контекст. Retrieval-Gated Generation — упрощённая версия для случаев, когда вы не хотите полагаться на суждения модели о качестве контекста и предпочитаете детерминированный порог.

### Когда использовать

- Fact-critical приложения, где галлюцинация хуже, чем отказ отвечать.
- Системы, где пользователь понимает ответ «не знаю» и может переформулировать вопрос.
- Доменные Q&A-системы с ограниченной knowledge base.

### Когда НЕ использовать

- Brainstorming и creative-задачи, где генерация «вдохновения» ценнее точности.
- Случаи, где «не знаю» неприемлемо и любой ответ лучше молчания.

---

## 20.8. MapReduce для длинных входов

### Проблема

Вход не помещается в context window. Или помещается, но слишком длинный для качественной обработки — внимание модели размывается на больших контекстах ([глава 5](05_long_context.md) о lost-in-the-middle). Примеры: суммаризация 500-страничного документа, анализ кодовой базы из сотен файлов, обработка логов за месяц.

### Решение

Разбей вход на фрагменты → обработай каждый независимо → объедини результаты. Название — по аналогии с MapReduce из распределённых вычислений: map-функция обрабатывает фрагменты параллельно, reduce-функция агрегирует результаты.

### Три варианта

**1. Map-Reduce (классический):**

```
[Документ] → split → [Chunk 1] → LLM → [Summary 1]
                      [Chunk 2] → LLM → [Summary 2]  → Reduce LLM → [Final Summary]
                      [Chunk 3] → LLM → [Summary 3]
```

Плюс: фрагменты обрабатываются параллельно.
Минус: reduce-шаг не видит оригинальные детали, только промежуточные результаты.

**2. Map-Refine (последовательный):**

```
[Chunk 1] → LLM → [Draft 1]
[Chunk 2] + [Draft 1] → LLM → [Draft 2]
[Chunk 3] + [Draft 2] → LLM → [Draft 3 = Final]
```

Плюс: каждый шаг учитывает накопленный контекст.
Минус: полностью последовательный, нельзя распараллелить.

**3. Hierarchical (рекурсивный):**

```
Level 0: chunks → LLM → summaries
Level 1: summaries → group → LLM → meta-summaries
Level 2: meta-summaries → LLM → final
```

Плюс: масштабируется на очень большие входы (книги, кодовые базы).
Минус: максимальная потеря деталей.

### Реализация

Алгоритм: (1) split текста на чанки с перекрытием (overlap), чтобы не терять контекст на границах; (2) map — параллельная обработка каждого чанка через LLM (например, «суммаризуй в 3–5 ключевых пунктов»); (3) reduce — объединение промежуточных результатов в финальный сводный вывод.

> **Промпт для генерации MapReduce-суммаризатора:**
> *Напиши функцию `map_reduce_summarize(text, llm_call, chunk_size=4000, overlap=200)` на Python. Разбивает текст на чанки с перекрытием, обрабатывает каждый параллельно через `ThreadPoolExecutor` (шаг map), затем объединяет результаты одним вызовом LLM (шаг reduce). `llm_call` — функция `(str) -> str`. Добавь вариант map-refine (последовательный). Обработка ошибок: если один чанк провалился — продолжить без него, но залогировать.*

### Когда использовать

- Суммаризация документов, превышающих context window.
- Анализ больших кодовых баз (по файлам/модулям).
- Extraction из массивов (логи, таблицы, отзывы).

### Когда НЕ использовать

- Задачи, требующие глобального контекста: «какова общая нарративная арка романа?» — map-reduce потеряет нити между фрагментами.
- Если документ помещается в context window frontier-модели с длинным контекстом (1M+ токенов у Gemini 3.x, Claude Opus 4.6, GPT-5.4) — попробуйте сначала полный контекст. MapReduce — не единственный ответ на «длинный вход» ([глава 5](05_long_context.md)).

---

## 20.9. Constrained Decoding

### Проблема

Выход LLM должен быть машиночитаемым: JSON для API, SQL для базы данных, YAML для конфигурации. Free-form generation порождает невалидные структуры, пропущенные поля, лишние комментарии.

### Решение

Ограничь пространство генерации формальной грамматикой или схемой. Подробно — [глава 6](06_prompt_is_a_protocol.md), где structured outputs рассмотрены как ключевой элемент промпт-протокола.

**Механизмы:**
- **JSON Schema** через `response_format` (OpenAI, Anthropic) — модель гарантирует структуру.
- **Function calling / tool use** — модель заполняет параметры заранее определённых функций.
- **Grammar-based decoding** (Outlines, llama.cpp grammars) — на этапе сэмплирования допускаются только токены, валидные по грамматике.

### Когда использовать

- Любой выход, который парсится downstream-кодом.
- Заполнение форм, extraction из текста, classification.

### Когда НЕ использовать

- Free-form текст (объяснения, эссе, творческий контент).

---

## 20.10. Краткие карточки ранее разобранных паттернов

### Chain-of-Thought ([глава 9](09_multistep_reasoning.md))

Промпт «Think step by step» или structured reasoning template выводит промежуточные шаги, повышая точность на задачах с рассуждениями. Работает лучше всего на задачах с объективно проверяемым ответом (математика, логика, код). Подробно — [глава 9, §9.1](09_multistep_reasoning.md).

### Self-Consistency ([глава 8, §8.3](08_multiple_hypotheses.md))

Сгенерируй N ответов с повышенной температурой → выбери наиболее частый (majority vote). Повышает точность за счёт N-кратного увеличения расхода токенов.

### Best-of-N ([глава 8, §8.4](08_multiple_hypotheses.md))

Сгенерируй N ответов → оцени каждый моделью-судьёй или функцией качества → верни лучший. Отличие от Self-Consistency: выбор по качеству, а не по частоте.

### CoVe ([глава 13, §13.2](13_anti_hallucination_loop.md))

Generate → plan verification questions → answer questions independently → revise original. Снижает hallucination rate на фактоидных задачах.

### ReAct ([глава 10, §10.2](10_agent_not_chat.md))

Thought → Action → Observation в цикле. «Рассуждай вслух, действуй, наблюдай результат». Стандартный agent loop для систем с tool use.

### Reflexion ([глава 10, §10.2](10_agent_not_chat.md))

Агент after failure записывает рефлексию в память и использует её при повторной попытке. Нужна persistent memory между итерациями.

### Multi-Agent ([глава 10, §10.4](10_agent_not_chat.md))

Оркестратор делегирует задачи специализированным агентам. Варианты: supervisor, hierarchy, peer-to-peer. Оправдан при сложных задачах с чётко разделяемыми подзадачами.

### RAG ([глава 12](12_rag.md))

Retrieve relevant documents → augment prompt with context → generate answer. Подробно — [глава 12](12_rag.md) целиком.

### Self-RAG ([глава 12, §12.7](12_rag.md))

Модель решает, нужен ли retrieval; оценивает, полезен ли контекст; генерирует с reflection-токенами.

### GraphRAG ([глава 12, §12.7](12_rag.md))

Строит граф сущностей и связей, выполняет multi-hop reasoning по графу. Для задач, где ответ требует соединения фактов из разных источников.

### Semantic Exoskeleton ([глава 7, §7.4](07_markup_tags_and_prompt_architecture.md))

XML-теги разделяют смысловые блоки промпта, предотвращая интерференцию секций. Базовый паттерн структурирования сложных промптов.

---

## 20.11. Выбор паттерна: дерево решений

Перед системой стоит конкретная задача. Как выбрать паттерн? Пройдите по дереву:

```
Задача — single-shot или multi-step?
│
├── Single-shot:
│   ├── Нужен гарантированный формат?
│   │   └── ДА → Constrained Decoding (§20.9)
│   │
│   ├── High-stakes, нужна точность?
│   │   └── ДА → Generator-Verifier (§20.4) или CoVe
│   │
│   ├── Нужны разнообразные варианты?
│   │   └── ДА → Best-of-N или Self-Consistency
│   │
│   └── Простой ответ достаточен?
│       └── ДА → Chain-of-Thought
│
└── Multi-step:
    ├── Разные типы задач на входе?
    │   └── ДА → Router (§20.2) → далее по ветке
    │
    ├── Нужно одобрение человека?
    │   └── ДА → Human-in-the-Loop (§20.5)
    │
    ├── Нужны внешние знания?
    │   └── ДА → RAG + Retrieval-Gated (§20.7)
    │
    ├── Вход слишком длинный?
    │   └── ДА → MapReduce (§20.8)
    │
    ├── Требуется reliability в production?
    │   └── ДА → Fallback Chain (§20.6)
    │
    └── Сложное рассуждение?
        └── ДА → Planner-Executor / ReAct (§20.3)
```

Важно: это дерево — отправная точка, а не жёсткий алгоритм. В реальных системах паттерны комбинируются. Типичная цепочка для production-агента:

```
Router → Planner-Executor → [RAG + Retrieval-Gate] → Generator-Verifier
  → Constrained Decoding → Human-in-the-Loop (для рискованных действий)
    → Fallback Chain (обёртка всего пайплайна)
```

## 20.12. Decision tree выбора паттерна

Каталог из 20 паттернов полезен как справочник. Но когда вы проектируете новую систему, нужен обратный процесс: «у меня задача X — какой паттерн выбрать?». Это дерево решений для шести типовых сценариев.

### Сценарий 1: «Модель должна отвечать в строгом формате (JSON)»

→ Паттерн: **Constrained Decoding** (#2).
→ Подробнее: Глава 6, §6.4.

### Сценарий 2: «Модель должна использовать внешние данные (документы, БД)»

1. Данные обновляются чаще, чем раз в месяц? → **RAG** (#8).
2. Нужны связи между сущностями (X работает в Y, Y принадлежит Z)? → **GraphRAG** (#10).
3. Модель сама должна решать, когда нужен retrieval? → **Self-RAG** (#9).
4. Перед генерацией нужно проверить, что retrieval что-то нашёл? → **Retrieval-Gated Generation** (#11).

### Сценарий 3: «Модель ошибается в фактах (галлюцинирует)»

1. Факты можно проверить вторым вызовом LLM? → **Generator-Verifier** (#6).
2. Нужна систематическая проверка каждого утверждения? → **CoVe** (#7).
3. Нужна оценка качества целой системы? → Eval-дисциплина (Глава 14).

### Сценарий 4: «Задача требует нескольких шагов»

1. Шаги независимы и могут выполняться параллельно? → **Planner-Executor** (#13) с DAG-планированием (Глава 9).
2. Каждый шаг зависит от результата предыдущего? → **ReAct** (#14).
3. Агент повторяет одни и те же ошибки? → **Reflexion** (#15).
4. Нужны разные роли (писатель, редактор, проверяющий)? → **Multi-Agent** (#16).

### Сценарий 5: «Один ответ ненадёжен, нужно несколько»

1. Нужно выбрать лучший из N вариантов (качество)? → **Best-of-N** (#5).
2. Нужен консенсус нескольких генераций (стабильность)? → **Self-Consistency** (#4).

### Сценарий 6: «Разные запросы требуют разной обработки»

1. Простые запросы должны обрабатываться дёшево, сложные — качественно? → **Router** (#12).
2. Основная модель может отказать, нужен запасной вариант? → **Fallback Chain** (#19).
3. Некоторые действия слишком рискованны для автоматизации? → **Human-in-the-Loop** (#18).

### Сценарий 7: «Вход слишком велик для одного контекстного окна»

→ **MapReduce** (#17): разбей → обработай фрагменты → объедини результаты.

### Быстрый опросник

Ответьте на 4 вопроса — и получите комбинацию паттернов:

1. **Выход должен быть в строгом формате?** [Да / Нет]
2. **Нужны внешние данные?** [Да, часто обновляются / Да, статичные / Нет]
3. **Задача требует нескольких шагов?** [Да, последовательных / Да, параллельных / Нет]
4. **Цена ошибки?** [Высокая / Средняя / Низкая]

Пример:
- Да, Да (часто обновляются), Да (последовательных), Высокая → **Constrained Decoding + RAG + ReAct + Generator-Verifier**.
- Да, Нет, Нет, Средняя → **Constrained Decoding + CoVe**.
- Нет, Нет, Нет, Низкая → **Chain-of-Thought** (базовый промпт).

### Композиция паттернов

Паттерны не исключают друг друга. Типичная production-система использует 3-5 паттернов одновременно:

```
Input → Router (#12) → RAG (#8) → ReAct (#14) → Generator-Verifier (#6) → Output
                           ↑                          ↑
                      Self-RAG (#9)           Human-in-the-Loop (#18)
```

Главное правило: **начинайте с одного паттерна** (#1 Chain-of-Thought или #2 Constrained Decoding). Добавляйте следующий только когда текущий перестал справляться. 90% проблем решаются комбинацией из 2-3 базовых паттернов.

---

## Практический вывод

1. **Начните с простого.** Chain-of-Thought + Constrained Decoding покрывают ~80% задач. Не добавляйте Router, пока у вас одна задача, и Generator-Verifier, пока цена ошибки низка.

2. **Добавляйте верификацию для high-stakes.** Generator-Verifier или CoVe — когда ошибка стоит дорого. Два вызова LLM дешевле одного инцидента.

3. **RAG — когда нужны знания, а не догадки.** Добавьте Retrieval-Gated Generation, чтобы модель молчала, когда данных нет, вместо того чтобы галлюцинировать.

4. **Router — когда система мультизадачна.** Не гоните все запросы через frontier-модель. Маршрутизация экономит деньги и повышает качество за счёт специализации.

5. **Fallback Chain — для production.** Запасной план на rate limit, timeout, content filter. Каждый уровень возвращает ответ в одном формате.

6. **Human-in-the-Loop — для необратимых действий.** Gate на удаление, платежи, отправку. Правило: если действие нельзя откатить — требуй подтверждение.

7. **Комбинируйте, но не переусложняйте.** Каждый паттерн добавляет latency и стоимость. Добавляйте следующий слой только когда eval показывает, что текущий недостаточен.

8. **Мониторьте, какой паттерн срабатывает.** Если Fallback Chain регулярно проваливается до уровня 3 — чините основной промпт, а не добавляйте уровень 4. Если Router отправляет 95% в один handler — он не нужен.

### Задания

1. **Аудит паттернов в существующем пайплайне.** Возьмите любой LLM-пайплайн, который вы используете или разрабатываете. Пройдите по дереву решений из §20.11 и определите, какие паттерны уже присутствуют (возможно, неявно), каких не хватает, и какие избыточны. Ожидаемый результат: таблица «паттерн — статус — действие».

2. **Router + Fallback Chain.** Реализуйте для своего проекта Router (любой из трёх вариантов из §20.2) + Fallback Chain (§20.6). Подключите логирование каждого перехода. Прогоните 100 реальных запросов и измерьте: (a) распределение по маршрутам, (b) fallback rate, (c) latency по маршрутам. Ожидаемый результат: дашборд с метриками маршрутизации.

3. **Retrieval-Gated Generation.** Возьмите существующий RAG-пайплайн (или соберите минимальный по [главе 12](12_rag.md)). Добавьте gate между retrieval и generation (§20.7). Соберите eval-набор из 50 запросов с ответом в базе + 50 запросов без ответа. Постройте precision-recall кривую для разных `score_threshold`. Ожидаемый результат: выбранный порог и метрики до/после включения gate — hallucination rate должен снизиться.

4. **Спроектируйте систему через decision tree.** Возьмите новую задачу из вашего проекта (или придумайте). Пройдите «Быстрый опросник» из 4 вопросов. Определите комбинацию паттернов. Нарисуйте диаграмму компонентов (текстовую или mermaid). Обоснуйте: почему именно эти паттерны, а не альтернативы? **Ожидаемый результат:** архитектурная схема с обоснованием выбора паттернов.

---

## Источники

**Паттерны, детально разобранные в книге:**
- Chain-of-Thought — [глава 9](09_multistep_reasoning.md); Wei, J., et al. (2022). "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models." NeurIPS 2022. arXiv:2201.11903
- Self-Consistency — [глава 8](08_multiple_hypotheses.md); Wang, X., et al. (2022). "Self-Consistency Improves Chain of Thought Reasoning in Language Models." arXiv:2203.11171
- ReAct — [глава 10](10_agent_not_chat.md); Yao, S., et al. (2023). "ReAct: Synergizing Reasoning and Acting in Language Models." ICLR 2023. arXiv:2210.03629
- Reflexion — [глава 10](10_agent_not_chat.md); Shinn, N., et al. (2023). "Reflexion: Language Agents with Verbal Reinforcement Learning." NeurIPS 2023. arXiv:2303.11366
- CoVe — [глава 13](13_anti_hallucination_loop.md); Dhuliawala, S., et al. (2023). "Chain-of-Verification Reduces Hallucination in Large Language Models." arXiv:2309.11495
- RAG — [глава 12](12_rag.md); Lewis, P., et al. (2020). "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks." NeurIPS 2020. arXiv:2005.11401
- Self-RAG — [глава 12](12_rag.md); Asai, A., et al. (2023). "Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection." arXiv:2310.11511
- GraphRAG — [глава 12](12_rag.md); Edge, D., et al. (2024). "From Local to Global: A Graph RAG Approach to Query-Focused Summarization." arXiv:2404.16130
- Structured Outputs / Constrained Decoding — [глава 6](06_prompt_is_a_protocol.md); Willard, B. T. & Louf, R. (2023). "Efficient Guided Generation for Large Language Models." arXiv:2307.09702

**Общие архитектурные паттерны:**
- Weng, L. (2023). "LLM Powered Autonomous Agents." https://lilianweng.github.io/posts/2023-06-23-agent/
- Anthropic. (2024). "Building Effective Agents." https://www.anthropic.com/research/building-effective-agents

---

**Навигация:**
- Назад: [Глава 19. Дообучение и post-training](19_fine_tuning_and_post_training.md)
- Далее: [Глава 21. Serving и runtime LLM-систем](21_serving_and_runtime_of_llm_systems.md)
