Unreal Agent: архитектура async-first agent harness на Go

· 2 мин чтения
agent-harness async go open-source
📂 Исходный код на GitHub

Открытый async-first agent harness на Go: библиотека с coordinator, session store, LLM adapter, tool registry и operation manager, а также runner и benchmark components.

Unreal Agent: архитектура async-first agent harness на Go

Unreal Agent

Unreal Agent — это async-first agent harness от Unreal Labs, написанный на Go. Это не набор готовых prompts или отдельный UI, а архитектурная библиотека для агентного цикла: она разделяет управление сессией, работу с LLM, валидацию tool calls, durable operations и сборку контекста. В репозитории также есть исполняемый runner и benchmark runners, которые используют эту библиотеку.

Что лежит в репозитории

Структура проекта разделена на три основные части:

Репозиторий написан на Go и распространяется по лицензии MIT. Его README описывает проект как harness, а не как отдельный кодинг-агент с единственным пользовательским интерфейсом. Это важное различие: основная ценность проекта — в composable components и правилах, по которым из них собирается агентная система.

Что означает async-first

В README async-first выражен не через конкретный UI или отдельный фреймворк, а через разделение работы между event loop и durable execution. Tool translator работает synchronously на event loop coordinator: он проверяет tool call и превращает его в одну или несколько operations. При этом translator не должен выполнять I/O или приостанавливать event loop.

Вместо того чтобы выполнять длительную работу непосредственно внутри translator, он возвращает сериализуемое описание operation. Её состояние отслеживает отдельный operation manager. Такая схема позволяет агенту быстро зафиксировать принятое намерение, а выполнению — продолжаться через отдельный actor runtime. Именно это различие между переводом вызова и исполнением операции делает harness пригодным для подключения удалённого или изолированного execution.

Основные понятия

README использует несколько терминов, которые описывают границы между входом, сессией, моделью и инструментами:

  • Input — событие с глобально уникальным ID, который задаёт вызывающая сторона и сохраняется при повторной доставке.
  • Inbox — volatile, session-scoped deduplication внешних, control и crash inputs. Он живёт в памяти в пределах одной сессии.
  • Session — append-only история, сохранённая в хранилище и поддерживающая fork.
  • LLM turn — последовательность вокруг одного логического запроса к LLM, которой управляет coordinator.
  • Tool — capability, описанная schema и связанная с tool translator.
  • Tool call — запрос модели на использование tool.
  • Tool translator — компонент, который валидирует tool call и переводит его в operations или в ошибку валидации.
  • Tool call status — результат перевода: validation error или ссылки на отправленные operations. Состояние самих operations отслеживается отдельно.
  • Operation — сериализуемое описание работы, подготовленное tool translator для асинхронного выполнения.

Эти определения показывают, почему проект называется harness: он задаёт не одну большую функцию агента, а контракты между отдельными частями цикла. Для расширения доступны primitives, которые operation manager может использовать при реализации своих задач.

Компоненты harness

В README перечислены следующие компоненты и их зоны ответственности:

Компонент Ответственность
Session inbox Volatile, session-scoped input idempotency.
Coordinator Сохраняет принятые inputs, запускает LLM turns, находит tool translators в registry и отправляет committed operations.
Session store Сохраняет canonical session history и состояние operations, поддерживает recovery и forks, атомарно записывает tool call status вместе с operations.
Context builder Собирает model input в памяти и возвращает запись о том, что было опущено, усечено или compacted. Не выполняет I/O и не зависит от persistence.
LLM Adapter Отправляет подготовленный model input провайдеру и возвращает нормализованный completed response. Отвечает за authentication, cancellation и provider errors.
Tool registry Хранит фиксированные определения Bash, ViewImage и skill-use вместе с их translators и предоставляет выбранный host набор инструментов.
Tool translator Валидирует tool call, создаёт status и operations, а затем формирует model-facing result из записанного status и результатов подготовленных operations. Не выполняет I/O.
Operation manager Actor runtime для durable operations; локальную реализацию можно заменить.

Coordinator выступает центральным оркестратором, но не пытается самостоятельно закрыть все интеграции. LLM Adapter скрывает authentication, cancellation и ошибки provider. Context builder отвечает только за представление входа модели, а persistence и I/O остаются за пределами его responsibilities. Такое разделение уменьшает число мест, где одновременно нужно учитывать состояние сессии, model input и внешний мир.

Как проходит один агентный цикл

Описанную архитектуру можно представить в виде последовательности из нескольких этапов:

  1. Внешний или control input получает глобально уникальный ID и проходит через session inbox, который отсекает повторную доставку в пределах сессии.
  2. Coordinator сохраняет принятый input и запускает LLM turn.
  3. Context builder собирает model input в памяти и отмечает всё, что было опущено, усечено или compacted.
  4. LLM Adapter отправляет подготовленный input провайдеру и возвращает нормализованный ответ.
  5. Если модель вызвала tool, coordinator находит соответствующий translator в tool registry.
  6. Translator синхронно проверяет вызов и создаёт tool call status с operations. Он не выполняет I/O и не блокирует event loop.
  7. Operation manager принимает durable operation и отслеживает её состояние отдельно от результата tool call.
  8. После подготовки результата операции translator формирует model-facing result, который coordinator передаёт в следующий цикл.

Важно, что status tool call и состояние operation — разные сущности. Status отвечает на вопрос, что произошло при переводе запроса: была ли ошибка валидации или какие operations были отправлены. Состояние operation отвечает на вопрос, что в итоге произошло с самой работой. Такое разделение оставляет место для recovery, повторной обработки и внешнего execution без смешивания двух состояний в один результат.

Сессии и контекст

Session store отвечает не только за append-only историю. Он также хранит состояние operations, поддерживает recovery и forks и атомарно фиксирует tool call status вместе с operations. Это делает состояние сессии частью протокола агентного цикла, а не просто журналом сообщений.

Context builder устроен иначе. Он statefully собирает вход модели в памяти и возвращает не только сам input, но и запись обо всём, что пришлось опустить, truncate или compact. У него нет I/O и dependencies на persistence. Таким образом, решение о том, как модель увидит контекст, отделено от способа хранения сессии.

Расширение harness

README прямо призывает делать компоненты composable и использовать alternative implementations их interfaces. У проекта заявлены следующие invariants:

  • элементы session store должны быть serializable, а формат хранения — versioned;
  • обратная совместимость сессий поддерживается по возможности, но unsupported session version всегда приводит к явной ошибке при resume;
  • operations должны быть versioned и всегда serializable.

Один из описанных в README сценариев — proxy operations manager. Он может отправлять сериализованные operations локальному operations manager, который работает в отдельном процессе внутри remote sandbox. Это не обязательная схема, а пример того, почему границы между coordinator, translator и operation manager полезны: исполнение можно унести туда, где оно действительно нужно, не переписывая весь агентный цикл.

Tool registry содержит фиксированный набор Bash, ViewImage и skill-use, но предоставляет host-selected set. Это разделение позволяет host выбирать доступный набор инструментов, сохраняя единый механизм registry и translators.

Формат проекта и статус

Модуль проекта указывает Go 1.27.0. В репозитории есть Makefile с командами build, test и check, а также Dockerfile, который собирает unreal-agent-runner и готовит рабочий образ с /workspace и /state. Эти файлы показывают, что проект рассчитан не только на чтение архитектурных идей, но и на запуск отдельного runner в контейнерной среде.

Авторы CONTRIBUTING.md уточняют, что репозиторий содержит selected components из более крупной внутренней кодовой базы. Команда собирается делиться дополнительными компонентами, features и tools по мере оценки их надёжности, token usage и влияния на поведение агента. Pull requests сейчас фактически не принимаются из-за ограниченной возможности команды, хотя issues для вопросов, feature requests и bug reports приветствуются.

Unreal Agent интересен прежде всего как компактная архитектурная база для async agent runtime. Он не пытается заменить модель или отдельный tool; его задача — показать, как соединить вход, session state, LLM turns, tool translation и durable operations в один расширяемый протокол.

Источник: https://github.com/unreallabsai/unreal-agent