Генеративный чат с пользователями и документами на базе искусственного интеллекта на Java с помощью PaLM и LangChain4J.

1. Введение

Последнее обновление: 5 февраля 2024 г.

Что такое генеративный ИИ

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

Генеративный ИИ основан на базовых моделях (крупных моделях ИИ), которые могут выполнять несколько задач и выполнять нестандартные задачи, включая обобщение, вопросы и ответы, классификацию и многое другое. Кроме того, при минимальном обучении базовые модели можно адаптировать для целевых случаев использования с очень небольшим количеством примеров данных.

Как работает генеративный ИИ?

Генеративный ИИ работает с использованием модели ML (машинного обучения) для изучения закономерностей и взаимосвязей в наборе данных контента, созданного человеком. Затем он использует изученные шаблоны для создания нового контента.

Самый распространенный способ обучения генеративной модели ИИ — использование обучения с учителем: модели дается набор созданного человеком контента и соответствующие метки. Затем он учится генерировать контент, похожий на контент, созданный человеком, и помеченный теми же ярлыками.

Каковы распространенные приложения генеративного ИИ?

Генеративный ИИ обрабатывает обширный контент, создавая идеи и ответы с помощью текста, изображений и удобных для пользователя форматов. Генеративный ИИ может использоваться для:

  • Улучшите взаимодействие с клиентами за счет улучшенного чата и поиска.
  • Исследуйте огромные объемы неструктурированных данных с помощью диалоговых интерфейсов и обобщений.
  • Помогите выполнять повторяющиеся задачи, такие как ответы на запросы предложений (RFP), локализация маркетингового контента на пять языков, проверка договоров с клиентами на соответствие требованиям и многое другое.

Какие предложения генеративного искусственного интеллекта есть в Google Cloud?

С помощью Vertex AI взаимодействуйте, настраивайте и встраивайте базовые модели в свои приложения — практически не требуется знаний в области машинного обучения. Получите доступ к базовым моделям в Model Garden , настройте модели с помощью простого пользовательского интерфейса в Generative AI Studio или используйте модели в блокноте для анализа данных.

Vertex AI Search and Conversation предлагает разработчикам самый быстрый способ создания генеративных поисковых систем и чат-ботов на базе искусственного интеллекта.

Duet AI — это ваш помощник на базе искусственного интеллекта, доступный в Google Cloud и IDE, который поможет вам делать больше и быстрее.

На чем фокусируется эта кодовая лаборатория?

В этой лаборатории кода основное внимание уделяется модели большого языка PaLM 2 (LLM), размещенной в Google Cloud Vertex AI, которая охватывает все продукты и услуги машинного обучения.

Вы будете использовать Java для взаимодействия с API PaLM в сочетании с оркестратором инфраструктуры LangChain4J LLM. Вы рассмотрите различные конкретные примеры, чтобы воспользоваться преимуществами LLM для ответов на вопросы, генерации идей, извлечения сущностей и структурированного контента, а также обобщения.

Расскажите мне больше о фреймворке LangChain4J!

Платформа LangChain4J — это библиотека с открытым исходным кодом для интеграции больших языковых моделей в ваши приложения Java путем координации различных компонентов, таких как сам LLM, а также других инструментов, таких как векторные базы данных (для семантического поиска), загрузчики документов и разделители (для анализа документов). и учитесь у них), анализаторы вывода и многое другое.

c6d7f7c3fd0d2951.png

Что вы узнаете

  • Как настроить проект Java для использования PaLM и LangChain4J
  • Как извлечь полезную информацию из неструктурированного контента (извлечение сущности или ключевого слова, вывод в формате JSON)
  • Как создать диалог с вашими пользователями
  • Как использовать модель чата, чтобы задавать вопросы по собственной документации

Что вам понадобится

  • Знание языка программирования Java
  • Проект Google Cloud
  • Браузер, например Chrome или Firefox.

2. Настройка и требования

Самостоятельная настройка среды

  1. Войдите в Google Cloud Console и создайте новый проект или повторно используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • Имя проекта — это отображаемое имя для участников этого проекта. Это строка символов, не используемая API Google. Вы всегда можете обновить его.
  • Идентификатор проекта уникален для всех проектов Google Cloud и является неизменяемым (невозможно изменить после его установки). Cloud Console автоматически генерирует уникальную строку; обычно тебя не волнует, что это такое. В большинстве лабораторий кода вам потребуется указать идентификатор проекта (обычно идентифицируемый как PROJECT_ID ). Если вам не нравится сгенерированный идентификатор, вы можете создать другой случайный идентификатор. Альтернативно, вы можете попробовать свой собственный и посмотреть, доступен ли он. Его нельзя изменить после этого шага и он сохраняется на протяжении всего проекта.
  • К вашему сведению, есть третье значение — номер проекта , которое используют некоторые API. Подробнее обо всех трех этих значениях читайте в документации .
  1. Затем вам необходимо включить выставление счетов в Cloud Console, чтобы использовать облачные ресурсы/API. Прохождение этой кодовой лаборатории не будет стоить много, если вообще что-то стоить. Чтобы отключить ресурсы и избежать выставления счетов за пределами этого руководства, вы можете удалить созданные вами ресурсы или удалить проект. Новые пользователи Google Cloud имеют право на участие в программе бесплатной пробной версии стоимостью 300 долларов США .

Запустить Cloud Shell

Хотя Google Cloud можно управлять удаленно с вашего ноутбука, в этой лаборатории вы будете использовать Cloud Shell , среду командной строки, работающую в облаке.

Активировать Cloud Shell

  1. В Cloud Console нажмите «Активировать Cloud Shell». d1264ca30785e435.png .

cb81e7c8e34bc8d.png

Если вы запускаете Cloud Shell впервые, вы увидите промежуточный экран с описанием того, что это такое. Если вам был представлен промежуточный экран, нажмите «Продолжить» .

d95252b003979716.png

Подготовка и подключение к Cloud Shell займет всего несколько минут.

7833d5e1c5d18f54.png

Эта виртуальная машина загружена всеми необходимыми инструментами разработки. Он предлагает постоянный домашний каталог объемом 5 ГБ и работает в Google Cloud, что значительно повышает производительность сети и аутентификацию. Большую часть, если не всю, работу в этой лаборатории кода можно выполнить с помощью браузера.

После подключения к Cloud Shell вы увидите, что вы прошли аутентификацию и что для проекта установлен идентификатор вашего проекта.

  1. Выполните следующую команду в Cloud Shell, чтобы подтвердить, что вы прошли аутентификацию:
gcloud auth list

Вывод команды

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Выполните следующую команду в Cloud Shell, чтобы убедиться, что команда gcloud знает о вашем проекте:
gcloud config list project

Вывод команды

[core]
project = <PROJECT_ID>

Если это не так, вы можете установить его с помощью этой команды:

gcloud config set project <PROJECT_ID>

Вывод команды

Updated property [core/project].

3. Подготовка среды разработки

В этой лаборатории кода вы будете использовать терминал Cloud Shell и редактор кода для разработки программ на Java.

Включить API Vertex AI

  1. В консоли Google Cloud убедитесь, что название вашего проекта отображается в верхней части консоли Google Cloud . Если это не так, нажмите «Выбрать проект» , чтобы открыть «Выбор проекта» , и выберите нужный проект.
  2. Если вы не находитесь в разделе Vertex AI консоли Google Cloud, выполните следующие действия:
  3. В поле «Поиск» введите Vertex AI, затем вернитесь.
  4. В результатах поиска нажмите Vertex AI. Появится панель управления Vertex AI.
  5. Нажмите «Включить все рекомендуемые API» на панели управления Vertex AI.

Это активирует несколько API, но наиболее важным для лаборатории кода является aiplatform.googleapis.com , который вы также можете включить из командной строки в терминале Cloud Shell, выполнив следующую команду:

$ gcloud services enable aiplatform.googleapis.com

Создание структуры проекта с помощью Gradle

Для создания примеров кода Java вы будете использовать инструмент сборки Gradle и версию Java 17. Чтобы настроить проект с помощью Gradle, в терминале Cloud Shell создайте каталог (здесь palm-workshop ) и запустите команду gradle init в этом каталоге:

$ mkdir palm-workshop
$ cd palm-workshop

$ gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 3

Split functionality across multiple subprojects?:
  1: no - only one application project
  2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2] 1

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no] 

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit Jupiter) [1..4] 4

Project name (default: palm-workshop): 
Source package (default: palm.workshop): 

> Task :init
Get more help with your project: https://docs.gradle.org/7.4/samples/sample_building_java_applications.html

BUILD SUCCESSFUL in 51s
2 actionable tasks: 2 executed

Вы будете собирать приложение (вариант 2), используя язык Java (вариант 3), без использования подпроектов (вариант 1), используя синтаксис Groovy для файла сборки (вариант 1), не используйте новые функции сборки (вариант № ), генерируя тесты с помощью JUnit Jupiter (вариант 4), а в качестве имени проекта можно использовать palm-workshop , и аналогично для исходного пакета можно использовать palm.workshop .

Структура проекта будет выглядеть следующим образом:

├── gradle 
│   └── ...
├── gradlew 
├── gradlew.bat 
├── settings.gradle 
└── app
    ├── build.gradle 
    └── src
        ├── main
        │   └── java 
        │       └── palm
        │           └── workshop
        │               └── App.java
        └── test
            └── ...

Давайте обновим файл app/build.gradle добавив некоторые необходимые зависимости. Вы можете удалить зависимость guava , если она присутствует, и заменить ее зависимостями для проекта LangChain4J и библиотекой журналирования, чтобы избежать ворчания отсутствующих сообщений журнала:

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'

    // Logging library
    implementation 'org.slf4j:slf4j-jdk14:2.0.9'

    // This dependency is used by the application.
    implementation 'dev.langchain4j:langchain4j-vertex-ai:0.24.0'
    implementation 'dev.langchain4j:langchain4j:0.24.0'
}

Для LangChain4J есть две зависимости:

  • один по основному проекту,
  • и один для специального модуля Vertex AI.

Чтобы использовать Java 17 для компиляции и запуска наших программ, добавьте следующий блок под блоком plugins {} :

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

Еще одно изменение: обновите блок application app/build.gradle , чтобы пользователи могли переопределить основной класс для запуска из командной строки при вызове инструмента сборки:

application {
    mainClass = providers.systemProperty('javaMainClass')
                         .orElse('palm.workshop.App')
}

Чтобы убедиться, что ваш файл сборки готов к запуску вашего приложения, вы можете запустить основной класс по умолчанию, который печатает простое сообщение Hello World! сообщение:

$ ./gradlew run -DjavaMainClass=palm.workshop.App

> Task :app:run
Hello World!

BUILD SUCCESSFUL in 3s
2 actionable tasks: 2 executed

Теперь вы готовы программировать с использованием текстовой модели большого языка PaLM, используя проект LangChain4J!

Для справки, вот как теперь должен выглядеть полный файл сборки app/build.gradle :

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
}

java {
    toolchain {
        // Ensure we compile and run on Java 17
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'

    // This dependency is used by the application.
    implementation 'dev.langchain4j:langchain4j-vertex-ai:0.24.0'
    implementation 'dev.langchain4j:langchain4j:0.24.0'
    implementation 'org.slf4j:slf4j-jdk14:2.0.9'
}

application {
    mainClass = providers.systemProperty('javaMainClass').orElse('palm.workshop.App')
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}

4. Первый звонок в модель чата PaLM

Теперь, когда проект правильно настроен, пришло время вызвать API PaLM.

Создайте новый класс с именем ChatPrompts.java в каталоге app/src/main/java/palm/workshop (рядом с классом App.java по умолчанию) и введите следующее содержимое:

package palm.workshop;

import dev.langchain4j.model.vertexai.VertexAiChatModel;
import dev.langchain4j.chain.ConversationalChain;

public class ChatPrompts {
    public static void main(String[] args) {
        VertexAiChatModel model = VertexAiChatModel.builder()
            .endpoint("us-central1-aiplatform.googleapis.com:443")
            .project("YOUR_PROJECT_ID")
            .location("us-central1")
            .publisher("google")
            .modelName("chat-bison@001")
            .maxOutputTokens(400)
            .maxRetries(3)
            .build();

        ConversationalChain chain = ConversationalChain.builder()
            .chatLanguageModel(model)
            .build();

        String message = "What are large language models?";
        String answer = chain.execute(message);
        System.out.println(answer);

        System.out.println("---------------------------");

        message = "What can you do with them?";
        answer = chain.execute(message);
        System.out.println(answer);

        System.out.println("---------------------------");

        message = "Can you name some of them?";
        answer = chain.execute(message);
        System.out.println(answer);
    }
}

В этом первом примере вам необходимо импортировать класс VertexAiChatModel и LangChain4J ConversationalChain , чтобы упростить обработку многоходового аспекта разговоров.

Далее в main методе вы собираетесь настроить модель языка чата, используя построитель VertexAiChatModel , чтобы указать:

  • конечная точка,
  • проект,
  • регион,
  • издатель,
  • и имя модели ( chat-bison@001 ).

Теперь, когда языковая модель готова, вы можете подготовить ConversationalChain . Это абстракция более высокого уровня, предлагаемая LangChain4J для совместной настройки различных компонентов для обработки разговора, таких как сама языковая модель чата, но, возможно, и других компонентов для обработки истории разговора в чате или подключения других инструментов, таких как средства извлечения, для получения информации из векторные базы данных. Но не волнуйтесь, мы вернемся к этому позже в этой лаборатории.

Затем вам предстоит совершить многоходовой разговор с моделью чата, задать несколько взаимосвязанных вопросов. Сначала вы задаетесь вопросом о программах LLM, затем спрашиваете, что вы можете с ними делать и каковы некоторые из них. Обратите внимание, что вам не нужно повторяться: LLM знает, что «они» означают LLM в контексте этого разговора.

Чтобы начать этот многоходовой разговор, вы просто вызываете метод execute() в цепочке, он добавит его в контекст разговора, модель чата сгенерирует ответ и также добавит его в историю чата.

Чтобы запустить этот класс, выполните следующую команду в терминале Cloud Shell:

./gradlew run -DjavaMainClass=palm.workshop.ChatPrompts

Вы должны увидеть вывод, похожий на этот:

$ ./gradlew run -DjavaMainClass=palm.workshop.ChatPrompts
Starting a Gradle Daemon, 2 incompatible and 2 stopped Daemons could not be reused, use --status for details

> Task :app:run
Large language models (LLMs) are artificial neural networks that are trained on massive datasets of text and code. They are designed to understand and generate human language, and they can be used for a variety of tasks, such as machine translation, question answering, and text summarization.
---------------------------
LLMs can be used for a variety of tasks, such as:

* Machine translation: LLMs can be used to translate text from one language to another.
* Question answering: LLMs can be used to answer questions posed in natural language.
* Text summarization: LLMs can be used to summarize text into a shorter, more concise form.
* Code generation: LLMs can be used to generate code, such as Python or Java code.
* Creative writing: LLMs can be used to generate creative text, such as poems, stories, and scripts.

LLMs are still under development, but they have the potential to revolutionize a wide range of industries. For example, LLMs could be used to improve customer service, create more personalized marketing campaigns, and develop new products and services.
---------------------------
Some of the most well-known LLMs include:

* GPT-3: Developed by OpenAI, GPT-3 is a large language model that can generate text, translate languages, write different kinds of creative content, and answer your questions in an informative way.
* LaMDA: Developed by Google, LaMDA is a large language model that can chat with you in an open-ended way, answering your questions, telling stories, and providing different kinds of creative content.
* PaLM 2: Developed by Google, PaLM 2 is a large language model that can perform a wide range of tasks, including machine translation, question answering, and text summarization.
* T5: Developed by Google, T5 is a large language model that can be used for a variety of tasks, including text summarization, question answering, and code generation.

These are just a few examples of the many LLMs that are currently being developed. As LLMs continue to improve, they are likely to play an increasingly important role in our lives.

BUILD SUCCESSFUL in 25s
2 actionable tasks: 2 executed

PaLM ответил на ваши 3 связанных вопроса!

Построитель VertexAIChatModel позволяет вам определять дополнительные параметры, которые уже имеют некоторые значения по умолчанию, которые вы можете переопределить. Вот несколько примеров:

  • .temperature(0.2) — чтобы определить, насколько креативным должен быть ответ (0 — низкий уровень креативности и часто более фактический, а 1 — более креативный результат)
  • .maxOutputTokens(50) — в примере было запрошено 400 токенов (3 токена примерно эквивалентны 4 словам), в зависимости от того, какой длины вы хотите, чтобы сгенерированный ответ был
  • .topK(20) — случайным образом выбрать слово из максимального количества возможных слов для завершения текста (от 1 до 40)
  • .topP(0.95) — для выбора возможных слов, общая вероятность которых в сумме равна этому числу с плавающей запятой (от 0 до 1).
  • .maxRetries(3) — если вы превышаете квоту запроса на время, вы можете, например, заставить модель повторить вызов 3 раза.

5. Полезный чат-бот с индивидуальностью!

В предыдущем разделе вы сразу начали задавать вопросы чат-боту LLM, не придавая ему никакого конкретного контекста. Но вы можете специализировать такого чат-бота, чтобы стать экспертом в конкретной задаче или по определенной теме.

Как ты это делаешь? Подготавливая почву: объясняя LLM поставленную задачу, контекст, возможно, приводя несколько примеров того, что он должен делать, какой образ он должен иметь, в каком формате вы хотели бы получать ответы и, возможно, тон , если вы хотите, чтобы чат-бот вел себя определенным образом.

Эта статья о создании подсказок прекрасно иллюстрирует этот подход на следующем рисунке:

8a4c67679dcbd085.png

https://medium.com/@eldatero/master-the-perfect-chatgpt-prompt-formula-c776adae8f19

Чтобы проиллюстрировать эту мысль, давайте почерпнем вдохновение из веб-сайта Prompts.chat , на котором перечислено множество замечательных и забавных идей по созданию чат-ботов, адаптированных к вашим потребностям, которые позволят им действовать как:

Есть один пример превращения чат-бота LLM в шахматиста ! Давайте реализуем это!

Обновите класс ChatPrompts следующим образом:

package palm.workshop;

import dev.langchain4j.chain.ConversationalChain;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.vertexai.VertexAiChatModel;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;

public class ChatPrompts {
    public static void main(String[] args) {
        VertexAiChatModel model = VertexAiChatModel.builder()
            .endpoint("us-central1-aiplatform.googleapis.com:443")
            .project("YOUR_PROJECT_ID")
            .location("us-central1")
            .publisher("google")
            .modelName("chat-bison@001")
            .maxOutputTokens(7)
            .maxRetries(3)
            .build();

        InMemoryChatMemoryStore chatMemoryStore = new InMemoryChatMemoryStore();

        MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
            .chatMemoryStore(chatMemoryStore)
            .maxMessages(200)
            .build();

        chatMemory.add(SystemMessage.from("""
            You're an expert chess player with a high ELO ranking.
            Use the PGN chess notation to reply with the best next possible move.
            """
        ));


        ConversationalChain chain = ConversationalChain.builder()
            .chatLanguageModel(model)
            .chatMemory(chatMemory)
            .build();

        String pgn = "";
        String[] whiteMoves = { "Nf3", "c4", "Nc3", "e3", "Dc2", "Cd5"};
        for (int i = 0; i < whiteMoves.length; i++) {
            pgn += " " + (i+1) + ". " + whiteMoves[i];
            System.out.println("Playing " + whiteMoves[i]);
            pgn = chain.execute(pgn);
            System.out.println(pgn);
        }
    }
}

Давайте разберем это шаг за шагом:

  • Некоторые новые импорты необходимы для обработки памяти чата.
  • Вы создаете экземпляр модели чата, но с небольшим максимальным количеством жетонов (здесь 7), поскольку мы просто хотим сгенерировать следующий ход, а не целую диссертацию по шахматам!
  • Затем вы создаете хранилище памяти чата для сохранения разговоров в чате.
  • Вы создаете настоящую память оконного чата, чтобы сохранять последние ходы.
  • В память чата вы добавляете «системное» сообщение, которое инструктирует модель чата о том, кем она должна быть (т. е. опытным шахматистом). «Системное» сообщение добавляет некоторый контекст, тогда как сообщения «пользователя» и «ИИ» представляют собой собственно обсуждение.
  • Вы создаете цепочку разговоров, сочетающую память и модель чата.
  • Затем у нас есть список ходов за белых, который вы повторяете. Цепочка каждый раз выполняется следующим ходом белых, а модель чата отвечает следующим лучшим ходом.

Когда вы запустите этот класс с этими движениями, вы должны увидеть следующий результат:

$ ./gradlew run -DjavaMainClass=palm.workshop.ChatPrompts
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :app:run
Playing Nf3
1... e5
Playing c4
2... Nc6
Playing Nc3
3... Nf6
Playing e3
4... Bb4
Playing Dc2
5... O-O
Playing Cd5
6... exd5 

Вау! PaLM умеет играть в шахматы? Ну, не совсем, но во время обучения модель, должно быть, видела некоторые комментарии к шахматным партиям или даже файлы PGN (Portable Game Notation) прошлых игр. Однако этот чат-бот, скорее всего, не победит AlphaZero (ИИ, который побеждает лучших игроков в го, сёги и шахматы), и разговор может сойти на нет в дальнейшем, поскольку модель на самом деле не будет помнить фактическое состояние игры.

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

6. Извлечение информации из неструктурированного текста

В предыдущем разделе вы создали диалоги между пользователем и языковой моделью чата. Но с помощью LangChain4J вы также можете использовать модель чата для извлечения структурированной информации из неструктурированного текста.

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

Вы обновите класс ChatPrompts следующим образом:

package palm.workshop;

import dev.langchain4j.model.vertexai.VertexAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.UserMessage;

public class ChatPrompts {

    static class Person {
        String name;
        int age;
    }

    interface PersonExtractor {
        @UserMessage("""
            Extract the name and age of the person described below.
            Return a JSON document with a "name" and an "age" property, \
            following this structure: {"name": "John Doe", "age": 34}
            Return only JSON, without any markdown markup surrounding it.
            Here is the document describing the person:
            ---
            {{it}}
            ---
            JSON: 
            """)
        Person extractPerson(String text);
    }

    public static void main(String[] args) {
        VertexAiChatModel model = VertexAiChatModel.builder()
            .endpoint("us-central1-aiplatform.googleapis.com:443")
            .project("YOUR_PROJECT_ID")
            .location("us-central1")
            .publisher("google")
            .modelName("chat-bison@001")
            .maxOutputTokens(300)
            .build();
        
        PersonExtractor extractor = AiServices.create(PersonExtractor.class, model);

        Person person = extractor.extractPerson("""
            Anna is a 23 year old artist based in Brooklyn, New York. She was born and 
            raised in the suburbs of Chicago, where she developed a love for art at a 
            young age. She attended the School of the Art Institute of Chicago, where 
            she studied painting and drawing. After graduating, she moved to New York 
            City to pursue her art career. Anna's work is inspired by her personal 
            experiences and observations of the world around her. She often uses bright 
            colors and bold lines to create vibrant and energetic paintings. Her work 
            has been exhibited in galleries and museums in New York City and Chicago.    
            """
        );

        System.out.println(person.name);
        System.out.println(person.age);
    }
}

Давайте посмотрим на различные шаги в этом файле:

  • Класс Person определен для представления деталей, описывающих человека (его имя и возраст).
  • Интерфейс PersonExtractor создается с помощью метода, который по неструктурированной текстовой строке возвращает созданный экземпляр Person .
  • extractPerson() помечен аннотацией @UserMessage , которая связывает с ним приглашение. Это приглашение, которое модель будет использовать для извлечения информации и возврата деталей в форме документа JSON, который будет проанализирован для вас и неупорядочен в экземпляре Person .

Теперь давайте посмотрим на содержимое метода main() :

  • Создается экземпляр модели чата.
  • Объект PersonExtractor создается благодаря классу AiServices LangChain4J.
  • Затем вы можете просто вызвать Person person = extractor.extractPerson(...) чтобы извлечь сведения о человеке из неструктурированного текста и получить обратно экземпляр Person с именем и возрастом.

Теперь запустите этот класс с помощью следующей команды:

$ ./gradlew run -DjavaMainClass=palm.workshop.ChatPrompts

> Task :app:run
Anna
23

Да! Это Анна, ей 23!

Что особенно интересно в этом подходе AiServices так это то, что вы работаете со строго типизированными объектами. Вы не взаимодействуете напрямую с чатом LLM. Вместо этого вы работаете с конкретными классами, такими как класс Person для представления извлеченной личной информации, и у вас есть класс PersonExtractor с методом extractPerson() , который возвращает экземпляр Person. Понятие LLM абстрагировано, и как разработчик Java вы просто манипулируете обычными классами и объектами.

7. Поисковая дополненная генерация: общение с документами

Вернемся к разговорам. На этот раз вы сможете задавать вопросы о ваших документах. Вы создадите чат-бота, который сможет извлекать соответствующую информацию из базы данных выдержек из ваших документов, и эта информация будет использоваться моделью для «обоснования» своих ответов, а не для попыток генерировать ответы, полученные в результате ее обучения. Этот шаблон называется RAG, или Retrival Augmented Generation .

Вкратце, в поисковой дополненной генерации есть две фазы:

  1. Фаза приема — документы загружаются, разбиваются на более мелкие фрагменты, а их векторное представление ( «векторное внедрение» ) сохраняется в «векторной базе данных» , способной выполнять семантический поиск.

6c5bb5cb2e3b8088.png

  1. Фаза запроса . Теперь пользователи могут задавать вашему чат-боту вопросы о документации. Вопрос также будет преобразован в вектор и сравнен со всеми другими векторами в базе данных. Наиболее похожие векторы обычно семантически связаны и возвращаются базой данных векторов. Затем LLM предоставляется контекст разговора, фрагменты текста, соответствующие векторам, возвращаемым базой данных, и его просят обосновать свой ответ, просмотрев эти фрагменты.

2c279c506d7606cd.png

Подготовка ваших документов

В этой новой демонстрации вы зададите вопросы об архитектуре нейронной сети «трансформер» , впервые разработанной Google, и именно так в настоящее время реализуются все современные модели больших языков.

Вы можете получить исследовательскую работу, описывающую эту архитектуру («Внимание — это все, что вам нужно»), используя команду wget для загрузки PDF-файла из Интернета:

wget -O attention-is-all-you-need.pdf \
    https://proceedings.neurips.cc/paper_files/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf

Реализация диалоговой поисковой цепочки

Давайте постепенно рассмотрим, как построить двухэтапный подход: сначала с приемом документа, а затем с временем запроса, когда пользователи задают вопросы о документе.

Прием документов

Самый первый шаг на этапе приема документа — найти PDF-файл, который мы загружаем, и подготовить PdfParser для его чтения:

PdfDocumentParser pdfParser = new PdfDocumentParser();
Document document = pdfParser.parse(
    new FileInputStream(new File("/home/YOUR_USER_NAME/palm-workshop/attention-is-all-you-need.pdf")));

Вместо создания обычной модели языка чата перед этим вы создадите экземпляр модели «встраивания» . Это особая модель и конечная точка, роль которой заключается в создании векторных представлений фрагментов текста (слов, предложений или даже абзацев).

VertexAiEmbeddingModel embeddingModel = VertexAiEmbeddingModel.builder()
    .endpoint("us-central1-aiplatform.googleapis.com:443")
    .project("YOUR_PROJECT_ID")
    .location("us-central1")
    .publisher("google")
    .modelName("textembedding-gecko@001")
    .maxRetries(3)
    .build();

Далее вам понадобится несколько классов для совместной работы, чтобы:

  • Загрузите и разделите PDF-документ на части.
  • Создайте векторные вложения для всех этих фрагментов.
InMemoryEmbeddingStore<TextSegment> embeddingStore = 
    new InMemoryEmbeddingStore<>();

EmbeddingStoreIngestor storeIngestor = EmbeddingStoreIngestor.builder()
    .documentSplitter(DocumentSplitters.recursive(500, 100))
    .embeddingModel(embeddingModel)
    .embeddingStore(embeddingStore)
    .build();
storeIngestor.ingest(document);

EmbeddingStoreRetriever retriever = EmbeddingStoreRetriever.from(embeddingStore, embeddingModel);

Экземпляр InMemoryEmbeddingStore , векторной базы данных в памяти, создается для хранения векторных векторных представлений.

Документ разбивается на части благодаря классу DocumentSplitters . Текст PDF-файла будет разделен на фрагменты по 500 символов с перекрытием в 100 символов (со следующим фрагментом, чтобы не разбивать слова или предложения на части).

«Приемник» хранилища связывает разделитель документов, модель внедрения для расчета векторов и базу данных векторов в памяти. Затем метод ingest() позаботится о приеме данных.

Теперь первый этап завершен, документ преобразован в текстовые фрагменты со связанными с ними векторными представлениями и сохранен в базе данных векторов.

Задавать вопросы

Пришло время быть готовым задавать вопросы! Для начала разговора можно создать обычную модель чата:

VertexAiChatModel model = VertexAiChatModel.builder()
    .endpoint("us-central1-aiplatform.googleapis.com:443")
    .project("YOUR_PROJECT_ID")
    .location("us-central1")
    .publisher("google")
    .modelName("chat-bison@001")
    .maxOutputTokens(1000)
    .build();

Вам также понадобится класс «retriever» , который свяжет векторную базу данных (в переменной embeddingStore ) и модель внедрения. Его задача — выполнить запрос к базе данных векторов, вычислив векторное вложение для запроса пользователя, чтобы найти похожие векторы в базе данных:

EmbeddingStoreRetriever retriever = 
    EmbeddingStoreRetriever.from(embeddingStore, embeddingModel);

На этом этапе вы можете создать экземпляр класса ConversationalRetrievalChain (это просто другое имя для шаблона расширенной генерации извлечения):

ConversationalRetrievalChain rag = ConversationalRetrievalChain.builder()
    .chatLanguageModel(model)
    .retriever(retriever)
    .promptTemplate(PromptTemplate.from("""
        Answer to the following query the best as you can: {{question}}
        Base your answer on the information provided below:
        {{information}}
        """
    ))
    .build();

Эта «цепочка» связывает воедино:

  • Модель языка чата , которую вы настроили ранее.
  • Средство извлечения сравнивает запрос на встраивание вектора с векторами в базе данных.
  • В шаблоне подсказки явно указано, что модель чата должна отвечать, основываясь на предоставленной информации (т. е. соответствующих выдержках из документации, векторное внедрение которых аналогично вектору вопроса пользователя).

И теперь вы, наконец, готовы задавать вопросы!

String result = rag.execute("What neural network architecture can be used for language models?");
System.out.println(result);
System.out.println("------------");

result = rag.execute("What are the different components of a transformer neural network?");
System.out.println(result);
System.out.println("------------");

result = rag.execute("What is attention in large language models?");
System.out.println(result);
System.out.println("------------");

result = rag.execute("What is the name of the process that transforms text into vectors?");
System.out.println(result);

Запустите программу с помощью:

$ ./gradlew run -DjavaMainClass=palm.workshop.ChatPrompts

В выводе вы должны увидеть ответ на ваши вопросы:

The Transformer is a neural network architecture that can be used for 
language models. It is based solely on attention mechanisms, dispensing 
with recurrence and convolutions. The Transformer has been shown to 
outperform recurrent neural networks and convolutional neural networks on 
a variety of language modeling tasks.
------------
The Transformer is a neural network architecture that can be used for 
language models. It is based solely on attention mechanisms, dispensing 
with recurrence and convolutions. The Transformer has been shown to 
outperform recurrent neural networks and convolutional neural networks on a 
variety of language modeling tasks. The Transformer consists of an encoder 
and a decoder. The encoder is responsible for encoding the input sequence 
into a fixed-length vector representation. The decoder is responsible for 
decoding the output sequence from the input sequence. The decoder uses the 
attention mechanism to attend to different parts of the input sequence when 
generating the output sequence.
------------
Attention is a mechanism that allows a neural network to focus on specific 
parts of an input sequence. In the context of large language models, 
attention is used to allow the model to focus on specific words or phrases 
in a sentence when generating output. This allows the model to generate 
more relevant and informative output.
------------
The process of transforming text into vectors is called word embedding. 
Word embedding is a technique that represents words as vectors in a 
high-dimensional space. The vectors are typically learned from a large 
corpus of text, and they capture the semantic and syntactic relationships 
between words. Word embedding has been shown to be effective for a variety 
of natural language processing tasks, such as machine translation, question 
answering, and sentiment analysis.

Полное решение

Чтобы облегчить копирование и вставку, вот полное содержимое класса ChatPrompts :

package palm.workshop;

import dev.langchain4j.chain.ConversationalRetrievalChain;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.parser.PdfDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment; 
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.vertexai.VertexAiChatModel;
import dev.langchain4j.model.vertexai.VertexAiEmbeddingModel;
import dev.langchain4j.retriever.EmbeddingStoreRetriever;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class ChatPrompts {
    public static void main(String[] args) throws IOException {
        PdfDocumentParser pdfParser = new PdfDocumentParser();
        Document document = pdfParser.parse(new FileInputStream(new File("/ABSOLUTE_PATH/attention-is-all-you-need.pdf")));

        VertexAiEmbeddingModel embeddingModel = VertexAiEmbeddingModel.builder()
            .endpoint("us-central1-aiplatform.googleapis.com:443")
            .project("YOUR_PROJECT_ID")
            .location("us-central1")
            .publisher("google")
            .modelName("textembedding-gecko@001")
            .maxRetries(3)
            .build();

        InMemoryEmbeddingStore<TextSegment> embeddingStore = 
            new InMemoryEmbeddingStore<>();

        EmbeddingStoreIngestor storeIngestor = EmbeddingStoreIngestor.builder()
            .documentSplitter(DocumentSplitters.recursive(500, 100))
            .embeddingModel(embeddingModel)
            .embeddingStore(embeddingStore)
            .build();
        storeIngestor.ingest(document);

        EmbeddingStoreRetriever retriever = EmbeddingStoreRetriever.from(embeddingStore, embeddingModel);

        VertexAiChatModel model = VertexAiChatModel.builder()
            .endpoint("us-central1-aiplatform.googleapis.com:443")
            .project("genai-java-demos")
            .location("us-central1")
            .publisher("google")
            .modelName("chat-bison@001")
            .maxOutputTokens(1000)
            .build();

        ConversationalRetrievalChain rag = ConversationalRetrievalChain.builder()
            .chatLanguageModel(model)
            .retriever(retriever)
            .promptTemplate(PromptTemplate.from("""
                Answer to the following query the best as you can: {{question}}
                Base your answer on the information provided below:
                {{information}}
                """
            ))
            .build();

        String result = rag.execute("What neural network architecture can be used for language models?");
        System.out.println(result);
        System.out.println("------------");

        result = rag.execute("What are the different components of a transformer neural network?");
        System.out.println(result);
        System.out.println("------------");

        result = rag.execute("What is attention in large language models?");
        System.out.println(result);
        System.out.println("------------");

        result = rag.execute("What is the name of the process that transforms text into vectors?");
        System.out.println(result);
    }
}

8. Поздравления

Поздравляем, вы успешно создали свое первое чат-приложение с генеративным искусственным интеллектом на Java, используя LangChain4J и PaLM API! Попутно вы обнаружили, что большие модели языковых чатов довольно мощны и способны решать различные задачи, такие как вопросы/ответы, даже в вашей собственной документации, извлечение данных, и в некоторой степени они даже могут играть в шахматы!

Что дальше?

Ознакомьтесь со следующими лабораториями кода, чтобы продолжить работу с PaLM на Java:

Дальнейшее чтение

Справочная документация