Зачем вообще фреймворк
В предыдущих уроках мы написали агента с нуля — ~80 строк Python. Это работает. Но при росте сложности начинаются проблемы: как сохранить состояние между сессиями? Как запустить несколько ветвей параллельно? Как сделать паузу и дождаться подтверждения от пользователя? Как отлаживать агента, когда он делает 20 итераций?
Фреймворки решают именно эти инженерные задачи. Они не меняют фундаментальный цикл perceive → think → act → observe — они дают инструменты для управления этим циклом в production: персистентность, параллелизм, наблюдаемость, human-in-the-loop.
Гибкость vs простота. Контроль vs скорость разработки. Нет «лучшего» фреймворка — есть подходящий для конкретной задачи. Именно поэтому важно понимать идею каждого, а не просто уметь им пользоваться.
LangGraph строит агентов как направленные графы состояний. Каждый узел (node) — это функция Python, которая принимает состояние и возвращает обновлённое состояние. Рёбра (edges) определяют маршрут: обычные или условные (conditional), по которым граф ветвится в зависимости от значения состояния.
Идея пришла из теории конечных автоматов. Агент — это не «умный чат-бот», а детерминированная машина, которую можно нарисовать на бумаге: вот узлы, вот переходы, вот условия. Это делает LangGraph самым прозрачным и предсказуемым из всех фреймворков.
Ключевые абстракции
operator.add).(state) → dict. Возвращает только изменённые поля. Может быть синхронной или async.MemorySaver — в памяти, SqliteSaver / PostgresSaver — в БД. Основа для resume и HITL.Как работает LangGraph
Минимальный пример
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from typing import TypedDict, Annotated
import operator, anthropic
client = anthropic.Anthropic()
# ── Состояние агента ─────────────────────────────────────────
class AgentState(TypedDict):
messages: Annotated[list, operator.add] # += слияние списков
iterations: int
# ── Узлы графа ───────────────────────────────────────────────
def node_agent(state: AgentState) -> dict:
"""Вызов LLM — ядро агента."""
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=state["messages"],
)
return {
"messages": [{"role": "assistant", "content": response.content}],
"iterations": state["iterations"] + 1,
}
def node_tools(state: AgentState) -> dict:
"""Выполнение всех запрошенных инструментов."""
last_msg = state["messages"][-1]["content"]
results = []
for block in last_msg:
if hasattr(block, "type") and block.type == "tool_use":
result = dispatch_tool(block.name, block.input)
results.append({"type": "tool_result", "tool_use_id": block.id, "content": result})
return {"messages": [{"role": "user", "content": results}]}
# ── Роутер (conditional edge) ────────────────────────────────
def router(state: AgentState) -> str:
last = state["messages"][-1]["content"]
if state["iterations"] >= 10:
return "end"
for block in (last if isinstance(last, list) else []):
if hasattr(block, "type") and block.type == "tool_use":
return "tools"
return "end"
# ── Сборка графа ─────────────────────────────────────────────
builder = StateGraph(AgentState)
builder.add_node("agent", node_agent)
builder.add_node("tools", node_tools)
builder.set_entry_point("agent")
builder.add_conditional_edges("agent", router, {"tools": "tools", "end": END})
builder.add_edge("tools", "agent") # после инструментов — снова к агенту
graph = builder.compile(
checkpointer=MemorySaver(),
interrupt_before=["agent"], # Human-in-the-loop: пауза перед каждым шагом
)
# ── Запуск ───────────────────────────────────────────────────
config = {"configurable": {"thread_id": "session-1"}}
initial_state = {"messages": [{"role": "user", "content": "Найди последние новости"}], "iterations": 0}
for chunk in graph.stream(initial_state, config=config):
print(chunk) # стриминг каждого шага
interrupt_before/afterНужен production-агент с явной бизнес-логикой, resume после сбоев, human-in-the-loop, сложными ветвлениями. Стандарт для enterprise. Хорошо когда граф можно нарисовать на доске до написания кода.
CrewAI строит агентов как команду специалистов с ролями, целями и инструментами. Метафора — отдел компании: есть исследователь, аналитик, автор — каждый делает свою часть работы и передаёт результат дальше. Агент понимает свою роль через system prompt, который CrewAI генерирует автоматически из role, goal и backstory.
Ключевая идея: задача (Task) назначается агенту, а не агент сам выбирает что делать. Это делает поведение системы более предсказуемым. Процесс исполнения — последовательный (Process.sequential) или иерархический (Process.hierarchical), где менеджер-LLM распределяет задачи.
Ключевые абстракции
role, goal, backstory и tools. System prompt генерируется автоматически из этих полей.description и expected_output. Назначается конкретному агенту или менеджеру для делегирования.sequential — задачи идут по цепочке. hierarchical — менеджер-LLM решает кому и что делегировать.Как работает CrewAI
Минимальный пример
from crewai import Agent, Task, Crew, Process
from crewai.tools import tool
# ── Инструмент ───────────────────────────────────────────────
@tool("Search Web")
def search_web(query: str) -> str:
"""Поиск актуальной информации в интернете."""
return f"Результаты поиска по '{query}': ..."
# ── Агенты (роли с автоматическим system prompt) ─────────────
researcher = Agent(
role="Senior AI Researcher",
goal="Найти актуальные и достоверные данные по теме {topic}",
backstory=(
"Опытный исследователь с 10-летним стажем. "
"Специализируется на быстром поиске и верификации источников."
),
tools=[search_web],
verbose=True,
)
writer = Agent(
role="Technical Writer",
goal="Написать понятный технический отчёт на основе исследования",
backstory="Технический автор, умеющий превращать сложные данные в читаемые тексты.",
tools=[],
)
# ── Задачи (привязаны к агентам) ─────────────────────────────
research_task = Task(
description="Изучи тему '{topic}': найди последние события, статистику, экспертные мнения.",
expected_output="Структурированный список из 5-7 ключевых фактов с источниками.",
agent=researcher,
)
write_task = Task(
description="На основе результатов исследования напиши отчёт на русском языке.",
expected_output="Markdown-отчёт с заголовками, тезисами и выводом.",
agent=writer,
context=[research_task], # автоматически получает результат research_task
)
# ── Команда ──────────────────────────────────────────────────
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task],
process=Process.sequential,
verbose=True,
)
result = crew.kickoff(inputs={"topic": "AI Agents в 2025"})
print(result.raw)
role/goal/backstoryНужна быстрая разработка мульти-агентной системы с чёткими ролями. Идеален для content automation, research pipelines, бизнес-аналитики. Если хочется прототип за час — CrewAI лучший выбор.
AutoGen строит агентов как участников чата. Каждый агент — это ConversableAgent, который умеет получать сообщения, отвечать на них (через LLM или функцию) и инициировать разговор. Несколько агентов объединяются в GroupChat, где GroupChatManager решает кому слово.
Уникальная особенность AutoGen — выполнение кода. UserProxyAgent с code_execution_config запускает Python-код, который сгенерировал AssistantAgent, анализирует вывод и передаёт результат обратно. Это делает AutoGen идеальным для data science и coding-задач.
Ключевые абстракции
human_input_mode: ALWAYS / TERMINATE / NEVER.Как работает AutoGen
Минимальный пример
import autogen
# ── Конфигурация LLM ─────────────────────────────────────────
llm_config = {
"config_list": [{"model": "gpt-4o", "api_key": "sk-..."}],
"temperature": 0,
}
# ── Агент: генерирует и исправляет код ───────────────────────
assistant = autogen.AssistantAgent(
name="Coder",
system_message=(
"Ты опытный Python-разработчик. "
"Пиши чистый, рабочий код. "
"Если код не прошёл тесты — исправь ошибку."
),
llm_config=llm_config,
)
# ── Прокси: выполняет код, может спрашивать пользователя ─────
user_proxy = autogen.UserProxyAgent(
name="Executor",
human_input_mode="TERMINATE", # спросить человека только при завершении
max_consecutive_auto_reply=10,
is_termination_msg=lambda x: "TERMINATE" in x.get("content", ""),
code_execution_config={
"work_dir": "coding",
"use_docker": False, # True в prod — изоляция через Docker
},
)
# ── Запуск диалога ───────────────────────────────────────────
user_proxy.initiate_chat(
assistant,
message=(
"Напиши Python-скрипт для анализа данных из файла sales.csv: "
"загрузи данные, посчитай топ-10 продуктов по выручке, "
"построй график и сохрани в report.png."
),
)
# ── GroupChat с несколькими агентами ────────────────────────
coder = autogen.AssistantAgent("Coder", llm_config=llm_config, system_message="Пишешь код.")
reviewer = autogen.AssistantAgent("Reviewer", llm_config=llm_config, system_message="Проверяешь код. Говоришь APPROVED или объясняешь что исправить.")
executor = autogen.UserProxyAgent("Executor", human_input_mode="NEVER", code_execution_config={"work_dir": "coding"})
groupchat = autogen.GroupChat(
agents=[executor, coder, reviewer],
messages=[],
max_round=20,
speaker_selection_method="auto", # LLM выбирает следующего спикера
)
manager = autogen.GroupChatManager(groupchat=groupchat, llm_config=llm_config)
executor.initiate_chat(manager, message="Реши задачу сортировки массива за O(n log n)")
Задача — итеративная разработка кода, data analysis, или нужна система где агенты реально «обсуждают» проблему. Идеален для coding assistants и research automation с выполнением кода.
OpenAI Agents SDK — самый минималистичный из четырёх. Его философия: агент + инструменты + handoff. Агент работает с инструментами пока не решит задачу или не передаст управление другому агенту через handoff. Это официальный SDK от OpenAI, built on top of Responses API.
Особенность — Handoffs: механизм передачи управления между агентами. Агент-роутер принимает запрос и решает кому его делегировать. Специализированный агент выполняет задачу. Такая архитектура чище чем GroupChat, потому что каждый агент знает свою роль и не знает о существовании других.
Ключевые абстракции
name, instructions, tools, handoffs. Всё что нужно для агента — в одном объекте. Без лишнего boilerplate.Runner.run() запускает агента. Внутри — реактивный цикл с инструментами. Поддерживает streaming и async.Как работает OpenAI Agents SDK
Минимальный пример
import asyncio
from agents import Agent, Runner, function_tool
# ── Инструменты ──────────────────────────────────────────────
@function_tool
def get_order_status(order_id: str) -> str:
"""Получить статус заказа по его ID."""
orders = {"ORD-123": "доставлен", "ORD-456": "в пути"}
return orders.get(order_id, "заказ не найден")
@function_tool
def process_refund(order_id: str, reason: str) -> str:
"""Оформить возврат средств по заказу."""
return f"Возврат по заказу {order_id} оформлен. Причина: {reason}. Срок: 3-5 дней."
@function_tool
def search_faq(query: str) -> str:
"""Поиск по базе знаний и FAQ."""
return f"Статья по теме '{query}': [ссылка на документ]"
# ── Специализированные агенты ────────────────────────────────
billing_agent = Agent(
name="Billing Specialist",
instructions=(
"Ты специалист по расчётам и возвратам. "
"Помогай с вопросами о заказах, счетах и возвратах средств."
),
tools=[get_order_status, process_refund],
)
support_agent = Agent(
name="Support Specialist",
instructions=(
"Ты агент поддержки. "
"Отвечай на общие вопросы, ищи информацию в базе знаний."
),
tools=[search_faq],
)
# ── Агент-роутер ─────────────────────────────────────────────
triage_agent = Agent(
name="Triage Agent",
instructions=(
"Ты диспетчер. Определи тип запроса и передай нужному агенту:\n"
"- Вопросы о заказах, платежах, возвратах → billing_agent\n"
"- Все остальные вопросы → support_agent\n"
"Не отвечай сам — только маршрутизируй."
),
handoffs=[billing_agent, support_agent],
)
# ── Запуск ───────────────────────────────────────────────────
async def main():
result = await Runner.run(triage_agent, "Хочу вернуть деньги за заказ ORD-123")
print(result.final_output)
asyncio.run(main())
Используешь OpenAI-модели и нужна быстрая разработка с минимумом кода. Идеален для customer-facing агентов с чёткой маршрутизацией: triage → специалист. Отличная точка старта для команд, которые только начинают с агентами.
Сравнение всех четырёх фреймворков
| Критерий | LangGraph | CrewAI | AutoGen | OpenAI SDK |
|---|---|---|---|---|
| Основная метафора | Граф состояний | Команда ролей | Чат агентов | Агент + handoff |
| Порог входа | ●●● высокий | ● низкий | ●● средний | ● низкий |
| Контроль над логикой | ●●● максимальный | ● ограниченный | ●● средний | ●● средний |
| Checkpointing / Resume | ●●● best-in-class | ● базовое | ● ограниченное | ✗ нет |
| Human-in-the-loop | ●●● нативный | ●● есть | ●●● нативный | ●● через guardrails |
| Выполнение кода | ●● через инструменты | ●● через инструменты | ●●● встроенный sandbox | ●● через инструменты |
| Мульти-агентность | ●●● subgraphs | ●●● нативная | ●●● GroupChat | ●●● handoffs |
| Observability | ●●● LangSmith | ●● встроенный | ●● встроенный | ●●● OpenAI Dashboard |
| Поддержка моделей | любые LLM | любые LLM | любые LLM + local | OpenAI-first |
| Лучший use case | Production с сложной логикой |
Content & research automation |
Coding & data analysis |
Customer-facing с маршрутизацией |
Шпаргалка
Быстрый выбор фреймворка Нужен полный контроль, checkpointing, HITL, сложные ветвления? → LangGraph (enterprise, production, long-running workflows) Хочешь быстро собрать команду специалистов для автоматизации? → CrewAI (research, content, data pipelines) Задача — генерация и выполнение кода, итеративная разработка? → AutoGen (coding assistant, data analysis, scientific computing) Строишь на OpenAI-моделях, нужна маршрутизация к специалистам? → OpenAI Agents SDK (customer support, triage, simple multi-agent) Хочешь понять как всё работает под капотом / нужен полный контроль? → Чистый Python (обучение, специфические требования, legacy-интеграции) Ключевые различия Парадигма: LangGraph=граф CrewAI=команда AutoGen=чат OpenAI=routing Контроль: максимальный средний средний базовый Скорость MVP: медленно быстро быстро быстро Checkpointing: best-in-class базовое ограниченное нет Код: очень явный декларативный разговорный минималистичный
Практическое задание
-
Установи все четыре. Создай виртуальное окружение и установи
langgraph crewai autogen openai-agents. Запусти минимальный пример каждого (взяв code из этого урока) и сравни время до первого работающего результата. - Один агент — четыре реализации. Возьми задачу «агент читает новостной RSS, выбирает 3 интересные новости, пишет краткое резюме» и реализуй её в каждом из четырёх фреймворков. Что пришлось написать в каждом? Сколько строк? Что оказалось неудобным?
-
LangGraph + Checkpointing. Добавь в LangGraph-пример
SqliteSaverвместоMemorySaver. Запусти агента, убей процесс на середине, перезапусти с тем жеthread_id— агент должен продолжить с места остановки.