The Jerusalem PostIDF, Shin Bet arrest 10 suspects connected to attack on US journalist, EU diplomats in West BankPunchMorning recap: Wike meets Fubara, FG disputes World Bank debt figures, other top storiesCNN Türk19 EKİM HAVA DURUMU İSTANBUL SON DAKİKA! Bugün hava nasıl olacak, İstanbul’da şiddetli yağış olacak mı?InquirerChurch, youth groups push action vs political dynastiesBollywood HungamaTiger Shroff Shooting Incident: Mumbai Police register FIR against 3 unknown accused, form 15 teams to trace shootersESPNPackers use third-down conversions, takeaways to frustrate struggling CowboysESPN DeportesWatson y Browns escapan con la victoria divisional한겨레12년 만의 남북 탁구 대결, 이번에도 박홍규가 웃었다RapplerLazaro summons China ambassador Jing, says it’s not his place to taunt PH officialsZDF heuteEntdecken Sie das ZDF-Nachrichtenstudioالنهارماذا بعد ميزان المنازلة الأخيرة بين السّلطة و"حزب الله"؟The South AfricanDa Cruz calls for unity after Kaizer Chiefs’ loss in Zambia
The Daily Newsstand · Free, Always
Monday, October 19, 2026

AG-UI 1.0: у протокола наконец появилась спецификация. Разбираем, что изменилось, на связке AG2 + CopilotKit

Translate
AG-UI 1.0: что нового в протоколе

AG-UI 1.0: что нового в протоколе

Привет! Меня зовут Семён Фролов, я инженер в AG2.

15 октября Tesla показала Roadster, который умеет летать, и весь интернет разбирает эту презентацию по кадрам. А мы вместо этого рассмотрим не менее важную вещь: мажорный релиз AG-UI.

TL;DR:

  • 17 сентября вышли пакеты 1.0.0, 30 сентября CopilotKit объявил релиз;

  • из нового самое заметное: interrupts (агент ставит run на паузу и ждёт человека), субагенты, reasoning, usage и отмена run'а, мультимодальные результаты инструментов;

  • протокол стал строже: версионируется, а его поведение описано как нормативное (MUST/SHOULD), с JSON Schema, из которой генерируются SDK;

  • ниже на примере из ag2-samples смотрим, как это выглядит в живом приложении: AG2 на бэкенде, CopilotKit на фронте.

AG-UI в двух словах

Клиент шлёт один POST, сервер отвечает потоком SSE. Каждое событие в потоке - JSON с полем type. Вот так выглядит ответ, в котором агент вызвал инструмент и потом написал текст (JSON собран по схеме 1.0, значения условные):

Один run - один поток SSE: события run, tool call и message

Один run - один поток SSE: события run, tool call и message

Всё, что стримится (текст, аргументы тула, рассуждения), оформлено как START → CONTENT → END с идентификатором. Плюс есть события состояния (STATE_SNAPSHOT, STATE_DELTA) и служебные. Всего в 1.0 их 31. Агенту безразлично, что за интерфейс по ту сторону, а интерфейсу - на каком фреймворке написан агент.

Что нового в 1.0

Interrupts: агент ждёт человека

Раньше «агент просит подтверждения» каждый делал по-своему. Теперь это часть протокола: run заканчивается событием RUN_FINISHED, в котором outcome равен interrupt. Продолжается всё новым запросом на том же threadId, с ответом человека в поле resume.

Interrupt: пауза - это конец run'а, продолжение - новый run

Interrupt: пауза - это конец run'а, продолжение - новый run

Никакое соединение на время паузы держать не нужно: человек может подумать хоть час. Если interrupt'ов несколько, resume обязан ответить на каждый.

Субагенты

Если агент зовёт другого агента, интерфейс теперь видит это явно: SUBAGENT_STARTED, SUBAGENT_FINISHED, SUBAGENT_ERROR, а события субагента помечены subagentRunId. Можно показать «сейчас работает планировщик поездок», а не просто крутилку.

Reasoning вместо thinking

Старые THINKING_* заменены семейством REASONING_*. Появилось и событие для зашифрованных рассуждений, которые клиент не читает, а возвращает модели как есть.

Usage и отмена

В RUN_FINISHED теперь может приезжать расход токенов за run (с учётом субагентов), а отменённый пользователем run закрывается честно: outcome: cancelled, а не маскируется под успех.

Мультимодальность

Результат инструмента больше не обязан быть строкой: это может быть список частей, например текст плюс картинка по ссылке.

Протокол стал строже

  • События несут protocolVersion, и стороны могут понять, с кем разговаривают

  • Необязательное поле либо есть, либо его нет: null не допускается

  • Свои поля на события больше не вешаются, для этого есть metadata

  • Поведение (что обязано открываться, закрываться, как реагировать на неизвестное) описано как нормативное, а типы SDK генерируются из схемы.

Как это выглядит в AG2 + CopilotKit

В ag2-samples лежит «Weather AG UI»: агент погоды на AG2 и чат на Next.js с CopilotKit. В нём собраны почти все новые фичи

Архитектура примера ag2-samples: браузер, Next.js и FastAPI с AG2

Архитектура примера ag2-samples: браузер, Next.js и FastAPI с AG2

Бэкенд: AGUIStream вокруг агента

Агент оборачивается в AGUIStream, а дальше у вас два эндпоинта: GET отдаёт описание возможностей агента (capabilities), POST запускает run

@router.get("/weather")
async def capabilities() -> JSONResponse:
    return JSONResponse(stream.capabilities().model_dump(by_alias=True, exclude_none=True))

@router.post("/weather")
async def run(
    run_input: RunAgentInput,
    user: Annotated[User, Depends(authorised_user)],
    accept: Annotated[str | None, Header()] = None,
) -> StreamingResponse:
    events = stream.dispatch(
        run_input, 
        dependencies={"user": user, "http": http}, 
        config=config, accept=accept
    )
    return StreamingResponse(events)

dispatch сам переводит внутренние события агента в события AG-UI. Роут написан руками, потому что стандартный stream.build_asgi() не отдаёт заголовки запроса, а здесь из Authorization нужно достать пользователя и передать его в зависимости агента. Пользователь едет именно в dependencies, а не в state, потому что state клиент может перезаписать.

Субагенты: два агента как инструменты

agent = Agent(
    "WeatherAgent",
    prompt=PROMPT,
    config=CONFIG,
    tools=[
        get_coords_by_city,
        get_current_weather_by_coords,
        get_weather_next_week,
        save_favorite_city,
        clothing_advisor.as_tool(description="Suggest what to wear for the given weather. ..."),
        trip_planner.as_tool(description="Compare the weather of several cities and recommend where to go."),
    ],
)

Субагент подключается через .as_tool(), а фронт получает SUBAGENT_*-события, на которые подписана боковая панель:

const { agent } = useAgent({ agentId: "agenticChatAgent" });

agent.subscribe({
  onSubagentStartedEvent:  ({ event }) => setStarted((all) => [...all, event]),
  onSubagentFinishedEvent: ({ event }) => setFinished((all) => ({ ...all, [event.subagentRunId]: event })),
  onSubagentErrorEvent:    ({ event }) => setFailed((all) => ({ ...all, [event.subagentRunId]: event })),
});

Interrupt: подтверждение в одну строчку

На бэкенде инструмент помечается middleware:

@tool(
    description="Save a city to the user's favorites. The user is asked to confirm before anything is saved.",
    middleware=[ApprovalRequired("Save this city to your favorites?\n{tool_arguments}", allow_always=False)],
)
async def save_favorite_city(context: Context, city: ..., country: ... = "") -> list[str]:
    ...

На фронте один хук рисует кнопки:

useInterrupt({
  agentId: "agenticChatAgent",
  render: ({ interrupt, resolve }) => (
    <div>
      <p>{interrupt?.message ?? "The assistant needs your confirmation."}</p>
      <button onClick={() => resolve(true)}>Approve</button>
      <button onClick={() => resolve(false)}>Reject</button>
    </div>
  ),
});

Нажатие на «Approve» отправляет новый запрос с resume, и агент выполняет инструмент. Попробуйте в примере «Save Lisbon to my favorites».

Остальное на фронте

  • Generative UI. useRenderTool превращает результат инструмента в React-компонент, например, карточку погоды вместо текста. Имя должно совпадать с именем инструмента на бэкенде.

  • Shared state. Изменённые context.variables (избранные города, найденная локация) уезжают клиенту в STATE_SNAPSHOT, и панель читает их из agent.state.

  • Инструмент в браузере. На «погода здесь» агент вызывает getUserLocation, который через useFrontendTool берёт геолокацию браузера. Отказ пользователя возвращается как данные, а не исключение.

Итог

В 1.0 мне нравится, что вещи, которые каждый делал по-своему (подтверждение человеком, субагенты, отмена, расход токенов), стали частью протокола. Для интерфейса это значит меньше самописного клея, для агента - одно место, куда всё выводить.

Ссылки:

- Репозиторий примера ag2-samples https://github.com/ag2ai/ag2-samples

- Спецификация AG-UI 1.0 https://docs.ag-ui.com/spec/1.0

- AG2: AG-UI https://docs.ag2.ai/docs/user-guide/ag-ui/overview/

- CopilotKit: AG2 https://docs.copilotkit.ai/ag2

- Разбор миграции 0.x → 1.0 https://nerdleveltech.com/ag-ui-1-0-migration-tested

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.