Разговорная парадигма AutoGen

AutoGen смотрит на мультиагентную систему под особым углом: всё взаимодействие — это обмен сообщениями. Агент получает сообщение, что-то с ним делает (зовёт LLM, выполняет код, спрашивает человека) и отправляет ответное сообщение собеседнику. Координация выражается не графом и не вызовами функций, а диалогом — естественной для людей метафорой.

Это отличает AutoGen от LangGraph из модуля 03. Там мы строили граф: узлы, рёбра, общее состояние. Здесь — беседу: участники, сообщения, реплики по очереди. Оба подхода решают одну задачу (оркестрация агентов), но мыслят по-разному: LangGraph — «поток управления как структура», AutoGen — «координация как разговор».

ℹ️ Экосистема: AutoGen, AG2 и v0.4

У AutoGen непростая история. Классический API (ConversableAgent, initiate_chat) живёт в пакете pyautogen и в форке AG2. Microsoft параллельно выпустила переписанную архитектуру AutoGen 0.4 (autogen-agentchat) с асинхронным ядром. Концепции — агенты-собеседники, обмен сообщениями — общие для всех версий. Мы изучаем именно их на классическом, наиболее документированном API; перенести идеи на новую версию несложно.

ConversableAgent — универсальный агент

ConversableAgent — базовый класс всех агентов AutoGen. «Conversable» = «способный к разговору»: у него есть имя, поведение (системный промпт + конфиг LLM) и методы, чтобы слать и принимать сообщения. Почти все остальные агенты (AssistantAgent, UserProxyAgent — следующий урок) — это ConversableAgent с предустановленными настройками.

Создание ConversableAgent
python
from autogen import ConversableAgent

llm_config = {"model": "gpt-4o-mini", "api_key": "sk-..."}   # конфиг модели

agent = ConversableAgent(
    name="assistant",                       # имя — адрес в разговоре
    system_message="Ты вежливый помощник.", # роль/поведение (как роль в промпте)
    llm_config=llm_config,                  # какой моделью думать
    human_input_mode="NEVER",               # не спрашивать человека
)

Ключевые параметры:

  • name — уникальное имя; по нему агента адресуют в разговоре.
  • system_message — системный промпт: кто этот агент и как себя ведёт (привет уроку про роли из модуля 01).
  • llm_config — конфигурация LLM (модель, ключ, параметры). Если поставить False — агент не использует LLM (например, чисто исполнитель кода или прокси человека).
  • human_input_mode — спрашивать ли человека: "NEVER" (автономно), "ALWAYS" (каждый ход), "TERMINATE" (только перед завершением).

Как агенты обмениваются сообщениями

Сообщение в AutoGen — это знакомый по модулю 01 формат {"role": ..., "content": ...}. Разговор — это растущий список таких сообщений, общий для собеседников. У агента есть низкоуровневые методы send / receive и метод generate_reply, который по истории формирует ответ.

100%
колёсико — масштаб  ·  зажать и тянуть — перемещение
agent A name: "user" initiate_chat(B, ...) agent B name: "assistant" generate_reply() 1 · {role:user, content:"задача"} 2 · {role:assistant, content:"ответ"} 3 · {role:user, content:"уточнение"} разговор = растущий список сообщений; реплики по очереди, пока не сработает остановка

Важная деталь: роль (user/assistant) в сообщении — относительная. С точки зрения каждого агента собеседник — это «user», а его собственные ответы — «assistant». AutoGen сам ведёт эту перспективу для каждого участника, тебе об этом думать обычно не нужно.

initiate_chat: запуск диалога

Вручную дёргать send/receive не нужно — есть initiate_chat. Один агент инициирует разговор с другим, передаёт стартовое сообщение, и AutoGen прокручивает обмен репликами автоматически, пока не сработает условие остановки.

Два агента ведут диалог
python
from autogen import ConversableAgent

llm_config = {"model": "gpt-4o-mini", "api_key": "sk-..."}

# Два специализированных агента (разные system_message — разные роли)
comedian = ConversableAgent(
    name="comedian",
    system_message="Ты комик. Шути коротко и по делу.",
    llm_config=llm_config, human_input_mode="NEVER",
)
critic = ConversableAgent(
    name="critic",
    system_message="Ты критик юмора. Оцени шутку одним предложением.",
    llm_config=llm_config, human_input_mode="NEVER",
)

# critic запускает разговор с comedian
result = critic.initiate_chat(
    comedian,
    message="Пошути про программистов.",
    max_turns=2,                 # ограничиваем число обменов
)

print(result.summary)            # итог беседы

Что произошло: critic отправил стартовое сообщение, comedian ответил шуткой, critic её оценил — и на этом max_turns=2 остановил беседу. Никаких рёбер и состояния — просто два агента поговорили. initiate_chat вернул объект с историей и summary разговора.

⚠️ Всегда ставь предел разговору

Без ограничения два LLM-агента могут «вежливо беседовать» бесконечно — каждый отвечает на ответ другого. Всегда задавай max_turns и/или условие завершения (is_termination_msg, ниже). Это прямой аналог recursion_limit из LangGraph: страховка от вечного цикла, только здесь цикл — это диалог.

Как разговор завершается

У беседы должно быть условие выхода. AutoGen даёт несколько механизмов остановки:

  • max_turns — жёсткий лимит числа обменов репликами. Самый простой предохранитель.
  • is_termination_msg — функция-предикат: если сообщение ей подходит (например, содержит «TERMINATE»), разговор завершается. Так агент сам сигналит «я закончил».
  • max_consecutive_auto_reply — сколько автоответов подряд агент даёт без участия человека.
Завершение по ключевому слову
python
agent = ConversableAgent(
    name="solver",
    system_message="Реши задачу. Когда закончишь — ответь словом TERMINATE.",
    llm_config=llm_config,
    human_input_mode="NEVER",
    # разговор остановится, как только в сообщении встретится TERMINATE
    is_termination_msg=lambda m: "TERMINATE" in (m.get("content") or ""),
)
Сочетай предохранитель и логику

Как и в Reflection/циклах из модуля 03, надёжнее всего два условия выхода: осмысленное (is_termination_msg — агент сам решил, что готово) и аварийное (max_turns — на случай, если он не остановится). Первое отвечает за нормальный сценарий, второе — за защиту от зацикливания.

Откуда берётся ответ

Когда агент получает сообщение, он не обязательно идёт в LLM. У ConversableAgent есть цепочка генераторов ответа (reply functions), которые пробуются по порядку: проверить условие завершения → выполнить код, если он есть в сообщении → спросить человека (если режим требует) → и только потом сгенерировать ответ через LLM. Первый сработавший генератор и даёт ответ.

Это делает ConversableAgent гибким: один и тот же класс может быть «думающим» агентом (LLM), «исполнителем» (запуск кода без LLM) или «прокси человека» — всё определяется конфигом и набором reply-функций. Регистрировать свои генераторы можно через register_reply, но на старте хватает встроенного поведения.

Типичные ошибки

Ошибка 1: разговор без предела

Без max_turns или is_termination_msg два агента могут беседовать бесконечно, сжигая токены. Всегда задавай условие остановки.

Ошибка 2: ждать TERMINATE без инструкции

Если завершение завязано на слово TERMINATE, но в system_message не сказано его писать — агент его не напишет, и разговор не остановится. Условие остановки и промпт должны быть согласованы.

Ошибка 3: одинаковые имена агентов

Имя — это адрес в разговоре. Два агента с одинаковым name создают путаницу в маршрутизации сообщений. Имена должны быть уникальными.

Ошибка 4: llm_config там, где не нужен LLM

Агенту-исполнителю кода или прокси человека LLM не нужен — ставь llm_config=False. Иначе он будет лишний раз дёргать модель (и тратить токены) там, где должен просто выполнить действие.

Шпаргалка

ConversableAgent — всё в одном месте
python
from autogen import ConversableAgent

# ИДЕЯ: мультиагент = разговор; агенты обмениваются сообщениями

# Создание агента
agent = ConversableAgent(
    name="assistant",                 # адрес в разговоре (уникальный!)
    system_message="Ты ...",          # роль/поведение
    llm_config={"model": "...", "api_key": "..."},  # или False — без LLM
    human_input_mode="NEVER",         # NEVER | ALWAYS | TERMINATE
    is_termination_msg=lambda m: "TERMINATE" in (m.get("content") or ""),
)

# Запуск диалога: один агент инициирует разговор с другим
result = a.initiate_chat(b, message="...", max_turns=2)
result.summary                        # итог беседы

# Остановка:
#   max_turns                — жёсткий лимит обменов (предохранитель)
#   is_termination_msg       — агент сам сигналит "готово" (логика)
#   max_consecutive_auto_reply — автоответов подряд без человека

# Сообщение = {"role": "user"|"assistant", "content": "..."}
#   роль ОТНОСИТЕЛЬНА: собеседник = user, свои ответы = assistant

# Правила:
#  • ВСЕГДА условие остановки (предохранитель + логика)
#  • уникальные имена агентов
#  • нет LLM у агента → llm_config=False
#  • TERMINATE в условии ⇒ упомяни его в system_message

Практическое задание

Собери первый разговор агентов:

Задание: диалог двух ConversableAgent

  1. Установи pyautogen (или AG2), настрой llm_config со своей моделью и ключом.
  2. Создай двух агентов с разными ролями через system_message — например, «учитель» и «ученик»: ученик задаёт вопросы, учитель отвечает.
  3. Запусти диалог через initiate_chat с max_turns=3. Выведи result.summary и посмотри полную историю.
  4. Поменяй human_input_mode на "ALWAYS" у одного агента и поучаствуй в разговоре вручную. Чем отличается поведение?
  5. Добавь завершение по is_termination_msg (слово TERMINATE) и не забудь упомянуть это в системном промпте. Проверь, что разговор останавливается «по смыслу», а не только по лимиту.
  6. Со звёздочкой: сделай третьего агента-«критика» и устрой беседу так, чтобы после ответа учителя критик его оценивал (пока без GroupChat — просто цепочкой initiate_chat).

Что дальше

Ты освоил базовый кирпичик AutoGen — агента-собеседника. Дальше — два самых ходовых вида ConversableAgent и классический паттерн их связки: AssistantAgent (думает) и UserProxyAgent (исполняет/представляет человека).