A B C D E F G H I J K L M N O P Q R S T V W Y Z А Б В Г Е И К М О П С Т Ц

Multi-agent system

Multi-agent system

 

Multi-agent system (Мультиагентная система, MAS) это система из нескольких автономных ИИ-агентов, которые обмениваются результатами и совместно решают одну задачу. У каждого агента своя роль, свой набор инструментов и нередко своя модель. Один ищет данные. Второй их анализирует. Третий проверяет вывод на выдумки, четвёртый собирает итоговый отчёт. Разбираем, из чего собирается такая команда, какие топологии применяются на практике и чем именно приходится платить за координацию.

 

Что такое мультиагентная система и какую задачу она закрывает

Начнём с уровня ниже. ИИ-агент (AI agent) это языковая модель, работающая в цикле. Она сама решает, какой инструмент вызвать, смотрит на результат и выбирает следующий шаг. Пока задача укладывается в один контекст и один набор инструментов, такого агента достаточно. Проблемы начинаются, когда задача разъезжается. Надо параллельно проверить двадцать источников, свести данные из четырёх систем, а потом ещё и перепроверить результат чужими глазами.

Мультиагентная система относится к классу оркестрационных решений поверх больших языковых моделей. Она не добавляет модели новых способностей, она перераспределяет работу. Вместо одного универсального исполнителя появляется несколько узких, у каждого свой системный промпт, своя зона ответственности и свой контекст. Выигрыш тут не в интеллекте, а в ёмкости. У каждого агента отдельное окно контекста. Команда суммарно переваривает больше информации, чем влезло бы в один диалог.

Инженеры Anthropic описали это на своей системе Research. Лид-агент планирует исследование и порождает субагентов. Те ищут параллельно и возвращают сжатые выжимки. Суть поиска, по их формулировке, это сжатие, а субагенты сжимают в несколько потоков сразу. Та же логика работает везде, где ветки задачи независимы. Например, обработка обращений, аудит документов, генерация и ревью кода.

 

Архитектура — из чего собрана команда агентов

Мультиагентная система держится на трёх вещах: роли агентов, общее состояние и правило передачи управления. Разберём каждую.

  • Роль и инструменты. Роль задаётся системным промптом и списком доступных инструментов. Поисковому агенту дают доступ к поиску, аналитику доступ не нужен вообще, контролёру полезен доступ к справочнику фактов. Чем уже роль, тем предсказуемее поведение.
  • Общее состояние. Агенты редко общаются свободным текстом напрямую. Обычно есть разделяемая структура данных, куда каждый пишет своё поле, а читает нужные ему чужие. Это резко упрощает отладку: видно, кто что положил.
  • Передача управления. Кто-то должен решать, чья очередь. Решение принимает либо отдельный агент-диспетчер, либо жёсткий граф переходов, либо сами агенты, передавая ход друг другу.

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

Мультиагентная система с супервизором, роли агентов и общее состояние

 

 

 

Почему агенту нужен собственный контекст

Отдельное окно контекста у каждого агента это не техническая деталь, а главный смысл разделения. Один агент тащит в себе сырые результаты поиска, промежуточные рассуждения и требования к формату. Качество ответов падает раньше, чем упирается лимит токенов. Субагент возвращает не сырьё, а выжимку, и в контекст координатора попадает уже сжатая информация. По сути это разделение труда ради экономии внимания модели, а не ради красоты схемы.

 

Топологии мультиагентных систем

На практике встречаются четыре базовых схемы, всё остальное это их комбинации.

  • Супервизор. Центральный агент принимает запрос, выбирает исполнителя и решает, звать ли следующего. Маршрутизация это его единственная работа. Поэтому решения предсказуемы и хорошо читаются в трассировке.
  • Иерархия. Тот же супервизор, но исполнители сами являются супервизорами своих подкоманд. Схема нужна, когда задача делится на крупные независимые направления.
  • Рой. Диспетчера нет, агенты передают ход напрямую тому, кто нужен по смыслу. Быстрее, потому что нет лишнего звена, но труднее отлаживать: маршрут перестаёт быть централизованным.
  • Конвейер. Фиксированная последовательность ролей без всякого выбора. Формально это не агентная система, а рабочий процесс, зато она детерминирована и дешева.

Выбор между ними определяется не модой, а тем, известен ли маршрут заранее. Если известен, конвейер выигрывает у всех остальных по стоимости и надёжности.

Топологии мультиагентной системы, супервизор, иерархия, рой и конвейер

 

 

Топология Кто выбирает следующего Сильная сторона Слабое место
Супервизор Отдельный агент-диспетчер Прозрачный маршрут, простая отладка Лишний вызов модели на каждом шаге
Иерархия Диспетчеры на нескольких уровнях Масштабирование на крупные направления Растёт задержка и сложность трассировки
Рой Сами агенты Меньше задержка, нет узкого горла Маршрут размазан, ошибки труднее локализовать
Конвейер Никто, порядок задан кодом Детерминированность и низкая цена Не адаптируется к нестандартному запросу

Разумная стратегия проста. Начинайте с конвейера. Переходите к супервизору, когда маршрут перестал быть предсказуемым. Иерархия и рой это следующий шаг, а не первый.

 

ИИ-агенты для оптимизации бизнес-процессов

Код курса
AGENT
Ближайшая дата курса
26 октября, 2026
Продолжительность
24 ак.часов
Стоимость обучения
66 000

 

Цена координации — чем платит мультиагентная система

Главный миф вокруг темы звучит так: несколько агентов всегда лучше одного. Данные говорят иначе, и разбираться стоит по двум статьям расходов.

Первая статья это токены. По замерам Anthropic, связка из ведущего агента на Claude Opus 4 и субагентов на Claude Sonnet 4 обошла одиночного Claude Opus 4 на 90,2 процента во внутренней оценке исследовательских задач. Ценой стал расход. Агенты в среднем тратят вчетверо больше токенов, чем обычный чат. Мультиагентные системы примерно в пятнадцать раз больше. Иначе говоря, качество выросло за счёт того, что на задачу просто потратили намного больше вычислений.

Вторая статья, менее очевидная, это надёжность. Ошибки в цепочке агентов не гасятся, а накапливаются. Неточная выжимка поисковика становится входом аналитика. Кривая формулировка аналитика ломает контролёра. Плюс сама координация нестабильна. В нашем прогоне на локальной модели qwen2.5:7b агент-диспетчер все четыре раза вернул одну и ту же роль. Правильный маршрут вытянул предохранитель в коде, а не решение модели. Контролёр в другом сценарии уверенно выдал ложную ошибку, объявив корректный код курса неверным.

Из этого следует практический вывод, который редко пишут в маркетинговых материалах: проверки, которые можно сделать обычным кодом, не надо поручать агенту. Сверку кода со справочником делает функция на пять строк, и она не ошибается.

 

Когда мультиагент оправдан, а когда мешает

Мультиагентная система окупается на задачах с высокой ценностью результата и хорошо распараллеливаемой структурой. Типичные кандидаты такие. Исследование рынка по множеству источников. Разбор большого корпуса документов. Поддержка с разными доменами знаний. Генерация кода с отдельным этапом ревью.

Обратный список не короче. Схема противопоказана, когда все участники обязаны видеть один и тот же контекст. Или когда шаги жёстко зависят друг от друга. Или когда задача не окупает пятнадцатикратный расход токенов. Инженеры Anthropic отдельно отмечают, что команды внутри компании месяцами строили сложные мультиагентные архитектуры и потом обнаруживали, что аккуратная работа с промптом одиночного агента даёт тот же результат. Перед тем как разносить логику по агентам, стоит честно проверить именно этот вариант, а заодно измерить качество: без метрик и трассировки мультиагентную систему невозможно ни отладить, ни защитить перед бизнесом, о чём подробно говорит практика LLMOps.

Отдельный вопрос это связь агентов между собой. Пока агенты живут в одном процессе, они общаются через общее состояние. Как только они разъезжаются по разным сервисам и командам, нужен протокол обмена и способ сообщить о своих возможностях. Тему стыковки агентов с внешним событийным контуром хорошо разбирает статья Проблема изоляции ИИ-агентов и её решение с помощью потоковой передачи.

 

Разработка и внедрение ML-решений

Код курса
MLOPS
Ближайшая дата курса
24 августа, 2026
Продолжительность
24 ак.часов
Стоимость обучения
66 000

 

Практика. Команда агентов на LangGraph и Ollama

По традиции весь код используемый в статье выкладываем на наш GitHub репозиторий
Multi-Agent System (мультиагентная система) from airflow import DAG from airflow.operators.bash import BashOperator from datetime import datetime with DAG( dag_id="spark_submit_demo", start_date=datetime(2025, 1, 1), schedule="@daily", catchup=False ) as dag: run = BashOperator( task_id="run_job", bash_command="spark-submit app.py" ) GitHub code example Multi-Agent System (мультиагентная система)

Соберём мультиагентную систему с супервизором на фреймворке LangGraph и локальной модели qwen2.5:7b через Ollama. Задача бытовая: подобрать курс под запрос слушателя. Ролей четыре. Супервизор выбирает исполнителя, поисковик достаёт курсы из каталога. Аналитик формулирует рекомендацию, контролёр сверяет её с разрешённым списком кодов. Файл supervisor_demo.py целиком:

# LangGraph 1.2.11, langchain-ollama 1.1.0, Python 3.12.13, Ollama 0.32.9, модель qwen2.5:7b. Прогнано на стенде.
"""Мультиагентная система с супервизором: подбор курса под запрос слушателя.

Четыре роли: супервизор решает, кто работает следующим, поисковик достаёт курсы
из каталога, аналитик формулирует рекомендацию, критик проверяет её на выдумки.
"""

import operator
from typing import Annotated, TypedDict

from langchain_ollama import ChatOllama
from langgraph.graph import END, START, StateGraph

MODEL = "qwen2.5:7b"

# Локальный каталог вместо внешней базы: демо не должно зависеть от сети.
CATALOG = [
    {"code": "DEVKI", "title": "Apache Kafka для инженеров данных", "about": "потоковая передача, топики, продюсеры и консьюмеры"},
    {"code": "FLINK", "title": "Потоковая обработка данных с Apache Flink", "about": "стриминговые джобы, окна, состояние"},
    {"code": "AIRF", "title": "Apache Airflow для инженеров данных", "about": "пакетные пайплайны, DAG, расписания"},
    {"code": "AGENT", "title": "ИИ-агенты для оптимизации бизнес-процессов", "about": "LLM-агенты, инструменты, мультиагентные системы"},
]


class TeamState(TypedDict):
    """Общая память команды. Каждый агент дописывает своё поле, чужие не трогает."""
    request: str
    findings: str
    analysis: str
    review: str
    route_log: Annotated[list[str], operator.add]
    steps: int
    next: str


llm = ChatOllama(model=MODEL, temperature=0)


def ask(system: str, user: str) -> str:
    """Один вызов модели с системной ролью агента."""
    return llm.invoke([("system", system), ("human", user)]).content.strip()


def supervisor(state: TeamState) -> TeamState:
    """Супервизор не решает задачу, он только выбирает следующего исполнителя."""
    done = [k for k in ("findings", "analysis", "review") if state.get(k)]
    verdict = ask(
        "Ты диспетчер команды агентов. Отвечай ровно одним словом из списка: "
        "researcher, analyst, critic, FINISH. Без пояснений.",
        f"Запрос пользователя: {state['request']}\n"
        f"Уже готово: {done or 'ничего'}\n"
        "Порядок работы: сначала researcher, потом analyst, потом critic, потом FINISH.",
    )
    choice = next((r for r in ("researcher", "analyst", "critic", "FINISH") if r.lower() in verdict.lower()), None)
    # Предохранитель: модель на 7 миллиардов параметров иногда возвращает мусор
    # или зацикливает роль, поэтому детерминированный порядок остаётся страховкой.
    fallback = "researcher" if not state.get("findings") else "analyst" if not state.get("analysis") else "critic" if not state.get("review") else "FINISH"
    if choice is None or (choice != "FINISH" and state.get({"researcher": "findings", "analyst": "analysis", "critic": "review"}[choice])):
        choice = fallback
    return {
        "next": choice,
        "steps": state["steps"] + 1,
        "route_log": [f"supervisor -> {choice} (сырой ответ модели: {verdict!r})"],
    }


def researcher(state: TeamState) -> TeamState:
    """Поисковик работает только с каталогом и не додумывает курсы от себя."""
    catalog = "\n".join(f"{c['code']}: {c['title']} - {c['about']}" for c in CATALOG)
    out = ask(
        "Ты поисковый агент. Выбери из каталога 1-2 подходящих курса. "
        "Отвечай строками вида КОД: причина. Курсы вне каталога называть запрещено.",
        f"Каталог:\n{catalog}\n\nЗапрос: {state['request']}",
    )
    return {"findings": out, "route_log": ["researcher: отработал"]}


def analyst(state: TeamState) -> TeamState:
    """Аналитик превращает находки в рекомендацию для человека."""
    out = ask(
        "Ты агент-аналитик. По находкам коллеги дай рекомендацию строго одним абзацем "
        "из 2-3 предложений на русском языке. Новых курсов не добавляй, запрос не повторяй, "
        "диалог не продолжай.",
        f"Запрос: {state['request']}\nНаходки:\n{state['findings']}",
    )
    return {"analysis": out, "route_log": ["analyst: отработал"]}


def critic(state: TeamState) -> TeamState:
    """Критик сверяет рекомендацию с каталогом, это дешёвая защита от выдумок."""
    codes = ", ".join(c["code"] for c in CATALOG)
    out = ask(
        "Ты агент-контролёр. Разрешённые коды курсов: "
        f"{codes}. Проверь, что в рекомендации нет других кодов. Первым словом ответь ОК или ОШИБКА, "
        "дальше одна строка пояснения.",
        f"Рекомендация:\n{state['analysis']}",
    )
    return {"review": out, "route_log": ["critic: отработал"]}


def route(state: TeamState) -> str:
    """Переход по решению супервизора плюс жёсткий лимит шагов."""
    if state["steps"] > 8:
        return END
    return state.get("next", END)


graph = StateGraph(TeamState)
graph.add_node("supervisor", supervisor)
graph.add_node("researcher", researcher)
graph.add_node("analyst", analyst)
graph.add_node("critic", critic)
graph.add_edge(START, "supervisor")
graph.add_conditional_edges("supervisor", route, {"researcher": "researcher", "analyst": "analyst", "critic": "critic", "FINISH": END, END: END})
for worker in ("researcher", "analyst", "critic"):
    graph.add_edge(worker, "supervisor")
app = graph.compile()

if __name__ == "__main__":
    task = "Я инженер данных, работаю с батчами, хочу перейти в потоковую обработку. Что учить?"
    result = app.invoke({"request": task, "findings": "", "analysis": "", "review": "", "route_log": [], "steps": 0, "next": ""})
    print("=== Маршрут ===")
    for line in result["route_log"]:
        print(line)
    print("\n=== Находки поисковика ===\n" + result["findings"])
    print("\n=== Рекомендация аналитика ===\n" + result["analysis"])
    print("\n=== Вердикт критика ===\n" + result["review"])

Обратите внимание на функцию супервизора. Формально маршрут выбирает модель, фактически рядом стоит детерминированный запасной вариант и лимит шагов. Вывод прогона объясняет, зачем они нужны.

Multi-Agent System, вывод прогона supervisor_demo.py: диспетчер четыре раза выбирает researcher, контролёр подтверждает рекомендацию

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

Второй файл, cost_compare.py, решает ту же задачу двумя способами и считает токены с временем по метаданным ответов Ollama.

# LangGraph 1.2.11, langchain-ollama 1.1.0, Python 3.12.13, Ollama 0.32.9, модель qwen2.5:7b. Прогнано на стенде.
"""Цена координации: одна и та же задача одним агентом и командой из трёх агентов.

Скрипт считает токены и время по метаданным ответов Ollama, чтобы разница между
двумя подходами была не рассуждением, а числом.
"""

import time

from langchain_ollama import ChatOllama

MODEL = "qwen2.5:7b"
llm = ChatOllama(model=MODEL, temperature=0)

CATALOG = """DEVKI: Apache Kafka для инженеров данных - потоковая передача, топики, продюсеры и консьюмеры
FLINK: Потоковая обработка данных с Apache Flink - стриминговые джобы, окна, состояние
AIRF: Apache Airflow для инженеров данных - пакетные пайплайны, DAG, расписания
AGENT: ИИ-агенты для оптимизации бизнес-процессов - LLM-агенты, инструменты, мультиагентные системы"""

TASK = "Я инженер данных, работаю с батчами, хочу перейти в потоковую обработку. Что учить?"


def call(system: str, user: str) -> tuple[str, int]:
    """Возвращает текст ответа и суммарное число токенов промпта и генерации."""
    msg = llm.invoke([("system", system), ("human", user)])
    meta = msg.response_metadata
    tokens = meta.get("prompt_eval_count", 0) + meta.get("eval_count", 0)
    return msg.content.strip(), tokens


def single_agent() -> tuple[str, int]:
    """Один агент делает всю работу за один вызов модели."""
    return call(
        "Ты консультант по обучению. Подбери курс из каталога, обоснуй выбор, "
        "проверь себя. Отвечай только на русском.",
        f"Каталог:\n{CATALOG}\n\nЗапрос: {TASK}",
    )


def multi_agent() -> tuple[str, int]:
    """Три агента по очереди: поиск, рекомендация, проверка. Три вызова модели."""
    total = 0
    found, t = call("Ты поисковый агент. Выбери 1-2 курса из каталога, отвечай строками КОД: причина.",
                    f"Каталог:\n{CATALOG}\n\nЗапрос: {TASK}")
    total += t
    advice, t = call("Ты агент-аналитик. Дай рекомендацию строго одним абзацем из 2-3 предложений на русском языке. Запрос не повторяй, диалог не продолжай.",
                     f"Запрос: {TASK}\nНаходки:\n{found}")
    total += t
    review, t = call("Ты агент-контролёр. Проверь, что упомянуты только коды DEVKI, FLINK, AIRF, AGENT. Ответь ОК или ОШИБКА и причину.",
                     f"Рекомендация:\n{advice}")
    total += t
    return f"{advice}\n[контроль] {review}", total


if __name__ == "__main__":
    for name, fn in (("Один агент", single_agent), ("Команда из трёх агентов", multi_agent)):
        start = time.monotonic()
        answer, tokens = fn()
        elapsed = time.monotonic() - start
        print(f"=== {name} ===")
        print(f"токенов: {tokens}, время: {elapsed:.1f} с")
        print(answer)
        print()

Результат оказался поучительнее ожидаемого. Один агент потратил 597 токенов и 34,4 секунды, команда из трёх агентов 574 токена и 13,1 секунды. На маленькой задаче команда вышла даже чуть дешевле и заметно быстрее. Причина в том, что три коротких ответа генерируются быстрее одного длинного, а генерация тут узкое место. Зато контролёр заявил, что код FLINK неверный и должен быть AGENT. Это ложная ошибка на корректной рекомендации.

Multi-Agent System, вывод прогона cost_compare.py: один агент против команды из трёх агентов по токенам и времени

Вывод из двух прогонов такой. Пятнадцатикратный расход берётся не из самого факта разделения на агентов. Он берётся из объёма работы, который субагенты успевают сделать в реальных задачах с поиском и длинными документами. А вот падение надёжности видно уже на игрушечном примере, и именно оно съедает больше всего инженерного времени.

 

Заключение

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

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

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

 

Референсные ссылки