Мультиагентные системы: как «команда ИИ» берёт сложность штурмом

в 4:38, , рубрики: AI, bug, codereview

Введение: мир, где одному ИИ тесно

Софт стал распределённым организмом: микросервисы, бесконечные API, CI/CD, инфраструктура как код, пользователи в разных часовых поясах. Ошибки проявляются не линейно, а «каскадами». Один умный помощник-универсал (даже очень большой) часто упирается в пределы: медленно, хрупко, дорого. Как и в инженерных командах, нужна кооперация ролей — планирование, исследование, реализация, проверка, эксплуатация. Эту идею и воплощают мультиагентные системы (MAS): коллекция автономных агентов, которые общаются, координируются и вместе решают задачи, где одиночный ИИ буксует. Репозиторий кода - https://github.com/iRatG/mas.

Почему одиночному ИИ тяжело

  • Рост сложности и динамики. Задачи распадаются на подзадачи с конфликтующими целями (качество, скорость, стоимость). Один агент «видит» лишь локальную оптимальность и спотыкается на компромиссах.

  • Ограничение внимания и времени ответа. Даже крупные модели ограничены контекстом и токен-бюджетом. Попытка «думать дольше» (длинные chain-of-thought) увеличивает стоимость и латентность, а не гарантирует качество.

  • Отсутствие надёжной верификации. Во многих доменах (код, математика) решение требует проверки — тестов, пруверов, формальных критериев. Без внешнего «судьи» одиночный агент склонен к уверенным, но неверным ответам.

Что такое мультиагентная система (MAS)

MAS — это набор автономных агентов со своими целями, наблюдениями и действиями. Они обмениваются сообщениями, договариваются и координируются. На практике: планировщик дробит задачу, исследователь собирает контекст, разработчик пишет патч, критик проверяет, а оператор запускает тесты. Коммуникация может быть явной (сообщения) или опосредованной арбитрами/агрегаторами; координация — синхронной (барьеры, раунды) или асинхронной (гонки гипотез, «первый дошёл — молодец»).

Фундамент: как LLM-агенты «думают, действуют и сверяются»

ReAct (Reason+Act) показывает, что связка рассуждения и действия (инструменты, среда) даёт устойчивые траектории решения: агент мыслит шагами, вызывает инструменты, читает наблюдения и корректирует план [1].
Self-Consistency добавляет стохастическое «многоголосие» — мы генерируем несколько независимых рассуждений и берём консенсус. Эта простая техника заметно повышает точность на текстовых задачах [2].
Процесс-супервизия (PRM) учит модели/критиков оценивать каждый шаг рассуждения, а не только финальный ответ. Это превращает «верим-не верим» в проверяемую лестницу шагов и синергирует с инструментами верификации [3].

Отсюда рождаются два практических рычага, которые MAS используют системно:

  1. Время на вывод (inference-time compute): больше проб и ветвей → выше шанс, что хотя бы один путь правильный (а значит, нужен хороший отборщик). 2) Множественность мнений: разнородные агенты/модели смотрят на задачу под разными углами, а агрегатор «собирает» ответ (Mixture‑of‑Agents) [5].

Современные архитектуры: от ансамблей к мультиагентам

Mixture‑of‑Agents (MoA)

MoA — это «слоёная» кооперация моделей/агентов: в каждом слое несколько proposer‑ов предлагают версии, а aggregator синтезирует итог. Важны разнообразие предложений и способность агрегатора не просто ранжировать, а переосмысливать входы. Эмпирически MoA улучшает метрики (AlpacaEval, FLASK) и даже обгоняет сильные одиночные модели, особенно по устойчивости и полноте ответа; минус — рост времени до первого токена [5].

Повышение отдачи от времени вывода

Работы по массовому пересэмплингу показывают почти «законы масштабирования» на тест‑тайме: покрытие задач растёт лог‑линейно с числом проб на порядки, особенно там, где есть автоматический вердикт (юнит‑тесты, пруверы) [6,9]. Параллельно возникает вопрос «compute‑optimal инференса»: когда выгоднее взять меньшую модель, но больше проб/поиска, и какими алгоритмами (best‑of‑n, взвешенное голосование, дерево‑поиск) это делать — здесь появляются эмпирические правила и новые алгоритмы (например, REBASE), дающие лучшую «цена/качество» кривую [9].

Агент ↔ компьютер: ACI [4] и реальные среды

В инженерных задачах ключевое — связать агента с окружением. Парадигма Agent‑Computer Interface (ACI) задаёт дискретное действие («открой файл», «отредактируй патч», «запусти тесты», «проанализируй лог») и обратную связь (диагностика, линт, тест‑репорты). Такая формализация увеличивает воспроизводимость, расширяемость «навыков» и эффективность планирования. В задачах уровня SWE‑bench ACI‑подход сочетается с многораундовой стратегией и даёт прирост качества — особенно когда действия тонко подобраны (например, отдельное edit с атомарными диффами + автоматический линт) [4].

Платформы агентов

Практические фреймворки (например, «реальные компьютерные агенты» в изолированных Docker‑песочницах, с набором AgentSkills и делегацией подзадач) демонстрируют, как строить безопасные траектории действий в ОС/IDE и кооперацию агентов через шины событий и контроль политик [7].

Оценка и подводные камни

Софт‑агенты легко «переобучаются на бенчмарк»: утечки данных, детерминированные окружения, «подгонка под валидатор». Для честного прогресса важны: скрытые тесты, перемешивание/рандомизация окружения, проверка патча end‑to‑end, и независимые верификаторы (а не «LLM‑судья») [8,10].

Как это приземлить в «репозитории» (разбор кода и режимов)

Ниже — «экскурсия» по ключевым идеям и их воплощению в репозитории. Мы берём концепты из теории (ReAct, self‑consistency, процесс‑супервизия, MoA, inference‑time compute) и показываем, где они «сидят» в коде.

Подход 1: Синхронный (sync)

Идея. Чистая реализация ReAct [1]: один агент ведёт диалог с окружением/инструментами по шагам «Думаю → Действую → Наблюдаю → Корректирую план». Это минимальная, но надёжная форма координации — особенно когда есть явный верификатор (юнит‑тесты/чек‑листы). Теоретически это хорошо сочетается с процесс‑критикой: «маленький PRM» может проверять промежуточные гипотезы и фильтровать галлюцинации [1][3].
В репозитории. Оркестратор sync и CLI‑точка mas.cli.main --approach sync последовательно выполняют кейсы, поддерживают ретраи и собирают метрики (время/кандидаты/успехи). Это ровно тот baseline, на который хорошо «навешивать» self‑consistency и маленькие проверяющие [см. ниже].

Подход 2: Асинхронный (async, через оркестратор)

Идея. Роли и сообщения: Analyst локализует проблему, Fixer предлагает патч, Controller следит за бюджетом/таймаутами, Coordinator собирает итог. Это «тонкий MoA» [5]: несколько агентов дают разный взгляд на задачу, а оркестратор решает, когда остановиться и что принять. Теоретически это усиливает устойчивость (диверсификация гипотез), но повышает цену/латентность — значит, важно управлять глубиной и числом сообщений [5][6].
В репозитории. mas.cli.main --approach async включает шину сообщений и считает коммуникативные метрики (сколько сообщений, таймауты, эффективность). Роли реализованы как отдельные компоненты/хэндлеры, что упрощает расширение (легко добавить «Browser» или «Doc‑search»).

Подход 3: Итеративный/продвинутый

Идея. Масштабируем качество за счёт времени вывода: запускаем несколько независимых попыток (разных температур/подсказок), собираем «лучшее из N» по верификатору, запоминаем историю разборов (что сработало и почему), и повторно используем успешные траектории. Это прямая реализация результатов по repeated sampling/compute‑optimal инференсу [6]: иногда «меньше модель + больше попыток» превосходит «больше модель + одна попытка» [6].
В репозитории. mas.cli.iterative хранит историю, поддерживает батчи попыток и критерии отбора (юнит‑тесты/чек‑листы), а затем выводит сводные метрики. Этот режим легко комбинируется с sync/async: итерации можно вкручивать и в последовательный, и в многоролевой сценарий.


Два режима работы LLM: имитация и реальный API

A) Имитация LLM (для быстрых прогонов и демонстрации)

Зачем. Стабильно воспроизводить прогоны без интернета и расходов; быстро демонстрировать оркестрацию. Теоретически это важно для валидности экспериментов: мы фиксируем сиды/кейсы и исключаем дрейф модели.
В репозитории. Модуль mas.llm.mock_client возвращает предсказуемые ответы для заранее описанных кейсов («выход за границы», «деление на ноль» и т. п.), что позволяет сравнивать архитектуры в «чистом» виде и видеть, сколько сообщений/шагов/ретраев реально нужно каждому подходу.

Б) Реальный LLM через API (OpenAI и др.)

Зачем. Проверить, как система ведёт себя «в поле»: промпт‑инжиниринг, шумные логи, неоднозначные сообщения об ошибках, лимиты токенов. Теоретически здесь раскрывается роль ACI: чем чище действие и информативнее отклик (линт, дифф, трассировка), тем надёжнее петля ReAct [4].
В репозитории. mas.llm.real_llm (см. env_example.txt и docs/SETUP_OPENAI.md) подключает API, уважает модель/параметры, и проксирует вызовы через общий интерфейс. CLI:

python -X utf8 -m mas.cli.main --use-openai --openai-model gpt-4 --approach both --cases 1 2 3

Как собирается промпт (в упрощённом виде)

Точная реализация может отличаться между ролями/подходами, но общий конструкт выглядит так — и хорошо соответствует современной практике:

[system]
Вы — инженер‑разработчик в мультиагентной команде. Работаете атомарными действиями (ACI): READ_FILE, EDIT_PATCH, RUN_TESTS, ANALYZE_LOG.
Правила: делайте минимальные правки; поясняйте план; не выдумывайте факты; уважайте формат вывода.

[user]
Контекст: <описание кейса + фрагменты кода + сообщение об ошибке/тест‑фейл>
Цель: исправить баг так, чтобы прошли тесты.
Ограничения: изменяйте только указанные файлы; не меняйте API; время ≤ X сек; бюджет токенов ≤ Y.

[assistant]
План (шаги):
1) локализую строку/функцию;
2) предложу патч;
3) запущу тесты; если упали — уточню план и повторю.

[assistant → tool]
EDIT_PATCH <<<< unified‑diff
@@ file.py:42‑50 @@
- return a / b
+ if b == 0:
+     return 0  # safe‑guard
+ return a / b
>>>>

[tool → assistant]
RUN_TESTS → FAIL (test_div_zero)
LOG:
Traceback ... ZeroDivisionError ...

[assistant]
Обновляю гипотезу: нужно вернуть специальный код, а не 0. Предлагаю патч v2…

Теория рядом. Такой промпт прямо «вшивает» ReAct (план→действие→наблюдение), добавляет процесс‑супервизию [3] (объясняй шаги/решения), и готов к self‑consistency: можно параллельно сгенерировать несколько планов и свести их агрегатором. Для async‑режима роли получают свой system‑блок («Ты — Analyst», «Ты — Fixer») и общаются через короткие сообщения‑артефакты.


Как система «решает проблему в реальности» (путь данных)

  1. Загрузка кейса (evaluation/test_cases): исходник+тест/описание фейла.

  2. Планирование (sync или async): формируется трасса шагов и бюджет.

  3. Действия по ACI: чтение/редактирование/запуск тестов/анализ логов (песочница). Именно здесь «чистота» интерфейсов критична: атомарные патчи с диффом и явная диагностика снижают галлюцинации [4].

  4. Верификация (evaluation/patching, тест‑раннер): проходим/падаем → даём агенту наблюдение; для iter‑режима — best‑of‑N по вердиктору.

  5. Аналитика (analytics/results): время, число сообщений, ретраи, успехи/фейлы; сравнение подходов (mas.cli.compare). Это связано с идеями compute‑optimal инференса: считаем не только качество, но и цену/латентность [6].


Примеры (test_cases)

        # Пытаемся вызвать функции с проблемными параметрами
        if case['id'] == 1:  # Выход за границы
            result = env['calculate_sum']([1, 2, 3])
            print(f"❌ НЕОЖИДАННО: код выполнился! Результат: {result}")
            
        elif case['id'] == 2:  # None обращение
            result = env['process_data'](None)
            print(f"❌ НЕОЖИДАННО: код выполнился! Результат: {result}")
            
        elif case['id'] == 3:  # Деление на ноль
            result = env['divide'](10, 0)
            print(f"❌ НЕОЖИДАННО: код выполнился! Результат: {result}")
            
        elif case['id'] == 4:  # Несоответствие типов
            result = env['add_numbers']('hello', 5)
            print(f"❌ НЕОЖИДАННО: код выполнился! Результат: {result}")
            
        elif case['id'] == 5:  # Неинициализированная переменная
            env['count_down'](2)
            print("❌ НЕОЖИДАННО: код выполнился без ошибок!")
            
    except Exception as e:
        print(f"✅ ОШИБКА ОБНАРУЖЕНА: {type(e).__name__}: {e}")
        print(f"   Это именно та проблема, которую должна решить MAS система!")

Запуски

# Синхронный/асинхронный прогон (имитация LLM)
python -X utf8 -m mas.cli.main --approach sync  --cases 1 2 3
python -X utf8 -m mas.cli.main --approach async --cases 1 2 3

# Реальный LLM через API
python -X utf8 -m mas.cli.main --use-openai --openai-model gpt-4 --approach both --cases 1 2 3

# Продвинутый итеративный режим (память и отбор «лучшее из N»)
python -X utf8 -m mas.cli.iterative

# Сравнение подходов и сводные метрики
python -X utf8 -m mas.cli.compare

«Скриншоты» вывода

SYNC, кейс 1

Запуск СИНХРОННОГО подхода (seed=42)
--------------------------------------------------
--- SYNC кейс 1: Выход за границы массива ---
✅ Статус: success
   Время: 0.003 сек
   Кандидатов: 2
   Ретраев: 0

ASYNC, кейс 1 + сводка

Запуск АСИНХРОННОГО подхода в ПОСЛЕДОВАТЕЛЬНОМ режиме (seed=42)
--------------------------------------------------
--- ASYNC кейс 1: Выход за границы массива ---
✅ Статус: success
   Время: 0.005 сек
   Сообщений: 3/3
   Таймауты: 0

================================================================================
СВОДНЫЙ ОТЧЁТ ПО АНАЛИЗУ ПОДХОДОВ
================================================================================
Общее время выполнения: 2.156 сек
Всего обработано кейсов: 10
 СИНХРОННЫЙ ПОДХОД:
  • Обработано кейсов: 5
  • Успешных: 5, Неудачных: 0
  • Процент успеха: 100%
  • Среднее время: 0.004 сек
  • Всего ретраев: 0
  • Среднее кандидатов на кейс: 2.0
 АСИНХРОННЫЙ ПОДХОД:
  • Обработано кейсов: 5
  • Успешных: 5, Неудачных: 0, Таймаутов: 0
  • Процент успеха: 100%
  • Среднее время: 0.008 сек
  • Всего сообщений: 15
  • Эффективность сообщений: 1.0
⚡ СРАВНЕНИЕ:
  • Быстрее: sync подход
  • Разница во времени: 0.004 сек
  • Больше успешных: equal
  • Разница в успешности: 0 кейсов
================================================================================

Мельчайший ACI‑фрагмент

Идея — показать, как связываются действия и обратная связь; конкретные названия функций/классов могут отличаться, что в репозитории.

class ACI:
    def read_file(self, path: str) -> str:      # READ_FILE
        return fs.read_text(path)

    def edit_patch(self, diff: str) -> None:    # EDIT_PATCH (unified diff)
        apply_unidiff(diff)

    def run_tests(self) -> TestReport:          # RUN_TESTS
        return run_pytests_capture()

# Пример шага агента (sync/async одинаково)
obs = ACI().read_file("src/module.py")
plan = agent.think(obs)                         # ReAct [1]
ACI().edit_patch(plan.to_unidiff())
report = ACI().run_tests()
if not report.ok:
    critique = critic.judge(report)             # процесс‑супервизия [3]
    plan = agent.revise(plan, critique)

Async-оркестратор с реальным LLM (идеологический псевдокод)

# === ключевые компоненты ===
class LLM:
    def ask(role, messages) -> str: ...  # реальный API: chat(messages) → текст/JSON

class ACI:
    def read(path) -> str: ...
    def patch(unified_diff) -> None: ...
    def test() -> Report: ...  # pass/fail, logs

# === сообщения и роли ===
def bus_emit(topic, payload): ...
def fork_variant(payload): ...  # меняем подсказку/температуру → параллельная ветка

def start(case):
    bus_emit("plan", case)

def Analyst_on_plan(case):
    out = LLM.ask("Analyst", prompt(role="Analyst", ctx=case))   # ReAct [1]
    bus_emit("propose_patch", out)

def Fixer_on_propose_patch(plan):
    diff = LLM.ask("Fixer", prompt(role="Fixer", ctx=plan))      # минимальный unified diff
    ACI.patch(diff)
    report = ACI.test()
    bus_emit("report_ready", {"report": report, "diff": diff})

def Controller_on_report_ready(payload):
    if payload["report"].pass:
        bus_emit("done", payload)
    elif budget.has_room():
        bus_emit("retry", fork_variant(payload))                  # repeated sampling [6]
    else:
        bus_emit("fail", payload)

def Coordinator_on_retry(payload):
    bus_emit("propose_patch", payload)                            # ветка идёт параллельно (mini-MoA [5])

def Coordinator_on_done_or_fail(payload):
    persist(payload)                                              # логируем, выбираем best-of-N по тестам

Мини-шаблон запроса к реальному LLM:

[system] Ты — <ROLE> в команде. Действуешь через ACI: READ_FILE, EDIT_PATCH, RUN_TESTS.
[user] Контекст кейса + цель (исправить баг так, чтобы прошли тесты).
[assistant] Верни: PLAN и ACTION в JSON (если Fixer — unified diff).

Идея в трёх строках:
Analyst даёт план/локацию → Fixer делает патч и тест через ACI → Controller решает готово/ветвим попытки; Coordinator параллелит варианты и выбирает лучшее по тестам. Это скрещивает ReAct, ACI, mini-MoA и repeated sampling — подход, который демонстрирует репозиторий кода - https://github.com/iRatG/mas.


Что здесь важно по идее

  • ReAct внутри ролей. Каждая роль строит план → действие (ACI) → наблюдение → ревизию; это снижает галлюцинации и делает шаги проверяемыми (микро-процесс-супервизия) — ролевая версия ReAct [1][3].

  • Явный ACI. Только атомарные действия: read_file / apply_unidiff_patch / run_tests_capture. Это уменьшает «шум CLI», делает обратную связь плотной (линт/логи/дифф) и резко повышает воспроизводимость.

  • Асинхронность как «мини-MoA». Оркестратор параллелит ветви решения: альтернативные планы/патчи (температуры/подсказки). Затем Controller делает best-of-N по вердиктору (тестам). Это сочетает MoA и repeated sampling: больше диверсифицированных кандидатов при контролируемом бюджете [5][6].

  • Бюджеты как контракт. Budget(tokens, time, attempts) — не просто счётчик, а политика остановки/повтора. Для Habr-читателя это ключ к «инженерной дисциплине»: мы управляем ценой и задержкой, а не гонимся вслепую за точностью.

  • Верификатор на двух уровнях. judge_step (микро-PRM: отбрасываем бредовые шаги) и judge_final (юнит-тесты). Там, где юнит-тесты «дырявые», step-judger спасает от тупиков и бешеного роста попыток.

  • Наблюдаемость. corr_id в каждом сообщении, persist_success/failure с логами и диффами — позже это корм для отчётов и «памяти попыток» (ваш продвинутый режим).


Мини-пример промпта (смысл)

[system] Ты — Fixer в мультиагентной команде. Действуй атомарно через ACI: READ_FILE, APPLY_UNIDIFF_PATCH, RUN_TESTS.
[developer] Политика: минимальные правки, не трогай API, объясняй мысль коротко.
[user] Цель: исправить падение test_div_zero в module.py. Логи и фрагменты кода ниже.
[assistant] План: 1) локализация → 2) минимальный unified diff → 3) запуск тестов → 4) ревизия при провале.
# Ожидаемый ответ: JSON с полями {rationale, patch_unidiff}

Мультиагентность в репозитории — это не набор скриптов, а попытка реализации научных идей: ReAct даёт траектории; self‑consistency и MoA — разнообразие гипотез; процесс‑супервизия — контроль качества промежуточных шагов; ACI — плотную связь с реальной средой; итеративность — «масштаб качества» за счёт времени вывода. В сумме получается практичная система, где архитектура объясняет метрики, а метрики — подсказки к архитектуре. Мы оставляем код на минимуме в тексте — всё остальное читатель увидит сам в «репозитории».

Получается, что

мультиагентность — не «мода», а инженерный ответ на сложность: параллелизм гипотез, разделение ролей, проверяемость шагов и экономия за счёт compute‑optimal вывода. MAS соединяют лучшие идеи LLM‑эпохи — ReAct, self‑consistency, процесс‑супервизию [3], MoA, ACI — в практическую дисциплину построения «команд ИИ», которые действительно закрывают задачи. Будущее ИИ‑инженерии — это не один огромный мозг, а оркестр специалистов с хорошим дирижёром и строгим приёмщиком.


Источники

[1] ReAct [1]: Synergizing Reasoning and Acting in Language Models (arXiv:2210.03629).
[2] Self‑Consistency Improves Chain‑of‑Thought Reasoning in Language Models (arXiv:2203.11171).
[3] Let’s Verify Step by Step: Process Supervision via Stepwise Verifiers (PRM/PRM800K) (arXiv:2305.20050).
[4] SWE‑agent: Agent‑Computer Interface для решения задач SWE‑bench (arXiv:2405.15793).
[5] Mixture‑of‑Agents Enhances LLM Capabilities (arXiv:2406.04692).
[6] Large Language Monkeys: Scaling Inference Compute with Repeated Sampling (arXiv:2407.21787).
[7] OpenHands: Real Computer Agents — дизайн, песочницы, делегация (arXiv:2407.16741).
[8] SpecRover: робастная оценка и ловушки бенчмаркинга в программной инженерии (arXiv:2408.02232).
[9] Inference Scaling Laws: Compute‑Optimal Inference для LLM‑решения задач (ICLR’25) (arXiv:2408.00724).
[10] Agents that Matter: практическое руководство по честной оценке ИИ‑агентов (аргументы, риски LLM‑судей, репликабельность) (arXiv:2507.02825).
[11] Обсуждение SWE‑bench и Verified‑вариантов, требования к окружению и метрикам — по материалам [4], [7], [8].
[12] Связанные работы по ToT/дебатам/маршрутизации экспертов и проверке рассуждений — см. обзоры в [5], [6], [9].

Автор: AyratGil

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js