Введение: от одного агента к команде
В первой статье этой серии мы разобрали, как работает Function Calling и Tool Use — механизмы, позволяющие LLM-агенту взаимодействовать с внешним миром: вызывать функции, обращаться к API, выполнять действия. Во второй статье мы построили полноценный RAG-пайплайн — от индексации документов до генерации ответов с опорой на контекст. Те статьи были про то, как сделать одного агента умным и полезным. А сегодня поговорим о том, как заставить нескольких агентов работать вместе.
Почему это важно? Потому что реальные задачи редко укладываются в возможности одного агента.
Представьте систему обработки клиентских заявок: один агент классифицирует входящий запрос, другой ищет информацию в базе знаний, третий проверяет историю клиента в CRM, четвёртый формирует ответ, а пятый валидирует его на соответствие политикам компании. Каждый специализирован, каждый хорош в своём деле, и вместе они решают задачу, которая одному агенту просто не по силам.
Цифры подтверждают тренд. По данным McKinsey, 23% организаций уже масштабируют агентные AI-системы, а ещё 39% активно экспериментируют. Прогнозы на 2026 год говорят о том, что до 80% корпоративных рабочих нагрузок будут опираться на AI-управляемые системы. И вот что особенно интересно — мульти-агентная перекрёстная валидация повышает точность результатов до 40%, просто за счёт того, что один агент проверяет работу другого.
В этой статье мы разберём архитектурные паттерны мульти-агентных систем, сравним ключевые фреймворки 2026 года, напишем рабочий код на LangGraph и CrewAI, разберёмся в протоколах MCP и A2A, и честно обсудим, когда мульти-агентные системы — это оверинжиниринг. Итак, поехали.
Архитектурные паттерны мульти-агентных систем
Прежде чем погружаться в код, важно понять, как агенты могут взаимодействовать между собой. Выбор архитектурного паттерна — это, пожалуй, самое важное решение при проектировании мульти-агентной системы. От него зависит, кто принимает решения, как передаётся контекст и как система масштабируется.
Supervisor (центральный координатор)
Самый распространённый и, честно говоря, самый понятный паттерн. Есть один главный агент — супервайзер — который получает входящий запрос, решает, какому из подчинённых агентов его передать, собирает результаты и формирует финальный ответ. По сути, это менеджер проекта, который распределяет задачи по команде.
Плюсы: простота реализации, предсказуемый поток данных, легко добавлять новых агентов. Минусы: супервайзер — единая точка отказа, и при большом количестве агентов он может стать узким местом. Этот паттерн рекомендуется для большинства начальных реализаций — именно его мы будем использовать в практическом примере ниже.
Hierarchical (иерархический)
Расширение паттерна Supervisor на несколько уровней. Главный супервайзер управляет несколькими подсупервайзерами, каждый из которых координирует свою группу агентов. Напоминает организационную структуру крупной компании: CEO → директора направлений → руководители отделов → исполнители.
Этот паттерн хорош для задач, которые естественным образом распадаются на независимые подзадачи. Например, система автоматизации маркетинга: один подсупервайзер отвечает за контент (копирайтер, дизайнер, SEO-оптимизатор), другой — за аналитику (сборщик данных, аналитик, визуализатор), третий — за дистрибуцию (email-агент, SMM-агент, рекламный агент). Красиво ложится на бизнес-структуру, правда?
Fully Connected (полносвязная сеть)
Каждый агент может общаться с каждым напрямую, без центрального координатора. Звучит демократично, но на практике — это хаос, если не ввести строгие протоколы взаимодействия. Количество возможных связей растёт квадратично: для 5 агентов — 10 связей, для 10 — уже 45.
Оправдан в сценариях, где агенты должны динамически обнаруживать и использовать возможности друг друга — например, в исследовательских системах, где один агент может вдруг понять, что ему нужна помощь другого, о существовании которого он не знал заранее. На практике используется редко из-за кошмарной сложности отладки.
Dispatcher (интеллектуальная маршрутизация)
Вариация паттерна Supervisor, где координатор не просто делегирует задачи, а выполняет интеллектуальную маршрутизацию на основе анализа запроса. Диспетчер классифицирует входящий запрос и направляет его к наиболее подходящему агенту (или цепочке агентов). В отличие от супервайзера, диспетчер обычно не собирает результаты — направил запрос и забыл.
Типичный пример: входящий запрос в техподдержку. Диспетчер определяет, что это проблема с биллингом, и направляет к биллинг-агенту. Или видит, что это технический баг, и перенаправляет к агенту технической поддержки. Простой, эффективный, хорошо масштабируемый паттерн.
Sequential Chain (последовательная цепочка)
Агенты выстроены в конвейер: выход первого является входом для второго, выход второго — входом для третьего, и так далее. Самый простой паттерн, и он идеально подходит для задач с чёткой последовательностью шагов: извлечение данных → анализ → генерация отчёта → проверка качества.
Ограничение здесь довольно очевидное: если одному из агентов в середине цепочки вдруг нужна информация от агента, стоящего позади, система ломается. Для таких случаев придётся переходить к циклическим графам или возвращаться к паттерну с супервайзером.
Обзор фреймворков 2026 года
Рынок фреймворков для мульти-агентных систем за последний год стал заметно зрелее. Если в 2024 году это были экспериментальные библиотеки для энтузиастов, то к 2026-му несколько решений уверенно вышли на уровень production-ready. Давайте разберём ключевых игроков.
LangGraph
Мой текущий фаворит для production-систем (и я говорю это, попробовав все остальные). LangGraph — фреймворк от создателей LangChain, построенный на концепции направленных графов. Каждый агент — это узел графа, рёбра определяют потоки данных между ними. Это даёт исключительную гибкость: можно строить любые паттерны — от простой последовательной цепочки до сложных циклических графов с условными переходами.
Ключевые преимущества: работа с состоянием (state management) из коробки, поддержка стриминга, встроенное persistence для возобновления прерванных воркфлоу, и — что немаловажно — бенчмарки показывают, что он в 2.2 раза быстрее CrewAI. LangChain сейчас активно рекомендует паттерн supervisor через функцию create_supervisor, где супервайзер использует подчинённых агентов как инструменты (tools).
CrewAI
CrewAI предлагает совсем другую метафору: вместо графов — команда сотрудников с ролями. Вы определяете агентов как «членов экипажа» с конкретными должностями, навыками и целями. Для людей, привыкших мыслить в терминах организационных структур, это очень интуитивно.
Версия 0.152.0 поддерживает два основных процесса: Sequential (агенты работают друг за другом) и Hierarchical (агент-менеджер распределяет задачи). Параметр allow_delegation=True позволяет агентам делегировать подзадачи друг другу. CrewAI отлично подходит для быстрого прототипирования и систем, где бизнес-логика хорошо ложится на ролевую модель. Но для сложных графов с условными переходами и циклами LangGraph будет удобнее.
OpenAI Agents SDK
Эволюция экспериментального проекта Swarm, теперь — production-ready SDK от OpenAI. Ключевые фичи: Handoffs (передача управления между агентами), Guardrails (встроенные ограничители), Sessions (управление состоянием диалога), Human-in-the-loop (подключение человека в критических точках), Tracing (полная трассировка выполнения) и поддержка Realtime Agents.
Если ваш стек уже построен на OpenAI и менять провайдера вы не планируете, Agents SDK — логичный выбор. Он хорошо интегрируется с остальной экосистемой OpenAI и предоставляет удобный механизм хэндоффов, когда один агент передаёт управление другому вместе с контекстом беседы. Из минусов — привязка к экосистеме OpenAI (впрочем, для кого-то это и плюс).
Google ADK (Agent Development Kit)
Открытый фреймворк от Google, представленный на Cloud NEXT 2025. ADK предлагает три типа workflow-агентов: Sequential (последовательное выполнение), Parallel (параллельное выполнение) и Loop (циклическое выполнение). В феврале 2026 добавлена поддержка TypeScript.
Главное отличие ADK — нативная поддержка протокола A2A (Agent-to-Agent), о котором мы поговорим ниже. Если вы строите систему, где агенты от разных провайдеров должны взаимодействовать друг с другом, ADK с A2A — пока единственное по-настоящему зрелое решение для этого сценария.
AutoGen (Microsoft)
AutoGen делает ставку на разговорную архитектуру: агенты общаются между собой в формате диалога, динамически распределяя роли. Фреймворк отлично поддерживает human-in-the-loop — включение человека в диалог агентов на любом этапе.
AutoGen особенно хорош для сценариев, где процесс не определён заранее и агенты должны сами договориться, кто что делает. Типичный пример — группа агентов для разработки ПО: один пишет код, другой ревьюирует, третий пишет тесты, и они итерируют в диалоге, пока не достигнут приемлемого результата. Динамическое ролевое взаимодействие — это и сила, и слабость AutoGen: мощный инструмент, но результат менее предсказуем, чем в детерминированных графах LangGraph.
Практика: мульти-агентная система на LangGraph
Ладно, хватит теории — переходим к коду. Построим мульти-агентную систему для анализа новостей с использованием паттерна Supervisor в LangGraph. Система будет состоять из трёх агентов: исследователь (ищет информацию), аналитик (анализирует найденное) и редактор (формирует финальный текст).
Сначала установим необходимые пакеты:
pip install langgraph langchain-openai langchain-core
Теперь определим агентов и построим граф:
from typing import Annotated, Any
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.tools import tool
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import create_react_agent
# Инициализируем LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0.1)
# --- Определяем инструменты для агентов ---
@tool
def search_news(query: str) -> str:
"""Поиск свежих новостей по заданной теме."""
# В реальной системе здесь был бы вызов News API или веб-поиска
return (
f"Результаты поиска по запросу '{query}':\n"
"1. AI-стартапы привлекли $12 млрд инвестиций в Q1 2026\n"
"2. Новый стандарт A2A получил поддержку 150+ организаций\n"
"3. 80% предприятий планируют внедрение агентных систем к концу 2026"
)
@tool
def analyze_sentiment(text: str) -> str:
"""Анализ тональности и ключевых трендов в тексте."""
return (
f"Анализ текста:\n"
"- Общая тональность: позитивная\n"
"- Ключевые тренды: рост инвестиций, стандартизация, "
"массовое внедрение\n"
"- Уровень достоверности: высокий (множественные источники)"
)
@tool
def format_report(content: str, style: str = "brief") -> str:
"""Форматирование контента в структурированный отчёт."""
return f"[Отчёт в стиле '{style}']\n{content}"
# --- Создаём специализированных агентов ---
researcher = create_react_agent(
llm,
tools=[search_news],
prompt=(
"Ты — исследователь. Твоя задача — находить актуальную "
"информацию по заданной теме. Используй инструмент поиска "
"новостей. Возвращай только факты, без интерпретаций."
),
)
analyst = create_react_agent(
llm,
tools=[analyze_sentiment],
prompt=(
"Ты — аналитик. Твоя задача — анализировать предоставленную "
"информацию, выявлять тренды, оценивать достоверность "
"и делать выводы. Будь объективен и критичен."
),
)
editor = create_react_agent(
llm,
tools=[format_report],
prompt=(
"Ты — редактор. Твоя задача — взять результаты исследования "
"и анализа, и сформировать из них краткий, понятный отчёт "
"для бизнес-аудитории. Структурируй информацию."
),
)
# --- Строим граф с паттерном Supervisor ---
from langgraph.prebuilt import create_supervisor
# Супервайзер автоматически управляет маршрутизацией
# между агентами на основе текущего состояния задачи
supervisor = create_supervisor(
llm,
agents={
"researcher": researcher,
"analyst": analyst,
"editor": editor,
},
prompt=(
"Ты — координатор аналитической команды. "
"Управляй процессом анализа: сначала поручи "
"исследователю собрать информацию, затем аналитику — "
"проанализировать её, и наконец редактору — "
"оформить финальный отчёт. "
"Убедись, что каждый этап завершён перед переходом "
"к следующему."
),
)
# Компилируем граф
app = supervisor.compile()
# --- Запускаем систему ---
result = app.invoke({
"messages": [
HumanMessage(
content="Подготовь краткий аналитический обзор "
"о текущем состоянии рынка AI-агентов"
)
]
})
# Выводим финальный результат
for message in result["messages"]:
if hasattr(message, "content") and message.content:
print(f"[{message.type}]: {message.content[:200]}...")
print("---")
Что тут происходит? Функция create_supervisor создаёт агента-координатора, который видит подчинённых агентов как инструменты. Когда поступает запрос, супервайзер сам решает, в каком порядке вызывать агентов и когда считать задачу выполненной. При этом каждый агент — полноценный ReAct-агент со своими инструментами и системным промптом.
Обратите внимание на важный момент: мы не прописываем жёстко последовательность «исследователь → аналитик → редактор». Супервайзер определяет порядок сам, на основе текущего состояния. Если аналитику понадобится дополнительная информация, супервайзер может вернуть задачу исследователю. Вот за эту гибкость мы и ценим графовую архитектуру LangGraph.
Практика: команда агентов в CrewAI
Теперь построим аналогичную систему в CrewAI, чтобы почувствовать разницу подходов. Здесь мы мыслим не графами, а ролями и задачами — и это, надо сказать, совершенно другой опыт.
pip install crewai crewai-tools
from crewai import Agent, Task, Crew, Process
# --- Определяем агентов как «членов экипажа» ---
researcher = Agent(
role="Исследователь рынка AI",
goal=(
"Находить актуальную и достоверную информацию "
"о рынке AI-агентов, технологических трендах "
"и ключевых игроках"
),
backstory=(
"Ты — опытный технологический аналитик с 10-летним "
"стажем. Специализируешься на рынке искусственного "
"интеллекта. Известен своей тщательностью в проверке "
"фактов и умением находить неочевидные связи между "
"трендами."
),
verbose=True,
allow_delegation=True, # Может делегировать подзадачи
)
analyst = Agent(
role="Стратегический аналитик",
goal=(
"Глубоко анализировать собранные данные, выявлять "
"закономерности и формулировать стратегические выводы"
),
backstory=(
"Ты — аналитик из топ-консалтинговой компании. "
"Умеешь переводить технические данные в бизнес-инсайты. "
"Всегда подкрепляешь выводы цифрами и критически "
"оцениваешь источники."
),
verbose=True,
allow_delegation=False,
)
editor = Agent(
role="Главный редактор",
goal=(
"Создавать чёткие, структурированные отчёты, "
"понятные бизнес-аудитории без технического бэкграунда"
),
backstory=(
"Ты — редактор технологического издания с опытом "
"работы в деловых СМИ. Превращаешь сложные "
"технические материалы в увлекательные и понятные "
"тексты. Ценишь краткость и ясность."
),
verbose=True,
allow_delegation=False,
)
# --- Определяем задачи ---
research_task = Task(
description=(
"Исследуй текущее состояние рынка мульти-агентных "
"AI-систем. Найди ключевые фреймворки, статистику "
"внедрения, основных игроков и свежие тренды 2026 года. "
"Обрати особое внимание на протоколы MCP и A2A."
),
expected_output=(
"Структурированный список фактов и данных с указанием "
"источников. Минимум 10 ключевых фактов."
),
agent=researcher,
)
analysis_task = Task(
description=(
"На основе собранных исследователем данных проведи "
"стратегический анализ. Определи: 1) главные тренды, "
"2) риски и возможности, 3) прогноз развития на "
"ближайшие 12 месяцев. Подкрепи выводы цифрами."
),
expected_output=(
"Аналитическая записка с разделами: тренды, риски, "
"возможности, прогноз. Каждый вывод подкреплён данными."
),
agent=analyst,
context=[research_task], # Зависит от результатов исследования
)
editing_task = Task(
description=(
"Возьми результаты исследования и анализа и создай "
"финальный отчёт для CEO технологической компании. "
"Отчёт должен быть не длиннее одной страницы, "
"содержать executive summary, ключевые цифры и "
"рекомендации к действию."
),
expected_output=(
"Краткий executive-отчёт на одну страницу с "
"чёткой структурой и конкретными рекомендациями."
),
agent=editor,
context=[research_task, analysis_task],
)
# --- Собираем команду и запускаем ---
crew = Crew(
agents=[researcher, analyst, editor],
tasks=[research_task, analysis_task, editing_task],
process=Process.sequential, # Последовательное выполнение
verbose=True,
)
# Запуск команды
result = crew.kickoff()
print("=" * 50)
print("ФИНАЛЬНЫЙ ОТЧЁТ:")
print("=" * 50)
print(result.raw)
Сравните с LangGraph: здесь мы не строим граф, а описываем команду в терминах ролей (role), целей (goal) и предысторий (backstory). Задачи (Task) связаны через параметр context, который указывает зависимости. Процесс sequential гарантирует выполнение задач в заданном порядке.
А если нам нужна иерархическая организация? Достаточно заменить буквально одну строку:
crew = Crew(
agents=[researcher, analyst, editor],
tasks=[research_task, analysis_task, editing_task],
process=Process.hierarchical, # Менеджер распределяет задачи
manager_llm=ChatOpenAI(model="gpt-4o", temperature=0.1),
verbose=True,
)
В иерархическом режиме CrewAI автоматически создаёт агента-менеджера, который решает, какому агенту передать очередную задачу. По духу это близко к паттерну Supervisor в LangGraph, но с меньшим контролем над логикой маршрутизации.
Мой честный вывод: CrewAI быстрее в прототипировании и интуитивнее для людей, мыслящих в терминах организаций. LangGraph даёт больше контроля и работает быстрее в production. Строите MVP — начните с CrewAI. Строите систему на тысячи запросов в день — берите LangGraph. Я сам прошёл этот путь: начал с CrewAI, потом мигрировал на LangGraph, когда стало важно контролировать каждый шаг.
Протоколы межагентного взаимодействия: MCP и A2A
До сих пор мы говорили об агентах внутри одной системы, одного фреймворка, одного приложения. Но что если ваши агенты используют разные LLM, разные фреймворки, и вообще развёрнуты на разных серверах? Тут на сцену выходят протоколы стандартизации — и в 2026 году два из них стали де-факто стандартами.
MCP (Model Context Protocol)
MCP — открытый стандарт от Anthropic для интеграции LLM с внешними инструментами и источниками данных. Если Function Calling — это способ вызвать функцию, то MCP — стандарт того, как описывать и подключать эти функции единообразно.
Ключевая идея: любой инструмент, база данных или API описывается в стандартном формате, который понимает любой LLM-клиент. Пишете MCP-сервер один раз — и он работает с Claude, GPT, Gemini и любой другой моделью, поддерживающей протокол. Это решает проблему, с которой мы все сталкивались: для каждого нового LLM приходилось переписывать интеграции. Занятие, мягко говоря, утомительное.
В 2025-2026 годах MCP получил массовую поддержку: OpenAI, Microsoft, Google, LangChain — все ключевые игроки приняли стандарт. Экосистема MCP-серверов растёт стремительно: подключение к файловой системе, базам данных, API SaaS-сервисов, браузерам — всё уже доступно в виде готовых MCP-серверов.
Архитектура MCP достаточно проста: есть MCP-хост (приложение, в котором работает LLM), MCP-клиент (компонент, управляющий подключениями) и MCP-серверы (лёгкие процессы, предоставляющие доступ к инструментам и данным). Серверы описывают свои возможности (tools, resources, prompts), а клиент автоматически обнаруживает их и предоставляет LLM.
# Пример: простой MCP-сервер для доступа к базе данных
# (используя Python SDK для MCP)
from mcp.server import Server
from mcp.types import Tool, TextContent
server = Server("database-server")
@server.tool()
async def query_database(sql: str) -> str:
"""Выполняет SQL-запрос к аналитической базе данных.
Args:
sql: SQL-запрос для выполнения (только SELECT)
"""
# Валидация: разрешаем только SELECT-запросы
if not sql.strip().upper().startswith("SELECT"):
return "Ошибка: разрешены только SELECT-запросы"
# В реальной системе здесь было бы подключение к БД
return f"Результат запроса: [данные из БД по запросу '{sql}']"
@server.tool()
async def get_table_schema(table_name: str) -> str:
"""Возвращает схему указанной таблицы.
Args:
table_name: Имя таблицы
"""
schemas = {
"orders": "id INT, customer_id INT, amount DECIMAL, "
"created_at TIMESTAMP, status VARCHAR",
"customers": "id INT, name VARCHAR, email VARCHAR, "
"plan VARCHAR, created_at TIMESTAMP",
}
return schemas.get(
table_name,
f"Таблица '{table_name}' не найдена"
)
Ценность MCP для мульти-агентных систем в том, что агенты получают единообразный доступ к инструментам. Один и тот же MCP-сервер базы данных может использоваться агентом-исследователем (для извлечения данных), агентом-аналитиком (для проверки гипотез) и агентом-мониторинга (для отслеживания метрик). Никакого дублирования кода — и это, поверьте, экономит часы отладки.
A2A (Agent-to-Agent Protocol)
Если MCP стандартизирует взаимодействие «агент — инструмент», то A2A стандартизирует связь «агент — агент». Этот протокол, разработанный Google и переданный в Linux Foundation, определяет, как агенты на разных платформах и фреймворках могут обнаруживать друг друга и взаимодействовать.
Центральный элемент A2A — Agent Card: JSON-файл, описывающий возможности агента. Это как визитная карточка, которую агент публикует, чтобы другие знали, что он умеет, какие форматы данных принимает и как с ним связаться.
{
"name": "financial-analyst",
"description": "Анализирует финансовые данные и генерирует отчёты",
"url": "https://agents.example.com/financial-analyst",
"version": "1.2.0",
"capabilities": {
"streaming": true,
"pushNotifications": true,
"stateTransitionHistory": true
},
"skills": [
{
"id": "financial-analysis",
"name": "Финансовый анализ",
"description": "Анализ финансовых показателей компании",
"inputModes": ["text/plain", "application/json"],
"outputModes": ["application/json", "text/markdown"]
},
{
"id": "report-generation",
"name": "Генерация отчётов",
"description": "Создание структурированных финансовых отчётов",
"inputModes": ["application/json"],
"outputModes": ["application/pdf", "text/markdown"]
}
],
"authentication": {
"schemes": ["bearer"]
}
}
В версии v0.3 A2A добавлена поддержка gRPC в дополнение к HTTP, что значительно снижает задержки при взаимодействии между агентами. Уже более 150 организаций поддержали протокол, включая крупнейших облачных провайдеров.
MCP + A2A вместе формируют полный стек стандартизации: MCP для «вертикальной» интеграции (агент → инструменты/данные), A2A для «горизонтальной» (агент ↔ агент). В production-системе вашему агенту нужны оба: MCP для доступа к базам данных, API и файлам, A2A для общения с агентами от других команд, отделов или даже организаций.
Паттерны проектирования production-систем
Построить работающую мульти-агентную систему — это, скажем так, полдела. Заставить её надёжно работать в production — совсем другая история. Вот паттерны, которые реально помогают в боевых условиях.
Обработка ошибок и устойчивость
В мульти-агентной системе ошибки неизбежны: LLM может сгаллюцинировать, API может вернуть таймаут, агент может зациклиться. Ключевой принцип: каждый агент должен уметь gracefully деградировать.
import asyncio
from typing import Any
class ResilientAgent:
"""Обёртка для агента с обработкой ошибок."""
def __init__(
self,
agent: Any,
max_retries: int = 3,
timeout_seconds: int = 30,
fallback_response: str = "Агент не смог обработать запрос",
):
self.agent = agent
self.max_retries = max_retries
self.timeout_seconds = timeout_seconds
self.fallback_response = fallback_response
async def invoke(self, input_data: dict) -> dict:
"""Вызов агента с ретраями и таймаутом."""
last_error = None
for attempt in range(1, self.max_retries + 1):
try:
result = await asyncio.wait_for(
self.agent.ainvoke(input_data),
timeout=self.timeout_seconds,
)
return result
except asyncio.TimeoutError:
last_error = f"Таймаут (попытка {attempt})"
# Экспоненциальный бэкофф
await asyncio.sleep(2 ** attempt)
except Exception as e:
last_error = f"Ошибка (попытка {attempt}): {e}"
await asyncio.sleep(2 ** attempt)
# Все попытки исчерпаны — возвращаем fallback
return {
"messages": [{"content": self.fallback_response}],
"error": last_error,
"status": "degraded",
}
Управление стоимостью
Мульти-агентные системы могут оказаться неожиданно дорогими: каждый агент делает вызовы к LLM, а если супервайзер итерирует между агентами, количество вызовов растёт быстро. Вот несколько практических советов, проверенных на собственном опыте:
- Устанавливайте лимиты итераций. Если супервайзер не завершил задачу за 5 итераций — что-то пошло не так. Лучше вернуть частичный результат, чем сжечь весь бюджет.
- Используйте разные модели для разных агентов. Роутер или классификатор спокойно работают на дешёвой модели (GPT-4o-mini), а агент, генерирующий финальный ответ — на мощной (GPT-4o или Claude Opus).
- Кэшируйте результаты агентов. Если агент-исследователь уже искал информацию по теме «рынок AI-агентов» 5 минут назад, зачем делать это снова?
- Мониторьте расход токенов по агентам. Часто один «болтливый» агент съедает 80% бюджета. Найдите его и оптимизируйте промпт или ограничьте длину ответа.
Наблюдаемость (Observability)
В обычном приложении стектрейс ошибки показывает, где что сломалось. В мульти-агентной системе всё сложнее — нужно знать: какой агент был вызван, с каким контекстом, что он вернул, почему супервайзер решил передать задачу дальше и сколько токенов потратил каждый.
Минимальный набор метрик для production:
- Количество вызовов каждого агента и среднее время выполнения
- Количество итераций супервайзера до завершения задачи
- Расход токенов (input/output) по каждому агенту
- Частота ошибок и таймаутов по агентам
- Полный трейс: последовательность вызовов агентов с их входами и выходами
LangGraph предоставляет встроенную поддержку трейсинга через LangSmith, CrewAI логирует каждый шаг при verbose=True, а OpenAI Agents SDK имеет полноценный Tracing API из коробки. Для production-систем рекомендую дополнительно подключить Langfuse или аналог — для долгосрочного хранения и анализа трейсов это незаменимый инструмент.
Guardrails: ограничители безопасности
Когда у вас несколько агентов, каждый с доступом к инструментам, поверхность атаки увеличивается кратно. Guardrails — это ограничители, которые проверяют вход и выход каждого агента на соответствие политикам безопасности.
from typing import Callable
class AgentGuardrail:
"""Ограничитель для валидации входов/выходов агента."""
def __init__(self):
self.input_validators: list[Callable] = []
self.output_validators: list[Callable] = []
def add_input_validator(self, validator: Callable):
self.input_validators.append(validator)
def add_output_validator(self, validator: Callable):
self.output_validators.append(validator)
def validate_input(self, input_data: str) -> tuple[bool, str]:
for validator in self.input_validators:
is_valid, message = validator(input_data)
if not is_valid:
return False, message
return True, "OK"
def validate_output(self, output_data: str) -> tuple[bool, str]:
for validator in self.output_validators:
is_valid, message = validator(output_data)
if not is_valid:
return False, message
return True, "OK"
# Пример использования
def no_pii_validator(text: str) -> tuple[bool, str]:
"""Проверяет, что текст не содержит персональных данных."""
import re
# Простая проверка на email и телефоны
if re.search(r'\b[\w.+-]+@[\w-]+\.[\w.]+\b', text):
return False, "Обнаружен email-адрес в ответе"
if re.search(r'\b\d{10,}\b', text):
return False, "Обнаружен возможный номер телефона"
return True, "OK"
def max_length_validator(max_tokens: int = 2000):
"""Фабрика валидаторов максимальной длины."""
def validator(text: str) -> tuple[bool, str]:
# Грубая оценка: 1 токен ~ 4 символа
estimated_tokens = len(text) // 4
if estimated_tokens > max_tokens:
return False, f"Ответ слишком длинный: ~{estimated_tokens} токенов"
return True, "OK"
return validator
guardrail = AgentGuardrail()
guardrail.add_output_validator(no_pii_validator)
guardrail.add_output_validator(max_length_validator(2000))
В OpenAI Agents SDK guardrails встроены и определяются декларативно для каждого агента. В LangGraph их обычно реализуют как дополнительные узлы графа — «проверочные станции», через которые проходит результат агента перед передачей дальше.
Когда НЕ нужны мульти-агентные системы
Считаю важным закончить техническую часть этой статьи честным разговором о том, когда мульти-агентные системы — это избыточное решение. В текущем хайпе вокруг агентов легко увлечься и начать городить систему из пяти агентов там, где хватило бы одного промпта.
Не нужны мульти-агенты, когда задача решается одним хорошим промптом. Классификация тикетов, генерация ответов по шаблону, извлечение данных из текста — один агент с правильным промптом и набором инструментов справится прекрасно. Добавление супервайзера, координации и межагентного общения только увеличит латентность, стоимость и количество точек отказа.
Не нужны мульти-агенты, когда нет чёткого разделения ответственности. Если вы не можете объяснить одним предложением, за что отвечает каждый агент — скорее всего, вы искусственно разделяете задачу. Мульти-агентная архитектура работает, когда есть естественная декомпозиция: исследование, анализ, генерация, проверка. Не видите таких фаз — не изобретайте их.
Не нужны мульти-агенты, когда критична низкая латентность. Каждый дополнительный агент — это дополнительные вызовы LLM. Пользователь ждёт ответ в чате за 2 секунды, а цепочка из трёх агентов выдаёт результат через 15-20? Это неприемлемо.
Не нужны мульти-агенты, когда нет инфраструктуры для наблюдаемости. Отлаживать одного агента, который делает что-то не то, уже непросто. Отлаживать пять взаимодействующих агентов без трейсинга и логирования — практически невозможно. Нет инфраструктуры для мониторинга? Начните с одного агента.
Практическое правило: начинайте с одного агента. Добавляйте второго только тогда, когда первый явно не справляется — задача слишком сложна для одного промпта, или нужна специализация, которую невозможно вместить в один контекст. Мульти-агентная архитектура — это инструмент для решения сложных задач, а не способ сделать простую задачу сложнее.
Заключение и что дальше
Мы прошли большой путь в этой статье: от архитектурных паттернов до рабочего кода, от теории графов до протоколов межагентного взаимодействия.
Давайте подведём итоги.
Архитектура решает. Выбор паттерна (Supervisor, Hierarchical, Dispatcher, Sequential) определяет 80% успеха системы. Начинайте с Supervisor — он покрывает большинство сценариев.
Фреймворки зрелые. LangGraph для production с его графовой гибкостью, CrewAI для быстрого прототипирования с ролевой моделью, OpenAI Agents SDK для экосистемы OpenAI, Google ADK для межплатформенного взаимодействия через A2A. Выбор зависит от вашего стека и конкретной задачи.
Стандарты — это важно. MCP и A2A — не просто модные аббревиатуры, а реальные стандарты, которые определят, как AI-системы будут интегрироваться в ближайшие годы. Стоит вложить время в их понимание уже сейчас.
Production — это не demo. Обработка ошибок, управление стоимостью, наблюдаемость и guardrails — вот что отличает демо-проект на хакатоне от системы, обрабатывающей реальные запросы пользователей.
В следующих статьях серии мы продолжим углубляться в практические аспекты AI-инженерии. Мы уже разобрали Function Calling, RAG-пайплайны и мульти-агентные системы — фундаментальные блоки современных AI-приложений. Впереди — оценка качества LLM-систем, prompt engineering для production и многое другое.
Мульти-агентные системы — это не будущее. Это настоящее. И лучшее время начать строить их — прямо сейчас. Только, пожалуйста, начните с одного агента.