利用生成式 AI 通过 PaLM 和 LangChain4J 以 Java 与用户和文档聊天

1. 简介

上次更新日期:2024 年 2 月 5 日

什么是生成式 AI

生成式 AI(生成式人工智能)是指使用 AI 来创作新内容,例如文本、图片、音乐、音频和视频。

生成式 AI 基于可以执行多任务处理和执行开箱即用任务(包括摘要、问答、分类等)的基础模型(大型 AI 模型)。此外,基础模型只需极少的训练即可针对目标应用场景进行调整,而且只需要非常少量的示例数据。

生成式 AI 的工作原理是什么?

生成式 AI 使用机器学习 (ML) 模型来学习包含人类创建内容的数据集中的模式和关系。然后,它会使用学到的模式生成新内容。

训练生成式 AI 模型的最常用方法是使用监督式学习,即为模型提供一组人类创建的内容及相应的标签。然后,它会学习如何生成类似于人工创建的内容,并使用相同的标签进行标记。

常见的生成式 AI 应用有哪些?

生成式 AI 可处理大量内容,并通过文本、图片和方便用户使用的格式提供数据洞见和答案。生成式 AI 可用于:

  • 通过增强的聊天和搜索体验改善客户互动
  • 通过对话界面和摘要探索大量非结构化数据
  • 协助处理重复性任务,例如回复提案请求 (RFP)、将营销内容本地化为五种语言,以及检查客户合同是否合规等

Google Cloud 提供哪些生成式 AI 产品?

借助 Vertex AI,您可以与基础模型进行交互、自定义基础模型并将其嵌入您的应用 - 几乎不需要机器学习专业知识。在 Model Garden 上访问基础模型,通过 Generative AI Studio 中的简单界面对模型进行调参,或者在数据科学笔记本中使用模型。

Vertex AI Search and Conversation 为开发者提供了构建生成式 AI 赋能的搜索引擎和聊天机器人的最快方法。

Duet AI 是 AI 赋能的协作工具,可在 Google Cloud 和 IDE 中使用,帮助您更快地完成更多任务。

此 Codelab 侧重于哪些内容?

此 Codelab 重点介绍了托管在 Google Cloud Vertex AI 上的 PaLM 2 大语言模型 (LLM),其中包括所有机器学习产品和服务。

您将使用 Java 与 PaLM API 进行交互,并结合使用 LangChain4J LLM 框架编排系统。您将通过不同的具体示例,利用 LLM 进行问答、生成想法、提取实体和结构化内容,以及生成摘要。

请详细讲讲 LangChain4J 框架!

LangChain4J 框架是一个开源库,通过编排各种组件(例如 LLM 本身),以及矢量数据库(用于语义搜索)、文档加载器和拆分器(用于分析文档并从中学习)、输出解析器等,将大型语言模型集成到 Java 应用中。

c6d7f7c3fd0d2951.png

学习内容

  • 如何设置 Java 项目以使用 PaLM 和 LangChain4J
  • 如何从非结构化内容中提取有用信息(实体或关键字提取,JSON 输出)
  • 如何创建与用户对话
  • 如何使用聊天模型在自己的文档中提问

所需条件

  • 了解 Java 编程语言
  • Google Cloud 项目
  • 浏览器,例如 Chrome 或 Firefox

2. 设置和要求

自定进度的环境设置

  1. 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个

295004821bab6a87

37d264871000675d

96d86d3d5655cdbe.png

  • 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时对其进行更新。
  • 项目 ID 在所有 Google Cloud 项目中是唯一的,并且是不可变的(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(通常用 PROJECT_ID 标识)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且此 ID 在项目期间会一直保留。
  • 此外,还有第三个值,即部分 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档
  1. 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有的话)。若要关闭资源以避免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除项目。Google Cloud 新用户符合参与 300 美元免费试用计划的条件。

启动 Cloud Shell

虽然 Google Cloud 可以通过笔记本电脑远程操作,但在此 Codelab 中,您将使用 Cloud Shell,这是一个在云端运行的命令行环境。

激活 Cloud Shell

  1. 在 Cloud Console 中,点击激活 Cloud Shelld1264ca30785e435.png

cb81e7c8e34bc8d.png

如果这是您第一次启动 Cloud Shell,系统会显示一个中间屏幕,说明它是什么。如果您看到中间屏幕,请点击继续

d95252b003979716.png

预配和连接到 Cloud Shell 只需花几分钟时间。

7833d5e1c5d18f54

这个虚拟机装有所需的所有开发工具。它提供了一个持久的 5 GB 主目录,并在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。您在此 Codelab 中的大部分(即使不是全部)工作都可以通过浏览器完成。

在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且相关项目已设为您的项目 ID。

  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. 准备开发环境

在此 Codelab 中,您将使用 Cloud Shell 终端和代码编辑器来开发 Java 程序。

启用 Vertex AI API

  1. 在 Google Cloud 控制台中,确保您的项目名称显示在 Google Cloud 控制台的顶部。如果不是,请点击选择项目以打开项目选择器,然后选择所需的项目。
  2. 如果您没有进入 Google Cloud 控制台的 Vertex AI 部分,请执行以下操作:
  3. 搜索中,输入 Vertex AI,然后返回
  4. 在搜索结果中,点击 Vertex AI。系统随即会显示 Vertex AI 信息中心。
  5. 点击 Vertex AI 信息中心内的启用所有推荐的 API

这将启用多个 API,但对于此 Codelab,最重要的一个是 aiplatform.googleapis.com,您也可以在命令行上、在 Cloud Shell 终端中运行以下命令来启用该 API:

$ 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

您将使用 Java 语言(选项 3)构建应用(选项 3),不使用子项目(选项 1),使用构建文件的 Groovy 语法(选项 1),不使用新的构建功能(选项 4),使用 JUnit Jupiter(选项 4)生成测试,对于项目名称,您可以使用 palmSimilar-workshoppalmSimilar-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)
    }
}

还需要进行一项更改:更新 app/build.gradleapplication 代码块,让用户能够在调用构建工具时替换要在命令行上运行的主类:

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

如需检查 build 文件是否已准备好运行您的应用,您可以运行默认主类,该类会输出一条简单的 Hello World! 消息:

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

> Task :app:run
Hello World!

BUILD SUCCESSFUL in 3s
2 actionable tasks: 2 executed

现在,您可以使用 LangChain4J 项目来使用 PaLM 大语言文本模型进行编程了!

以下是完整的 app/build.gradle build 文件现在应如下所示,供您参考:

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 聊天模型

现在项目已正确设置,可以调用 PaLM API 了。

app/src/main/java/palm/workshop 目录中创建一个名为 ChatPrompts.java 的新类(在默认的 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 提供的一种更高级别的抽象,用于将不同的组件配置在一起以处理对话(例如聊天语言模型本身),但也可能配置其他组件来处理聊天对话的历史记录,或者插入其他工具(例如检索器),以便从矢量数据库中获取信息。但不用担心,我们稍后会在此 Codelab 中继续讨论这一点。

然后,您将与聊天模型进行多轮对话,询问几个相互关联的问题。首先是了解 LLM,然后问问 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

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 个),因为我们只是想生成下一个招式,而不是完整的国际象棋论文!
  • 接下来,您将创建聊天记忆存储区以保存聊天对话。
  • 您可以创建一个窗口化的聊天记忆,以保留最后的动作。
  • 在聊天记忆中,您添加了一个“系统”消息,告知聊天模型该角色是谁(例如,国际象棋专家)。“系统”消息会添加一些上下文,而“用户”和“AI”才是真正的讨论
  • 您将创建一个将记忆和聊天模型结合在一起的对话链。
  • 然后,我们提供白色动作列表,您可以对其进行迭代。每次执行下一个白色动作时链都会执行,聊天模型以下一个最佳动作回复。

当您运行此类移动操作时,您应该会看到以下输出:

$ ./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(便携式游戏标记)文件。PGN不过,这个聊天机器人可能无法战胜 AlphaZero(打败最佳围棋、将棋和国际象棋的 AI),并且对话可能会随着模型无法真正记住游戏的真实状态而偏离发展趋势。

聊天模型功能非常强大,可以与用户进行丰富的互动,并处理各种上下文任务。在下一部分中,我们将了解一项有用的任务:从文本中提取结构化数据

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() 方法的内容:

  • 聊天模型已实例化。
  • 得益于 LangChain4J 的 AiServices 类,系统创建了一个 PersonExtractor 对象。
  • 然后,只需调用 Person person = extractor.extractPerson(...) 即可从非结构化文本中提取人物的详细信息,并获取包含姓名和年龄的 Person 实例。

现在,使用以下命令运行此类:

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

> Task :app:run
Anna
23

是的!我是 Anna,她今年 23 岁!

使用此 AiServices 方法尤其值得注意的是,您可以使用强类型对象。您不会直接与聊天 LLM 互动。而是使用具体的类(例如用于表示所提取个人信息的 Person 类),并有一个 PersonExtractor 类,其中包含会返回 Person 实例的 extractPerson() 方法。LLM 的概念被抽象化了,作为 Java 开发者,您只需操控普通类和对象。

7. Retrieval Augmented Generation:与您的文档聊天

我们再回到对话。届时,您可以询问与您的文档相关的问题。您将构建一个聊天机器人,它能够从文档提取的数据库中检索相关信息,然后模型会将这些信息用于“评估”其答案,而不是尝试生成来自其训练的回答。这种模式称为 RAG,即“检索增强生成”。

简而言之,在“检索增强生成”中,分为两个阶段:

  1. 提取阶段 — 加载文档,将其拆分为较小的区块,然后将文档的向量表示(“向量嵌入”)存储在能够执行语义搜索的“向量数据库”中。

6c5bb5cb2e3b8088

  1. 查询阶段 - 用户现在可以向聊天机器人询问有关文档的问题。此问题也将转换为一个向量,并与数据库中的所有其他向量进行比较。最相似的向量通常在语义上相关,并由向量数据库返回。然后,向 LLM 提供对话上下文,即与数据库返回的向量对应的文本片段,并要求 LLM 查看这些片段,以得出答案。

2c279c506d7606cd.png

准备文档

在这个新的演示中,您将询问有关由 Google 开创的“transformer”神经网络架构的问题,当今所有现代大语言模型都是通过该架构实现的。

您可以使用 wget 命令从互联网下载 PDF 文件,以便检索描述此架构的研究论文(“您需要注意的就是这些内容”):

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

实现对话式检索链

我们来逐一探讨如何构建 2 阶段式方法,首先介绍文档提取,然后再介绍用户针对文档提问时的查询时间。

文档提取

文档提取阶段的第一步是找到我们下载的 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")));

在此之前,您需要创建一个 "embedding" 模型实例,而不是创建常见的聊天语言模型。这是一个特定的模型和端点,其作用是创建文本片段(字词、句子甚至段落)的向量表示。

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();

您还需要一个将矢量数据库(在 embeddingStore 变量中)和嵌入模型的 "retriever" 类。它的任务是通过计算用户查询的矢量嵌入来查询矢量数据库,以便在数据库中查找类似的矢量:

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. 恭喜

恭喜!您已成功使用 LangChain4J 和 PaLM API 以 Java 构建了您的第一个生成式 AI 聊天应用!在此过程中,您发现大语言聊天模型非常强大,能够处理各种任务(例如问答),甚至可处理您自己的文档,还可以提取数据,在某种程度上甚至能够玩一些国际象棋!

后续操作

查看以下 Codelab,进一步在 Java 中使用 PaLM:

深入阅读

参考文档