Практическое применение RAG для создания полезных систем Spring AI и OpenAI GPT на основе ваших собственных документов

Практическое использование RAG для создания полезных систем Spring AI и OpenAI GPT на основе ваших собственных документов

Проект AIDocumentLibraryChat использует проект Spring AI с OpenAI для поиска ответов на вопросы в библиотеке документов. Для этого используется Retrieval Augmented Generation на документах.

Retrieval Augmented Generation

Процесс выглядит следующим образом:

Процесс выглядит следующим образом:

  • Загрузить документ
  • Сохранить документ в базе данных Postgresql.
  • Разделить документ для создания вложений.
  • Создать вложения с использованием вызова модели вложения OpenAI.
  • Сохранить вложения документа в базе данных Postgresql Vector.

Поиск документов:

  • Создать поисковое подсказку
  • Создать вложение поисковой подсказки с использованием вызова модели вложения OpenAI.
  • Запросить базу данных Postgresql Vector для документов с наиболее близкими расстояниями вложений.
  • Запросить базу данных Postgresql для документа.
  • Создать запрос с поисковой подсказкой и фрагментом текста документа.
  • Запросить ответ от модели GPT и показать ответ на основе поисковой подсказки и фрагмента текста документа.

Загрузка документа

Загруженный документ хранится в базе данных для нахождения источника ответа. Текст документа должен быть разделен на фрагменты для создания вложений на каждый фрагмент. Вложения создаются моделью вложения OpenAI и представляют собой векторы с более чем 1500 измерениями для представления фрагмента текста. Вложение сохраняется в AI-документе с текстом фрагмента и идентификатором исходного документа в базе данных векторов.

Поиск документов принимает поисковую подсказку и использует модель вложения Open AI для преобразования ее во вложение. Вложение используется для поиска наиболее близкого вектора в базе данных векторов. Это означает, что ищутся вложения поисковой подсказки и фрагмента текста, которые имеют наибольшую схожесть. Идентификатор в AIDocument используется для чтения документа из реляционной базы данных. С помощью поисковой подсказки и фрагмента текста AIDocument создается запрос. Затем вызывается модель OpenAI GPT с запросом для создания ответа на основе поисковой подсказки и контекста документа. Это позволяет модели создавать ответы, которые тесно основаны на предоставленных документах и улучшают точность. Ответ модели GPT возвращается и отображается с ссылкой на документ для предоставления источника ответа.

Архитектура

Архитектура проекта построена вокруг Spring Boot с использованием Spring AI. Angular UI обеспечивает пользовательский интерфейс для отображения списка документов, загрузки документов и предоставления поисковой подсказки с ответом и исходным документом. Он взаимодействует с бэкэндом Spring Boot через REST-интерфейс. Бэкэнд Spring Boot предоставляет контроллеры REST для фронтенда и использует Spring AI для взаимодействия с моделями OpenAI и базой данных векторов Postgresql. Документы хранятся с использованием Jpa в реляционной базе данных Postgresql. База данных Postgresql используется, потому что она объединяет реляционную базу данных и базу данных векторов в Docker-образе.

Реализация

Фронтенд

Фронтенд основан на лениво загружаемых автономных компонентах, созданных с помощью Angular. Ленивно загружаемые автономные компоненты настраиваются в app.config.ts:

Конфигурация устанавливает маршруты и включает HTTP-клиент и анимации.

Ленивно загружаемые маршруты определены в app.routes.ts:

В ‘loadChildren’ ‘import(“…”).then((mod) => mod.XXX)’ ленивно загружает маршрут из указанного пути и устанавливает экспортируемые маршруты, определенные в константе ‘mod.XXX’.

Ленивно загружаемый маршрут ‘docsearch’ имеет index.ts для экспорта константы:

Который экспортирует doc-search.routes.ts:

Он определяет маршрутизацию до ‘DocSearchComponent’.

Компонент ‘fileupload’ можно найти в DocImportComponent с шаблоном doc-import.component.html:

Загрузка файла происходит с использованием тега ‘<input type=”file” (change)=”onFileInputChange($event)”>’. Он предоставляет возможность загрузки и вызывает метод ‘onFileInputChange(…)’ после каждой загрузки.

Кнопка ‘Upload’ вызывает метод ‘upload()’ для отправки файла на сервер при щелчке.

doc-import.component.ts содержит методы для шаблона:

Это независимый компонент со своими импортами модулей и внедренным ‘DestroyRef’.

Метод ‘onFileInputChange(…)’ принимает параметр события и сохраняет его свойство ‘files’ в константу ‘files’. Затем он проверяет первый файл и сохраняет его в свойстве компонента ‘file’.

Метод ‘upload()’ проверяет свойство ‘file’ и создает ‘FormData()’ для загрузки файла. Константа ‘formData’ имеет тип данных (‘file’), содержимое (‘this.file’) и добавленное имя файла (‘this.file.name’). Затем используется ‘documentService’ для отправки объекта ‘FormData()’ на сервер. Функция ‘takeUntilDestroyed(this.destroyRef)’ отписывается от потока Rxjs после уничтожения компонента. Это делает отписку от потоков очень удобной в Angular.

Backend

Backend – это приложение Spring Boot с использованием фреймворка Spring AI. Spring AI управляет запросами к моделям OpenAI и запросами к векторной базе данных.

Настройка базы данных Liquibase

Настройка базы данных выполняется с помощью Liquibase, и скрипт можно найти в db.changelog-1.xml:

В changeset 4 создается таблица для сущности Jpa документа с первичным ключом ‘id’. Тип/размер содержимого неизвестен и поэтому установлен как ‘blob’. В changeset 5 создается последовательность для сущности Jpa с помощью стандартных свойств Hibernate 6 последовательностей, используемых Spring Boot 3.x.

В changeset 6 создается таблица ‘vector_store’ с первичным ключом ‘id’ типа ‘uuid’, созданным расширением ‘uuid-ossp’. Столбец ‘content’ имеет тип ‘text’ (‘clob’ в других базах данных) и имеет гибкий размер. Столбец ‘metadata’ хранит метаданные для AIDocuments в формате ‘json’. Столбец ’embedding’ хранит вектор вложений с указанием числа измерений OpenAI.

В changeset 7 устанавливается индекс для быстрого поиска столбца ’embeddings’. Из-за ограниченных параметров ‘<createIndex …>’ Liquibase используется переход к созданию индекса с помощью ‘<sql>’.

Реализация Spring Boot / Spring AI

Для фронтенда DocumentController выглядит следующим образом:

‘handleDocumentUpload(…)’ обрабатывает загруженный файл с помощью ‘documentService’ по пути ‘/rest/document/upload’.

‘getDocumentList()’ обрабатывает запросы на получение списка документов и удаляет содержимое документа, чтобы сохранить размер ответа.

‘getDocumentContent(…)’ обрабатывает запросы на получение содержимого документа. Он загружает документ с помощью ‘documentService’ и отображает ‘DocumentType’ в ‘MediaType’. Затем возвращает содержимое, тип содержимого, и браузер открывает содержимое на основе типа содержимого.

Метод ‘postDocumentSearch(…)’ помещает содержимое запроса в объект ‘SearchDto’ и возвращает результат, сгенерированный с помощью искусственного интеллекта в вызове ‘documentService.queryDocuments(…)’.

Метод ‘storeDocument(…)’ DocumentService выглядит следующим образом:

Метод ‘storeDocument(…)’ сохраняет документ в реляционной базе данных. Затем документ преобразуется в ‘ByteArrayResource’ и читается с помощью ‘TikaDocumentReader’ Spring AI, чтобы превратить его в список AIDocument. Затем список AIDocument разбивается на части с помощью метода ‘splitToTokenLimit(…)’, а затем эти части преобразуются в новые AIDocuments с ‘id’ сохраненного документа в карте Metadata. ‘id’ в Metadata позволяет загружать соответствующую документу сущность для AIDocuments. Затем добавляются векторы для AIDocuments неявно с вызовами метода ‘documentVsRepository.add(…)’, который вызывает модель векторного вложения OpenAI и сохраняет AIDocuments с векторами в базе данных векторов. Затем результат возвращается.

Метод ‘queryDocument(…)’ выглядит следующим образом:

В первую очередь метод загружает документы, наиболее подходящие для ‘searchDto.getSearchString()’ из базы данных векторов. Для этого вызывается модель векторного вложения OpenAI, чтобы преобразовать поисковую строку в вложение, и с помощью этого вложения обращаются к базе данных векторов для получения AIDocuments с наименьшим расстоянием (расстояние между векторами поискового вложения и вложениями базы данных). Затем AIDocuments с наименьшим расстоянием сохраняются в переменной ‘mostSimilar’. Затем все AIDocuments частей документа собираются путем сопоставления идентификатора сущности документа из их Metadata ‘id’. ‘systemMessage’ создается с содержимым ‘documentChunks’ или ‘mostSimilar’. Метод ‘getSystemMessage(…)’ берет их и обрезает ‘contentChunks’ до размера, который модели OpenAI GPT могут обработать, и возвращает ‘Message’. Затем ‘systemMessage’ и ‘userMessage’ преобразуются в ‘prompt’, который отправляется с помощью ‘aiClient.generate (prompt)’ модели OpenAi GPT. После этого ответ искусственного интеллекта доступен, и для загрузки сущности документа загружается идентификатор метаданных AIDocument ‘mostSimilar’. Создается ‘AiResult’ с поисковой строкой, ответом GPT, сущностью документа и возвращается.

Репозиторий базы данных векторов DocumentVsRepositoryBean с использованием Spring AI ‘VectorStore’ выглядит следующим образом:

Репозиторий имеет свойство ‘vectorStore’, которое используется для доступа к базе данных векторов. Оно создается в конструкторе с помощью внедряемых параметров при вызове ‘new PgVectorStore(…)’. Класс PgVectorStore представляет расширение векторной базы данных Postgresql. Он имеет ’embeddingClient’, чтобы использовать модель векторного вложения OpenAI, и ‘jdbcTemplate’ для доступа к базе данных.

Метод ‘add(…)’ вызывает модель векторного вложения OpenAI и добавляет AIDocuments в базу данных векторов.

Методы ‘retrieve(…)’ запрашивают базу данных векторов для получения вложений с наименьшими расстояниями.

Заключение

Angular сделал создание фронтенда легким. Стандалонные компоненты с ленивой загрузкой сделали начальную загрузку небольшой. Компоненты Angular Material очень помогли с реализацией и просты в использовании.

Spring Boot с Spring AI сделал использование моделей большого языка удобным. Spring AI предоставляет инструментарий для скрытия создания вложений и предоставляет простой интерфейс для сохранения AIDocuments в базе данных векторов (поддерживается несколько). Создание вложения для поисковой строки для загрузки ближайших AIDocuments также выполняется за вас, и интерфейс базы данных векторов прост. Классы Spring AI prompt также упрощают создание запроса для модели OpenAI GPT. Вызов модели выполняется с помощью внедряемого ‘aiClient’, и результаты возвращаются.

Spring AI – очень хороший фреймворк от команды Spring. Не было никаких проблем с экспериментальной версией.

Благодаря Spring AI использование моделей большого языка на собственных документах стало легким.