53 KiB
ГЛАВА 20. ПАТТЕРНЫ ПРОЕКТИРОВАНИЯ LLM-ПРИЛОЖЕНИЙ
В 1994 году «Банда четырёх» (Gang of Four) выпустила книгу Design Patterns. Она не изобрела паттерны — паттерны уже существовали в коде тысяч проектов. Книга дала им имена. Когда у решения есть имя, инженеры перестают объяснять его заново на каждом совещании. «Используем Observer» — и комната понимает. Не потому что Observer сложен, а потому что имя заменяет десять минут рисования на доске.
LLM-инженерия к 2026 году находится в похожей точке. В главах 6–19 книги десятки паттернов — от структуры промпта до агентной архитектуры — разбросаны по контексту, в котором они впервые понадобились. Chain-of-Thought объяснялся в главе 9, ReAct — в главе 10, CoVe — в главе 13. Каждый раз паттерн раскрывался «изнутри» — механизм, формулы, когда работает и почему. Но инженеру, проектирующему новую систему, нужен другой вид: каталог. Одно место, где видны все паттерны, их назначение и критерии выбора.
Эта глава — каталог. Она не повторяет механику (за ней — в исходную главу), а даёт обзорную карту и добавляет паттерны, которые ранее не были формализованы: 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) 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)
- ReAct loop и Plan-and-Execute → глава 10 (§10.2)
- Минимальный контур Planner/Executor/Verifier/Orchestrator → глава 23 (§23.4)
- Generator-Verifier → глава 13 (§13.1)
Варианты
| Вариант | Особенность | Глава |
|---|---|---|
| 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).
Варианты:
| Вариант | Механизм | Глава |
|---|---|---|
| Два вызова одной модели | Разные системные промпты (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) в контексте наблюдаемости: оператор просматривает выходы, ловит систематические ошибки, настраивает пороги.
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с упорядоченным списком шагов (dataclassFallbackStepс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). Если система регулярно проваливается на 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. Подход:
- Соберите eval-набор: 50+ запросов с known-answer + 50+ запросов без ответа в базе.
- Постройте precision-recall кривую для разных порогов.
- Выберите порог, при котором recall приемлем (не отклоняет слишком много хороших запросов), а precision высока (не пропускает «пустые» контексты).
Связь с Self-RAG
Self-RAG (глава 12, §12.7) — более сложный вариант: модель сама решает, нужен ли retrieval, и сама оценивает, полезен ли извлечённый контекст. Retrieval-Gated Generation — упрощённая версия для случаев, когда вы не хотите полагаться на суждения модели о качестве контекста и предпочитаете детерминированный порог.
Когда использовать
- Fact-critical приложения, где галлюцинация хуже, чем отказ отвечать.
- Системы, где пользователь понимает ответ «не знаю» и может переформулировать вопрос.
- Доменные Q&A-системы с ограниченной knowledge base.
Когда НЕ использовать
- Brainstorming и creative-задачи, где генерация «вдохновения» ценнее точности.
- Случаи, где «не знаю» неприемлемо и любой ответ лучше молчания.
20.8. MapReduce для длинных входов
Проблема
Вход не помещается в context window. Или помещается, но слишком длинный для качественной обработки — внимание модели размывается на больших контекстах (глава 5 о 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).
20.9. Constrained Decoding
Проблема
Выход LLM должен быть машиночитаемым: JSON для API, SQL для базы данных, YAML для конфигурации. Free-form generation порождает невалидные структуры, пропущенные поля, лишние комментарии.
Решение
Ограничь пространство генерации формальной грамматикой или схемой. Подробно — глава 6, где 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)
Промпт «Think step by step» или structured reasoning template выводит промежуточные шаги, повышая точность на задачах с рассуждениями. Работает лучше всего на задачах с объективно проверяемым ответом (математика, логика, код). Подробно — глава 9, §9.1.
Self-Consistency (глава 8, §8.3)
Сгенерируй N ответов с повышенной температурой → выбери наиболее частый (majority vote). Повышает точность за счёт N-кратного увеличения расхода токенов.
Best-of-N (глава 8, §8.4)
Сгенерируй N ответов → оцени каждый моделью-судьёй или функцией качества → верни лучший. Отличие от Self-Consistency: выбор по качеству, а не по частоте.
CoVe (глава 13, §13.2)
Generate → plan verification questions → answer questions independently → revise original. Снижает hallucination rate на фактоидных задачах.
ReAct (глава 10, §10.2)
Thought → Action → Observation в цикле. «Рассуждай вслух, действуй, наблюдай результат». Стандартный agent loop для систем с tool use.
Reflexion (глава 10, §10.2)
Агент after failure записывает рефлексию в память и использует её при повторной попытке. Нужна persistent memory между итерациями.
Multi-Agent (глава 10, §10.4)
Оркестратор делегирует задачи специализированным агентам. Варианты: supervisor, hierarchy, peer-to-peer. Оправдан при сложных задачах с чётко разделяемыми подзадачами.
RAG (глава 12)
Retrieve relevant documents → augment prompt with context → generate answer. Подробно — глава 12 целиком.
Self-RAG (глава 12, §12.7)
Модель решает, нужен ли retrieval; оценивает, полезен ли контекст; генерирует с reflection-токенами.
GraphRAG (глава 12, §12.7)
Строит граф сущностей и связей, выполняет multi-hop reasoning по графу. Для задач, где ответ требует соединения фактов из разных источников.
Semantic Exoskeleton (глава 7, §7.4)
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: «Модель должна использовать внешние данные (документы, БД)»
- Данные обновляются чаще, чем раз в месяц? → RAG (#8).
- Нужны связи между сущностями (X работает в Y, Y принадлежит Z)? → GraphRAG (#10).
- Модель сама должна решать, когда нужен retrieval? → Self-RAG (#9).
- Перед генерацией нужно проверить, что retrieval что-то нашёл? → Retrieval-Gated Generation (#11).
Сценарий 3: «Модель ошибается в фактах (галлюцинирует)»
- Факты можно проверить вторым вызовом LLM? → Generator-Verifier (#6).
- Нужна систематическая проверка каждого утверждения? → CoVe (#7).
- Нужна оценка качества целой системы? → Eval-дисциплина (Глава 14).
Сценарий 4: «Задача требует нескольких шагов»
- Шаги независимы и могут выполняться параллельно? → Planner-Executor (#13) с DAG-планированием (Глава 9).
- Каждый шаг зависит от результата предыдущего? → ReAct (#14).
- Агент повторяет одни и те же ошибки? → Reflexion (#15).
- Нужны разные роли (писатель, редактор, проверяющий)? → Multi-Agent (#16).
Сценарий 5: «Один ответ ненадёжен, нужно несколько»
- Нужно выбрать лучший из N вариантов (качество)? → Best-of-N (#5).
- Нужен консенсус нескольких генераций (стабильность)? → Self-Consistency (#4).
Сценарий 6: «Разные запросы требуют разной обработки»
- Простые запросы должны обрабатываться дёшево, сложные — качественно? → Router (#12).
- Основная модель может отказать, нужен запасной вариант? → Fallback Chain (#19).
- Некоторые действия слишком рискованны для автоматизации? → Human-in-the-Loop (#18).
Сценарий 7: «Вход слишком велик для одного контекстного окна»
→ MapReduce (#17): разбей → обработай фрагменты → объедини результаты.
Быстрый опросник
Ответьте на 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 базовых паттернов.
Практический вывод
-
Начните с простого. Chain-of-Thought + Constrained Decoding покрывают ~80% задач. Не добавляйте Router, пока у вас одна задача, и Generator-Verifier, пока цена ошибки низка.
-
Добавляйте верификацию для high-stakes. Generator-Verifier или CoVe — когда ошибка стоит дорого. Два вызова LLM дешевле одного инцидента.
-
RAG — когда нужны знания, а не догадки. Добавьте Retrieval-Gated Generation, чтобы модель молчала, когда данных нет, вместо того чтобы галлюцинировать.
-
Router — когда система мультизадачна. Не гоните все запросы через frontier-модель. Маршрутизация экономит деньги и повышает качество за счёт специализации.
-
Fallback Chain — для production. Запасной план на rate limit, timeout, content filter. Каждый уровень возвращает ответ в одном формате.
-
Human-in-the-Loop — для необратимых действий. Gate на удаление, платежи, отправку. Правило: если действие нельзя откатить — требуй подтверждение.
-
Комбинируйте, но не переусложняйте. Каждый паттерн добавляет latency и стоимость. Добавляйте следующий слой только когда eval показывает, что текущий недостаточен.
-
Мониторьте, какой паттерн срабатывает. Если Fallback Chain регулярно проваливается до уровня 3 — чините основной промпт, а не добавляйте уровень 4. Если Router отправляет 95% в один handler — он не нужен.
Задания
-
Аудит паттернов в существующем пайплайне. Возьмите любой LLM-пайплайн, который вы используете или разрабатываете. Пройдите по дереву решений из §20.11 и определите, какие паттерны уже присутствуют (возможно, неявно), каких не хватает, и какие избыточны. Ожидаемый результат: таблица «паттерн — статус — действие».
-
Router + Fallback Chain. Реализуйте для своего проекта Router (любой из трёх вариантов из §20.2) + Fallback Chain (§20.6). Подключите логирование каждого перехода. Прогоните 100 реальных запросов и измерьте: (a) распределение по маршрутам, (b) fallback rate, (c) latency по маршрутам. Ожидаемый результат: дашборд с метриками маршрутизации.
-
Retrieval-Gated Generation. Возьмите существующий RAG-пайплайн (или соберите минимальный по главе 12). Добавьте gate между retrieval и generation (§20.7). Соберите eval-набор из 50 запросов с ответом в базе + 50 запросов без ответа. Постройте precision-recall кривую для разных
score_threshold. Ожидаемый результат: выбранный порог и метрики до/после включения gate — hallucination rate должен снизиться. -
Спроектируйте систему через decision tree. Возьмите новую задачу из вашего проекта (или придумайте). Пройдите «Быстрый опросник» из 4 вопросов. Определите комбинацию паттернов. Нарисуйте диаграмму компонентов (текстовую или mermaid). Обоснуйте: почему именно эти паттерны, а не альтернативы? Ожидаемый результат: архитектурная схема с обоснованием выбора паттернов.
Источники
Паттерны, детально разобранные в книге:
- Chain-of-Thought — глава 9; Wei, J., et al. (2022). "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models." NeurIPS 2022. arXiv:2201.11903
- Self-Consistency — глава 8; Wang, X., et al. (2022). "Self-Consistency Improves Chain of Thought Reasoning in Language Models." arXiv:2203.11171
- ReAct — глава 10; Yao, S., et al. (2023). "ReAct: Synergizing Reasoning and Acting in Language Models." ICLR 2023. arXiv:2210.03629
- Reflexion — глава 10; Shinn, N., et al. (2023). "Reflexion: Language Agents with Verbal Reinforcement Learning." NeurIPS 2023. arXiv:2303.11366
- CoVe — глава 13; Dhuliawala, S., et al. (2023). "Chain-of-Verification Reduces Hallucination in Large Language Models." arXiv:2309.11495
- RAG — глава 12; Lewis, P., et al. (2020). "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks." NeurIPS 2020. arXiv:2005.11401
- Self-RAG — глава 12; Asai, A., et al. (2023). "Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection." arXiv:2310.11511
- GraphRAG — глава 12; 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; 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
Навигация: