Разбираем OpenRig: собираем Claude Code и Codex в одну команду с помощью YAML
OpenRig — это открытый harness для нескольких агентов: топология команды описывается в YAML-спецификации RigSpec, а такие сессии, как Claude Code и Codex, объединяются в одну команду. На этой неделе проект попал в GitHub trending, набрав 2,028 звёзд, а общее число звёзд приближается к 7,000. Текущая версия — 0.6.10, лицензия Apache-2.
Разбираем инженерный подход OpenRig: команда из нескольких агентов описывается в YAML, а задачи передаются через очередь, а не через историю чата. Этот подход можно перенести на собственную оркестрацию нескольких агентов.
OpenRig — это открытый мультиагентный harness, репозиторий создан 1 апреля, за эту неделю он набрал 2,028 звёзд на GitHub trending, всего почти 7,000, текущая версия 0.6.10, лицензия Apache-2. Первая фраза в README: «A harness wraps a model. A rig wraps your harnesses.» Claude Code и Codex — каждый сам по себе harness, оборачивающий модель, а OpenRig оборачивает несколько harness в одну команду. Он не меняет ни одного агента: каждый агент остаётся обычной сессией Claude Code или Codex и работает в tmux, к которому вы можете подключиться в любой момент. OpenRig добавляет четыре вещи: топологию команды, описанную в YAML, механизм доставки сообщений в терминал, очередь с учётом принадлежности задач и снимок, восстанавливающий команду в исходном виде после перезапуска. В этой статье я разбираю его исходники по этим четырём частям, а в конце перечисляю изменения конфигурации, о которых нужно знать перед установкой.
Самое интересное в этом проекте — ответ на вопрос, за счёт чего несколько агентов взаимодействуют друг с другом.
Распространённые мультиагентные решения заставляют агентов переписываться между собой и передавать задачи прямо в диалоге. Встроенные правила команды в OpenRig явно это запрещают: «Do not use chat text as the work record». Сообщения лишь будят адресата, а сама задача записывается в очередь SQLite — с принадлежностью, статусом и историей передачи; демон каждые 5 минуты сканирует очередь, находит записи, которые взяли, но не сделали, или сдали, но никто не принял, и создаёт новые записи, возвращая их ответственному агенту. Топология команды тоже лежит в файле: кто кому выдаёт работу и кто чей вывод видит, записано рядом в YAML, а не в промпте какого-то агента.
Контекст агента сжимается, сессия обрывается, процесс перезапускается — обещания из диалога могут пропасть в любой момент. OpenRig выносит всё, что должно пережить эти обрывы, за пределы агента, и это условие того, что команда способна работать непрерывно долгое время.
Определяем команду одним YAML: группы, роли и пять видов связей
Команда описывается одним YAML под названием RigSpec. На верхнем уровне — rig, внутри него несколько pod, в каждом pod — несколько member. Каждый member объявляет, какой runtime использует (
claude-code, codex, pi и т. д.), на какой AgentSpec ссылается (skill, guidance, hook роли) и в каком каталоге работает. После запуска каждый member становится seat с адресом вида dev-build@starter: pod — это dev, участник — build, а rig, в котором он находится, — starter.
Связи между участниками выражаются рёбрами, всего в схеме их пять:
| Ребро | Смысл | Задаёт ли порядок запуска |
|---|---|---|
delegates_to |
Источник выдаёт работу цели | Да |
spawned_by |
Источник поднимается целью, цель стартует первой | Да |
can_observe |
Источник может просматривать вывод цели | Нет |
collaborates_with |
Равноправное сотрудничество | Нет |
escalates_to |
Источник эскалирует решение цели | Нет |
Встроенная команда factory — законченный пример: в pod оркестрации есть lead (Codex) и advisor (Claude Code), в pod разработки — build (Claude Code), qa (Codex), design (Claude Code), в pod ревью — r1 (Claude Code) и r2 (Codex). У lead есть рёбра delegates_to ко всем остальным пяти seat; у двух ревьюеров только рёбра can_observe — они видят вывод build, design и qa, но никаких отношений выдачи работы у них нет, и внутри pod ревью рёбер тоже нет.
Такой подход превращает организационную структуру в файл, который можно ревьюить и диффить. Изменить команду — значит изменить YAML: rig grow и rig shrink можно добавлять и удалять на ходу.
Стартовая команда: Claude Code пишет, Codex ревьюит
Минимальная встроенная команда starter состоит всего из двух seat'ов: build запускает Claude Code, review — Codex, а одно ребро delegates_to ведёт от build к review. Правила команды (CULTURE.md) требуют, чтобы builder после первого осмысленного изменения отдал «точный diff и обещанное поведение» на независимую проверку в review и передал через rig queue handoff путь к репозиторию, кандидатный commit, прогнанные проверки и известные ограничения.
factory продолжает ту же схему: build — это Claude Code, qa — Codex; два независимых ревью проводят Claude Code и Codex. В README об этом сказано только «Claude Code and Codex in the same rig», без объяснения причин. Но по конфигурации встроенной команды видно, что написание и проверку намеренно развели по моделям разных вендоров — так ревью не разделяет с реализацией одни и те же слепые зоны. Одна и та же модель, проверяя свой код, склонна соглашаться с собственными рассуждениями; другая семья моделей приносит в процесс ревьюера с другими обучающими данными и предпочтениями.
Пользователям с подпиской только у одного вендора это тоже подходит. После установки kernel operator переписывает стартовую команду в одновендорную версию и сохраняет её под тем же именем.
Доставка сообщений: вставить в панель tmux и нажать Enter
Раз агент — это неизменённые сессии Claude Code и Codex, у OpenRig есть только один способ отправить им сообщение: вставить текст в панель tmux и нажать Enter. Именно так работает
rig send, реализованный в session-transport.ts, строка 2,236.
Такой длинный код в основном решает одну задачу: действительно ли в поле ввода лежит именно это сообщение, когда нажимается Enter. Если целевой агент как раз стоит на диалоге подтверждения прав, один Enter означает, что за пользователя одобрили операцию. Поэтому перед доставкой демон считывает содержимое панели и определяет состояние собеседника:
- Свободен: доставляем сразу.
- Работает: всё равно доставляем, но с предупреждением. В комментарии к коду прямо сказано «busy is NOT a block»: ранние версии отказывались доставлять, потом это заменили на предупреждение — нельзя блокировать общение в ожидании, пока собеседник освободится.
- Стоит на выборе варианта или подтверждении прав: единственный жёсткий отказ.
Перед нажатием Enter есть ещё одна сверка: смотрим только последнюю строку ввода ❯; строки с нумерованными вариантами считаем диалогом; после вставки текст сворачивается в плейсхолдер [Pasted text #N +X lines], и число строк должно совпадать с сообщением — если плейсхолдеров два, отказываем. Ещё одна деталь — сводка в сообщении-пробуждении очереди: её пишет другой агент, и она попадает в чужой терминал, поэтому все управляющие символы, переводы строк и символы нулевой ширины заменяются пробелами, чтобы сводка не подделала строки «From:» и «↩ Reply» в конверте сообщения.
Это самая инженерная часть OpenRig. Терминал как интерфейс между агентами хорош тем, что не зависит от API конкретного вендора, но за это приходится самому разбираться со всеми неоднозначностями терминала.
Очередь — это и есть запись работы
Сообщения только будят, а сама работа фиксируется в очереди. У каждой записи очереди есть исходный seat, целевой seat, статус и цепочка передачи; всего восемь статусов, из которых продолжаются
pending, in-progress и blocked, нормально завершаются done и handed-off, а есть ещё failed, denied и canceled. Получив задачу, seat сначала делает claim, а закончив — handoff следующему seat'у; при передаче создаётся новая запись и фиксируются handed_off_from и handed_off_to. Правила стартовой команды требуют, чтобы агент при каждом новом входе сначала читал очередь и артефакты проекта, не повторял уже сделанные изменения и не поднимал seat заново.
Другая половина очереди — постоянный сканер в демоне queue-stuck-sweep. По умолчанию он запускается каждые 300 секунд и ищет шесть видов застревания, включая «взяли, но не двигают», «отправили, но не доставили» и «больше 60 минут никто не забрал». Найдя проблему, он не кричит в терминал, а пишет новую запись в очередь ответственному seat'у и дедуплицирует по «застрявшая запись + тип проблемы»: на одну проблему будет только одно незакрытое напоминание. Раньше этот сканер был двумя командами, которые нужно было не забыть запустить; в комментарии к исходникам объяснено, почему его сделали постоянным циклом: если полагаться на человеческую память, задачи будут тихо застревать.
Статус blocked обрабатывается отдельно. Задачи, ждущие решения человека или завершения другой записи, остаются в blocked, и сканер считает это законным ожиданием и не тормошит их повторно.
Адрес seat'а не меняется, сессия может меняться
В README seat определён как «устойчивая роль и адрес, диалог на котором можно заменить, а контекст идентичности и конфигурации сохраняется». rig down --snapshot хранит всю топологию, rig up <name> восстанавливает по имени, и каждый seat получает однозначный результат: прежняя сессия восстановлена, новая сессия со стартовым контекстом, прежнюю сессию восстановить нельзя и нужен выбор пользователя, нужно ручное вмешательство, ошибка. Кроме того, каждый member может объявить в YAML restore_policy — один из трёх вариантов: восстанавливать, если возможно; всегда открывать новую; сохранять только чекпойнты.
Это тот же подход, что и с очередью. Диалог может оборваться в любой момент, поэтому команда держится на адресе и очереди: сообщение отправляется на dev-build@starter, задача фиксируется в записи, адресованной этому адресу, и какой бы сессией адрес ни сменился, цепочка передачи не порвётся.
Какие изменения конфигурации нужно знать перед установкой
OpenRig меняет локальную конфигурацию; в README этому посвящён целый раздел, и перед установкой рекомендуется его прочитать и сделать бэкап:
| Объект | Изменение |
|---|---|
| Claude Code | Запись метки доверия к рабочей области и отметки о завершении onboarding; в проекте в .claude/settings.local.json добавляются statusLine и hook активности; общие настройки ресурсов устанавливают permissions.defaultMode в acceptEdits |
| Codex | Запись ~/.codex/config.toml: включаем hooks, добавляем команду отчёта об активности и заранее записываем хэш доверия; рабочая область помечается как trusted |
| skills | При запуске daemon в ~/.claude/skills и ~/.agents/skills засеваются три skill |
| tmux | rig setup записывает в ~/.tmux.conf фрагмент конфигурации |
По умолчанию права урезаны: обычный seat в команде без подтверждения выполняет только команды rig, чтение проекта и стандартные тесты, а такие команды жизненного цикла, как rig up и rig down, по-прежнему требуют подтверждения; YOLO по умолчанию выключен. Исключение — kernel rig, управляющий всем инстансом: его seat Codex по умолчанию запускается с -s danger-full-access -a never. Отчёт об активности передаёт только тип события, идентификатор seat и метку времени, без текста промпта и параметров инструментов. В README также указано, что эти записи не дают полной гарантии отката.
Что взять на вооружение, если строишь мультиагентную систему сам
| Ваш случай | Что можно позаимствовать |
|---|---|
| Уже запускаете Claude Code и Codex одновременно в нескольких терминалах | Сначала дайте каждому сеансу фиксированный адрес и роль, а уже потом говорите об оркестрации |
| Агенты передают задачи друг другу через чат | Перенесите задачи в хранилище вне агентов, а сообщения используйте только для пробуждения |
| Боитесь, что задача тихо застрянет | Вместо «не забудь проверить» — постоянный сканер: нашёл проблему, оформил новую задачу и вернул её ответственному |
| Хотите, чтобы агенты ревьюили друг друга | Для написания и проверки берите модели разных вендоров, а проверяющей стороне давайте только право наблюдать, без права раздавать задачи |
| Нужно автоматически отправлять сообщения в терминалы агентов | Перед нажатием Enter убедитесь, что в поле ввода именно ваше сообщение; в окне запроса прав никогда не жмите Enter |
| Один человек, один репозиторий, разовое небольшое изменение | OpenRig не нужен, хватит одного сеанса с subagent |
Ценность OpenRig не в том, чтобы сделать агентов умнее, а в том, чтобы команда могла продолжить работу после разрыва сеанса, сжатия контекста или перезагрузки машины. Основные инженерные усилия уходят на то, что находится вне агентов: очереди, сканер, снимки состояния и 2,000 строк кода, которые следят, чтобы Enter был нажат в нужном месте.
Ссылки
- Репозиторий OpenRig: github.com/mvschwarz/o…
- Документация RigSpec: github.com/mvschwarz/o…
- Сборник практик по harness-инженерии: github.com/androidZzT/…
Источник: Juejin · ИИ-программирование · juejin.cn