Мульти-агентные системы
Как несколько агентов работают вместе: архитектуры, коммуникация, координация.
Этот раздел — ключевой для построения команды агентов твоего проекта.
← AI-агенты · Архитектура · Туториал: команда → · 🇬🇧 English
Содержание
- Зачем нужны несколько агентов
- Архитектуры мульти-агентных систем
- Supervisor Pattern
- Handoff Pattern
- Swarm Pattern
- GroupChat Pattern
- Иерархическая команда (для твоего проекта)
- Проблемы мульти-агентных систем
- Что дальше
1. Зачем нужны несколько агентов
Один агент — это цикл «думай → действуй → наблюдай». Он хорош для простых задач, но упирается в ограничения:
| Проблема одного агента | Решение мульти-агент |
|---|---|
| Контекстное окно — быстро заполняется | Каждый агент имеет свой узкий контекст |
| Одна экспертиза — модель не может быть экспертом во всём | Каждый агент специализируется на своём |
| Последовательность — всё делает по очереди | Агенты работают параллельно |
| Отсутствие проверки — модель не замечает свои ошибки | Один агент пишет, другой проверяет |
| Одна точка отказа — если зациклился, всё встало | Другие агенты продолжают работу |
Твой проект — идеальный сценарий для мульти-агентной системы:
- PM-агент ставит задачи и следит за сроками
- Аналитик собирает требования
- Программист пишет код
- Тестировщик проверяет
- DevOps деплоит
- Дизайнер создаёт интерфейсы
2. Архитектуры мульти-агентных систем
Supervisor (иерархическая):
┌──────────────┐
│ Supervisor │ ← центральный координатор
└──┬──┬──┬──┬──┘
│ │ │ │
┌──┘ │ │ └──┐
▼ ▼ ▼ ▼
┌────┐┌────┐┌────┐┌────┐
│ A1 ││ A2 ││ A3 ││ A4 │ ← специалисты
└────┘└────┘└────┘└────┘
Handoff (передача управления):
┌──────┐ ┌──────┐ ┌──────┐
│ Triage│──▶│ Sales│──▶│Support│
└──────┘ └──────┘ └──────┘
Агент сам решает, кому передать задачу
Swarm (роевая):
Агенты равны, каждый может взять задачу
┌──────┐
│ A1 │◀───┐
└──────┘ │
┌──────┐ │
│ A2 │◀───┼─── Общая очередь задач
└──────┘ │
┌──────┐ │
│ A3 │◀───┘
└──────┘
GroupChat (дискуссионная):
Все агенты в одном чате, обсуждают и приходят к решению
┌──────┐ ┌──────┐
│ A1 │◀──▶│ A2 │
└──────┘ └──────┘
▲ ▲
│ ┌──────┐ │
└────│ A3 │─┘
└──────┘
3. Supervisor Pattern
Один главный агент управляет специалистами. Supervisor решает, кому какую задачу дать, и собирает результаты.
Как работает
Пользователь: «Сделай исследование рынка AI-агентов»
Supervisor:
└─▶ Аналитик: собери данные о рынке
└─▶ Писатель: оформи отчёт на основе данных
└─▶ Дизайнер: создай диаграммы
Supervisor собирает результаты → финальный ответ
Реализация на Python
import requests
import json
OLLAMA = "http://localhost:11434/api/chat"
MODEL = "qwen3.5:4b"
class Agent:
"""Агент-специалист с ролью."""
def __init__(self, name: str, role: str, instruction: str):
self.name = name
self.role = role
self.instruction = instruction
def run(self, task: str, context: str = "") -> str:
response = requests.post(OLLAMA, json={
"model": MODEL,
"messages": [
{"role": "system", "content": self.instruction},
{"role": "user", "content": f"{context}\nЗадача: {task}"}
],
"stream": False
})
return response.json()["message"]["content"]
class Supervisor:
"""Supervisor — управляет командой агентов."""
def __init__(self, agents: list[Agent]):
self.agents = agents
def run(self, task: str) -> str:
results = {}
for agent in self.agents:
print(f" ▶ {agent.name} работает...")
context = "\n".join([f"{name}: {res}" for name, res in results.items()])
result = agent.run(task, context)
results[agent.name] = result
print(f" ✓ {agent.name} завершил")
# Supervisor собирает финальный ответ
summary_prompt = (
f"Задача: {task}\n\n"
f"Результаты команды:\n" +
"\n".join([f"{name}: {res}" for name, res in results.items()]) +
"\n\nСоставь сводный отчёт на основе работы всех участников."
)
response = requests.post(OLLAMA, json={
"model": MODEL,
"messages": [
{"role": "system", "content": "Ты — руководитель команды. Собери результаты в единый отчёт."},
{"role": "user", "content": summary_prompt}
],
"stream": False
})
return response.json()["message"]["content"]
# Пример: команда для анализа
analyst = Agent(
"Аналитик",
"Исследователь рынка",
"Ты — аналитик. Ищи данные, строй гипотезы, делай выводы."
)
writer = Agent(
"Писатель",
"Технический автор",
"Ты — писатель. Создавай понятные структурированные тексты."
)
designer = Agent(
"Дизайнер",
"Визуализатор данных",
"Ты — дизайнер. Предлагай, как визуализировать данные."
)
supervisor = Supervisor([analyst, writer, designer])
result = supervisor.run("Проанализируй текущее состояние рынка AI-агентов")
print(f"\n📋 Итог:\n{result}")
Плюсы и минусы
| Плюсы | Минусы |
|---|---|
| Чёткое распределение ролей | Supervisor — единая точка отказа |
| Простая логика координации | Supervisor может неверно распределить задачи |
| Легко добавлять новых агентов | Последовательное выполнение (можно сделать параллельным) |
4. Handoff Pattern
Агент сам решает, что не может выполнить задачу, и передаёт её другому агенту.
Этот паттерн использует OpenAI Agents SDK и подходит для сценариев, где заранее неизвестно, какой агент понадобится.
Как работает
Пользователь: «Хочу заказать пиццу»
Triage-агент:
→ Это про заказ еды? → Передаю агенту заказов
→ Нет, это про возврат → Передаю агенту поддержки
→ Нет, это общий вопрос → Отвечаю сам
Реализация
class HandoffAgent:
"""Агент, который может передать задачу другому."""
def __init__(self, name: str, description: str, system_prompt: str,
can_handoff_to: list = None):
self.name = name
self.description = description
self.system_prompt = system_prompt
self.can_handoff_to = can_handoff_to or []
def run(self, task: str) -> tuple[str, str]:
"""Возвращает (ответ, следующий_агент_или_None)."""
# Описываем доступные handoff-ы
handoff_desc = ""
if self.can_handoff_to:
agents_desc = "\n".join([
f"- {a.name}: {a.description}"
for a in self.can_handoff_to
])
handoff_desc = (
f"\n\nЕсли задача НЕ относится к твоей компетенции, "
f"напиши 'HANDOFF: имя_агента' в начале ответа.\n"
f"Доступные агенты:\n{agents_desc}"
)
response = requests.post(OLLAMA, json={
"model": "qwen3.5:4b",
"messages": [
{"role": "system", "content": self.system_prompt + handoff_desc},
{"role": "user", "content": task}
],
"stream": False
})
content = response.json()["message"]["content"]
if content.startswith("HANDOFF:"):
agent_name = content.replace("HANDOFF:", "").strip()
return None, agent_name
return content, None
class HandoffOrchestrator:
"""Оркестратор для handoff-агентов."""
def __init__(self, triage_agent: HandoffAgent):
self.triage_agent = triage_agent
# Строим карту всех агентов
self.agents_map = {}
self._build_map(triage_agent)
def _build_map(self, agent: HandoffAgent):
self.agents_map[agent.name] = agent
for child in agent.can_handoff_to or []:
self._build_map(child)
def run(self, task: str) -> str:
current = self.triage_agent
history = []
for hop in range(5): # максимум 5 передач
result, next_name = current.run(task)
history.append(f"{current.name}: получил задачу")
if next_name:
if next_name in self.agents_map:
print(f" ▶ {current.name} → {next_name}")
current = self.agents_map[next_name]
else:
return f"Ошибка: агент {next_name} не найден"
else:
return result
return "Слишком много передач между агентами"
5. Swarm Pattern
Агенты работают как «рой» — каждый может взять задачу из общей очереди.
Подходит для сценариев, где задачи независимы и могут выполняться параллельно.
import threading
from queue import Queue
class SwarmWorker(threading.Thread):
"""Агент в рое — работает в отдельном потоке."""
def __init__(self, name: str, role: str, task_queue: Queue, result_queue: Queue):
super().__init__()
self.name = name
self.role = role
self.task_queue = task_queue
self.result_queue = result_queue
def run(self):
while True:
task = self.task_queue.get()
if task is None: # сигнал остановки
break
print(f" ▶ {self.name} взял задачу: {task[:50]}...")
response = requests.post(OLLAMA, json={
"model": "qwen3.5:4b",
"messages": [
{"role": "system", "content": f"Ты — {self.name}. {self.role}"},
{"role": "user", "content": task}
],
"stream": False
})
result = response.json()["message"]["content"]
self.result_queue.put((self.name, task, result))
self.task_queue.task_done()
class Swarm:
"""Оркестратор роя агентов."""
def __init__(self, workers: list[SwarmWorker]):
self.workers = workers
def run(self, tasks: list[str]) -> dict:
task_queue = Queue()
result_queue = Queue()
# Запускаем workers
for w in self.workers:
w.task_queue = task_queue
w.result_queue = result_queue
w.start()
# Отправляем задачи
for task in tasks:
task_queue.put(task)
# Ждём завершения
task_queue.join()
# Останавливаем workers
for _ in self.workers:
task_queue.put(None)
# Собираем результаты
results = {}
while not result_queue.empty():
name, task, result = result_queue.get()
results[name] = result
return results
6. GroupChat Pattern
Все агенты в одном «чате» обмениваются сообщениями, пока не придут к решению.
Используется для дискуссий, мозговых штурмов, коллективного принятия решений.
class GroupChat:
"""Групповой чат агентов."""
def __init__(self, agents: list[Agent], max_rounds: int = 3):
self.agents = agents
self.max_rounds = max_rounds
self.messages = []
def run(self, topic: str) -> str:
self.messages = [
{"role": "system", "content": (
f"Групповое обсуждение: {topic}\n"
f"Участники: {', '.join(a.name for a in self.agents)}.\n"
f"Каждый участник высказывается по очереди."
)}
]
for round_num in range(self.max_rounds):
print(f"\n 📢 Раунд {round_num + 1}:")
for agent in self.agents:
response = requests.post(OLLAMA, json={
"model": "qwen3.5:4b",
"messages": [
{"role": "system", "content": (
f"Ты — {agent.name}. {agent.role}. "
f"Участвуй в обсуждении. Реагируй на других участников. "
f"Если согласен с кем-то — поддержи. Если нет — аргументируй."
)},
*self.messages[-6:], # последние 6 сообщений
{"role": "user", "content": f"Твой ход, {agent.name}. Что думаешь?"}
],
"stream": False
})
reply = response.json()["message"]["content"]
self.messages.append({
"role": "assistant",
"content": f"{agent.name}: {reply}"
})
print(f" {agent.name}: {reply[:80]}...")
# Суммаризация
summary = requests.post(OLLAMA, json={
"model": "qwen3.5:4b",
"messages": [
{"role": "system", "content": "Подведи итог дискуссии: какие решения приняты, какие аргументы прозвучали."},
{"role": "user", "content": "\n".join(m["content"] for m in self.messages[-10:])}
],
"stream": False
})
return summary.json()["message"]["content"]
7. Иерархическая команда (для твоего проекта)
Этот паттерн объединяет Supervisor + специалистов в иерархию, где каждый уровень управляет своими подчинёнными.
┌──────────────┐
│ Оркестратор │ ← главный координатор
│ (SuperPM) │
└──┬──┬──┬──┬──┘
│ │ │ │
┌──────────────┘ │ │ └──────────────┐
▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Аналитик │ │ Архитектор│ │ DevOps │
└──────────┘ └────┬─────┘ └──────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Бэкенд │ │ Фронтенд │ │ Тестировщик│
└──────────┘ └──────────┘ └──────────┘
CrewAI реализация для твоей команды
from crewai import Agent, Task, Crew, Process, LLM
llm = LLM(model="ollama/qwen3.5:4b", base_url="http://localhost:11434")
# === Аналитик ===
analyst = Agent(
role="Системный аналитик",
goal="Собрать и структурировать требования к проекту",
backstory="Ты — опытный аналитик. Превращаешь расплывчатые идеи в чёткие задачи.",
llm=llm
)
# === Архитектор (управляет разработчиками) ===
backend_dev = Agent(
role="Backend-разработчик",
goal="Реализовать серверную логику API",
backstory="Пишешь надёжный Python-код с тестами.",
llm=llm
)
frontend_dev = Agent(
role="Frontend-разработчик",
goal="Создать пользовательский интерфейс",
backstory="Специалист по React и TypeScript.",
llm=llm
)
tester = Agent(
role="QA-инженер",
goal="Найти баги до того, как их увидят пользователи",
backstory="Ты — дотошный тестировщик. Проверяешь всё.",
llm=llm
)
architect = Agent(
role="Архитектор",
goal="Координировать разработку: распределять задачи между разработчиками и проверять качество",
backstory="Ты — технический лидер. Понимаешь и бэкенд, и фронтенд.",
llm=llm
)
# === DevOps ===
devops = Agent(
role="DevOps-инженер",
goal="Настроить инфраструктуру, CI/CD и мониторинг",
backstory="Автоматизируешь всё, что можно автоматизировать.",
llm=llm
)
# === Supervisor (PM) ===
pm = Agent(
role="Project Manager",
goal=(
"Координировать всю команду. "
"После каждого этапа проверять результат и решать, что делать дальше."
),
backstory="Ты — опытный PM. Веди команду к релизу.",
llm=llm
)
# === Задачи ===
task_analyze = Task(
description="Проанализируй требования: пользователь хочет сделать TODO-приложение с веб-интерфейсом",
agent=analyst,
expected_output="Документ с требованиями: функциональные и нефункциональные"
)
task_architect = Task(
description="Спроектируй архитектуру: какие компоненты нужны, какие API",
agent=architect,
expected_output="Описание архитектуры"
)
task_backend = Task(
description="Реализуй REST API для TODO: создание, чтение, обновление, удаление задач",
agent=backend_dev,
expected_output="Код на Python (FastAPI)"
)
task_frontend = Task(
description="Создай веб-интерфейс: список задач, форма добавления, кнопка удаления",
agent=frontend_dev,
expected_output="Код на React"
)
task_test = Task(
description="Напиши тесты для API: проверь все CRUD-операции",
agent=tester,
expected_output="Тесты на pytest"
)
task_deploy = Task(
description="Настрой Docker Compose для запуска всего приложения",
agent=devops,
expected_output="docker-compose.yml + инструкция"
)
# === Собираем команду ===
crew = Crew(
agents=[pm, analyst, architect, backend_dev, frontend_dev, tester, devops],
tasks=[task_analyze, task_architect, task_backend, task_frontend, task_test, task_deploy],
process=Process.hierarchical, # PM управляет иерархически
manager_llm=llm,
verbose=True
)
result = crew.kickoff()
8. Проблемы мульти-агентных систем
8.1 Коммуникационные накладные расходы
Каждое сообщение между агентами — это токены. В групповом чате 5 агентов обмениваются 10+ сообщениями, и контекст быстро растёт.
Решение: Ограничивать раунды, использовать короткие сообщения, суммаризировать.
8.2 Противоречивые решения
Агенты могут не соглашаться друг с другом, и непонятно, чьё решение правильное.
Решение: Supervisor с правом вето, голосование, human-in-the-loop.
8.3 Каскадные ошибки
Ошибка одного агента (неправильные данные, неверное решение) распространяется на всех.
Решение: Валидация на каждом шаге, retry, fallback-агенты.
8.4 Потеря контекста
Каждый агент видит только свою часть. Общая картина может теряться.
Решение: Supervisor хранит общее состояние, регулярные синхронизации.
8.5 Когда НЕ нужно делать мульти-агент
Задача решается одним агентом за 1-2 шага
Нет чёткого разделения на разные экспертизы
Кажется, что «больше агентов = круче» (нет, больше агентов = больше проблем)
9. Что дальше
| Если хотите | Переходите | |————-|———–| | Собрать свою команду агентов (твой проект) | tutorials/02-agent-team.ru.md | | Понимать, как агенты хранят память | memory.ru.md | | Научиться писать промпты для агентов | prompting.ru.md | | Обезопасить агентов | safety.ru.md | | Вернуться к навигации | README.md | —
В разделе: architecture · evaluation · frameworks · memory · multi-agent · ollama-for-agents · orchestrators · patterns · prompting · ready-made · safety · skills
Связанные разделы: Нулевой уровень · Локальные модели · Use Cases · Ресурсы
Навигация: ← AI-агенты · ↑ На главную · 🇬🇧 English