Инструменты

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

Предопределенные инструменты

В дополнение входят предопределенные инструменты, которые ассистент может вызывать от имени пользователя. Они сгруппированы по назначению, и каждая группа описана в отдельном разделе ниже. На данный момент дополнение предоставляет одну группу: инструменты Data Load.

Инструменты Data Load

Инструменты Data Load позволяют ассистенту отвечать на вопросы о данных приложения.

Процесс загрузки данных

Инструменты Data Load реализуют процесс загрузки данных по запросу на естественном языке. Когда пользователь запрашивает данные, ассистент обычно проходит следующие шаги:

  1. Discover — получить список сущностей, доступных пользователю, и проанализировать нужные из них, включая атрибуты, связи и перечисления.

  2. Generate — сформировать JPQL-запрос только для чтения с именованными параметрами.

  3. Validate — проверить запрос по доменной модели и набору правил, включая режим только для чтения, синтаксис, известные сущности и атрибуты, поддерживаемые конструкции даты и времени и пагинацию.

  4. Repair — если валидация не прошла, попросить модель исправить запрос и повторно его провалидировать, не более заданного числа попыток.

  5. Execute — выполнить запрос через DataManager, применить права доступа к сущностям и атрибутам и вернуть страницу строк.

Все шаги выполняются на сервере. Запрос всегда представляет собой SELECT только для чтения. Операции записи и обходы через native SQL отклоняются на этапе валидации, а при выполнении дополнительно применяются права текущего пользователя на чтение. Запрос к сущности, которую пользователь не может читать, будет отклонен, а атрибуты, недоступные для чтения, будут исключены из возвращаемых строк, так же как dataGrid скрывает колонки, привязанные к нечитаемым атрибутам. В обоих случаях пользователь не получает данные, к которым у него нет доступа.

Доступные инструменты

Этот процесс предоставляется модели в виде трех инструментов. Имя инструмента — это идентификатор, который модель использует для его вызова. Это же имя используется при переопределении инструмента.

Имя инструмента Назначение

aitls_getAvailableEntities

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

aitls_getDomainModelForEntities

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

aitls_executeQuery

Валидирует, при необходимости исправляет и выполняет 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, после применения разрешения переопределений. Его методы:

Метод Описание

getAll()

Возвращает все итоговые инструменты в порядке регистрации.

findByName(String name)

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

findByMarker(Class<? extends JmixAiTool> marker)

Возвращает инструменты, исходный бин которых реализует указанный маркерный подинтерфейс JmixAiTool, например DataLoadAiTool.class.

getAllCallbacks()

Возвращает объекты Spring AI ToolCallback для всех итоговых инструментов, готовые для передачи в ChatClient.

Ассистент передает модели 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 статуса отсутствует, и методы просто ничего не делают, поэтому вызывать их всегда безопасно.