AG UI Protocol

Событийный контракт между агентом и пользовательским приложением: агент во время работы эмитит поток событий примерно шестнадцати стандартных типов по любому транспорту, а интерфейс их отрисовывает. Существенно, что в протокол встроено то, что обычно достраивают сбоку: синхронизация состояния снимком и дельтой, остановка ради человека как исход прогона и ответ элементами интерфейса вместо прозы.

Событие как единица, а не запрос и ответ

Обычная интеграция агента в интерфейс — это вызов, который через какое-то время возвращает текст, плюс самодельный поток частичных кусков поверх него. AG-UI переворачивает: единицей общения является событие, а сообщение собирается из событий на стороне интерфейса.

Категории событий разведены по назначению: жизненный цикл прогона, текстовые сообщения, вызовы инструментов, состояние, активность, подагенты и специальные. Транспорт не фиксирован — годится всё, по чему события можно доставить: server-sent events, веб-сокеты, вебхуки.

Границы прогона обязательны. Прогон открывается RunStarted и закрывается ровно одним из двух — RunFinished при успехе либо RunError при отказе; шаги внутри (StepStarted / StepFinished) необязательны и могут повторяться. Обязательность границ — не формальность: она даёт интерфейсу точку, в которой можно включить индикатор, и точку, в которой его обязательно выключат, в том числе при отказе.

Состояние: снимок заменяет, дельта дополняет

Состояние агента синхронизируется парой событий с явно разными правилами применения:

  • StateSnapshot — полное состояние; интерфейс заменяет им своё представление, а не сливает с прежним;
  • StateDelta — частичное изменение, приезжающее как JSON Patch;
  • MessagesSnapshot — то же для истории сообщений.

Схема «редкий снимок, частые дельты» решает две задачи разом: снимок — точка синхронизации, возвращающая интерфейс к известному состоянию, дельты — дешёвый поток между снимками. Правило «снимок заменяет, а не сливается» здесь важнее, чем кажется: слияние снимка со старым состоянием оставило бы в интерфейсе поля, которых у агента больше нет, — и расхождение осталось бы навсегда, потому что следующие дельты его не тронут (State Centric Execution — то же различение канонического состояния и производного представления внутри системы).

Остановка ради человека — исход прогона, а не звонок из процедуры

Самое переносимое место протокола. RunFinished несёт необязательный исход в виде объединения вариантов, и один из вариантов — interrupt с непустым списком открытых прерываний. Возобновление устроено так: клиент начинает новый прогон, во входе которого лежит массив resume, адресующий каждое открытое прерывание.

Три следствия, каждое полезное само по себе:

  • ожидание человека — это завершение, а не зависший вызов. Прогон закрыт корректно, ресурсы освобождены, а ожидание живёт снаружи как состояние;
  • адресуется каждое открытое прерывание, а не «последнее», — то есть протокол допускает несколько одновременных вопросов и требует ответить на все;
  • старый производитель, ничего не знающий об исходах, остаётся совместимым: отсутствие поля трактуется как обычное завершение.

Это ровно та форма, которую Human in the Loop описывает как «подтверждение — хранимое состояние, а не звонок в середине процедуры», только зафиксированная в протоколе, а не оставленная на реализацию.

Метаданные приезжают позже того, к чему относятся

У всех событий общий набор полей: тип, необязательная отметка времени, rawEvent и metadata.

metadata — открытый по ключам объект, объявленный один раз на базовом событии, поэтому его несёт каждый тип. Потребитель сливает метаданные события в собираемое сообщение по ключам, и последняя запись побеждает. Назначение названо прямо и оно неочевидно: так производитель может прислать расход токенов на последнем событии сообщения, не зная его в начале. Приём общий — телеметрию, известную только по завершении, не приходится ни угадывать, ни слать отдельным каналом (Agent CostControl).

rawEvent хранит исходное событие, если оно было преобразовано по дороге. Это сохранение происхождения на транспортном уровне: перевод во внешний словарь не уничтожает оригинал, и разбор расхождения остаётся возможным (Agent Audit Log).

subagentRunId есть у большинства событий и указывает, какой подагент их произвёл; событие без него принадлежит родителю. То есть атрибуция вывода — часть протокола, а не догадка интерфейса по тексту (Multi Agent Patterns).

Отдельно — parentRunId у RunStarted: указатель на предыдущий прогон в том же потоке, дающий ветвление и перемещение по истории. Документация описывает получающуюся структуру как журнал, в который только дописывают (LangGraph Time Travel — тот же приём внутри графа).

Генеративный интерфейс

Последняя из встроенных возможностей: ответ приходит не только прозой, но и структурными сообщениями, из которых интерфейс собирает элементы — форму, карточку, таблицу, кнопку подтверждения. Прикладной смысл тот же, что у плотности подачи в ревью: элемент интерфейса передаёт то же самое быстрее, чем абзац текста, и, в отличие от абзаца, допускает действие в ответ (Parallel Agent Dispatch — ускорение восприятия результата как отдельный рычаг).

Цена — связность: набор элементов, которые агент может попросить нарисовать, становится частью контракта между ним и приложением, и менять его в одностороннем порядке нельзя.

Место в стеке протоколов

Четыре открытых протокола отвечают на четыре разных вопроса, и путать их не стоит:

Протокол Вопрос
MCP что агент умеет: инструменты, данные, контекст
Agent2Agent Protocol как агенты разговаривают друг с другом
Agent Client Protocol кто пускает агента в среду и опосредует его действия
AG-UI как то, что делает агент, попадает к человеку в интерфейс

Сами авторы AG-UI описывают своё место так же и называют протокол дополняющим, а не конкурирующим. Практически это подтверждается перечнем поддержанных каркасов: LangGraph, CrewAI, Mastra, Pydantic AI, LlamaIndex, Agno, Google ADK, Microsoft Agent Framework, AWS Strands.

Связано с

  • Agent Client Protocol — соседний слой: там среда и права, здесь интерфейс и человек
  • Agent Harness — тот же поток событий изнутри обвязки, включая приём «слой перевода вместо внутренностей наружу»
  • Human in the Loop — прерывание как состояние, а не как блокирующий вызов
  • State Centric Execution — снимок против дельты внутри системы, здесь то же на границе с интерфейсом
  • Multi Agent Patterns — атрибуция вывода подагенту, вынесенная в поле события
  • Agent Observability — почему поток, на котором держится интерфейс, нельзя выключить ради экономии
  • MCP, Agent2Agent Protocol — остальные слои стека