Узнайте, как клиентские функции позволяют агенту делегировать локальные операции непосредственно коду на Dart, выполняемому на устройстве пользователя.
28 авг. 2026 г. · 5 мин чтения
Генеративный интерфейс (GenUI) меняет мой подход к разработке UI во Flutter. Вместо жесткого кодирования фиксированных экранов для каждого сценария, GenUI позволяет ИИ-агенту динамически создавать и настраивать компоненты пользовательского интерфейса во время выполнения. Используя JSON-сообщения Agent-to-User Interface (A2UI) и пакет genui, приложение на Flutter может «на лету» отрисовывать динамические карточки и поверхности, управляемые ИИ.
Однако, начав создавать реальные агентные приложения, я стал искать способы уменьшить задержки и снизить количество ошибок, совершаемых моделями. При работе с большими языковыми моделями (LLM) возникает соблазн полагаться на модель во всем. Но если просить LLM выполнять задачи, для которых она не обязательно предназначена (например, арифметические вычисления), это может увеличить задержку и создать больше возможностей для ошибок.
Именно здесь на помощь приходят клиентские функции A2UI.
В этой статье я расскажу, как клиентские функции позволяют агенту делегировать локальные операции непосредственно коду на Dart, выполняемому на устройстве пользователя, что сокращает количество обращений к серверу и обеспечивает более предсказуемые результаты в ваших приложениях на Flutter GenUI.
Что решают клиентские функции
Отправка «сырых» промптов туда и обратно в LLM для простых вычислений неэффективна: это увеличивает задержку и потребляет лишние токены. Вместо этого клиентские функции позволяют агенту вычислять такие значения, как стоимость ингредиентов, общая сумма налогов или конвертация единиц измерения, локально на устройстве.
Клиентские функции решают эту задачу, создавая четкое разделение труда:
- Клиент Flutter объявляет доступные клиентские функции в каталоге GenUI, информируя агента о том, какие локальные операции могут быть вызваны, какие параметры они ожидают и в каком формате возвращают результат.
- ИИ-агент решает, когда и где должен отображаться компонент, и выдает выражение A2UI, вызывающее клиентскую функцию с необходимыми аргументами (например, ID товара и количество).
- Клиент Flutter вычисляет выражение локально и выполняет математические операции синхронно в коде Dart, сразу же отображая на экране чистый, отформатированный результат.
Перекладывая вычисления на клиентское устройство, ваше приложение избегает ненужных сетевых накладных расходов, достигает единообразия форматирования и снижает общее потребление токенов.
dart
Давайте посмотрим, как я реализовал этот шаблон в примере приложения под названием Commis — интеллектуальном помощнике, созданном для коммерческих кухонь и кейтеринговых команд.
Анатомия класса CalculateCostFunction
При подготовке к предстоящему кейтеринговому мероприятию шеф-повар может задавать вопросы об ингредиентах, используемых в меню. Чтобы надежно рассчитывать и отображать стоимость ингредиентов, не дожидаясь ответов от сервера, я создал клиентскую функцию CalculateCostFunction.
В пакете genui синхронные клиентские функции расширяют SynchronousClientFunction. Вот полная реализация из файла client_functions.dart:
dart
Разберем ключевые части этого класса:
- name: Уникальный идентификатор (calculateCost), который ИИ-агент использует при генерации вызовов функций внутри полезной нагрузки A2UI.
- description: Краткое пояснение, отправляемое в LLM, чтобы она знала, когда и зачем вызывать эту функцию и какой формат вывода ожидать.
- returnType: Указывает тип данных, возвращаемый функцией (в данном случае строка).
- argumentSchema: Эта схема, созданная с помощью json_schema_builder, точно сообщает LLM, какие параметры требуются (ingredient_id и quantity).
- executeSync: Основной метод Dart, выполняемый на устройстве пользователя при отрисовке интерфейса. Он анализирует входящие аргументы, вызывает мой локальный CostService и возвращает отформатированную строку с ценой.
Если вы заметили, что этот шаблон очень похож на тот, что используется для записей в каталоге компонентов UI, вы абсолютно правы! Оба предоставляют метаданные для агента, которые он использует при рассуждении, в сочетании с логикой Dart, которая делает что-то полезное: либо создает виджеты, либо, как в данном случае, вычисляет значение.
Регистрация в каталоге и интеграция системного промпта
Чтобы агент знал о calculateCost, я регистрирую её в каталоге GenUI моего приложения.
При создании экземпляра Catalog я передаю CalculateCostFunction() в список функций вместе с моими компонентами UI:
dart
При инициализации сеанса общения PromptBuilder из пакета genui проверяет каталог и автоматически извлекает все объявления клиентских функций, включая их имена, описания и схемы в системный промпт, предоставляемый Gemini.
Благодаря этому, когда шеф-повар спрашивает о стоимости рецепта, Gemini не пытается угадать или вычислить общую сумму в долларах. Вместо этого он выдает сообщение A2UI, содержащее вызов calculateCost (в данном случае для цены трех банок фасоли):
json
Обратите внимание, как значение свойства call в сообщении A2UI совпадает с именем функции (calculateCost), зарегистрированной в каталоге. Когда SurfaceController получает это сообщение, он вычисляет calculateCost локально на устройстве с помощью executeSync и сразу же отображает на экране точную строку с ценой (например, $2.97).
Быстрая итерация разработки с помощью Flutter Hot Reload
Поскольку клиентские функции не заблокированы внутри бэкенд-микросервиса или облачной функции, работа с ними ощущается как обычный Dart.
Если я хочу обновить форматирование валюты (например, добавить логику оптовых скидок или изменить $4.50 на 4.50 USD), я могу просто отредактировать executeSync в Dart, сохранить файл и наблюдать, как Hot Reload обновляет мое приложение с новым результатом.
Резюме и следующие шаги
Готовы попробовать GenUI и клиентские функции в своих приложениях?
- Ознакомьтесь с официальным Codelab «Введение в GenUI», чтобы изучить основы генеративного интерфейса.
- Изучите пакет genui на pub.dev для получения подробной информации об API и определениях каталогов.
- Просмотрите репозиторий flutter/demos на GitHub, чтобы изучить полный исходный код Commis и другие примеры Dart GenUI.
Удачной разработки!









