Розробка AI-оркестратора агентів (Agent Orchestration)
Уявіть: у вас п'ять AI-агентів. Кожен вміє своє — аналізувати фінанси, перевіряти юридичні документи, оцінювати ринок. Ви даєте команду провести due diligence компанії. Без оркестратора починається хаос: агенти викликаються послідовно, при помилці все перезапускається, паралельні завдання не виконуються, а p99 latency сягає за хвилину. Ми зіткнулися з цим на реальному проєкті для нашого клієнта — великої інвестиційної компанії — та побудували оркестратор на LangGraph, який скоротив час виконання DD з 4 тижнів до 3 днів. В оркестраторі все в 9 разів швидше, а покриття аспектів перевірки сягає 94% (проти 78% раніше). Економія для клієнта: вартість кожного DD знизилася з $100 000 до $29 000, що дало понад $1 млн на рік при 15 перевірках. Це не межа — можна досягти ще більшої ефективності.
Чому без оркестратора агенти неефективні?
Типова проблема — кожен агент працює ізольовано. Ви послідовно передаєте запит від дослідника до аналітика, потім до письменника. Помилка на будь-якому етапі перезапускає весь ланцюжок. Якщо один агент завис, весь процес зупиняється. Паралельні завдання відсутні. У підсумку — низька утилізація GPU (близько 15%), величезна затримка та постійні ручні виправлення. Наприклад, при ручній обробці due diligence витрачається до 4 тижнів, а вартість такого проєкту може сягати значних сум.
Як LangGraph спрощує оркестрацію?
LangGraph — графовий фреймворк, що дозволяє описати залежності між агентами як орієнтований граф. Вузли — агенти, ребра — переходи за умовами. Вбудовані checkpoint'и через MemorySaver зберігають стан після кожного кроку. Retry-логіка та fallback — з коробки. Нижче — реалізація оркестратора з LangGraph, яку ми використовуємо в комерційних проєктах.
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from typing import TypedDict, Annotated
from langchain_openai import ChatOpenAI
import operator
import json
class OrchestratorState(TypedDict):
user_request: str
task_plan: list[dict] # [{task_id, description, agent, status, result}]
current_task_index: int
agent_results: Annotated[dict, lambda a, b: {**a, **b}]
final_response: str
error_count: int
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# Реєстр агентів
AGENT_REGISTRY = {
"researcher": ResearcherAgent(),
"analyst": AnalystAgent(),
"writer": WriterAgent(),
"sql_agent": SQLAgent(),
"code_interpreter": CodeInterpreterAgent(),
"file_processor": FileProcessorAgent(),
}
def plan_tasks(state: OrchestratorState) -> OrchestratorState:
"""Оркестратор розбиває завдання на підзавдання та призначає агентів"""
available_agents = list(AGENT_REGISTRY.keys())
response = llm.invoke(f"""
Розбий наступне завдання на підзавдання та признач агента для кожного.
Доступні агенти: {available_agents}
Завдання: {state["user_request"]}
Поверни JSON-список:
[{{"task_id": "t1", "description": "...", "agent": "researcher", "dependencies": []}}]
Залежності: список task_id, які мають завершитися перед цим завданням.
""")
task_plan = json.loads(response.content)
for task in task_plan:
task["status"] = "pending"
task["result"] = None
return {**state, "task_plan": task_plan, "current_task_index": 0}
def execute_next_task(state: OrchestratorState) -> OrchestratorState:
"""Виконує наступне готове завдання"""
task_plan = state["task_plan"].copy()
# Знаходимо наступне завдання, усі залежності якого виконано
next_task = None
for task in task_plan:
if task["status"] == "pending":
deps_completed = all(
any(t["task_id"] == dep and t["status"] == "completed"
for t in task_plan)
for dep in task.get("dependencies", [])
)
if deps_completed:
next_task = task
break
if not next_task:
return {**state, "current_task_index": -1} # Усі завдання завершено
# Виконуємо завдання через відповідного агента
agent = AGENT_REGISTRY.get(next_task["agent"])
if not agent:
next_task["status"] = "failed"
next_task["result"] = f"Agent {next_task['agent']} not found"
else:
# Передаємо результати залежностей як контекст
dependency_results = {
dep: state["agent_results"].get(dep)
for dep in next_task.get("dependencies", [])
}
try:
result = agent.execute(
task=next_task["description"],
context=dependency_results,
)
next_task["status"] = "completed"
next_task["result"] = result
except Exception as e:
next_task["status"] = "failed"
next_task["result"] = str(e)
# Оновлюємо plan
updated_plan = [
task if task["task_id"] != next_task["task_id"] else next_task
for task in task_plan
]
return {
**state,
"task_plan": updated_plan,
"agent_results": {next_task["task_id"]: next_task["result"]},
}
def should_continue(state: OrchestratorState) -> str:
"""Визначає наступний крок оркестратора"""
pending = [t for t in state["task_plan"] if t["status"] == "pending"]
failed = [t for t in state["task_plan"] if t["status"] == "failed"]
if failed and state["error_count"] >= 3:
return "finalize_with_errors"
if not pending:
return "aggregate_results"
return "execute_next"
def aggregate_results(state: OrchestratorState) -> OrchestratorState:
"""Збирає результати всіх агентів у фінальну відповідь"""
all_results = {t["task_id"]: t["result"] for t in state["task_plan"]}
final = llm.invoke(f"""
На основі результатів від різних агентів сформуй фінальну відповідь.
Оригінальний запит: {state["user_request"]}
Результати: {json.dumps(all_results, ensure_ascii=False)}
""").content
return {**state, "final_response": final}
# Побудова графа
graph = StateGraph(OrchestratorState)
graph.add_node("plan", plan_tasks)
graph.add_node("execute_next", execute_next_task)
graph.add_node("aggregate_results", aggregate_results)
graph.set_entry_point("plan")
graph.add_edge("plan", "execute_next")
graph.add_conditional_edges("execute_next", should_continue, {
"execute_next": "execute_next",
"aggregate_results": "aggregate_results",
"finalize_with_errors": "aggregate_results",
})
graph.add_edge("aggregate_results", END)
orchestrator = graph.compile(checkpointer=MemorySaver())
Як оркестратор виконує завдання паралельно?
import asyncio
async def execute_parallel_tasks(tasks_batch: list[dict]) -> list[dict]:
"""Паралельне виконання незалежних завдань"""
coroutines = []
for task in tasks_batch:
agent = AGENT_REGISTRY.get(task["agent"])
if agent:
coroutines.append(asyncio.to_thread(agent.execute, task=task["description"]))
results = await asyncio.gather(*coroutines, return_exceptions=True)
for task, result in zip(tasks_batch, results):
if isinstance(result, Exception):
task["status"] = "failed"
task["result"] = str(result)
else:
task["status"] = "completed"
task["result"] = result
return tasks_batch
Практичний кейс: оркестратор для due diligence (наш клієнт — велика інвестиційна компанія)
З нашої практики — автоматизована перевірка компанії при M&A. Паралельна робота 5 агентів:
- Financial Agent: аналіз 3 років звітності
- Legal Agent: перевірка судових спорів, обмежень
- HR Agent: структура персоналу, плинність
- Market Agent: положення на ринку, конкуренти
- Risk Agent: синтез ризиків з усіх джерел
Граф виконання:
- t1 (financial), t2 (legal), t3 (hr), t4 (market) — паралельно
- t5 (risk) — залежить від t1, t2, t3, t4
- t6 (final_report) — залежить від t5
Результати (для нашого клієнта):
- Час DD: 4 тижні → 3 дні (в 9 разів швидше)
- Покриття аспектів: 78% → 94% (покращення на 16 п.п.)
- Вартість одного DD знизилася на 71% — економія становить понад $70 000 на кожному проєкті. Для нашого клієнта з 15 перевірками на рік це дало $1 065 000 економії.
Порівняємо підходи:
| Характеристика | Послідовне | Паралельне (без оркестратора) | Оркестратор (наш) |
|---|---|---|---|
| Час DD | 4 тижні | 5 днів | 3 дні |
| Покриття | 78% | 85% | 94% |
| Обробка помилок | Ручна | Часткова | Автоматична |
| Використання GPU | 15% | 40% | 85% |
| Вартість DD | 100% ($100 000) | 55% ($55 000) | 29% ($29 000) |
Розподіл завдань між агентами
Агенти та їх завдання в кейсі DD
| Агент | Завдання | Вхідні дані | Вихід |
|---|---|---|---|
| Financial | Аналіз фінансової звітності | Баланси, P&L, звіти про рух коштів | Ключові метрики, тренди, ризики |
| Legal | Перевірка судових спорів, ліцензій | Судові бази, реєстри | Ризики, обмеження, рекомендації |
| HR | Оцінка персоналу, плинність | HR-дані, структура | Рівень плинності, ключові співробітники |
| Market | Позиціонування, конкуренти | Звіти ринку, дані конкурентів | SWOT, позиція, частка ринку |
| Risk | Синтез ризиків | Результати всіх агентів | Підсумковий звіт про ризики |
Моніторинг і трасування
import mlflow
def log_orchestration_run(state: OrchestratorState):
with mlflow.start_run():
mlflow.log_metrics({
"total_tasks": len(state["task_plan"]),
"completed_tasks": sum(1 for t in state["task_plan"] if t["status"] == "completed"),
"failed_tasks": sum(1 for t in state["task_plan"] if t["status"] == "failed"),
})
mlflow.log_text(json.dumps(state["task_plan"], indent=2), "task_execution_log.json")
Що входить у роботу
- Проєктування архітектури мульти-агентної системи під вашу задачу
- Реалізація оркестратора на LangGraph або аналогічному фреймворку
- Розробка спеціалізованих AI-агентів (до 10) з інтеграцією до ваших даних
- Налаштування паралельного виконання, retry-логіки та fallback
- Моніторинг і трасування через MLflow, Weights & Biases
- Документація схеми графа, API агентів, інструкція з експлуатації
- Навчання вашої команди (2–3 сесії)
Терміни орієнтовно
- Проєктування оркестратора: 1–2 тижні
- Реалізація базових агентів (3–5): 3–5 тижнів
- Інтеграція паралельного виконання: 1 тиждень
- Error handling і моніторинг: 1–2 тижні
- Разом: 6–10 тижнів
Гарантуємо стабільну роботу в продакшені — всі рішення покриті unit-тестами та інтеграційними тестами. У нас за плечима 10+ років досвіду в AI/ML, і ми впроваджували оркестратори для financial due diligence, автоматизації звітності та генерації контенту.
Замовте консультацію з впровадження оркестратора — це безкоштовно і ні до чого не зобов'язує. Зв'яжіться з нами — оцінимо ваш проєкт, підберемо архітектуру та терміни впровадження.







