Skip to content
IRC-CodingIRC-Coding
agent-orchestrationmnogoagentnye-sistemybest-practicesagent-patternsai-agenty

Orchestrация Agent: практики для многоагентных систем

Продвинутые техники orchestration AI агентов. Patterns, anti-patterns и best practices для надежных систем.

I

IRC-Coding Team

8 min read
Orchestrация Agent: практики для многоагентных систем

Оркестрировка агентов: Best Practices для многоагентных систем

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

TL;DR — оркестрировка агентов за 90 секунд

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

---

4 основных паттерна: Hub-and-Spoke (централизованный), Pipeline (последовательный), DAG (параллельный), Event-Driven (реактивный).

3 главные best practices: четкие роли (SRP), явные типизированные состояния, retry-логика с max_retries.

3 опасных антипаттерна: God Agent, бесконечные циклы, слепое доверие к выводам агентов.

Конец краткого объяснения!

Что такое оркестрировка агентов?

Оркестрировка агентов это координация нескольких ИИ-агентов с учетом:

  • Распределение задач: кто за что отвечает? какая экспертиза нужна для каждой части?
  • Коммуникация: как агенты обмениваются информацией? через состояние, сообщения или события?
  • Управление: кто принимает решение о следующем шаге? центральный оркестратор или условные маршруты?
  • Обработка ошибок: что происходит при сбое? повтор, fallback, вмешательство человека?
  • Управление ресурсами: как оптимизировать LLM-вызовы? кеширование, выбор модели, лимиты токенов?
  • Наблюдаемость: как отследить что происходит? логирование, трейсинг, метрики?

Single-Agent vs Multi-Agent — когда нужна оркестрировка?

Single-Agent достаточно для: линейных задач, одной специализации, без итераций, решаемых одним LLM-вызовом.

Multi-Agent необходим для: разных специализаций (поиск + написание + ревью), итераций (Writer ↔ Reviewer), параллелизма, задач выходящих за лимит контекста, разных инструментов, участия человека в процессе.

Паттерны оркестрировки — подробно

1. Hub-and-Spoke (централизованный оркестратор)

Центральный агент-оркестратор решает, какие агенты запускать и когда.

         [Orchestrator]
        /     |      \
[Agent A] [Agent B] [Agent C]

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

def orchestrator(state):
    task = state["task"]
    # LLM решает, какой агент нужен
    response = llm.invoke(f"Какой агент для: {task}? (research/code/write)")
    task_type = response.content.strip().lower()
    return {"task_type": task_type}

def route_to_agent(state):
    return state["task_type"]

# В LangGraph:
workflow.add_conditional_edges("orchestrator", route_to_agent, {
    "research": "research_agent",
    "code": "code_agent",
    "write": "writer_agent"
})

2. Pipeline (последовательный)

Агенты работают поочередно, каждый передает результат следующему. CrewAI по умолчанию использует Process.sequential.

[Agent A] → [Agent B] → [Agent C] → [Output]

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

Когда использовать? Для линейных workflow без ветвлений (поиск → написание → ревью).

3. DAG (Directed Acyclic Graph)

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

[Agent A] ──→ [Agent C] ──→ [Agent E] (Merge)
     └──→ [Agent B] ──→ [Agent D] ──┘

Преимущества: параллелизм — независимые задачи выполняются одновременно. Эффективность — общее время = самый длинный путь. Недостатки: сложно отлаживать, зависимости должны быть четко определены, возможны race conditions при общем состоянии.

Пример с LangGraph (параллельные исследователи):

class ResearchState(TypedDict):
    topic: str
    general_research: str
    technical_research: str
    combined_report: str

def general_researcher(state: ResearchState) -> dict:
    response = llm.invoke(f"Общая информация о: {state['topic']}")
    return {"general_research": response.content}

def technical_researcher(state: ResearchState) -> dict:
    response = llm.invoke(f"Технические детали о: {state['topic']}")
    return {"technical_research": response.content}

def merge_results(state: ResearchState) -> dict:
    combined = f"Общее:\n{state['general_research']}\n\nТехническое:\n{state['technical_research']}"
    return {"combined_report": combined}

# Graph с параллельными узлами
workflow = StateGraph(ResearchState)
workflow.add_node("general", general_researcher)
workflow.add_node("technical", technical_researcher)
workflow.add_node("merge", merge_results)

workflow.set_entry_point("general")
workflow.add_edge("general", "technical")  # Оба параллельно
workflow.add_edge("general", "merge")
workflow.add_edge("technical", "merge")
workflow.add_edge("merge", END)

Когда использовать? Когда независимые подзадачи можно выполнять параллельно (например 3 исследования, которые потом объединяются).

4. Event-Driven (реактивный)

Агенты реагируют на события вместо прямых вызовов. Event-bus распределяет события всем заинтересованным агентам.

[Event Bus]
  ├── [Agent A] (слушает "research.done")
  ├── [Agent B] (слушает "code.reviewed")
  └── [Agent C] (слушает "test.failed")

Преимущества: слабая связанность, масштабируемость, агенты легко добавлять динамически. Недостатки: сложно отследить логику, возможны race conditions, отладка затруднена.

from collections import defaultdict

class EventBus:
    def __init__(self):
        self.subscribers = defaultdict(list)
    
    def subscribe(self, event_type: str, handler):
        self.subscribers[event_type].append(handler)
    
    def publish(self, event_type: str, data: dict):
        results = []
        for handler in self.subscribers[event_type]:
            results.append(handler(data))
        return results

bus = EventBus()
bus.subscribe("research.done", writer_agent)
bus.subscribe("research.done", fact_checker_agent)
bus.subscribe("code.reviewed", refactor_agent)
bus.publish("research.done", {"topic": "ИИ 2026", "result": "..."})

Когда использовать? Для слабо связанных систем, где агенты добавляются динамически и порядок выполнения не зафиксирован.

Сравнение паттернов

ПаттернСложностьПараллелизмГибкостьОтладкаПрименение
PipelineНизкаяНизкаяПростаяЛинейные workflow
Hub-and-SpokeСредняяСредняяСредняяДинамическая маршрутизация
DAGВысокаяВысокаяСложнаяПараллельные задачи
Event-DrivenВысокаяОчень высокаяОчень сложнаяСлабо связанные системы

Best Practices — Подробный разбор

1. Определи роли четко (Single Responsibility Principle)

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

Плохо:

agent = Agent(
    role="Researcher, Writer and Tester",
    goal="Research, write and test everything"
)

Хорошо:

researcher = Agent(role="Researcher", goal="Find information")
writer = Agent(role="Writer", goal="Write content based on research")
tester = Agent(role="Tester", goal="Test functionality and report bugs")

2. Состояния делай явными

Используй типизированные States вместо неявной коммуникации. Это делает workflow понятным и устойчивым к ошибкам.

from typing import TypedDict, Optional, List, Annotated
from operator import add

class WorkflowState(TypedDict):
    input: str
    task_type: str
    research_result: Optional[str]
    draft: Optional[str]
    review_feedback: Optional[str]
    final_output: Optional[str]
    error: Optional[str]
    retry_count: int
    messages: Annotated[List[str], add]  # Будет добавляться, не перезаписываться

3. Обработка ошибок и retry-логика

LLM ненадежны, у них случаются rate-limits, таймауты, галлюцинации. Каждый агент нужен с обработкой ошибок:

import logging

logger = logging.getLogger("agent_orchestrator")

def agent_with_retry(agent_func, state, max_retries=3, fallback=None):
    for attempt in range(max_retries):
        try:
            logger.info(f"Agent {agent_func.__name__} - Попытка {attempt + 1}")
            result = agent_func(state)
            if validate_result(result):
                return result
            else:
                logger.warning(f"Agent {agent_func.__name__} - невалидный результат")
                state["error"] = "Invalid result"
        except Exception as e:
            logger.error(f"Agent {agent_func.__name__} - ошибка: {str(e)}")
            state["error"] = str(e)
            state["retry_count"] = attempt + 1
    
    if fallback:
        return fallback(state)
    return {**state, "error": f"Agent failed after {max_retries} retries"}

def validate_result(result):
    if not result:
        return False
    if "error" in result and result["error"]:
        return False
    return True

4. Контроль стоимости

Запросы к LLM стоят денег. При 5 агентах с 3 итерациями каждый, получается 15+ вызовов LLM. На GPT-4o это может быть 5-20$ за запуск.

class CostTracker:
    def __init__(self, max_budget=10.0):
        self.costs = {}
        self.max_budget = max_budget
    
    def track(self, agent_name, tokens, model="gpt-4o"):
        cost_per_1k = {
            "gpt-4o": 0.005,
            "gpt-4o-mini": 0.0003,
            "claude-3.5-sonnet": 0.003,
        }
        rate = cost_per_1k.get(model, 0.005)
        cost = (tokens / 1000) * rate
        self.costs[agent_name] = self.costs.get(agent_name, 0) + cost
        
        if self.total() > self.max_budget:
            raise Exception(f"Budget exceeded: ${self.total():.2f}")
    
    def total(self):
        return sum(self.costs.values())
    
    def report(self):
        lines = [f"Cost Report (Total: ${self.total():.4f})"]
        for agent, cost in sorted(self.costs.items()):
            lines.append(f"  {agent}: ${cost:.4f}")
        return "\n".join(lines)

Стратегии оптимизации стоимости:

  • Кеширование: не выполняй одинаковые промпты дважды
  • Выбор модели: простые задачи на GPT-4o-mini ($0.30/M), сложные на GPT-4o ($5.00/M)
  • Ранний выход: останавливайся когда результат “достаточно хорош”
  • Лимиты токенов: устанавливай max_tokens для каждого агента
  • Локальные модели: используй Ollama для разработки ($0)

5. Observability и логирование

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

import logging
import time
from functools import wraps

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("agent_orchestrator")

def observed_agent(agent_func):
    @wraps(agent_func)
    def wrapper(state):
        name = agent_func.__name__
        start = time.time()
        logger.info(f"[{name}] START - Input keys: {list(state.keys())}")
        try:
            result = agent_func(state)
            duration = time.time() - start
            logger.info(f"[{name}] DONE - {duration:.2f}s")
            return result
        except Exception as e:
            duration = time.time() - start
            logger.error(f"[{name}] ERROR - {duration:.2f}s - {str(e)}")
            raise
    return wrapper

Дополнительно: LangSmith для детального трейсинга - показывает каждый вызов LLM, переход состояния и вычисление токенов визуально:

import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls-..."

6. Человек в цикле

Для критических решений человек должен подтвердить. LangGraph это поддерживает нативно с помощью interrupt_before:

app = workflow.compile(
    checkpointer=memory,
    interrupt_before=["publisher"]  # Паузирует перед publisher
)

# Первое выполнение: идет до publisher
result = app.invoke(initial_state, config={"configurable": {"thread_id": "1"}})

# Человек проверяет и одобряет
approval = input("Approve? (y/n): ")
result = app.invoke(
    {"approved": approval == "y"},
    config={"configurable": {"thread_id": "1"}}
)

Когда использовать человека в цикле? Публикация контента, изменения кода в продакшене, финансовые последствия, низкая уверенность агента.

Антипаттерны - чего избегай

1. God Agent

Один агент делает всё. Промпт становится огромным, LLM теряет фокус, результаты плохие. Сложно отлаживать, дорого в работе. Решение: раздели на 3-5 специализированных агентов.

2. Бесконечные циклы

Агенты вызывают друг друга без условия остановки. Решение: установи максимальное количество итераций и реализуй экстренный выход:

MAX_ITERATIONS = 5

def route_after_review(state):
    if state.get("approved"):
        return "publish"
    if state.get("iteration_count", 0) >= MAX_ITERATIONS:
        return "human_review"  # Экстренный выход
    return "writer"

3. Отсутствие валидации

Слепое доверие к выходам агентов. LLM галлюцинируют и выдумывают источники. Решение: валидируй каждый результат:

def validate_research(result: str) -> bool:
    if not result or len(result) < 100:
        return False
    if "http" not in result:  # Хотя бы один источник
        return False
    return True

4. Слишком много агентов

Больше агентов, это больше координационных издержек, выше стоимость, больше точек отказа. Правило большого пальца: 3-5 агентов. Если нужно больше, раздели на несколько независимых crews.

5. Отсутствие контроля стоимости

Без отслеживания затрат multi-agent системы могут стоить сотни долларов за запуск. Решение: используй CostTracker с лимитом бюджета (см. выше).

Сравнение фреймворков для оркестрации

ФункцияLangGraphCrewAIКастомное
Pipeline✅ (sequential)
Hub-and-Spoke✅ (conditional edges)✅ (hierarchical)
DAG/Параллель✅ (native)✅ (asyncio)
Event-Driven❌ (не нативно)✅ (custom bus)
Циклы✅ (conditional edges)Ограничено
Human-in-the-Loop✅ (interrupt)Вручную
Checkpointing✅ (native)✅ (custom)

Ключевые моменты для экзамена

  • Оркестрация: координация нескольких агентов с распределением задач, обменом информацией, управлением и обработкой ошибок
  • 4 паттерна: Pipeline (последовательный), Hub-and-Spoke (централизованный), DAG (параллельный), Event-Driven (событийный)
  • Лучшие практики: четкие роли (SRP), явно типизированные состояния, повторные попытки с max_retries, контроль затрат через CostTracker, видимость системы с Logging + LangSmith, участие человека для критических решений
  • Антипаттерны: God Agent, бесконечные циклы, отсутствие валидации, избыток агентов, отсутствие контроля затрат
  • Выбор фреймворка: LangGraph для сложных графов, CrewAI для быстрого прототипирования, собственные решения для Event-Driven архитектуры

Часто задаваемые вопросы

Когда нужна мультиагентная система вместо одного агента? Когда задача требует разных областей expertise, нужна итерация (Writer ↔ Reviewer), возможна параллелизация или задача слишком сложна для одного промпта.

Сколько агентов оптимально? 3-5 для большинства случаев. Больше 8 трудно координировать и это дорого. При сложных требованиях лучше разбить на несколько независимых Crews или графов.

Какой паттерн оркестрации лучше? Pipeline для простых линейных задач, Hub-and-Spoke для динамической маршрутизации, DAG для параллельных операций, Event-Driven для слабо связанных систем. Большинство production-систем используют комбинацию нескольких подходов.

Как избежать бесконечных циклов? Всегда устанавливайте максимальное число итераций. Реализуйте аварийный выход, который при превышении числа итераций передает управление Human-in-the-Loop или завершает выполнение.

Как контролировать затраты в мультиагентных системах?

  1. CostTracker с лимитом бюджета. 2) Дешевые модели для простых задач. 3) Кеширование одинаковых промптов. 4) Ранняя остановка. 5) Локальные модели для разработки.

Нужен ли LangSmith в production? Настоятельно рекомендуется. Без tracing вы слепы: не видите, сколько времени нужно каждому агенту и где возникают ошибки. У LangSmith есть бесплатный уровень для небольших проектов.

Рекомендуемая литература

Keine Bücher für Kategorie "ki-agenten" gefunden.

Назад к блогу
Share:

Похожие статьи