Введение: мир, где одному ИИ тесно
Софт стал распределённым организмом: микросервисы, бесконечные API, CI/CD, инфраструктура как код, пользователи в разных часовых поясах. Ошибки проявляются не линейно, а «каскадами». Один умный помощник-универсал (даже очень большой) часто упирается в пределы: медленно, хрупко, дорого. Как и в инженерных командах, нужна кооперация ролей — планирование, исследование, реализация, проверка, эксплуатация. Эту идею и воплощают мультиагентные системы (MAS): коллекция автономных агентов, которые общаются, координируются и вместе решают задачи, где одиночный ИИ буксует. Репозиторий кода - https://github.com/iRatG/mas.
Почему одиночному ИИ тяжело
Рост сложности и динамики. Задачи распадаются на подзадачи с конфликтующими целями (качество, скорость, стоимость). Один агент «видит» лишь локальную оптимальность и спотыкается на компромиссах.
Ограничение внимания и времени ответа. Даже крупные модели ограничены контекстом и токен-бюджетом. Попытка «думать дольше» (длинные chain-of-thought) увеличивает стоимость и латентность, а не гарантирует качество.
Отсутствие надёжной верификации. Во многих доменах (код, математика) решение требует проверки — тестов, пруверов, формальных критериев. Без внешнего «судьи» одиночный агент склонен к уверенным, но неверным ответам.
Что такое мультиагентная система (MAS)
MAS — это набор автономных агентов со своими целями, наблюдениями и действиями. Они обмениваются сообщениями, договариваются и координируются. На практике: планировщик дробит задачу, исследователь собирает контекст, разработчик пишет патч, критик проверяет, а оператор запускает тесты. Коммуникация может быть явной (сообщения) или опосредованной арбитрами/агрегаторами; координация — синхронной (барьеры, раунды) или асинхронной (гонки гипотез, «первый дошёл — молодец»).
Фундамент: как LLM-агенты «думают, действуют и сверяются»
ReAct (Reason + Act) появился как ответ на две крайности: «чистое мышление» (модель долго рассуждает в пустоте и галлюцинирует) и «чистое действие» (модель кликает/бросает команды без осмысленного плана и тонет в шуме среды). Идея ReAct проста и мощна: чередовать мысль и шаг. Агент делает короткий вывод о следующем действии, целенаправленно вызывает инструмент (поиск, чтение файла, запуск тестов), смотрит на наблюдение, дописывает план и идёт дальше. Такое «шаг → обратная связь → правка плана» заземляет рассуждения на реальности, снижает галлюцинации и экономит бюджет: мы не гадаем вслепую, а задаём вопросы миру и опираемся на ответы [1].
Теоретически это можно рассматривать как приближение к управлению в частично наблюдаемой среде: мысли — это внутреннее состояние/план, действия — политика, наблюдения — обновление веры. Без наблюдений длинная цепочка мысли легко уезжает в фантазии; без мысли последовательность действий расползается в перебор. ReAct «сшивает» эти части и тем самым решает ключевую боль современных LLM-систем: как превратить генерацию текста в управляемую процедуру решения задачи. В исходной работе это проявилось на разнородных задачах — от фактологических вопросов до интерактивных сред: ReAct выигрывал у «CoT-только» и «Act-только» именно за счёт того, что каждый следующий шаг опирался на новое знание, добытое инструментом [1].
Почему мы опираемся на ReAct в нашей архитектуре? Потому что он естественно ложится на ACI (Agent–Computer Interface) и на мультиагентный дизайн. Внутренний цикл каждой роли — это ReAct: подумал → вызвал READ_FILE/EDIT_PATCH/RUN_TESTS → увидел логи → уточнил гипотезу. Это даёт нам:
устойчивость (каждый шаг проверяем и привязан к наблюдениям),
прозрачность (видно, почему агент сделал именно этот патч),
совместимость с более сильными приёмами — self-consistency, mini-MoA и repeated sampling (мы можем порождать несколько разумных «веток ReAct» и выбирать лучшую по тестам). В итоге ReAct — это не «ещё один трюк с промптом», а рабочая петля управления, на которой строятся и синхронный, и асинхронный режимы нашей системы. [1]
Self-Consistency — это способ заставить модель подумать несколькими путями, а не застревать в первом попавшемся рассуждении. Идея такая: рассуждения (chain-of-thought) — это скрытые «ветки» решения; если сэмплировать несколько независимых веток (разные температуры/сиды/переформулировки), а затем выбрать наиболее согласованный итог (частотное «большинство» по финальным ответам или их эквивалентностям), мы приближаемся к маргинализации по скрытым рассуждениям вместо рискованного выбора одной траектории. Это снимает две боли одиночного CoT: 1) хрупкость — одна локальная ошибка портит всю цепочку; 2) предвзятость детерминированного декодирования — модель «залипает» в один стиль ответа. В исходной работе показано, что такой «многоголосый» поиск повышает точность на арифметических, логических и здравосмысловых задачах именно за счёт mode-seeking эффекта: большинство веток сходятся к правильной гипотезе, и мы ловим этот «модальный» ответ [2].
Почему мы выбираем Self-Consistency в нашей системе? Потому что это дешёвый рычаг качества на этапе инференса: не требует дообучения, хорошо комбинируется с ReAct и ACI (каждая ветка — полноценный цикл «подумал→действовал→проверил»), а отбор победителя можно делать объективно — тестами/чек-листами, а не «LLM-судьёй». В мультиагентной постановке это ещё и естественный способ диверсифицировать «мнения» ролей: Analyst генерирует несколько планов, Fixer — несколько патчей, Controller выбирает best-of-N по вердикту тестов. Практический эффект — меньше галлюцинаций, выше калиброванность, лучше устойчивость к «случайным промахам» в одной ветке [2].
Какие проблемы закрывает на практике:
Случайные ошибки CoT. Несколько независимых ходов мысли уменьшают риск «одной фатальной опечатки» в длинной цепочке.
Декодинговые смещения. Вариативность (температура/переформулировки) разрушает эффект «первой доминанты» и позволяет выйти на более сильную моду распределения.
Отсутствие верификатора. Даже без жёстких тестов можно голосовать по финальным ответам; при наличии тестов — объединяем голосование с объективной проверкой.
Ограничения честно признаём: нужно достаточно разнообразия веток (иначе будет «массовое заблуждение»), и техника стоит вычислений — потому мы управляем числом попыток через бюджет. Но в сумме это один из самых выгодных «усилителей» качества на реальных задачах, особенно в связке с нашим ACI и контролируемым best-of-N. [2]
Процесс-супервизия (PRM) — это смена оптики: мы оцениваем не только конечный ответ, а каждый шаг рассуждения. Вместо «угадай финал правильно/неправильно» мы строим лестницу проверки, где каждый промежуточный шаг получает метку «корректен/некорректен/нейтрален». Теоретически это решает две фундаментальные боли outcome-подхода: (1) ложноположительные случаи, когда модель выходит на правильный финал через ошибочную логику (ORM это не видит), и (2) неинформативный градиент, когда из одного бита «верно/неверно» непонятно, где именно модель свернула не туда. В PRM информация локализуется: мы знаем первую ошибку, можем остановить разметку на ней, и по этой точке корректировать траекторию — это и дешевле по человеко-часам, и гораздо полезнее для обучения надёжных версификаторов и reward-моделей. [3]
Практика подтверждает: процессная разметка даёт более надёжные reward-модели, которые лучше ранжируют кандидатов в режиме best-of-N и устойчивее к «убедительным, но неверным» решениям. В работе вводится датасет PRM800K — ~800k шаговых меток (75k решений, 12k задач), собранный с активным отбором convincing wrong-answer траекторий: разметчики целево видят те решения, где PRM ещё ошибается, что резко повышает ценность каждой метки и снижает стоимость сбора (2.6× эффективность при активном обучении). На бенчмарке MATH процесс-супервизия даёт выигрыш над outcome-супервизией по всей шкале сложности задач, а агрегирование шаговых вероятностей в solution-score (произведение по шагам) показывает себя лучшей стратегией ранжирования кандидатов. Итог — выше точность best-of-N и более «ровная» деградация на сложных примерах, чем у ORM или простого majority voting. [3]
Почему мы опираемся на PRM в архитектуре? Потому что в реальных агентных сценариях (код, математика, планирование) успех определяется качеством отбора кандидатных решений и умением рано обнаруживать неверный поворот. PRM даёт шаговый «сигнал качества», который можно встроить в петлю ReAct: подумал → сделал (через ACI) → получил наблюдение → шагово проверил → поправил план. Это делает отбор объективным (по верификатору/PRM), снижает зависимость от «судьи-LLM» и прекрасно сочетается с repeated sampling и мини-MoA: больше диверсифицированных траекторий → PRM надёжнее находит «правильную» ветку. В сумме PRM — это не просто аккуратная разметка, а механизм управления поиском решений, который повышает переносимость и воспроизводимость результатов при масштабировании числа попыток. [3]
Отсюда рождаются два практических рычага, которые MAS используют системно:
Время на вывод (inference-time compute): больше проб и ветвей → выше шанс, что хотя бы один путь правильный (а значит, нужен хороший отборщик). 2) Множественность мнений: разнородные агенты/модели смотрят на задачу под разными углами, а агрегатор «собирает» ответ (Mixture‑of‑Agents) [5].
Современные архитектуры: от ансамблей к мультиагентам
(MoA)
Mixture-of-Agents (MoA) — это не просто «ансамбль ответов», а процедура коллективного мышления: на каждом слое несколько proposer-ов (часто с разными ролями, подсказками или даже моделями) независимо предлагают решения, а aggregator синтезирует итог — не выбирает «лучший абзац», а заново конструирует ответ, опираясь на аргументы, совпадения и расхождения между версиями [5]. Важный акцент — разнообразие источников: мы осознанно создаём ортогональные взгляды (температуры, промпты, модели, роли), чтобы покрыть «слепые зоны» одиночного ИИ и выйти из локальных минимумов, где один стиль рассуждения стабильно ошибается.
Чем MoA полезен на практике? Он решает три хронические проблемы одиночного агента:
Вариативность и хрупкость рассуждений. Несколько независимых траекторий уменьшают риск одной фатальной ошибки; агрегатор «сшивает» частичные успехи в цельный ответ.
Отсутствие сильного верификатора. Когда нет строгих тестов (эссе, объяснения, рефакторинг текста), агрегатор выступает «мягким критиком»: сверяет факты между версиями, требует обоснований, устраняет противоречия.
Покрытие пространства решений. Разнородные proposer-ы увеличивают шанс, что хотя бы одна ветка обнаружит редкую, но правильную идею; синтез даёт полноту ответа, а не просто «победителя голосования».
Эмпирически MoA даёт стабильные выигрыши на оценках вроде AlpacaEval/FLASK и сопоставимых бенчмарках открытых моделей, порой превосходя сильные одиночные модели — особенно по устойчивости и полноте длинных ответов [5]. Цена вопроса — латентность и бюджет (несколько веток + этап синтеза) и зависимость качества от умения агрегатора реально перерабатывать входы, а не слепо ранжировать. На практике это лечится: ограничивают глубину слоёв (1–2 дают основной прирост), добавляют анти-дубликаты и критерии качества (правила, чек-листы, PRM-сигналы), а также соединяют MoA с repeated sampling — получая структурированное, а не хаотическое «многоголосие». В сумме MoA — это «команда и редактор»: сначала собираем спектр разумных гипотез, затем умный редактор делает из них один согласованный и проверяемый ответ. [5]
Повышение отдачи от времени вывода
Идея проста: качество можно масштабировать не только «размером модели», но и вычислениями на этапе вывода. Мы рассматриваем инференс как управляемый бюджет — сколько попыток, какой ширины поиск, как глубоко «разворачивать» рассуждения — и подбираем конфигурацию, которая даёт лучшее качество за заданную цену. Теоретически это приводит к «законам масштабирования инференса»: если вероятность найти правильное решение за один прогон невысока, то при достаточно независимых попытках покрытие растёт примерно лог-линейно по числу проб, особенно там, где есть объективный вердиктор (юнит-тесты, пруверы). В таком режиме compute-optimal стратегия часто предпочитает больше попыток у «умеренной» модели вместо одной попытки «очень большой» — при том же бюджете итог точнее и устойчивее [9].
Что именно делает подход на практике? Он раздвигает «горлышко бутылки» одиночного прогона:
Диверсифицирует поиск. Мы намеренно порождаем несколько кандидатов (разные температуры/подсказки/ветвления) и отбираем лучший: best-of-N по тестам, взвешенное голосование, ранжирование верификатором.
Структурирует рассуждения. Вместо одной длинной цепочки — дерево поиска (ветвление по ключевым шагам, ранний отсев слабых веток), вплоть до процедур наподобие tree-of-thought/MCTS.
Распределяет бюджет адаптивно. «Лёгким» задачам — 1–2 прогона, «тяжёлым» — дополнительные попытки; ранние уверенные успехи останавливают поиск, экономя вычисления.
Какие проблемы это решает:
Хрупкость единичного тракта. Случайная ошибка в одной цепочке больше не фатальна — у нас есть альтернативы.
Длинный хвост задач. Редкие, но важные кейсы «вылавливаются» за счёт дополнительных веток.
Надёжность и калибровка. Объективный отбор по внешнему вердиктору снижает галлюцинации и делает ответы предсказуемее по качеству.
Где подводные камни: чем хуже верификатор и чем сильнее коррелированы кандидаты, тем быстрее иссякает отдача от «больше попыток»; растут латентность и стоимость. Поэтому в compute-optimal дизайне важно (а) добиваться разнообразия кандидатов (промпты, температуры, роли/модели), (б) использовать умный отборщик (шаговые сигналы качества, чек-листы, PRM), и (в) ограничивать ширину/глубину поиска до «сладкой точки» бюджета. В сумме это даёт лучшую «цена/качество» кривую на реальных задачах, чем догонять качество исключительно размером одной модели [9].
Агент ↔ компьютер: ACI и реальные среды
Agent-Computer Interface (ACI) — это не «ещё один набор команд», а контракт между мыслью агента и машиной. Мы сознательно формуем пространство действий: вместо бесконечного и шумного CLI/IDE агент получает конечный словарь атомарных операций с жёсткими типами и ожидаемой обратной связью: READ_FILE
, APPLY_UNIDIFF_PATCH
, RUN_TESTS
, ANALYZE_LOG
и т. д. Теоретически это — action-space shaping в духе управления в частично наблюдаемой среде (POMDP):
«Мысль» агента — план/состояние,
ACI-операция — действие,
лог/линт/тест-репорт — наблюдение,
а цикл «план → действие → наблюдение → корректировка» обеспечивает кредитное присвоение по шагам (понятно, где именно что-то пошло не так) и снижает галлюцинации, потому что каждый шаг заземлён на реальную обратную связь [4].
Какие проблемы это решает.
Шум и неоднозначность CLI/IDE. Свободный шелл выдаёт километры текста без структуры; ACI принуждает среду отвечать коротко и по схеме (статус, дифф, список упавших тестов), убирая «белый шум».
Хрупкость длинных траекторий. Атомарные, идемпотентные шаги с явным результатом позволяют рано ловить ошибки и чинить их, не срывая весь прогон.
Невоспроизводимость. Каждый шаг детерминирован и логируем: воспроизводимость трассы и разбор «почему так решил агент» становятся нормой.
Безопасность. Белые списки действий, песочницы и встроенные «ограды» (линт, dry-run, дифф-подтверждение) снижают риск разрушительных команд.
Стоимость/латентность. Когда обратная связь информативна (точные логи, локализация ошибок), агенту требуется меньше попыток и короче рассуждения для выхода на рабочий патч.
Дизайн-принципы ACI на практике.
Атомарность и типизация. Разделяем чтение, навигацию, правку и запуск тестов; правка — только unified-diff, чтобы легко валидировать и откатывать.
Насыщенная обратная связь. Вместо «команда завершена» — структурированный репорт: какие тесты упали, где стектрейс, что сказал линтер.
Реверсивность и валидации.
dry_run
, проверки формата патча, лимиты по размеру/времени, явные ошибки уровня интерфейса.Наблюдаемость. Трассировка шагов, сбор метрик по каждому действию — база для процесс-супервизии и лучшего отбора кандидатов.
В задачах уровня SWE-bench такая формализация особенно эффективна: агенту нужно точно править код и объективно проверять результат. Работа по SWE-agent показывает, что связка «правильно подобранный ACI + многораундовая стратегия» даёт заметный прирост по сравнению с «голым шеллом» и RAG-стратегиями: отдельное действие edit через атомарный дифф, автоматический линт и краткие тест-репорты ускоряют сходимость и повышают долю успешно закрытых задач [4]. Поэтому в нашей архитектуре ACI — несущий слой: на нём одинаково уверенно работают и ReAct-петли, и мультиагентная оркестрация, и repeated sampling — всё, что требует понятных шагов и честной, машинной проверки.[4]
Платформы агентов
Под «платформой агентов» понимаем не библиотеку промптов, а операционную среду для ИИ-исполнителей: изолированные песочницы (обычно Docker), стандартизованный реестр навыков (AgentSkills) с чёткими контрактами ввода/вывода, шину событий для координации и слой политик/безопасности (квоты CPU/IO/нетворка, белые списки действий, аудиторские логи). Такая архитектура решает сразу несколько хронических проблем:
— Невоспроизводимость и дрейф среды. Снимки образов, фиксированные версии зависимостей и детерминированные точки входа делают траектории действий повторяемыми «бит-в-бит».
— Шум и неоднозначность инструментов. Навыки упакованы в типизированные действия с предсказуемой обратной связью (код возврата, структурированные отчёты), что снижает «белый шум» CLI и помогает отладке.
— Безопасность и контроль риска. Контейнерная изоляция, capability-профили, лимиты ресурсов и журналирование каждой операции позволяют запускать агентов «над реальным компьютером» без страха «уронить» систему.
— Координация и делегация. Через шину событий агенты могут вызывать друг друга (делегировать подзадачи), а оркестратор применять правила вроде таймаутов, бюджетов и «best-of-N» отбора.
— Наблюдаемость и оценка. Трассировка сообщений/действий, сбор метрик и «чёрные ящики» (артефакты запусков) превращают агентный пайплайн в инженерный объект: его можно профилировать, сравнивать и улучшать.
В результате платформа становится «операционкой» для подходов из статьи: ReAct получает честные наблюдения, PRM — сигналы качества на каждом шаге, MoA — транспорт для многоголосия ролей, а repeated sampling — предсказуемую стоимость и управляемую параллельность. Пример такой системы — «реальные компьютерные агенты» с Docker-песочницами, каталогом AgentSkills и делегацией подзадач: они показывают, как собирать безопасные и воспроизводимые траектории в ОС/IDE и масштабировать мультиагентное взаимодействие без ручной магии [7].
Оценка и подводные камни
Оценивать софт-агентов сложнее, чем классические модели: они действуют в среде, где результат — не одна метрика, а цепочка шагов, побочных эффектов и «неожиданностей». Теоретически это ближе к оценке политик в стохастической среде: мы измеряем не только финальный «ответ», но и надёжность траектории, переносимость между окружениями и устойчивость к шуму. Отсюда — типовые риски:
Переобучение на бенчмарк. Агент «выучивает» валидатор/формат, а не решает задачу: утечки данных, запоминание патчей/логов, «подгонка промпта под датасет», детерминированные окружения, где повторяемость ≠ обобщение. [8]
LLM-судья и стилевые смещения. При текстовой экспертизе judge-LLM часто вознаграждает «уверенный стиль» и длину, а не корректность; возникает position/verbosity bias, что даёт ложные выигрыши в A/B. [10]
Хрупкость пайплайна. Окружение, токены, версии зависимостей, seed-ы — малейший дрейф меняет исход. Без строгой операционализации (контейнеры, фиксация версий, контроль источников данных) метрики «плавают». [8]
Нечестные прокси-метрики. «Количество пройденных шагов» или «средняя длина плана» не коррелируют с качеством; нужны end-to-end проверки, а не только локальные. [8]
Что с этим делать (рабочие принципы «робастной» оценки):
Скрытые/рандомизируемые тесты и окружения. Перемешивайте репозитории, коммиты и фикстуры; меняйте сиды и пути; подмешивайте «анти-копипаст» варианты. Успех = стабильность на нескольких разрозненных запусках. [8]
Только end-to-end. Применяйте патч, поднимайте окружение, гоняйте тесты «до/после», проверяйте сайд-эффекты. Финальная метрика — прохождение независимого набора тестов, а не «модель сказала, что ок». [8]
Минимизируйте LLM-судей. Если без них нельзя — делайте слепые парные сравнения, рандомизируйте порядок, используйте несколько судей и нормируйте по стилю/длине; фиксируйте промпты судьи заранее. [10]
Декадровка/де-контаминация. Сканируйте обучающие/подсказочные данные на совпадения с тестами; отчётно помечайте возможные пересечения и исключайте кейсы-двойники. [8]
Репликабельность как контракт. Контейнеры/образцы окружений, lock-файлы, фиксированные сиды, журналирование действий агента и вердиктов; публикуйте evaluation harness вместе с результатами и скриптами запуска. [8][10]
Сводные метрики + диаграммы ошибок. Помимо «% success» показывайте стоимость (вызовы, токены, время), доли фейлов по типам (локализация, патч, тест-раннер), доверительные интервалы; это защищает от «узких» улучшений. [10]
Главная мысль: хорошая оценка — это анти-хрупкий протокол, а не один лидерборд. Она отсеивает gains от «мелких хаков» (промпт-тюнинг под датасет, стилистические уловки под LLM-судью) и поощряет реальные улучшения: устойчивость к рандомизации, перенос между репами, честный e2e-профит. Именно такой дух задают SpecRover (робастные сценарии и анти-утечки) и практические гайды по «агентам, которые действительно имеют значение»: минимизируйте предвзятые прокси, фиксируйте протокол и делайте результаты воспроизводимыми третьей стороной. [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») и общаются через короткие сообщения‑артефакты.
Как система «решает проблему в реальности» (путь данных)
Загрузка кейса (
evaluation/test_cases
): исходник+тест/описание фейла.Планирование (
sync
илиasync
): формируется трасса шагов и бюджет.Действия по ACI: чтение/редактирование/запуск тестов/анализ логов (песочница). Именно здесь «чистота» интерфейсов критична: атомарные патчи с диффом и явная диагностика снижают галлюцинации [4].
Верификация (
evaluation/patching
, тест‑раннер): проходим/падаем → даём агенту наблюдение; для iter‑режима — best‑of‑N по вердиктору.Аналитика (
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].
Комментарии (6)
AleGen
10.09.2025 06:10Ничего не понятно, но очень интересно.
Но ничего не понятно.
Вопрос в практическом применении: как и насколько это улучшит результат по сравнению с запросом к LLM (когда мы пишем "сделай всё" подробно расписывая) в условном Курсоре, и не получится ли, что мы меняем шило на мыло? Чем это лучше, проще, удобнее и качественнее по результату?
И да, можно ли юзать и улучшить вывод сервисов с бесплатным или недорогим предоставлением доступа к моделям? Ведь основная проблема у всех - именно высокие цены при посредственном качестве.
Приведите примеры, пожалуйста.
Krios0
Это разбиение целого агента на множество, помогает лучше контролировать ход задачи, но мешает самому результату быть полноценным
uncia__poison
Именно. Лучше задать форму субъекта исходя из задачи. А не разбивать исполнителя на толпу подрядчиков.
Krios0
Пойду ещё дальше - нужно задать Себе такую форму, которую, отразив, ИИ перевёл бы в нужный результат.
uncia__poison
Сложно) проще использовать возможность конструирования роли. У меня есть неплохая техника.