透過 PaLM 和 LangChain4J,以 Java 編寫使用者與文件的生成式 AI 功能

1. 簡介

上次更新時間:2024 年 2 月 5 日

什麼是生成式 AI

生成式 AI 又稱為「生成式人工智慧」,是指使用 AI 技術生成新的文字、圖片、音樂、音訊和影片等內容。

生成式 AI 是由基礎模型 (大型 AI 模型) 驅動,可同時處理多項工作,並立即執行各種任務,包括提供摘要、問與答和分類等。此外,只需要少量訓練,就能以極少的範例資料針對指定用途調整基礎模型。

生成式 AI 的運作方式為何?

生成式 AI 的運作方式是使用機器學習模型,根據一系列人類創作內容的資料來掌握相關模式與關係,再運用學到的模式生成新內容。

「監督式學習」是最常用來訓練生成式 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 中皆可使用,能協助您以更快的速度完成更多工作。

本程式碼研究室的重點為何?

本程式碼研究室著重於 PaLM 2 大型語言模型 (LLM),該模型託管於 Google Cloud Vertex AI,涵蓋所有機器學習產品和服務。

您將使用 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.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • 專案名稱是這個專案參與者的顯示名稱。這是 Google API 未使用的字元字串。你隨時可以更新。
  • 專案 ID 在所有 Google Cloud 專案中都是不重複的,而且設定後即無法變更。Cloud 控制台會自動產生專屬字串,通常您不需要在意該字串為何。在大多數程式碼研究室中,您需要參照專案 ID (通常標示為 PROJECT_ID)。如果您不喜歡產生的 ID,可以產生另一個隨機 ID。你也可以嘗試使用自己的名稱,看看是否可用。完成這個步驟後就無法變更,且專案期間會維持不變。
  • 請注意,有些 API 會使用第三個值,也就是「專案編號」。如要進一步瞭解這三種值,請參閱說明文件
  1. 接著,您需要在 Cloud 控制台中啟用帳單,才能使用 Cloud 資源/API。完成這個程式碼研究室的費用不高,甚至可能完全免費。如要關閉資源,避免在本教學課程結束後繼續產生費用,請刪除您建立的資源或專案。Google Cloud 新使用者可參加 $300 美元的免費試用計畫。

啟動 Cloud Shell

雖然您可以透過筆電遠端操作 Google Cloud,但在本程式碼研究室中,您將使用 Cloud Shell,這是 Cloud 中執行的指令列環境。

啟用 Cloud Shell

  1. 在 Cloud 控制台,點選「啟用 Cloud Shell」 圖示 d1264ca30785e435.png

cb81e7c8e34bc8d.png

如果您是首次啟動 Cloud Shell,系統會顯示中繼畫面,說明這個指令列環境。如果出現中繼畫面,請按一下「繼續」

d95252b003979716.png

佈建並連至 Cloud Shell 預計只需要幾分鐘。

7833d5e1c5d18f54.png

這部虛擬機器已載入所有必要的開發工具,並提供永久的 5 GB 主目錄,而且可在 Google Cloud 運作,大幅提升網路效能並強化驗證功能。本程式碼研究室幾乎所有工作都可在瀏覽器上完成。

連至 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. 準備開發環境

在本程式碼研究室中,您將使用 Cloud Shell 終端機和程式碼編輯器開發 Java 程式。

啟用 Vertex AI API

  1. Google Cloud 控制台中,確認專案名稱顯示在頂端。如果不是,請點按「選取專案」開啟專案選取器,然後選取所需專案。
  2. 如果不在 Google Cloud 控制台的 Vertex AI 部分,請執行下列操作:
  3. 在「搜尋」中輸入「Vertex AI」,然後按下 Enter 鍵
  4. 在搜尋結果中,按一下「Vertex AI」,系統會顯示 Vertex AI 資訊主頁。
  5. 在 Vertex AI 資訊主頁中,點按「啟用所有建議的 API」

這會啟用多個 API,但本程式碼研究室最重要的 API 是 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) 建構應用程式 (選項 2),不使用子專案 (選項 1),並使用建構檔案的 Groovy 語法 (選項 1),不使用新的建構功能 (選項 0),使用 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 有 2 個依附元件:

  • 一個是核心專案,
  • 以及專屬的 Vertex AI 模組。

如要使用 Java 17 編譯及執行程式,請在 plugins {} 區塊下方新增下列區塊:

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

還有一項變更要做:更新 app/build.gradleapplication 區塊,讓使用者在叫用建構工具時,能夠覆寫要在指令列執行的主要類別:

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

現在您可以使用 LangChain4J 專案,透過 PaLM 大型語言文字模型進行程式設計!

如需參考,以下是完整的 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 的聊天模型

專案設定完成後,就可以呼叫 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 提供的高階抽象層,可將不同元件設定在一起,處理對話 (例如對話語言模型本身),但可能也會處理對話記錄,或插入其他工具 (例如檢索器),從向量資料庫擷取資訊。別擔心,我們稍後會在本程式碼研究室中探討這個主題。

接著,您將與聊天模型進行多輪對話,詢問多個相關問題。首先,您會對大型語言模型感到好奇,然後詢問這類模型有哪些用途,以及有哪些範例。請注意,您不必重複說明,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 說明手邊的任務、背景資訊,或許可以提供幾個範例,說明 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),因為我們只想生成下一步棋,而不是整篇西洋棋論文!
  • 接著,您會建立即時通訊記憶體儲存空間,用來儲存即時通訊對話。
  • 您會建立實際的視窗式即時通訊記憶體,保留最後的動作。
  • 在對話記憶體中,您會新增「系統」訊息,指示對話模型應扮演的角色 (例如:西洋棋專家)。「系統」訊息會新增一些背景資訊,而「使用者」和「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 (可攜式遊戲標記) 檔案。不過,這個聊天機器人可能無法擊敗 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() 方法的內容:

  • 即時通訊模型已例項化。
  • PersonExtractor 物件是透過 LangChain4J 的 AiServices 類別建立。
  • 接著,您只要呼叫 Person person = extractor.extractPerson(...),即可從非結構化文字中擷取人員詳細資料,並取得含有姓名和年齡的 Person 執行個體。

現在,請執行下列指令來執行這個類別:

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

> Task :app:run
Anna
23

當然可以!這是 Anna,她今年 23 歲!

這個 AiServices 方法特別有趣的地方在於,您可以使用強型別物件。您不會直接與聊天 LLM 互動,而是使用具體類別,例如代表擷取個人資訊的 Person 類別,並具有 PersonExtractor 類別和 extractPerson() 方法,可傳回 Person 執行個體。LLM 的概念已抽象化,身為 Java 開發人員,您只需操作一般類別和物件。

7. 檢索增強生成:與文件對話

讓我們回到對話。這次你可以詢問文件相關問題。您將建構一個聊天機器人,能夠從文件摘要資料庫擷取相關資訊,並由模型使用這些資訊「做為依據」生成回覆,而不是嘗試根據訓練資料生成回覆。這個模式稱為 RAG,也就是檢索增強生成

簡單來說,檢索增強生成分為兩個階段:

  1. 擷取階段:載入文件、分割成較小的區塊,並將文件的向量表示法 (即「向量嵌入」) 儲存在可執行語意搜尋的「向量資料庫」中。

6c5bb5cb2e3b8088.png

  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

實作對話式檢索鏈

讓我們逐步瞭解如何建構這項兩階段方法,首先是文件擷取,然後是使用者詢問文件相關問題時的查詢時間。

文件匯入

文件擷取階段的第一步,是找出我們下載的 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();

您還需要 「擷取器」類別,連結向量資料庫 (位於 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. 恭喜

恭喜,您已成功使用 LangChain4J 和 PaLM API,以 Java 建構第一個生成式 AI 即時通訊應用程式!您在過程中發現,大型語言聊天模型功能強大,能夠處理各種工作,例如問答 (即使是您自己的文件)、資料擷取,甚至還能下棋!

後續步驟

如要進一步瞭解如何使用 Java 存取 PaLM,請參閱下列程式碼研究室:

其他資訊

參考文件