Почему один LLM-агент ломается в продакшене
Монолитный агент — один LLM для retrieval, рассуждений, кода и проверки — легко демонстрируется и ломается при масштабировании. Проблемы структурные. К 2026 году большинство команд, пытавшихся масштабировать одного агента, упирались в одни и те же четыре ограничения.
Потолок контекста: Промежуточное состояние заполняет окно; качество рассуждений падает на длинных workflow.
Размытие ролей: Один агент для retrieval, генерации и аудита не делает ничего из этого хорошо.
Нет параллелизма: Последовательные шаги — суммарная latency равна сумме всех шагов.
Единая точка отказа: Один неудачный вызов модели или timeout инструмента останавливает всю pipeline.
Данные с продакшена: Внутренний Agent Bake-Off Google (MLflow 2026) сократил обработку с часа до десяти минут — ×6 после декомпозиции. AdaptOrch (2026): топология оркестрации важнее выбора модели, +12–23 % на SWE-bench.
Мультиагентная система (MAS) — набор независимых агентов, сотрудничающих через протоколы и оркестрацию для задач, которые один агент не тянет эффективно. Каждый агент: одна роль, свои инструменты, изолированное состояние, независимая заменяемость.
| Свойство агента | Значение в продакшене |
|---|---|
| Одна ответственность | Одна задача: retrieval, рассуждение, генерация или валидация |
| Инструменты | Доступ только к нужным инструментам через MCP Server |
| Изоляция состояния | Свой контекст и память; без загрязнения других агентов |
| Заменяемость | Менять worker-агента без перепрошивки всего графа |
Три топологии управления: Централизованная — оркестратор маршрутизирует всё (аудит, но узкое место). Децентрализованная — peer-сеть (устойчивость, сложный debug). Иерархическая — top orchestrator → team leads → workers.
| Режим | Структура | Плюсы | Минусы |
|---|---|---|---|
| Централизованный | Orchestrator управляет A/B/C | Аудируемость, контроль | Узкое место в центре |
| Децентрализованный | Peer-to-peer mesh | Эластичность, низкая latency | Сложный debug, недетерминизм |
| Иерархический | Top Orchestrator → Team Lead → Worker | Баланс контроля и масштаба | Средняя сложность дизайна |
Для продакшена мультиагентная архитектура почти всегда правильный выбор. Сложный вопрос — паттерн оркестрации, а не foundation-модель.
LangGraph vs CrewAI vs AutoGen: сравнение и выбор
Правильный фреймворк экономит месяцы своей оркестрации. Матрица: парадигма, языки, кривая обучения, state, HITL, observability, production, прототип, Azure, сценарии.
| Измерение | LangGraph | CrewAI | AutoGen (Microsoft) |
|---|---|---|---|
| Парадигма | Граф состояний | Команды по ролям | Диалоговые multi-agent |
| Языки | Python / JS/TS | Python | Python / .NET |
| Кривая обучения | Крутая | Пологая | Средняя |
| State | Нативно | Самим | Ограничено |
| Human-in-the-Loop | Нативный interrupt() | Самим | Поддерживается |
| Observability | LangSmith | Ограничено | Azure Monitor |
| Production-ready | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Быстрый прототип | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| Azure | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| Лучше для | Сложные stateful workflow | Role-based content pipelines | Диалог и дебаты |
Рекомендации:
LangGraph:Надёжность prod (финансы, медицина, compliance), персистентность, HITL, ветвления.
CrewAI:Прототип за 1–2 дня; команда думает ролями; контент и отчёты.
AutoGen:Microsoft/Azure; многораундовые дебаты; исследования.
Топология оркестрации важнее модели — сначала pattern и framework, потом модель.
Шесть паттернов оркестрации и MCP+A2A
Шесть паттернов покрывают >95 % prod. Паттерн 1 — Последовательный pipeline: выход A → вход B; линейно; контент, code review.
from langgraph.graph import StateGraph, START, END
from typing import TypedDict
class PipelineState(TypedDict):
query: str
retrieved_docs: str
analysis: str
final_report: str
def retrieval_agent(state: PipelineState):
docs = search_knowledge_base(state["query"])
return {"retrieved_docs": docs}
def analysis_agent(state: PipelineState):
result = llm.invoke(f"Analyze the following:{state['retrieved_docs']}")
return {"analysis": result.content}
def writer_agent(state: PipelineState):
report = llm.invoke(f"Write report from analysis:{state['analysis']}")
return {"final_report": report.content}
builder = StateGraph(PipelineState)
builder.add_node("retriever", retrieval_agent)
builder.add_node("analyzer", analysis_agent)
builder.add_node("writer", writer_agent)
builder.add_edge(START, "retriever")
builder.add_edge("retriever", "analyzer")
builder.add_edge("analyzer", "writer")
builder.add_edge("writer", END)
pipeline = builder.compile()
Паттерн 2 — Параллельный fan-out/fan-in: независимые подзадачи; latency = max(T1…Tn). LangGraph Send API + reducer.
from langgraph.types import Send
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Annotated
import operator
class ResearchState(TypedDict):
query: str
research_results: Annotated[list, operator.add]
final_synthesis: str
def supervisor(state: ResearchState):
subtasks = [
{"query": state["query"], "source": "academic"},
{"query": state["query"], "source": "industry"},
{"query": state["query"], "source": "news"},
]
return [Send("research_worker", task) for task in subtasks]
def research_worker(state: dict):
result = search_by_source(state["query"], state["source"])
return {"research_results": [result]}
def synthesizer(state: ResearchState):
combined = "\n".join(state["research_results"])
synthesis = llm.invoke(f"Synthesize research results:{combined}")
return {"final_synthesis": synthesis.content}
builder = StateGraph(ResearchState)
builder.add_node("research_worker", research_worker)
builder.add_node("synthesizer", synthesizer)
builder.add_conditional_edges(START, supervisor, ["research_worker"])
builder.add_edge("research_worker", "synthesizer")
builder.add_edge("synthesizer", END)
graph = builder.compile()
Паттерн 3 — Supervisor-worker: маршрутизация intent; специализированные workers. Keyword fast-path + LLM routing.
KEYWORD_ROUTING = {
"code": "code_agent", "code": "code_agent",
"search": "search_agent", "query": "search_agent",
"data": "data_agent",
}
def supervisor_with_fast_path(state):
query = state["query"].lower()
for keyword, agent_name in KEYWORD_ROUTING.items():
if keyword in query:
return {"next": agent_name}
routing_prompt = f"""
User request:{state['query']}
Available agents:code_agent, search_agent, data_agent
Return best agent name only。
"""
decision = llm.invoke(routing_prompt)
return {"next": decision.content.strip()}
Паттерн 4 — Swarm: peer-to-peer без координатора; жёсткий лимит раундов. AutoGen GroupChat:
import autogen
reviewer_1 = autogen.AssistantAgent(
name="SecurityReviewer",
system_message="Security expert focused on vulnerabilities."
)
reviewer_2 = autogen.AssistantAgent(
name="PerformanceReviewer",
system_message="Performance expert focused on efficiency."
)
human_proxy = autogen.UserProxyAgent(
name="CodeAuthor",
human_input_mode="NEVER",
max_consecutive_auto_reply=2,
is_termination_msg=lambda x: "APPROVED" in x.get("content", "")
)
groupchat = autogen.GroupChat(
agents=[human_proxy, reviewer_1, reviewer_2],
messages=[],
max_round=6
)
manager = autogen.GroupChatManager(groupchat=groupchat)
Паттерн 5 — Blackboard: общее workspace; долгие async jobs. Паттерн 6 — Hybrid: intent router + supervisor + fan-out + QA.
MCP + A2A (Agentic AI Foundation): MCP вертикально Agent ↔ tools/API; A2A горизонтально — делегирование.
from mcp.server import Server
from mcp.types import Tool, TextContent
app = Server("data-agent-mcp")
@app.list_tools()
async def list_tools():
return [
Tool(
name="query_customer_db",
description="Query customer DB by id, name, or email",
inputSchema={
"type": "object",
"properties": {
"field": {"type": "string", "enum": ["id", "name", "email"]},
"value": {"type": "string"}
},
"required": ["field", "value"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "query_customer_db":
result = db.query(arguments["field"], arguments["value"])
return [TextContent(type="text", text=str(result))]
Agent Card A2A на /.well-known/agent.json; делегирование JSON-RPC 2.0:
import httpx
async def discover_and_delegate(agent_url: str, task: str):
card_response = await httpx.get(f"{agent_url}/.well-known/agent.json")
agent_card = card_response.json()
available_skills = [s["id"] for s in agent_card["skills"]]
if "web_research" not in available_skills:
raise ValueError(f"Agent {agent_card['name']} lacks web_research skill")
payload = {
"jsonrpc": "2.0",
"method": "message/send",
"id": "task-001",
"params": {
"message": {
"role": "user",
"parts": [{"type": "text", "text": task}]
}
}
}
response = await httpx.post(agent_card["url"], json=payload)
return response.json()
Читать: MCP Server с нуля; MCP как HTTP эпохи ИИ.
Шестишаговый production runbook
Выбор и топология:Decision tree: pipeline/fan-out/hierarchy/blackboard/hybrid; 3–8 агентов.
Персистентность:PostgresSaver, recovery, thread_id на сессию.
Human-in-the-loop:interrupt() для high-risk — compliance и EU AI Act.
Circuit breaker:CircuitBreaker на внешние вызовы.
Token budget:TokenBudgetManager перед каждым вызовом.
Observability:OpenTelemetry, MONITORING_METRICS, LLM-as-Judge sampling.
from langgraph.checkpoint.postgres import PostgresSaver
from langgraph.types import interrupt
with PostgresSaver.from_conn_string("postgresql://user:pass@localhost/agentdb") as checkpointer:
graph = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "user-session-12345"}}
result = graph.invoke({"query": "Analyze Q2 report"}, config)
def high_risk_action_agent(state):
proposed_action = plan_action(state)
human_decision = interrupt({
"proposed_action": proposed_action,
"risk_level": "HIGH",
"message": "This modifies production DB. Confirm?"
})
if human_decision["approved"]:
return execute_action(proposed_action)
return {"status": "cancelled", "reason": human_decision.get("reason")}
import time
from functools import wraps
class CircuitBreaker:
def __init__(self, failure_threshold=5, recovery_timeout=60):
self.failure_count = 0
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.state = "CLOSED"
self.last_failure_time = None
def __call__(self, func):
@wraps(func)
async def wrapper(*args, **kwargs):
if self.state == "OPEN":
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = "HALF_OPEN"
else:
raise Exception("Circuit breaker OPEN - Agent temporarily unavailable")
try:
result = await func(*args, **kwargs)
if self.state == "HALF_OPEN":
self.state = "CLOSED"
self.failure_count = 0
return result
except Exception:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "OPEN"
raise
return wrapper
class TokenBudgetManager:
def __init__(self, total_budget: int = 100_000):
self.total_budget = total_budget
self.used_tokens = 0
self.agent_usage = {}
def check_budget(self, agent_name: str, estimated_tokens: int) -> bool:
remaining = self.total_budget - self.used_tokens
if estimated_tokens > remaining:
raise BudgetExceededException(
f"Agent {agent_name} requests {estimated_tokens} tokens,"
f"в бюджете осталось только {remaining} tokens"
)
return True
def record_usage(self, agent_name: str, actual_tokens: int):
self.used_tokens += actual_tokens
self.agent_usage[agent_name] = self.agent_usage.get(agent_name, 0) + actual_tokens
Данные observability, подводные камни и тренды 2026
MAST проанализировал 1642 multi-agent traces:
| Тип | Доля | Описание |
|---|---|---|
| Дизайн системы | 41.77% | Повторы, неверные tools, overflow, нет termination |
| Misalignment | 36.94% | Потеря контекста; галлюцинации как факты |
| Валидация | 21.30% | Ранний stop, неполная проверка |
57 % vs 8 %:57 % в prod, 8 % с LLM observability — HTTP 200 при неверном output.
Google Bake-Off ×6: Распределённая мультиагентная архитектура сократила обработку с часа до десяти минут; sub-агенты обновляются независимо.
AdaptOrch 12–23 %:Верная топология +12–23 % на SWE-bench vs смена модели.
from opentelemetry import trace
import uuid
tracer = trace.get_tracer("multi-agent-system")
def traced_agent_call(agent_name: str, task: dict, correlation_id: str = None):
if not correlation_id:
correlation_id = str(uuid.uuid4())
with tracer.start_as_current_span(f"agent.{agent_name}") as span:
span.set_attribute("agent.name", agent_name)
span.set_attribute("correlation.id", correlation_id)
span.set_attribute("task.type", task.get("type", "unknown"))
try:
result = agent_registry[agent_name].run(task)
span.set_attribute("agent.tokens_used", result.get("tokens", 0))
span.set_attribute("agent.status", "success")
return result
except Exception as e:
span.set_attribute("agent.status", "error")
span.set_attribute("error.message", str(e))
raise
MONITORING_METRICS = {
"task_success_rate": "task_success_rate target >85%",
"e2e_latency_p95": "e2e_latency_p95 target <30s",
"total_cost_per_task": "avg token cost per task",
"agent_error_rate": "agent_error_rate target <5%",
"agent_retry_count": "retry count high needs investigation",
"tool_call_budget_usage": "tool call budget ratio",
"output_quality_score": "output quality score",
"goal_alignment_score": "goal alignment score",
"hallucination_rate": "hallucination rate",
}
def evaluate_agent_output(original_task: str, agent_output: str) -> dict:
evaluation_prompt = f"""
Original task: {original_task}
Agent output: {agent_output}
Rate completeness, accuracy, relevance, hallucination (1-5 each).
Return JSON only: {{"completeness": x, "accuracy": x, "relevance": x,
"hallucination_detected": true/false, "comments": "..."}}
"""
evaluation = llm.invoke(evaluation_prompt)
return json.loads(evaluation.content)
Пять подводных камней:
Загрязнение контекста:Галлюцинации A → B/C. Fix: validate_agent_output на каждом handoff.
Бесконечные циклы:Runaway retries. Fix: жёсткие лимиты.
Over-engineering:Два шага → восемь агентов. Fix: pipeline сначала; 3–8 агентов.
Разрыв demo-prod:Edge inputs в prod. Fix: ProductionGuardrails, PII.
Параллельный sync:Supervisor раньше медленных веток. Fix: defer=True.
def validate_agent_output(output: dict, schema: dict) -> bool:
jsonschema.validate(output, schema)
if output.get("confidence_score", 1.0) < 0.7:
raise LowConfidenceError(f"Agent output confidence too low: {output['confidence_score']}")
required_fields = schema.get("required", [])
missing = [f for f in required_fields if not output.get(f)]
if missing:
raise MissingFieldsError(f"Missing required output fields: {missing}")
return True
MAX_ITERATIONS = 10
MAX_TOOL_CALLS_PER_AGENT = 20
MAX_TOTAL_TOKENS = 50_000
class ProductionGuardrails:
def validate_input(self, user_input: str) -> str:
if len(user_input) > 10000:
raise InputTooLongError("Input exceeds 10000 chars")
injection_patterns = ["ignore previous instructions", "forget everything"]
for pattern in injection_patterns:
if pattern.lower() in user_input.lower():
raise PromptInjectionError("Prompt injection detected")
return user_input.strip()
def validate_output(self, output: str) -> str:
output = self.pii_filter.redact(output)
if self.content_classifier.is_harmful(output):
raise HarmfulContentError("Harmful content in output")
return output
builder.add_node("supervisor", supervisor_node, defer=True)
Есть ли чёткие линейные зависимости?
├─ Да → Подзадачи параллельны?
│ ├─ Нет → [Последовательный pipeline]
│ └─ Да → [Fan-out + pipeline hybrid]
└─ Нет → Есть агент-решатель?
├─ Да → Нужны sub-teams?
│ ├─ Нет → [Supervisor-worker]
│ └─ Да → [Иерархия]
└─ Нет → Долгая async задача?
├─ Да → [Blackboard]
└─ Нет → ≤ 5 агентов?
├─ Да → [Swarm с termination]
└─ Нет → [Иерархия]
Пять ключевых тезисов:① Топология > модель; ② pipeline first; ③ MCP+A2A стандарт; ④ observability must; ⑤ 3–8 агентов.
Тренды 2026:Федеративная оркестрация, мультимодальные агенты, adaptive topology, EU AI Act audit trails.
Важно: Оркестрация на ноутбуке ломается из-за сна, нехватки RAM и сетевого jitter; дешёвый Linux VPS не запускает Xcode и Apple Silicon inference нативно. Мультиагентная архитектура — ключевая компетенция 2026, но продакшену нужен стабильный хост.
Для 7×24 оркестрации, PostgresSaver, MCP и Cursor/Claude Code — аренда Mac Mini cloud MESHLAUNCH: dedicated Apple Silicon, гибкий billing. Тарифы и центр помощи.
State, HITL, compliance → LangGraph; прототип 1–2 д → CrewAI; Azure → AutoGen. Хостинг: страница тарифов.
MCP вертикально; A2A горизонтально с Agent Cards и JSON-RPC 2.0. Оба под Agentic AI Foundation.
PostgreSQL, OpenTelemetry, token budget, circuit breakers, хост 7×24. Mac Mini для LangGraph, MCP, vectors. Деплой: центр помощи.