Инструменты
Ассистент не обращается к базе данных или внешним системам напрямую. Вместо этого он работает через серверные инструменты. Дополнение включает набор предопределенных инструментов, а также позволяет добавлять собственные и переопределять существующие.
Предопределенные инструменты
В дополнение входят предопределенные инструменты, которые ассистент может вызывать от имени пользователя. Они сгруппированы по назначению, и каждая группа описана в отдельном разделе ниже. На данный момент дополнение предоставляет одну группу: инструменты Data Load.
Инструменты Data Load
Инструменты Data Load позволяют ассистенту отвечать на вопросы о данных приложения.
Процесс загрузки данных
Инструменты Data Load реализуют процесс загрузки данных по запросу на естественном языке. Когда пользователь запрашивает данные, ассистент обычно проходит следующие шаги:
-
Discover — получить список сущностей, доступных пользователю, и проанализировать нужные из них, включая атрибуты, связи и перечисления.
-
Generate — сформировать JPQL-запрос только для чтения с именованными параметрами.
-
Validate — проверить запрос по доменной модели и набору правил, включая режим только для чтения, синтаксис, известные сущности и атрибуты, поддерживаемые конструкции даты и времени и пагинацию.
-
Repair — если валидация не прошла, попросить модель исправить запрос и повторно его провалидировать, не более заданного числа попыток.
-
Execute — выполнить запрос через
DataManager, применить права доступа к сущностям и атрибутам и вернуть страницу строк.
Все шаги выполняются на сервере. Запрос всегда представляет собой SELECT только для чтения. Операции записи и обходы через native SQL отклоняются на этапе валидации, а при выполнении дополнительно применяются права текущего пользователя на чтение. Запрос к сущности, которую пользователь не может читать, будет отклонен, а атрибуты, недоступные для чтения, будут исключены из возвращаемых строк, так же как dataGrid скрывает колонки, привязанные к нечитаемым атрибутам. В обоих случаях пользователь не получает данные, к которым у него нет доступа.
Доступные инструменты
Этот процесс предоставляется модели в виде трех инструментов. Имя инструмента — это идентификатор, который модель использует для его вызова. Это же имя используется при переопределении инструмента.
| Имя инструмента | Назначение |
|---|---|
Возвращает краткие метаданные для каждой сущности, доступной пользователю в текущий момент: имя сущности, локализованные названия и имена свойств. Сущности, скрытые фильтрацией приложения или подсистемой безопасности, не возвращаются. Ассистент использует этот инструмент для анализа модели данных и получения корректных имен сущностей для последующих вызовов. |
|
Возвращает подробные метаданные для запрошенных сущностей: точные имена атрибутов, связи для |
|
Валидирует, при необходимости исправляет и выполняет JPQL-запрос только для чтения, возвращая результирующие строки. Параметры передаются как структурированный запрос, содержащий текст запроса, именованные параметры, имена результирующих колонок и информацию о пагинации. Результат содержит строки, признак наличия дополнительных строк, а также ошибки валидации или выполнения. |
Эти три инструмента объединены маркерным интерфейсом DataLoadAiTool, поэтому реестр может собирать их как одну группу. См. AiToolRegistry.
Управление доступной моделью данных
Инструменты Data Load работают в рамках прав текущего пользователя на доступ к данным. Они показывают ассистенту только сущности, которые пользователь может читать, и атрибуты, которые он может просматривать, а каждый запрос выполняется с теми же правами, как описано в разделе Процесс загрузки данных. Ни одна из описанных ниже настроек не позволит пользователю получить данные, к которым у него нет доступа.
Кроме того, по умолчанию инструменты Data Load скрывают от ассистента:
-
Сущности фреймворка, то есть сущности из пакетов
io.jmix. -
Сущности и атрибуты с аннотацией @SystemLevel. Сущность системного уровня скрыта полностью. Атрибут системного уровня ассистент не видит в описании модели данных, но сгенерированный запрос все равно может его использовать.
-
Сущности, которые не являются сущностями JPA, например сущности DTO.
-
Атрибуты, которые ссылаются на скрытую сущность, например атрибут
stepсущностиUserStep, если сущностьStepисключена.Начиная с Jmix 3.1
-
Атрибуты с аннотацией
@Secret, например пароль пользователя. Ассистент не может ни увидеть их, ни использовать в запросе, и никакая настройка этого не меняет.
Что видит ассистент, можно изменить:
-
exclude-entities и exclude-packages скрывают сущности по имени или по пакету.
-
include-entities и include-packages открывают доступ к сущностям, скрытым по умолчанию. При этом
exclude-entitiesимеет приоритет над ними. -
exclude-system-level-entities и exclude-system-level-attributes со значением
falseотключают скрытие сущностей и атрибутов системного уровня. -
Аннотация @ExcludeFromAi скрывает сущность или атрибут так, что никакое свойство не может открыть к ним доступ.
-
Собственный бин AvailableEntityFilter определяет, какие сущности и атрибуты ассистент получает для текущего пользователя.
Исключение данных с помощью @ExcludeFromAi
Начиная с Jmix 3.1
Свойства jmix.aitools.dataload.* можно изменить при развертывании приложения, например через переменную окружения. Если ассистент не должен видеть данные ни при каких условиях, например учетные или персональные, используйте аннотацию io.jmix.aitools.ExcludeFromAi. Она задается в коде сущности, и никакое свойство не может ее переопределить. Аннотация влияет только на инструменты Data Load. Экраны, REST API и другие части приложения работают с этими данными как раньше.
Аннотацию можно поместить на класс сущности или на атрибут:
-
На классе сущности она скрывает всю сущность. Инструменты Data Load не возвращают сущность, а валидация отклоняет сгенерированный запрос, который ее использует, как если бы сущности не существовало. Подклассы сущности тоже скрываются, а запросы к сущностям-предкам не возвращают ее записи.
-
На атрибуте она скрывает только этот атрибут. Инструменты Data Load не возвращают атрибут, валидация отклоняет сгенерированный запрос, который его использует, а возвращаемые строки никогда не содержат его значения.
В следующем примере ассистент может загружать пользователей, но не видит их адреса электронной почты:
@ExcludeFromAi
@Email
@Column(name = "EMAIL")
protected String email;
Аннотацию учитывают предопределенные инструменты Data Load и бин AvailableEntityService. Если собственный инструмент загружает данные через DataManager, он все равно может передать модели скрытые данные.
|
Кастомизация доступности
Набор сущностей, предоставляемых модели, определяется через бин AvailableEntityFilter. Реализация по умолчанию скрывает сущности, на чтение которых у текущего пользователя нет прав, и атрибуты, просмотр которых ему не разрешен. Чтобы изменить это поведение, например применить собственные правила видимости, зарегистрируйте свой бин, реализующий io.jmix.aitools.dataload.introspection.AvailableEntityFilter.
Настроенные сущности и их метаданные предоставляет бин AvailableEntityService, который можно использовать и в собственных инструментах. См. пример переопределения.
Пользовательские инструменты и реестр
Помимо предопределенных инструментов, можно добавлять ассистенту собственные инструменты и переопределять инструменты, поставляемые дополнением.
Создание инструмента
Инструмент — это Spring-бин, который:
-
реализует маркерный интерфейс
io.jmix.aitools.tool.JmixAiTool -
объявляет один или несколько методов с аннотацией Spring AI
@Tool
При запуске дополнение находит все такие бины, собирает их методы @Tool и делает их доступными ассистенту. Следующий бин добавляет инструмент, который возвращает каталог этапов онбординга:
@Component
public class OnboardingTools implements JmixAiTool { (1)
@Autowired
private DataManager dataManager;
@Autowired
private AiToolStatusPublisher statusPublisher;
@Tool(name = "getStepCatalog", (2)
description = "Returns the catalog of onboarding steps with their duration in days.")
public String getStepCatalog(ToolContext toolContext) { (3)
String message = "Loading the onboarding step catalog";
statusPublisher.update(message, toolContext); (4)
List<Step> steps = dataManager.load(Step.class).all().list(); (5)
statusPublisher.complete(message, steps.size() + " steps", toolContext);
return steps.stream()
.sorted(Comparator.comparing(Step::getSortValue))
.map(step -> step.getName() + " — " + step.getDuration() + " day(s)")
.collect(Collectors.joining("\n"));
}
}
| 1 | Реализация JmixAiTool помечает бин как источник инструментов. |
| 2 | Атрибуты name и description аннотации @Tool — это то, что видит модель. Описание должно объяснять, когда и как использовать инструмент. |
| 3 | Параметр ToolContext предоставляется фреймворком и не раскрывается модели. Он нужен только для публикации обновлений статуса. См. Публикация обновлений статуса. |
| 4 | Публикует обновление статуса о начале выполнения перед запуском длительной операции. |
| 5 | Загружает сущности Step через DataManager с учетом прав текущего пользователя на доступ к данным. |
Параметры метода становятся входной схемой инструмента. Чтобы добавить описания, аннотируйте их @ToolParam. Возвращаемое значение отправляется модели как результат работы инструмента.
Для объявления методов @Tool в classpath приложения должен присутствовать Model API из Spring AI. Стартер модели Spring AI, который вы добавляете в разделе Подключение модели, уже его предоставляет.
|
AiToolRegistry
io.jmix.aitools.tool.AiToolRegistry — центральный реестр всех инструментов, доступных в приложении. Он создается один раз при запуске на основе всех бинов JmixAiTool, после применения разрешения переопределений. Его методы:
| Метод | Описание |
|---|---|
|
Возвращает все итоговые инструменты в порядке регистрации. |
|
Возвращает инструмент, зарегистрированный под заданным именем, либо пустой результат. |
|
Возвращает инструменты, исходный бин которых реализует указанный маркерный подинтерфейс |
|
Возвращает объекты Spring AI |
Ассистент передает модели getAllCallbacks(), поэтому любой добавленный вами инструмент становится доступным автоматически. Больше ничего регистрировать не нужно.
Переопределение существующего инструмента
Чтобы заменить существующий инструмент, включая предопределенный, аннотируйте метод @Tool аннотацией @ToolOverride и передайте имя инструмента, который нужно заменить. Метод по-прежнему должен быть аннотирован @Tool и объявлен в бине JmixAiTool.
Следующий бин переопределяет aitls_getAvailableEntities и возвращает доступные сущности, отсортированные по их локализованному имени:
@Component
public class SortedEntitiesTool implements DataLoadAiTool { (1)
@Autowired
private AvailableEntityService availableEntityService;
@Tool(description = "Returns entities available to the user, ordered by localized name.")
@ToolOverride("aitls_getAvailableEntities") (2)
public List<EntitySummary> getAvailableEntities() {
return availableEntityService.getEntitySummaries().stream() (3)
.sorted(Comparator.comparing(this::firstLocalizedName))
.toList();
}
private String firstLocalizedName(EntitySummary summary) {
return summary.getLocalizedNames().isEmpty()
? summary.getEntityName()
: summary.getLocalizedNames().get(0);
}
}
| 1 | Реализация DataLoadAiTool оставляет переопределение в группе инструментов Data Load. Для инструмента, не связанного с загрузкой данных, реализуйте JmixAiTool напрямую. |
| 2 | @ToolOverride указывает имя заменяемого инструмента. Переопределение публикуется под этим именем, поэтому собственное имя @Tool у метода не имеет значения. |
| 3 | Повторно используется стандартный AvailableEntityService, поэтому переопределение по-прежнему учитывает фильтрацию сущностей и безопасность, меняя только порядок сортировки. |
Как работает разрешение переопределений:
-
Если несколько методов
@Toolсоздают инструмент с одним и тем же именем, выигрывает метод, помеченный@ToolOverride, а исходный инструмент исключается из реестра. -
Переопределение наследует маркерные интерфейсы заменяемого инструмента, поэтому поиск в реестре по маркеру продолжает работать.
-
Если указанного инструмента не существует, в лог пишется предупреждение, а переопределение регистрируется как обычный новый инструмент под собственным именем
@Tool. Если это имя уже используется другим инструментом, будет выброшено исключение, чтобы избежать неявной замены несвязанного инструмента.
Публикация обновлений статуса
Длительно выполняющиеся инструменты могут передавать прогресс в UI через бин io.jmix.aitools.tool.AiToolStatusPublisher, показанный в примере выше. Он реализует двухфазный контракт:
-
update(message, toolContext)— шаг начался, и UI показывает индикатор выполнения. -
complete(message, snippet, toolContext)— шаг завершился, и UI добавляет фрагмент результата в ту же запись. Значениеmessageдолжно совпадать с тем, которое было передано вupdate.
Оба метода принимают ToolContext метода инструмента. Когда инструмент вызывается вне UI чата, например через программный API, callback статуса отсутствует, и методы просто ничего не делают, поэтому вызывать их всегда безопасно.