AI-инжиниринг
Паттерны оркестрации: routing, fallback, chain, map-reduce, branching
Цели урока
- Освоить sequential chain - конвейер обработки с передачей контекста между шагами
- Реализовать parallel (fan-out) pipeline с concurrency control и rate limiting
- Построить routing/branching систему с комбинацией rule-based и LLM-based классификации
- Применять map-reduce для обработки документов, не помещающихся в контекстное окно
- Реализовать fallback chains с retry, timeout, cascade и hedged requests
Pipeline - это конвейер. Каждый шаг получает вывод предыдущего. Если шаг 3 из 7 провалился - вся цепочка встала. Так работает большинство AI-систем в production прямо сейчас. Orchestration - это про то как не падать, а деградировать грациозно: fan-out вместо последовательных вызовов, map-reduce для 200K документов, hedged requests для P99 latency, BullMQ для того чтобы async задачи не терялись в void.
- Stripe AI - cascade fallback через 3 LLM-провайдера, 99.99% availability - каждый сбой OpenAI незаметен для клиентов
- Notion AI - parallel fan-out: анализирует документ одновременно на структуру, тон и ключевые идеи, экономя 60% времени
- Linear AI - conditional routing: bug-репорт → техническая модель с RAG по документации, feature-реквест → продуктовая модель без RAG
- Cursor (IDE) - map-reduce для анализа репозиториев: каждый файл суммаризируется отдельно, затем суммаризации объединяются для понимания архитектуры 500K+ токенов
- LangGraph 0.2 (2024) - state machine для agent orchestration: explicit graph вместо magic chains, checkpoint для human-in-the-loop
Предварительные знания
Anthropic кодифицирует паттерны оркестрации
Первые два года эры LLM-приложений паттерны оркестрации расходились по блогам, докладам и коду фреймворков без единого словаря. 19 декабря 2024 Anthropic опубликовала Building Effective Agents, и этот текст собрал практику в общий язык. Главное разграничение - workflows, где несколько вызовов LLM соединены заранее заданными путями, и agents, где модель сама решает, какие шаги делать. Документ описал базовые строительные блоки: prompt chaining (последовательная цепочка), routing (классификация запроса и направление в нужную ветку), parallelization (параллельный запуск с агрегацией), orchestrator-workers и evaluator-optimizer. Общий совет Anthropic - начинать с простейшего решения и добавлять сложность только когда она реально окупается.
Sequential Chain: конвейер обработки
Pipeline - это конвейер. Каждый шаг получает вывод предыдущего. Если шаг 3 из 7 провалился - вся цепочка встала. Sequential chain - простейший и самый распространённый паттерн, и именно он чаще всего виноват в недоступности AI-фичи в 3 часа ночи.
Где sequential chain работает в production прямо сейчас:
- **Content moderation pipeline** - классификация → фильтрация → генерация ответа (порядок критичен: нет смысла тратить `0.01` на ответ токсичному запросу)
- **Translation with quality check** - перевод → оценка качества → исправление
- **Code review bot** - парсинг diff → анализ по правилам → генерация комментариев
- **Customer support** - классификация тикета → извлечение сущностей → генерация ответа
Sequential chain - единственный паттерн, где **порядок шагов критичен**. Модерация должна идти до генерации (не тратить деньги на ответ для токсичного запроса). Классификация - до генерации (нужен правильный system prompt). Если шаги независимы - это сигнал использовать parallel pattern.
В sequential chain из 4 шагов третий шаг возвращает ошибку. Что происходит?
Parallel / Fan-Out: параллельная обработка
LLM-вызовы медленные - от 500ms до 30s на запрос. TTFT у GPT-4o в среднем 800ms, у Claude Sonnet - около 1s. Если два шага pipeline независимы и их гнать последовательно - это не архитектура, это потеря времени. **Fan-out** запускает несколько операций одновременно и собирает результаты в fan-in.
Классический пример: анализ резюме кандидата. Оценка skills, culture fit и red flags - три независимых LLM-вызова. Запускать их последовательно (3.6s) когда можно параллельно (1.5s) - это архитектурный долг.
**Promise.allSettled vs Promise.all** - критичный выбор. `Promise.all` падает при первой ошибке, теряя результаты успешных задач. `Promise.allSettled` возвращает все результаты: и успешные, и упавшие. Для production pipeline с LLM - всегда `allSettled`.
Продвинутый вариант - **Fan-Out с Rate Limiting**. 50 параллельных LLM-вызовов без ограничений → API вернёт 429. Решение: concurrency control через `p-limit`.
При fan-out паттерне 3 задачи запускаются параллельно. Задача A выполняется за 200ms, B - за 1500ms, C - за 800ms. Какое общее время выполнения?
Routing / Branching: условное выполнение
Не все запросы должны проходить один и тот же pipeline. Вопрос «Сколько будет 2+2?» не нуждается в RAG-поиске по базе знаний - это выброшенный вызов и деньги впустую. Жалоба клиента требует GPT-4o с empathetic system prompt, а FAQ-запрос спокойно закроет gpt-4o-mini за `0.15/1M`. **Routing** - это условный оператор для AI-архитектуры.
Два подхода к routing: **LLM-based** (классификатор на модели) и **rule-based** (детерминированная логика). В production выигрывает комбинация: правила закрывают 30-40% запросов бесплатно и за 0ms, LLM подключается только для неоднозначных.
Conditional routing - это не только экономия. Это LangGraph state machine в минимальном виде: классификатор определяет следующее состояние графа. LangGraph 0.2 (2024) строит такие графы явно, с persist state между шагами - это тот же паттерн, но с checkpoint и человеческим контролем в точках разветвления.
Почему в routing паттерне часто комбинируют rule-based и LLM-based классификацию?
Map-Reduce: обработка длинных документов
Контекстное окно GPT-4o - 128K токенов. Звучит много. Годовой отчёт Apple - 200K+ токенов. Кодовая база на 50K строк - 500K+. Лог за месяц - миллионы. **Map-Reduce** - это единственный способ обрабатывать данные, которые в принципе не влезают в контекст. Та же идея, что в Hadoop, только вместо MapReduce-задач - LLM-вызовы.
Cursor использует этот паттерн для анализа больших репозиториев: каждый файл суммаризируется отдельно (MAP), затем суммаризации объединяются для понимания архитектуры (REDUCE). Без map-reduce - проект с 1000 файлами невозможно проанализировать в одном prompt.
Map-Reduce имеет несколько вариаций, выбор зависит от задачи:
| Вариация | Как работает | Когда использовать |
|---|---|---|
| Map-Reduce | Обработка каждого chunk → финальный merge | Суммаризация, extraction из длинных документов |
| Map-Rerank | Обработка каждого chunk → сортировка по score → лучший | Поиск ответа в длинном тексте |
| Refine | Chunk 1 → ответ → chunk 2 + предыдущий ответ → уточнение → ... | Когда нужна связность между частями |
| Collapse | Рекурсивный reduce: если summaries слишком длинные → reduce ещё раз | Очень длинные документы (книги, кодовые базы) |
Ключевое решение в Map-Reduce - **какую модель использовать для MAP и REDUCE**. MAP обрабатывает десятки chunks - здесь экономия на модели важна (gpt-4o-mini, `0.15/1M`). REDUCE делает один вызов с критичным результатом - здесь оправдана мощная модель (gpt-4o, Claude Sonnet). При 67 chunks разница в стоимости MAP-фазы между gpt-4o и gpt-4o-mini - в 16 раз.
Вариация **Refine** - альтернатива Map-Reduce, когда важна связность:
Документ на 200K токенов нужно суммаризировать. Контекстное окно модели - 128K. Какой паттерн подойдёт?
Fallback Chains: отказоустойчивость pipeline
В марте 2024 года OpenAI API упал на 4 часа. Все приложения, жёстко привязанные к GPT-4, встали. Приложения с fallback chains переключились на Anthropic Claude за секунды - пользователи не заметили. Orchestration - это не про то, как вызвать LLM. Это про то как **не падать, а деградировать грациозно**.
Fallback - это не оптимизация. Это базовое требование для production. BullMQ с retry-стратегией, circuit breaker, cascade через нескольких провайдеров - всё это части одной идеи: система продолжает работать даже когда один компонент недоступен.
Продвинутая стратегия - **Hedged Request**. Вместо ожидания таймаута, запускаем primary и fallback параллельно, возвращаем первый ответ:
Hedged requests **удваивают стоимость** в worst case. Применять только для критических path, где latency важнее стоимости: real-time чатботы, trading signals, live customer support.
Сводная таблица паттернов оркестрации и когда их использовать:
| Паттерн | Latency | Cost | Use Case |
|---|---|---|---|
| Sequential | Sum(steps) | Sum(calls) | Зависимые шаги, pipeline обработки |
| Parallel | Max(steps) | Sum(calls) | Независимые задачи, multi-aspect анализ |
| Routing | Classify + 1 branch | Classify + 1 call | Разные типы запросов, оптимизация стоимости |
| Map-Reduce | Map (parallel) + Reduce | N × map + 1 reduce | Документы > контекстного окна |
| Fallback | Primary + retry/cascade | 1-3 calls | Отказоустойчивость, multi-provider |
| Hedged | Min(providers) | 1-2 calls | Минимальная latency для critical path |
Orchestration - это просто последовательные вызовы LLM
Orchestration - это управление состоянием, ошибками, retry-стратегиями и partial failure в распределённой системе, где каждый узел - LLM-вызов
Последовательный вызов трёх LLM - это не оркестрация, это скрипт. Оркестрация начинается там, где нужно решить: что делать если шаг 2 из 5 упал? Сохранять промежуточный результат? Retry на другой модели? Пропускать шаг? Уведомлять пользователя о деградации? LangGraph хранит explicit state между шагами - именно потому что без state management агент теряет контекст и зацикливается. BullMQ для async orchestration даёт retry, priority queues и dead letter queue - всё то, чего нет в голом Promise.all.
Production AI-чатбот обслуживает 10K пользователей. OpenAI API иногда отвечает за 5+ секунд. Какой fallback-паттерн снизит P99 latency?
Orchestration - это просто последовательные вызовы LLM
Orchestration - это управление состоянием, ошибками, retry и partial failure. Последовательный вызов трёх LLM - скрипт. Оркестрация - когда система решает что делать при падении шага 2 из 5
LangGraph хранит explicit state между шагами именно потому что без него агент теряет контекст и зацикливается. BullMQ для async orchestration даёт retry, priority queues и dead letter queue - всё то чего нет в голом Promise.all. Разница между скриптом и оркестрацией - это ответ на вопрос: что происходит когда что-то идёт не так?
Итоги
- Sequential chain: конвейер где каждый шаг зависит от предыдущего. Fail on any step - главная уязвимость
- Parallel (fan-out): независимые задачи параллельно. Latency = max(tasks), не sum(tasks). Без concurrency control → rate limit 429
- Routing: rule-based закрывает 30-40% запросов бесплатно за 0ms, LLM-based - остальные. Комбинация - золотой стандарт
- Map-Reduce: единственный способ обработать документ больше контекстного окна. MAP параллельно на gpt-4o-mini, REDUCE на gpt-4o
- Fallback: cascade + retry + hedged requests. Production без fallback - вопрос времени до инцидента
- Паттерны комбинируются: routing → sequential с parallel шагами → fallback на каждом LLM-вызове. LangGraph делает это явным графом
Вопросы для размышления
- Если шаг 4 из 6 в sequential pipeline периодически падает с 503 от OpenAI - как переделать pipeline чтобы он не начинал с шага 1 каждый раз? Подсказка: идемпотентность и checkpoint.
- Cursor анализирует репозиторий на 2000 файлов через map-reduce. Какой паттерн выбрать для MAP-фазы если файлы очень разного размера - от 10 строк до 2000? Как не потратить лишнее на микро-файлы?
- Hedged request экономит P99 latency за счёт двойного расхода токенов в 20% случаев. При каком соотношении цены токенов и стоимости плохого UX он перестаёт быть оправданным?
Что дальше
Routing в этом уроке направлял запросы в разные pipeline. Но можно роутить на уровне самой модели - отправлять простые запросы в дешёвую модель, а сложные в мощную. Это Model Routing - следующая тема.
- Model Routing — Автоматический выбор модели (GPT-4o vs Claude vs local) на основе сложности, стоимости, latency
- Error Handling в LLM — Retry стратегии, circuit breaker, graceful degradation для AI pipeline
- Cost Management — Оптимизация расходов: routing, caching, prompt compression
Связанные уроки
- aie-20-langchain-llamaindex — Фреймворки дают примитивы, которые используют эти паттерны
- aie-22-model-routing — Роутинг это один из паттернов оркестрации детально
- aie-32-error-handling-llm — Fallback и retry это паттерны надёжности
- aie-29-cost-management — Выбор оркестрации напрямую влияет на стоимость
- alg-19-divide-conquer — Map-reduce по вызовам LLM это разделяй и властвуй
- sd-09-message-queue — Параллельный fan-out похож на распределение работы через очереди
- net-55-message-queues