Умные приложения

Как разрешить агентам генерировать интерфейс в приложениях Android с помощью A2UI

Колонки

О приложении

О выступлении

A2UI переходит от веб-компонентов к нативным компонентам Android. Новое средство отрисовки Jetpack Compose для A2UI версии 0.9 позволяет разработчикам отрисовывать динамический интерфейс, созданный LLM, нативно. Вместо компонентов WebView в нем используется фреймворк Actor на основе сопрограмм, жизненные циклы запечатанных классов и нативная интеграция Material 3.

На этом занятии мы рассмотрим новую архитектуру, объясним, как деревья компонентов, управляемые сервером, преобразуются в реактивные узлы Compose и как встроенные переходы макета плавно обрабатывают структурные обновления, не блокируя пользовательский интерфейс.

Сводка

  • A2UI предоставляет декларативный открытый протокол JSON, который позволяет агентам описывать динамические пользовательские интерфейсы вместо текстовых ответов.
  • Протокол основан на каталогах, которые определяют разрешенные компоненты и функции, а также на фрагментах интерфейса, управляемых агентом.
  • Библиотека A2UI в Jetpack Compose выполняет нативный рендеринг моделей потоковых поверхностей, используя компоненты Material 3 со встроенными специальными возможностями и прогрессивным рендерингом.
  • Модели данных в памяти мгновенно обновляют связанные элементы интерфейса без обращения к агенту, а исходящие события передают действия пользователя обратно.
  • Разработчики могут настраивать существующие элементы каталога или создавать собственные компоненты, чтобы строго контролировать, что может отображать агент.

Ресурсы

Gemini Notebook

Темы

  • a2ui
  • generative-ui
  • jetpack-compose
  • android
  • ai-agents
  • material-design

Расшифровка

Введение в генеративный интерфейс и A2UI

  • Здравствуйте! Сегодня мы поговорим о том, как генерировать интерфейс в приложениях для Android с помощью нашей новой библиотеки A2UI.

  • Меня зовут Ник Бутчер, я менеджер по продукту Jetpack Compose.

  • Всем привет! Меня зовут Антон, я инженер в команде Compose.

  • Я работаю над новым рендерером Jetpack Compose A2UI, о котором мы сегодня расскажем.

  • Да, сейчас, когда возможности ИИ растут, многие разработчики приложений ищут способы использовать его, чтобы улучшить свои продукты или создать новые.

  • Один из интересных вариантов, который сейчас рассматривают, – это генеративный интерфейс. В этом случае агентные бэкенды могут создавать совершенно новые интерфейсы для пользователей во время выполнения.

  • Но, к сожалению, сейчас ответы многих агентных бэкендов заперты в стенах текста, как здесь.

  • Вот пример, в котором я попросил агента создать новую функцию для бронирования поездки.

  • Вот запрос.

  • Как видите, ответ вполне приличный, но он заперт в этой стене текста, с которой мне не очень удобно взаимодействовать, и он совсем не похож на остальную часть моего приложения.

  • Генеративный пользовательский интерфейс позволяет создавать новые интерфейсы на лету в ответ на динамический ввод, открывая возможности для большей настройки или новых возможностей.

  • Вот пример пользовательского интерфейса, созданного с использованием стандартных нативных компонентов.

  • Так что вы можете создать такое приложение уже сегодня, но вам придется самостоятельно определить формат обмена данными с сервером, поток событий, функции и т. д.

  • A2UI (Agent-to-UI) – это открытый протокол в формате JSON, который позволяет определять все эти элементы.

  • Мне нравится, что A2UI – это декларативный формат.

  • Агент просто возвращает JSON-представление интерфейса для отрисовки.

  • Он не возвращает код, который клиент должен выполнить, что может привести к уязвимостям в системе безопасности.

  • Это просто декларативный подход.

  • Итак, какие ещё концепции нам нужно знать об A2UI?

Основные понятия: каталоги и платформы

  • Вам нужно знать всего два понятия: каталоги и платформы.

  • В каталоге есть такие компоненты, как текст, кнопка, строка и столбец, которые агент может использовать для создания интерфейса.

  • В каталоге также есть функции, которые агент может использовать для форматирования текста, проверки пользовательского ввода или открытия URL.

  • Каталог – это контракт, который определяет, какие компоненты и функции поддерживает ваше приложение. Агент может использовать только их.

  • Это означает, что, как упомянул Ник, агент никогда не выполняет произвольный код в вашем приложении.

  • Он создает пользовательский интерфейс из разработанных вами и надежных строительных блоков.

  • Ещё одно важное понятие – поверхность.

  • Поверхность – это часть интерфейса, которой управляет агент, например планировщик поездок на слайде.

  • Агент описывает интерфейс для поверхности в формате JSON, используя сообщения протокола A2UI, такие как createSurface, updateComponents, а также компоненты и функции, определенные в вашем каталоге.

  • Каталог – это фундаментальная концепция A2UI, поскольку он определяет, какие компоненты агент может использовать для создания интерфейса.

  • В спецификации A2UI определен так называемый базовый каталог – набор общих компонентов, которые можно использовать для создания большинства пользовательских интерфейсов.

  • Рендерер Jetpack Compose A2UI предоставляет реализацию базового каталога с использованием компонентов Material 3.

  • Большинство компонентов каталога готовы к использованию сразу после установки.

  • В шаблоне есть три медиакомпонента: изображение, видео и аудиопроигрыватель. Для каждого из них нужно указать библиотеку.

  • Например, вы можете выбрать Coil для загрузки изображений и Media3 для видео- и аудиоплеера.

  • Но главное преимущество A2UI и нашей новой библиотеки рендеринга заключается в том, что вы можете создавать собственные компоненты и каталоги и полностью контролировать, что агент может использовать для создания пользовательского интерфейса.

  • К каталогам мы вернемся позже.

  • Давайте посмотрим, как это выглядит.

  • Давайте переключимся на демонстрационный компьютер.

  • Это наше демоприложение, которое мы будем использовать. Мы создадим средство визуализации A2UI, чтобы показать, насколько это просто.

  • Это набор запросов, которые можно отправить агенту. Они показаны на чипах в середине экрана.

  • Если мы нажмем на одну из подсказок, она будет отправлена реальному агенту. Скрестим пальцы, чтобы все прошло хорошо. Кроме того, мы добавим некоторые системные инструкции.

  • Итак, мы сообщаем серверной части о протоколе A2UI, о каталоге компонентов, в данном случае о базовом каталоге, который мы используем прямо здесь, который он может использовать для создания пользовательского интерфейса, и мы даем ему несколько примеров того, как это сделать.

  • Реализация сеанса чата в нашем демоприложении уже умеет извлекать необработанные сообщения A2UI в формате JSON из ответов агента.

  • В этом случае агент ответил текстом, за которым следовали сообщения протокола A2UI: createSurface, updateComponents.

  • Но в данный момент наше приложение просто показывает этот необработанный JSON и пока не отображает его как пользовательский интерфейс.

  • Прежде чем мы перейдем к реализации кода, давайте на секунду вернемся к слайдам и поговорим о дизайне системы, который мы будем использовать для реализации.

  • О, вот и оно.

Архитектура приложения и обработка сообщений

  • Да, вот план.

  • Итак, ИИ-агент передает ответ в наше приложение, которое получает класс ChatSession.

  • ChatSession извлекает необработанные сообщения JSON A2UI из ответов агента по мере их поступления и передает их обработчику сообщений A2UI.

  • Обработчик сообщений настроен с помощью синтаксического анализатора, который преобразует необработанные сообщения JSON в семантическое представление.

  • Затем обработчик проверяет сообщения на соответствие определению каталога и сопоставляет их с набором моделей интерфейса.

  • Модели поверхности определяют, как выглядит пользовательский интерфейс и какими его частями управляет агент.

  • Затем эти модели поверхностей отображаются как нативный интерфейс Compose с помощью composable-функции поверхности A2UI, предоставляемой нашей библиотекой.

  • Обмен данными между агентом и интерфейсом двунаправленный, поэтому, когда пользователь взаимодействует с интерфейсом, действия пользователя и ошибки передаются обратно агенту.

  • Итак, у нас есть три основных компонента: каталог, обработчик сообщений и платформа.

  • Давайте напишем для него код.

  • Да, давайте построим его, если сможем переключить машины.

  • Итак, самый первый шаг – добавление зависимости.

  • Как видите, мы уже добавили зависимость Material 3 A2UI и синхронизировали проект, чтобы не ждать Gradle.

  • Это зависимость верхнего уровня, которая определяет реализацию базового каталога с использованием этих компонентов Material 3, но под ней есть и другие, более детализированные зависимости.

  • Если вы не хотите использовать базовый каталог (почему – мы расскажем позже), вы можете использовать более детализированные.

  • Но это приведет к тому, что остальные будут подтянуты транзитивно.

  • Окей.

  • Итак, мы сказали, что есть три вещи.

  • Первым шагом было определение каталога.

  • Итак, в нашем приложении мы собираемся создать фабричную функцию, которая поможет создать экземпляр этой реализации Material 3 базового каталога.

  • Поэтому мы используем здесь эту библиотечную функцию, базовый каталог материалов A2UI, который также имеет версию.

  • Как мы уже говорили, в нем есть все необходимые компоненты, но мы не хотим заставлять вас использовать определенные библиотеки для загрузки изображений или рендеринга медиаконтента, поэтому вам нужно предоставить их самостоятельно.

  • Например, Антон уже создал обертку вокруг composable-функции AsyncImage из библиотеки Coil, которую мы будем использовать для загрузки изображений, а также добавил немного логики для поиска изображений в Википедии.

  • И поэтому мы можем очень легко подключить это к компонентам каталога.

  • Аналогично, у нас есть похожие реализации для рендеринга, видео и аудио, где мы будем использовать библиотеку Media3 для их рендеринга с помощью ExoPlayer.

  • Вот так просто мы можем подключить к нему наши собственные компоненты.

  • Следующее, что нужно каталогу, это набор стандартных функций.

  • Итак, это функции, которые может предоставить клиент.

  • Мы поставляем реализацию этого вместе с базовым каталогом, но нам нужно предоставить ему контекст, который вы будете использовать для выполнения определенных действий, например открытия URI, если пользователь нажимает на элемент.

  • Вот и всё, первый шаг сделан.

  • Мы создали каталог.

  • Следующим шагом, который мы должны сделать, была настройка сообщения, процессора.

  • Итак, это то, что на самом деле анализирует ответы и так далее.

  • Итак, мы переключимся на модель представления нашего приложения, где мы сначала создадим экземпляр каталога, который мы настроили ранее, потому что он нам понадобится.

  • Затем мы создадим синтаксический анализатор сообщений, объект обработчика сообщений, которому нужен доступ к каталогу, чтобы проверять ответы на соответствие компонентам, которые мы хотим использовать.

  • Ещё один компонент, который нам нужно создать, – это синтаксический анализатор, который фактически является объектом, ответственным за синтаксический анализ сообщений JSON по мере их возврата.

  • Итак, здесь мы создаем объект ChatSession.

  • Как я уже говорил, ChatSession извлекает необработанные сообщения A2UI из ответов агента.

  • Когда новое сообщение A2UI готово, вызывается обратный вызов onA2uiMessage.

  • Поэтому нам нужно лишь передавать каждое из этих сообщений обработчику по мере их поступления.

  • Мы вызываем функцию processInput, передаем ей синтаксический анализатор и сообщение.

  • Таким образом, функция processInput анализирует каждое сообщение, а затем ставит в очередь работу для процессора, чтобы проверить его по каталогу и обновить модели поверхности.

  • Таким образом, процессор предоставляет текущий набор активных моделей поверхности в виде activeSurfaces StateFlow.

  • В ChatSession есть логика, которая добавляет модели Surface в состояния пользовательского интерфейса ответов агента для рендеринга.

  • Поэтому мы просто передаем этот activeSurfaces StateFlow в качестве параметра в ChatSession.

  • Как уже упоминалось, функция processInput ставит задачи в очередь для процессора.

  • Но чтобы фактически начать обработку, нам нужно запустить сопрограмму в фоновом потоке.

  • Поэтому мы будем использовать для этого область модели представления и установим диспетчер по умолчанию.

  • И в этой сопрограмме нам просто нужно вызвать processor.collectMessages.

  • Пока эта сопрограмма активна, процессор будет обрабатывать сообщения из очереди.

  • Итак, ещё одна вещь, которую нам нужно сделать, это когда пользователь очищает историю чата, нам также нужно очистить текущие поверхности.

  • Поэтому мы вызываем clearSurfaces.

Отрисовка моделей поверхностей в интерфейсе

  • Окей.

  • Итак, мы выполнили первые два шага.

  • Мы создали каталог, мы создали процессор для анализа сообщений.

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

  • Поэтому мы собираем эти поверхности по мере их анализа синтаксическим анализатором.

  • Теперь в нашем пользовательском интерфейсе мы передадим их в этот объект ответа агента и просто переберем их.

  • Затем мы используем composable-функцию A2UI и передадим ей каждую из моделей, чтобы отобразить контент в нужном месте.

  • Обратите внимание, что агент может использовать список, если ему нужно показать большой объем контента.

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

  • Итак, мы реализовали базовый рендерер A2UI.

  • Теперь, если мы вернемся в приложение, перезапустим его и снова введем запрос, агент должен использовать этот инструмент, чтобы, да, вот, отобразить настоящий интерфейс.

  • Поэтому мы просто пишем код в прямом эфире.

  • Это не просто иллюзия.

  • Но да, он использует реальные изображения.

  • Оно использует вкладки, диалоги, окна выбора даты, ползунки – все стандартные компоненты Material, которые вы бы использовали для создания приложения, а не просто показывает текстовый ответ.

  • Поскольку это реальное приложение, использующее реальные компоненты Material, оно делает то, что делают реальные компоненты.

  • Оно работает так же, как нативное.

  • Например, он реагирует на тёмную тему.

  • Это компоненты Material.

  • Поэтому, если у вас есть внешняя composable-функция тематического оформления Material Design, в которой вы настроили цвета, формы, типографику и т. д., эти компоненты подхватят их, чтобы они выглядели так же, как и остальная часть вашего приложения.

  • Как упоминал Ник, поскольку это нативные компоненты Compose, они имеют нативную семантику.

  • Это означает, что поддерживаются все встроенные специальные возможности Compose.

  • Поэтому такие функции, как TalkBack, масштабирование шрифта и навигация с помощью клавиатуры, работают по умолчанию.

  • Поскольку вы вызываете агента для создания нового пользовательского интерфейса во время выполнения, может возникнуть некоторая задержка.

  • Возможно, вам придется подождать ответа специалиста.

  • Библиотека делает все возможное, чтобы минимизировать эту задержку.

  • Поэтому мы добавим задержки, чтобы показать вам, как мы минимизируем это.

  • Да, поскольку агенту может потребоваться некоторое время, чтобы передать полный ответ, мы используем так называемый прогрессивный рендеринг.

  • Поэтому, как только поступает корневой компонент, мы начинаем отображать пользовательский интерфейс для поверхности.

  • Все компоненты контейнера показывают плейсхолдеры загрузки для своих дочерних элементов, если они ещё не загрузились.

  • Когда дочерние компоненты прибывают, они анимируются с использованием схемы движения Material 3.

  • Окей.

  • Давайте перейдем к следующему запросу, чтобы показать ещё одну функцию библиотеки.

  • Вот ещё один запрос, в котором мы бронируем столик на ужин.

  • А здесь агент выбирает компонент ползунка. Да, он его использовал.

  • Как видите, вы можете перетаскивать элементы и взаимодействовать с интерфейсом, который агент создал на лету.

  • Обратите внимание, что меняется не только число в компоненте, но и строка формата ниже, где оно подсчитывается.

  • Надеюсь, для этого не нужно отправлять запрос агенту туда и обратно, чтобы обновить его, верно?

  • Это двусторонняя привязка.

  • Таким образом, компонент ползунка привязан к модели данных в памяти этой поверхности.

  • Все компоненты, связанные с одним и тем же путем в модели данных, обновляются автоматически при изменении значения.

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

  • То есть все это происходит локально, без обращения к агенту.

  • Но мы также можем попросить агента обновить данные.

  • Пусть будет 6 человек.

  • Обратите внимание на ползунок, когда агент отвечает.

  • Теперь их шесть.

  • Давайте посмотрим, что мы получили от агента.

  • Вместо того чтобы отправлять полное описание интерфейса, агент отправил одно небольшое сообщение updateDataModel, которое обновило путь для ползунка.

  • Поскольку ползунок привязан к этому пути, он обновился на месте.

  • Таким образом, модель данных каждой поверхности построена на состоянии снимка Compose, а каждое свойство компонента, привязанное к пути, является производным состоянием.

  • Поэтому всякий раз, когда пользователь или агент обновляет значение в модели данных, все компоненты, привязанные к одному и тому же значению, автоматически перекомпоновываются.

  • Окей.

  • Допустим, мы хотим забронировать столик. Что произойдет, если мы нажмем кнопку "Забронировать"?

  • Ничего не происходит.

  • Кнопка отправляет действие, но мы пока не передаем его агенту.

  • Давайте это исправим.

Обработка действий пользователей и потоков событий

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

  • Для этого мы используем поток outboundEvents, который предоставляет процессор, и собираем данные.

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

  • В этом примере мы отправляем все действия обратно агенту, чтобы он решил, что делать дальше. Но вы также можете решить, вызывать ли собственный бэкенд или перейти на другой экран в приложении.

  • Антон подключил его, и теперь, когда мы нажимаем кнопку подтверждения бронирования, это действие отправляется обратно агенту.

  • И здесь, вы знаете, вы могли бы на бэкэнде даже решить вызвать некоторые функции.

  • Давайте посмотрим, что мы отправляем агенту.

  • Это сообщение о действии A2UI.

  • У него есть контекст, который содержит набор свойств, запрошенных агентом для отправки при выполнении этого действия.

  • Когда я нажимаю кнопку, эти свойства определяются на основе текущих значений в модели данных, и именно это мы отправляем агенту.

  • Пока что агент был довольно полезен, но мы говорим об ИИ, а ИИ может ошибаться.

  • Давайте посмотрим, что произойдет, если агент вернет ошибку в интерфейсе, который он нам описывает.

  • В последнем запросе мы просим агента допустить ошибку, чтобы показать вам, как это работает.

  • Посмотрим, что произойдет.

  • Да.

  • Как видите, один из компонентов помечен как ошибочный.

  • Теперь все исправлено.

  • Это цикл самокоррекции агента.

  • Прежде чем каждый компонент достигнет пользовательского интерфейса, мы проверяем это сообщение на соответствие схеме каталога.

  • Если компонент не соответствует схеме, он помечается как ошибка.

  • При базовой реализации каталога ошибка будет показана только в том компоненте, в котором она возникла, а остальной интерфейс будет отображаться.

  • При этом средство визуализации отправляет агенту сообщение об ошибке, чтобы сообщить, что пошло не так.

  • В этом случае мы отправили сообщение о том, что тип компонента неизвестен и не найден в каталоге.

  • Затем агент мог отправить ответ, который обновлял существующий интерфейс, и интерфейс фиксировался на месте.

  • Да.

  • В текущей реализации базового каталога Material мы сразу показываем ошибки, но в вашем пользовательском компоненте вы можете решить продолжать показывать индикатор загрузки для компонента, если есть ошибка, ожидая, пока агент ее исправит.

  • Таким образом, ошибка может быть полностью скрыта от пользователя.

Создание собственных компонентов и расширение каталогов

  • Что касается каталогов и пользовательских компонентов, то до сих пор мы использовали только базовый каталог, который поставляется в комплекте, но вы, конечно, можете настроить его в соответствии со своими потребностями.

  • Итак, вы видите, что когда мы используем фабричную функцию для создания каталога, они являются значениями по умолчанию для всех различных компонентов.

  • Поэтому вы можете легко подключить собственные компоненты, если захотите.

  • Например, если мы хотим подключить нашу собственную систему проектирования в нашем приложении и его собственную кнопку, мы можем легко это сделать.

  • Поэтому мы просто переопределяем этот параметр при создании экземпляра каталога и создаем это определение компонента.

  • Если мы создадим новый класс для реализации этого, вы увидите, что здесь есть интерфейс, базовый каталог A2UI кнопки типа.

  • Поэтому, если мы реализуем это, мы можем подключить наш собственный компонент.

  • Да.

  • Поскольку мы переопределяем компонент в базовом каталоге, нам нужно реализовать этот интерфейс кнопки базового каталога A2UI.

  • Таким образом, этот интерфейс уже содержит некоторую информацию о компоненте, такую как имя, описание и схема свойства.

  • Поэтому всё, что нам нужно сделать, чтобы реализовать собственную версию, – это реализовать эту composable-функцию.

  • Он принимает несколько параметров.

  • Таким образом, эти параметры – значения, полученные от агента после проверки.

  • Это такие вещи, как идентификатор дочернего элемента, который ссылается на компонент, который будет содержимым кнопки.

  • У него также есть вариант, который описывает тип кнопки, определение действия и некоторые другие параметры.

  • В рабочей реализации кнопки нам нужно будет использовать все эти параметры, но для демонстрации нам нужны только дочерний элемент и действие.

  • Давайте реализуем эту composable-функцию.

  • Итак, в этой composable-функции мы собираемся создать собственный тип кнопки "Нравится".

  • Как видите, мы можем использовать параметры, которые были предоставлены и проверены A2UI.

  • Например, в функции onClick мы можем отправить это действие.

  • Мы можем использовать этот тип библиотеки DispatchAction и передать ему действие, определенное A2UI.

  • Мы используем собственную кнопку, но хотим отображать дочерний контент, ярлык или что-то ещё, что решил использовать агент.

  • Для этого мы используем функцию observeA2uiComponentState и передаем ей childId, чтобы получить дескриптор дочернего контента, который должен быть отрисован.

  • Затем мы используем composable-функцию A2uiComponent и передадим ей состояние дочернего компонента для отрисовки.

  • Здесь вы видите, что дочерний контент отображается, только если он загружен успешно. Но вы можете добавить собственную логику для случаев, когда контент загружается или возникает ошибка.

  • Если мы вернемся к одному из примеров запросов в нашем демо-приложении и заменим кнопку стандартной кнопкой Material.

  • Надеюсь, вы видите, как это красиво.

  • Как видите, здесь мы также используем настраиваемую кнопку.

  • Отлично, теперь вы знаете, как заменить некоторые типы библиотеки. Но что, если вы хотите расширить библиотеку собственными типами?

  • Это можно сделать очень просто, добавив их в сам каталог.

  • Поэтому Антон создаст новый класс, чтобы реализовать компонент оценки.

  • Итак, мы собираемся добавить в наше приложение что-то, что позволит нам оценивать рестораны, пока мы находимся в этой гипотетической поездке.

  • Поскольку мы не заменяем существующий компонент, мы используем более общий интерфейс, интерфейс компонента A2UI, в котором, как вы видите, есть несколько параметров, которые нам нужно переопределить.

  • Итак, первые два — это название и описание.

  • Итак, это описывает, что представляет собой наш компонент и что он делает.

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

  • Поэтому вам нужно быть достаточно описательным и сказать ИИ, что вы точно знаете, что это делает.

  • Следующее свойство, которое нам нужно переопределить, – это свойство properties, которое в основном определяет, с какими данными работает этот компонент.

  • Итак, мы делаем панель со звёздным рейтингом, чтобы вы могли оценить ресторан, и нам понадобятся два свойства.

  • Это будет значение, например, сколько звезд выбрал пользователь, которое будет изменяемым свойством, с которым пользователь может взаимодействовать и изменять, и максимальное количество звезд.

  • Итак, мы видим, что Антон использует это динамическое свойство числового типа для значения.

  • Это количество выбранных звезд.

  • У него тоже есть описание, которое объясняет агенту, для чего нужно это значение.

  • Затем у нас есть свойство статического типа, поэтому мы просто используем это свойство A2UI типа number, чтобы определить максимальное количество звезд, которое может показать этот компонент.

  • После того, как мы определили эти два свойства, нам просто нужно их вернуть.

  • Итак, переопределяя это свойство properties, мы создадим список и вернем его.

  • Так вы сообщаете агенту, с чем работает этот компонент.

  • Да.

  • Как упомянул Ник, эти определения свойств отправляются в составе схемы каталога, чтобы агент знал, как использовать этот компонент.

  • Но это свойство, те же определения свойств также используются для извлечения типизированных значений во время выполнения.

  • Таким образом, у вас будет единый источник достоверных данных.

  • Давайте посмотрим, что нужно сделать, чтобы реализовать это.

  • Итак, во-первых, чтобы получить доступ, скажем, к свойству max, поскольку свойство max является статическим свойством, это означает, что агент отправляет его значение встроенным в объект свойств.

  • Чтобы получить доступ к нему, нам просто нужно получить его из объекта свойств, сославшись на него, используя объект свойства proper-max в качестве ключа.

  • Поскольку это свойство определено как число, нам также необходимо преобразовать его в int и установить значение по умолчанию.

  • Следующий элемент – свойство value.

  • Поскольку свойство value является динамическим, оно ссылается на что-то в модели данных.

  • Чтобы получить доступ к нему, нужно использовать API properties.bind и передать объект свойства в качестве ключа как параметр.

  • Таким образом, он возвращает текущее значение, хранящееся в модели данных для этого свойства, а также подписывается на будущие обновления.

  • Поэтому при каждом обновлении модели данных этот компонент будет автоматически перекомпоновываться.

  • Аналогично, мы также преобразовали в int и установили некоторые значения по умолчанию.

  • Ещё нам нужно настроить двустороннюю привязку, чтобы можно было менять оценку.

  • Для этого мы используем API bindUpdater.

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

  • Теперь мы готовы выпустить наш компонент оценки.

  • Окей.

  • Теперь, когда мы определили собственный компонент, нам нужно добавить его в каталог, который мы используем.

  • Раньше мы использовали базовый каталог как есть.

  • Теперь нам нужно создать новый каталог, который будет содержать все элементы базового каталога и наш собственный компонент.

  • Итак, каталог состоит из трёх вещей.

  • Это идентификатор, набор компонентов и набор функций.

  • Для идентификатора мы будем использовать URI, чтобы определить, как вы знаете, уникально идентифицирует каталог, а также версию, с которой мы работаем.

  • Затем мы возьмем все компоненты из базового каталога и добавим к ним созданный нами компонент.

  • А для функций мы просто будем использовать функции из базового каталога.

  • Таким образом, мы расширили базовый каталог, добавив в него новый тип пользовательского компонента.

  • Как видите, создать собственный каталог довольно просто.

  • Агент узнает, как использовать каталог, из системных инструкций, которые мы ему отправляем.

  • Поскольку мы создали собственный каталог, давайте также обновим системные инструкции.

  • Поэтому мы передадим объект каталога в нашу функцию, которая создает системные инструкции.

  • Вот и всё.

  • До сих пор мы отправляли статическое определение схемы для базового каталога, поскольку использовали стандартный каталог.

  • Теперь, когда у нас есть собственный каталог, мы можем динамически генерировать схему с помощью функции toJsonSchemaString.

  • Это создаст полное представление JSON этого каталога, включая компонент оценки, который мы только что добавили.

  • Системные инструкции также включают примеры ответов для агента.

  • Поэтому мы также меняем здесь идентификатор каталога, чтобы примеры ссылались на правильный каталог.

  • Окей.

  • Таким образом, мы расширили базовый каталог, добавив собственные типы.

  • Итак, если мы повторно развернем это и попробуем одну из этих подсказок, которая, надеюсь, даст нам пользовательский интерфейс рейтинга.

  • Затем агент отвечает.

  • И здесь вы можете видеть, что этот компонент звезды – это совершенно новый компонент, который агент никогда не видел, пока мы его не реализовали прямо сейчас.

  • Это не часть базового каталога, мы только что создали это.

  • Когда я нажимаю на звездочку, вызывается лямбда-функция bindUpdater и значение обновляется в модели данных.

  • Если мы отправим оценки, то сможем посмотреть действие, и в контексте действия будут значения, которые мы только что установили.

  • Хорошо, теперь мы можем вернуться к слайдам.

  • Мы быстро рассмотрели много информации, но, надеемся, смогли показать, как легко перейти от агентов, отвечающих стеной текста, к созданию реальных нативных интерфейсов.

Основные выводы и ресурсы

  • Надеемся, мы показали вам, что интеграция довольно проста.

  • Вам нужно выполнить три основных действия: создать каталог компонентов, реализовать логику синтаксического анализа, а затем отобразить его в пользовательском интерфейсе.

  • Надеемся, мы смогли показать, что библиотека делает за вас много работы, например прогрессивный рендеринг, проверку схемы, циклы самокоррекции и обработку ошибок.

  • Надеемся, что сегодня мы смогли быстро продемонстрировать, насколько легко его настроить.

  • Мы показали, как базовый каталог уже реагирует на ваше тематическое оформление Material Design, если вы используете Material Design, но расширить или заменить компоненты в каталоге действительно просто.

  • Но я думаю, что наибольшую пользу вы получите, если создадите собственный каталог.

  • Если вы хотите полностью контролировать, какие элементы интерфейса может использовать агент, вы можете создать полностью настраиваемые компоненты.

  • Если вы хотите дать ему, знаете, полную свободу действий, вы можете дать ему очень простые компоненты, такие как текстовые метки и кнопки, и сказать: "Вы создаете пользовательский интерфейс".

  • Если вы хотите больше контроля, вы можете создать более сложные компоненты и сказать: "Это единственное, что вы можете использовать".

  • Таким образом, вы контролируете, что агент может делать и показывать пользователям.

  • Это новая библиотека рендеринга A2UI и библиотека Jetpack, которые мы только что запустили.

    implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")
  • Сейчас она находится на этапе альфа-тестирования, и недавно мы выпустили версию Alpha 02.

  • Перейдите по ссылкам ниже, чтобы узнать больше.

  • На сайте developer.android.com есть документация и несколько примеров.

  • У нас есть базовый пример, показывающий базовый каталог, а также более сложный пример и пример приложения JetPacker, которое мы только что создали. Вы снова увидите его в сегодняшнем докладе ADS, демонстрирующем, как можно создавать более сложные компоненты для управления тем, что видит пользователь.

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

  • Поэтому, пожалуйста, загляните в библиотеку и оставьте отзыв.

  • И вот что у нас получилось.

  • И мне не терпится увидеть, что вы и ваши агенты создадите с его помощью.

  • Спасибо.

  • Спасибо!

Код

Фрагменты кода

Фрагмент Kotlin Script на 27:40 в разделе "Основные выводы и ресурсы"
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")