КЛАСТЕР 04 · MCP КОМПОЗИЦИЯ ~18 МИНУТ
День 19 · из 35

Композиция
инструментов

Один инструмент — просто. Несколько инструментов подряд — задача. Сегодня — как агент связывает их в цепочку: передаёт данные между вызовами, реагирует на сбои, не теряется на середине пути.

Суть урока

Реальные задачи редко решаются одним вызовом инструмента. Цепочка из 3-7 инструментов — это норма. Сегодня — про композицию: как агент передаёт результаты одного инструмента в параметры следующего, какие бывают паттерны (последовательная цепочка, параллельные ветки, разветвления), и как обрабатывать ошибки на каждом шаге так, чтобы один сбой не убивал всю задачу.

Почему композиция — отдельная задача

Возьми реальный запрос: «найди в моих письмах последние сообщения от Ивана, проверь, есть ли среди них что-то про проект «Альфа», и если есть — создай задачу в Linear с этими деталями».

Это не «вызови инструмент». Это последовательность из четырёх связанных вызовов:

Цепочка вызовов · реальная задача
1. search_emails from=«Иван», limit=20 результат: 20 писем с темами и snippet
модель решает: какие читать
2. read_email (xN) id из шага 1 результат: полные тексты писем
модель ищет упоминания «Альфа»
3. (внутренний шаг) анализ текстов, извлечение деталей результат: 2 релевантных письма + детали
модель формирует задачу
4. create_task project=«Альфа», title, description результат: ID созданной задачи

Каждый следующий вызов зависит от предыдущего: что искать в почте → какие письма читать → какие детали извлекать → что писать в задачу. Это не статичная последовательность — это цепочка решений, где модель на каждом шаге смотрит на результат предыдущего и решает следующее действие.

Это и есть композиция. И здесь возникают совсем новые проблемы, которых не было с одним инструментом:

  • Сохранение контекста между шагами. Что из результата шага 1 нужно нести дальше, что выбросить? (Возвращаемся к Дню 10).
  • Передача данных. Как ID из шага 1 попадает в параметр шага 2? Модель должна это «помнить» через контекст.
  • Сбой на середине. Шаг 3 провалился — что делать? Откатывать шаг 2 нельзя (письма уже прочитаны). Возобновлять? С какого места?
  • Накопление мусора. 20 писем с шага 2 — это 20 длинных текстов в контексте. К шагу 4 модель утонет в шуме.

Один работающий инструмент ничего не значит. Десять работающих инструментов, которые не складываются в цепочки — это не агент, это набор кнопок. Композиция — это отдельный навык, требующий своего проектирования.

Паттерны композиции

Из тысяч цепочек, которые ты увидишь в реальных агентах, выделяются несколько повторяющихся паттернов. Освоив их — будешь узнавать в любой задаче:

Паттерн 01

Последовательная цепочка

A → B → C → D, каждый шаг зависит от предыдущего

Самый частый паттерн. Каждый следующий инструмент использует результат предыдущего. Пример выше — типичный sequential.

Когда применять: когда логика задачи действительно линейна. Найти → прочитать → проанализировать → действовать.

Подводный камень: контекст линейно растёт. К 5-7 шагу модель уже видит огромный массив messages. Особенно проблема, когда каждый шаг возвращает много данных (как «прочитай 20 писем»).

Паттерн 02

Параллельные ветки

A → (B и C одновременно) → D

Когда две задачи независимы друг от друга, их можно делать одновременно. Например: одновременно искать почту И искать в Slack-каналах. Когда обе вернули — синтезировать ответ.

Когда применять: когда есть несколько независимых источников информации. Существенно ускоряет агента (5 секунд вместо 15).

Подводный камень: модель сама обычно делает шаги последовательно, потому что генерирует один tool_call за раз. Параллельность нужно явно поддерживать на уровне твоего кода — некоторые провайдеры (Anthropic, OpenAI) умеют parallel tool calls в одном ответе.

Паттерн 03

Разветвление по результату

A → если X: B, иначе: C

Шаг 1 возвращает результат, на основе которого модель выбирает один из нескольких путей. Например: проверь баланс → если >0, выполнить покупку; если 0, уведомить пользователя.

Когда применять: везде, где есть «если/иначе» в логике задачи. Что характерно — это естественное поведение цикла reason-act-observe-decide: модель сама строит ветки.

Подводный камень: модель может выбрать «не ту» ветку, если результат шага 1 неоднозначен. Лечится явными инвариантами (День 14): «если баланс < 0, обязательно вызвать notify_user».

Паттерн 04

Цикл (map)

A → для каждого X из результата: B(X) → синтез

Шаг 1 вернул список (например, 10 ссылок). Шаг 2 — нужно выполнить с каждым (прочитать каждую ссылку). Шаг 3 — синтез из всех результатов.

Когда применять: когда есть «обработать N однотипных элементов». Типичный кейс в исследовательских агентах: нашли 10 статей — нужно прочитать все.

Подводный камень: главная проблема всего кластера. 10 шагов read_url = 10 длинных текстов в контексте. Возвращаемся к стратегиям Дня 9: суммировать каждый результат сразу, а не копить.

Паттерн 05

Уточняющий цикл (retry-with-refinement)

A → не получилось → A с уточнёнными параметрами → ...

Запрос не дал нужного результата (поиск вернул 0 совпадений, проверка не прошла) — модель сама корректирует параметры и пробует снова.

Когда применять: в поисковых задачах, в задачах с неточной формулировкой пользователя. Очень мощный паттерн, особенно с современными reasoning-моделями.

Подводный камень: бесконечный цикл. Всегда нужен лимит итераций (День 6, грабли о бесконечных циклах). Иначе агент может крутиться 50 раз, варьируя параметры на копейки.

Реальный сложный агент — это комбинация этих паттернов. Например: исследовательский агент может пройти sequential (план → search) → map (прочитать каждый источник) → branch (если данных мало — refine, если достаточно — синтез).

Обработка ошибок

В лабораторных условиях каждый инструмент работает. В реальности — нет. API падают, токены протухают, лимиты исчерпываются, сети дрожат. Обработка ошибок — это половина инженерии агентов, особенно в продакшене.

Типы ошибок, с которыми придётся работать, и подходы к ним:

Временные сбои (transient)

Сеть не дошла, сервис временно недоступен, rate limit, таймаут. Через 2-5 секунд всё работает. Это самый частый тип ошибок в проде.

Что делать:

  • Не показывать модели. На уровне твоего кода — retry с экспоненциальным backoff (1с, 2с, 4с, 8с). Обычно 3 попытки решают 95% случаев.
  • Если все 3 попытки провалились — тогда вернуть модели сообщение «инструмент временно недоступен, попробуй позже».
  • Иначе модель начнёт «лечить» проблему которой нет: переформулирует параметры, выберет другой инструмент, запутается.

Принцип: временные сбои — это проблема инфраструктуры, не задачи модели. Решай их в коде до того, как они попадут в контекст.

Неверные параметры

Модель вызвала create_task(due_date="завтра"), а API ждёт ISO-8601. Или передала несуществующий ID проекта. Это ошибка самой модели.

Что делать:

  • Вернуть в контекст модели конкретное описание ошибки: «поле due_date должно быть в формате YYYY-MM-DD, получено: завтра». Не «ошибка валидации».
  • Модель почти всегда сама исправит на следующей итерации.
  • Если систематически ошибается с одним и тем же параметром — это сигнал улучшить описание инструмента (см. День 17): дать пример в description, уточнить формат.

Принцип: ошибки валидации — это обратная связь для модели. Дай ей возможность учиться по ходу задачи, а потом улучшай описания.

Бизнес-ошибки

Параметры формально правильные, но действие не выполнить. Недостаточно баланса, нет доступа к проекту, задача уже закрыта. Эти ошибки осмысленны — на них нельзя «просто повторить».

Что делать:

  • Вернуть в контекст детальное описание почему: «нельзя создать задачу в архивном проекте». Это не ошибка вызова, это сигнал «нужно действовать по-другому».
  • Дать модели информацию, достаточную, чтобы она выбрала альтернативу: разархивировать проект? создать в другом? спросить пользователя?
  • В сложных случаях — переход в красную зону Дня 15: остановиться и спросить пользователя.

Принцип: бизнес-ошибки — это законная часть задачи, не баг. Агент должен уметь их понимать и обходить, а не «крашиться».

Фатальные ошибки

Токен отозван, аккаунт заблокирован, сервис закрылся, неправильно настроенный MCP-сервер. Эти ошибки не лечатся в рамках текущей задачи.

Что делать:

  • Прерывать агента сразу. Дальше работать бессмысленно, цикл будет бесконечно биться о ту же стену.
  • Передавать пользователю с конкретной диагностикой: «не могу подключиться к Linear: токен недействителен. Обнови в настройках».
  • Логировать для разработчика — это, скорее всего, проблема конфигурации, не модели.

Принцип: агент не должен пытаться восстановиться после фатальных ошибок. Чем быстрее упадёт с понятным сообщением — тем лучше.

Главный принцип — где ловить ошибку

Каждая ошибка возникает где-то. У тебя три уровня:

Уровень 1

В MCP-сервере

Сетевые сбои, ретраи, базовая валидация. Сервер не должен пробрасывать «сырое исключение» наружу — он должен переводить в осмысленные ответы.

Уровень 2

В клиенте

Решение, что с ошибкой делать: повторить сразу, вернуть модели, прервать агента. Это policy-логика, не привязанная к конкретному инструменту.

Уровень 3

В контексте модели

Когда ошибка действительно должна попасть в контекст — она должна быть понятной для модели. С указанием, что попробовать дальше.

Самая распространённая ошибка дизайна — всё валить на третий уровень: любой сбой летит в контекст модели «как есть». Получаешь длинные стек-трейсы в массиве messages и модель, которая «лечит» баги инфраструктуры. Хороший агент фильтрует, что показывать модели, и решает большинство проблем до этого.

Решения AI-инженера

Развилка 1. Доверять модели цепочку или строить программно

Два полюса дизайна агента:

  • Свободный (autonomous): «вот тебе 10 инструментов, реши задачу как хочешь». Модель сама решает цепочку. Гибко, но непредсказуемо.
  • Программный (orchestrated): цепочка чётко описана в коде, модель вызывается только на отдельные шаги (например, «извлеки из этого письма имя клиента»). Жёстко, но надёжно.

На практике — гибрид. Жёсткая внешняя структура, гибкие отдельные шаги. Например: код задаёт «сначала собрать данные, потом проанализировать, потом действовать» как три этапа. Внутри каждого — модель свободно вызывает нужные инструменты.

Этот гибрид — естественное продолжение task state machine из Дня 13. Состояния задаёт код, действия в каждом состоянии — модель.

Развилка 2. Сжимать ли результаты в цепочке

Чем длиннее цепочка — тем критичнее обработка результатов перед следующим шагом (День 8, грабли «возвращать сырой результат»).

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

Например, шаг «прочитать 10 веб-страниц» внутри себя: для каждой страницы — fetch + сжатие маленькой моделью в 200 токенов с ключевыми фактами. В контекст основного агента уходят 10 коротких саммари вместо 10 полных страниц. Цепочка остаётся работоспособной до 20+ шагов.

Концептуальные грабли

Соблазн: дать агенту 30 инструментов, пусть комбинирует. На практике модель путается среди такого числа, ошибается с выбором, делает абсурдные цепочки. Лучше работают 5-7 инструментов с тщательно подобранным набором.

Принцип композиции: каждый инструмент должен быть «строительным блоком», который полезен в нескольких цепочках. Не плодить специализированные «выполни задачу X целиком» — это убивает гибкость.

Хорошо тестируется первый шаг (запустили — вызвалось). Хорошо тестируется последний шаг (результат пришёл — ура). А средние 3-5 шагов — никто не смотрит. Именно там и копятся проблемы: накопление мусора в контексте, ошибки передачи параметров, потеря важных деталей.

Привычка: логировать каждый шаг цепочки полностью (как в Дне 7) и периодически просматривать середину. Большинство «странного поведения» агента — это «накопленный шум» на 4-7 шаге.

«Любая ошибка — это исключение, ловлю, возвращаю в контекст». В результате модель видит и временные сбои сети, и серьёзные бизнес-блокировки одинаково — как «что-то пошло не так». Не может выбрать правильную реакцию.

Хорошая практика: классифицировать ошибки на уровне сервера/клиента и подавать модели уже типизированную информацию. «Временная неудача (попробовать снова)» — это не то же самое, что «нет прав доступа (нужно действие пользователя)».

Возвращаемся к Дню 8: контекст растёт квадратично по числу итераций. Цепочка из 15 шагов — это не «в 15 раз больше», это в 100+ раз больше токенов, чем одиночный вызов. Если в день у тебя 1000 запусков агента, и каждый средний — 15 шагов, экономика становится критичной.

Оптимизация цепочек — это не «потом, когда работает». Это часть проектирования с первого дня. Сжатие результатов, prompt caching, маленькая модель на простые шаги.

Практика

Эксперимент 01 · Разбери реальную цепочку

Посмотри в Claude / Cursor цепочку из 5+ шагов

В современных ассистентах с инструментами (Claude в режиме research, Cursor в режиме agent) сделай запрос, который требует нескольких шагов. Например: «найди в этом репозитории все TODO и составь приоритезированный список». Развернуть «thinking» / «tool use» — увидишь полную цепочку. Распознай паттерны: где sequential, где map, где branch. Какой шаг создал больше всего токенов в контексте? Это даст живое ощущение, как работают цепочки в проде.

Эксперимент 02 · Спроектируй цепочку на бумаге

Из своей рабочей задачи

Возьми реальную задачу из своей работы, которая занимает 10-30 минут вручную (например: «проанализировать обращения за неделю и составить отчёт»). Распиши её как цепочку инструментов: какие шаги, какие данные передаются между ними, где ветки, где циклы. Если бы ты строил такого агента — какие инструменты ему нужны? 5-10 в наборе достаточно для большинства цепочек.

Эксперимент 03 · Найди слабое место

Где цепочка ломается без обработки ошибок

В той же цепочке из предыдущего упражнения — пройди по каждому шагу и придумай, что может пойти не так. На каждом шаге: какой тип ошибки (временная / валидация / бизнес / фатальная)? Что должен делать агент при каждой? Это упражнение учит думать защитно — большинство сломанных продакшен-агентов не подумали о middle-layer ошибок.

Что в Дне 20
Знаем, как строить цепочки. Финал кластера: что делать, когда задача слишком большая для одного агента. Оркестрация и мультиагентные системы. Когда лучше три специализированных агента, чем один универсальный. Где разделять, как они общаются.