Команда Spring АйО перевела туториал, в котором рассматриваются возможности Spring AI для интеграции с LLM.
Вы узнаете, как использовать API Function Calling для выполнения задач на естественном языке, генерировать ответы в JSON-формате и сохранять контекст диалога.
Обзор
В этом туториале мы рассмотрим концепции Spring AI, которые помогут создать AI-ассистента с использованием LLM, таких как ChatGPT, Ollama, Mistreal и других.
Компании все чаще внедряют AI-ассистентов для улучшения UX в самых разных бизнес-задачах:
-
Ответы на вопросы пользователей
-
Выполнение операций на основе пользовательского ввода
-
Суммирование длинных предложений и документов
Хотя это лишь несколько базовых возможностей LLM, их потенциал значительно шире.
Возможности Spring AI
Фреймворк Spring AI предлагает множество интересных функций для реализации AI-ориентированных возможностей:
-
интерфейсы, которые могут бесшовно интегрироваться с базовыми LLM-сервисами и Vector DB
-
генерация ответов с учетом контекста и выполнение действий с использованием RAG и Function Calling API
-
преобразование ответов LLM в POJO или машиночитаемые форматы, такие как JSON, с помощью структурированных конвертеров
-
обогащение промптов и применение ограничений через перехватчики (интерсепторы), предоставляемые Advisor API
-
улучшение взаимодействия с пользователем за счет сохранения состояния диалога
Мы также можем это визуализировать:
Чтобы продемонстрировать некоторые из этих функций, мы создадим чат-бота для легаси системы управления заказами (Order Management System, OMS).
Типичные функции OMS включают:
-
Создание заказа
-
Получение заказов пользователя
Предварительные требования
Во-первых, нам понадобится подписка на OpenAI (комментарий от команды Spring АйО: может использоваться другая модель) для использования его LLM-сервиса. Затем в приложении Spring Boot мы добавим Maven-зависимости для библиотек Spring AI. Мы уже подробно рассмотрели предварительные требования в других статьях, поэтому не будем углубляться в эту тему.
Кроме того, для быстрого старта мы будем использовать in-memory базу данных HSQLDB. Давайте создадим необходимые таблицы и добавим в них данные:
CREATE TABLE User_Order ( order_id BIGINT NOT NULL PRIMARY KEY, user_id VARCHAR(20) NOT NULL, quantity INT ); INSERT INTO User_Order (order_id, user_id, quantity) VALUES (1, 'Jenny', 2); INSERT INTO User_Order (order_id, user_id, quantity) VALUES (2, 'Mary', 5); INSERT INTO User_Order (order_id, user_id, quantity) VALUES (3, 'Alex', 1); INSERT INTO User_Order (order_id, user_id, quantity) VALUES (4, 'John', 3); INSERT INTO User_Order (order_id, user_id, quantity) VALUES (5, 'Sophia', 4); --and so on..
Мы используем несколько стандартных свойств конфигурации, связанных с инициализацией HSQLDB и клиента OpenAI, в файле application.properties
Spring:
spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver spring.datasource.url=jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1 spring.datasource.username=sa spring.datasource.password= spring.jpa.hibernate.ddl-auto=none spring.ai.openai.chat.options.model=gpt-4o-mini spring.ai.openai.api-key=xxxxxxx
Комментарий от команды Spring АйО
Значение данных настроек может меняться в зависимости от выбранной вами LLM модели.
Выбор подходящей модели для конкретного сценария использования — это сложный итеративный процесс, включающий множество проб и ошибок. Тем не менее, для простого демонстрационного приложения в рамках этой статьи подойдет экономичная GPT-4o mini модель.
API вызова функций (Function Call API)
Эта фича является одной из основ популярной агентной концепции в приложениях на основе LLM. Она позволяет приложениям выполнять сложный набор конкретных задач и принимать решения самостоятельно.
Например, это может существенно помочь в создании чат-бота для легаси приложения управления заказами. Чат-бот сможет помогать пользователям создавать запросы на заказы, получать историю заказов и выполнять другие действия описанные на естественном языке.
Эти специализированные навыки обеспечиваются одной или несколькими функциями приложения. Мы определяем алгоритм в промпте, отправляемом LLM вместе со схемой поддерживающих функций. LLM получает эту схему и определяет правильную функцию для выполнения запрошенного действия. Затем LLM сама вызывает выбранную функцию у приложения.
Наконец, приложение выполняет функцию и отправляет дополнительную информацию обратно в LLM:
Сначала давайте рассмотрим основные компоненты легаси приложения:
Класс OrderManagementService
содержит две важные функции: создание заказов и получение истории заказов пользователя. Обе функции используют бин OrderRepository
для интеграции с базой данных:
@Service public class OrderManagementService { @Autowired private OrderRepository orderRepository; public Long createOrder(OrderInfo orderInfo) { return orderRepository.save(orderInfo).getOrderID(); } public Optional<List<OrderInfo>> getAllUserOrders(String userID) { return orderRepository.findByUserID(userID); } }
Теперь давайте посмотрим, как Spring AI может помочь в реализации чат-бота в легаси приложении:
На диаграмме классов OmAiAssistantConfiguration
представлен как конфигурационный бин Spring. Он регистрирует бины обратного вызова функций: createOrderFn
и getUserOrderFn
:
@Configuration public class OmAiAssistantConfiguration { @Bean @Description("Create an order. The Order ID is identified with orderID. " + "The order quantity is identified by orderQuantity." + "The user is identified by userID. " + "The order quantity should be a positive whole number." + "If any of the parameters like user id and the order quantity is missing" + "then ask the user to provide the missing information.") public Function<CreateOrderRequest, Long> createOrderFn(OrderManagementService orderManagementService) { return createOrderRequest -> orderManagementService.createOrder(createOrderRequest.orderInfo()); } @Bean @Description("get all the orders of an user. The user ID is identified with userID.") public Function<GetOrderRequest, List<OrderInfo>> getUserOrdersFn(OrderManagementService orderManagementService) { return getOrderRequest -> orderManagementService.getAllUserOrders(getOrderRequest.userID()).get(); } }
Аннотация @Description
помогает сгенерировать схему функции. Затем приложение отправляет эту схему в LLM как часть промпта.
Кроме того, CreateOrderRequest
и GetOrderRequests
— это Record
-классы, которые помогают Spring AI создавать POJO для вызовов downstream-сервисов:
record GetOrderRequest(String userID) {} record CreateOrderRequest(OrderInfo orderInfo) {}
Наконец, давайте рассмотрим новый класс OrderManagementAIAssistant
, который будет отправлять запросы пользователей в сервис LLM:
@Service public class OrderManagementAIAssistant { @Autowired private ChatModel chatClient; public ChatResponse callChatClient(Set<String> functionNames, String promptString) { Prompt prompt = new Prompt(promptString, OpenAiChatOptions .builder() .withFunctions(functionNames) .build() ); return chatClient.call(prompt); } }
Метод callChatClient()
помогает зарегистрировать функции в объекте Prompt
. Затем он вызывает метод ChatModel#call()
, чтобы получить ответ от сервиса LLM.
Сценарии вызова функций
Для пользовательского запроса или инструкции, переданной AI-ассистенту, мы рассмотрим несколько базовых сценариев:
-
LLM принимает решение и определяет одну или несколько функций для выполнения.
-
LLM сообщает о недостаточной информации для выполнения функции.
-
LLM выполняет инструкции в зависимости от определенных условий.
Мы уже обсудили основные концепции, поэтому далее будем использовать эту функцию для создания чат-бота.
5.1. Выполнение функции обратного вызова один или несколько раз
Теперь давайте изучим поведение LLM, когда мы вызываем его с промптом, содержащим запрос пользователя и схему функции.
Начнем с примера, где создается заказ:
void whenOrderInfoProvided_thenSaveInDB(String promptString) { ChatResponse response = this.orderManagementAIAssistant .callChatClient(Set.of("createOrderFn"), promptString); String resultContent = response.getResult().getOutput().getContent(); logger.info("The response from the LLM service: {}", resultContent); }
Удивительно, но с использованием естественного языка мы получаем желаемые результаты:
Промпт |
LLM Запрос |
Наблюдение |
Создайте заказ с количеством 20 для пользователя с идентификатором Jenny и случайным образом сгенерируйте положительное целое число для идентификатора заказа. |
Заказ был успешно создан со следующими данными: – ID заказа: 123456 – ID пользователя: Jenny – Количество: 20 |
Программа создает заказ с использованием информации, предоставленной в промпте. |
Создайте два заказа. Первый заказ для пользователя с идентификатором Sophia с количеством 30. Второй заказ для пользователя с идентификатором Mary с количеством 40. Случайным образом сгенерируйте положительные целые числа для идентификаторов заказов. |
Заказы были успешно созданы: Заказ для Sophia: ID заказа 1, количество 30. Заказ для Mary: ID заказа 2, количество 40. Если вам нужно что-то еще, не стесняйтесь обращаться! |
Программа создает два заказа. LLM оказался достаточно умным, чтобы запросить выполнение функции дважды. |
Продолжая, давайте проверим, сможет ли LLM понять промпт, запрашивающий получение данных о заказах пользователя:
void whenUserIDProvided_thenFetchUserOrders(String promptString) { ChatResponse response = this.orderManagementAIAssistant .callChatClient(Set.of("getUserOrdersFn"), promptString); String resultContent = response.getResult().getOutput().getContent(); logger.info("The response from the LLM service: {}", resultContent); }
Результаты аналогичны предыдущему случаю. LLM успешно идентифицирует зарегистрированную функцию для выполнения. Затем приложение Spring Boot вызывает запрошенную функцию и возвращает данные о заказах одного или нескольких пользователей.
Комментарий от команды Spring АйО
Возможно, тут ожидалось изображение с результатами, но, к сожалению, в оригинальной статье оно отсутствует.
5.2. Условное выполнение функций обратного вызова
Рассмотрим программу, которая создает заказ только при выполнении определенных критериев:
void whenUserIDProvided_thenCreateOrderIfUserHasLessThanTwoOrders(String promptString) { ChatResponse response = this.orderManagementAIAssistant .callChatClient(Set.of("getUserOrdersFn", "createOrderFn"), promptString); String resultContent = response.getResult() .getOutput() .getContent(); logger.info("The response from the LLM service: {}", resultContent); }
Мы зарегистрировали функции getUserOrderFn
и CreateOrderFn
в бине ChatModel
. Далее LLM должен определить, какая функция обеспечит наилучший результат для запрошенного действия.
Как и ранее, результаты обнадеживают:
Промпт |
Вывод |
Наблюдение |
Создайте заказ для пользователя с идентификатором Alex с количеством 25. Не создавайте заказ, если у пользователя уже более 2 заказов. При создании заказа случайным образом сгенерируйте положительное целое число для идентификатора заказа. |
У пользователя Alex уже более 2 заказов (в частности, 4 заказа). Поэтому я не буду создавать новый заказ. |
LLM может условно вызывать функции downstream на основе вывода предыдущих функций. |
Создайте заказ для пользователя с идентификатором David с количеством 25. Не создавайте заказ, если у пользователя уже более 2 заказов. При создании заказа случайным образом сгенерируйте положительное целое число для идентификатора заказа. |
Заказ успешно создан для пользователя с идентификатором «David» с количеством 25, и идентификатор заказа — 42. |
На этот раз |
Создайте заказ с количеством 20. |
Пожалуйста, укажите ваш идентификатор пользователя для создания заказа. |
LLM сразу определил, что отсутствует идентификатор пользователя, и прекратил дальнейшую обработку. |
API Advisors в Spring AI
В предыдущих разделах мы обсудили функциональные аспекты приложения. Однако существуют некоторые общие проблемы, которые затрагивают все функциональности, например:
-
Предотвращение ввода пользователями конфиденциальной информации
-
Логирование и аудит запросов пользователей
-
Поддержание состояния диалога
-
Обогащение промптов
К счастью, API Advisors позволяет последовательно решать эти проблемы. Мы уже подробно объясняли это в одной из наших статей.
API структурированного вывода Spring AI и Spring AI RAG
LLM, как правило, генерируют ответы на запросы пользователей в виде сообщений на естественном языке. Однако downstream-сервисы чаще всего работают с данными в машиночитаемых форматах, таких как POJO, JSON и т. д. Именно здесь возможность Spring AI генерировать структурированный вывод играет важную роль.
Как и в случае с API Advisors, мы подробно объясняли это в одной из наших статей. Поэтому не будем углубляться в эту тему и перейдем к следующему разделу.
Комментарий от команды Spring АйО
По нашему мнению, речь идет о данной статье.
Иногда приложениям необходимо обращаться к Vector DB для выполнения семантического поиска по сохраненным данным и получения дополнительной информации. Затем полученные результаты из Vector DB используются в подсказке, чтобы предоставить LLM контекстную информацию. Этот подход называется техникой RAG, которую также можно реализовать с помощью Spring AI.
Заключение
В этой статье мы рассмотрели ключевые возможности Spring AI, которые могут помочь в создании AI-ассистента. Spring AI активно развивается, предлагая множество готовых функций. Однако правильный выбор базового LLM-сервиса и Vector DB имеет решающее значение, независимо от используемого программного фреймворка. Кроме того, достижение оптимальной конфигурации этих сервисов может быть сложным и требовать значительных усилий. Тем не менее, это важно для широкого распространения приложения.
Как обычно, исходный код, использованный в этой статье, доступен на GitHub.
Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.
ссылка на оригинал статьи https://habr.com/ru/articles/869080/
Добавить комментарий