Java与GPT4All集成打造自己的AI


GPT4All 是一个生态系统,用于训练和部署强大的定制大语言模型 (LLM),这些模型在没有特殊功能(例如 GPU)的标准机器上本地运行。

GPT4All 在人工智能革命中打开了一扇新的大门,让您在不依赖云实例或封闭模型的情况下使用人工智能。GPT4All 可让您私下训练、部署和使用 AI,而无需依赖外部服务提供商。

目标很简单——成为任何个人或企业都可以自由使用、分发和构建的最佳指令调整助理式语言模型。

GPT4All 模型是一个 3GB - 8GB 大小的文件,直接集成到您正在开发的软件中。

可使用自动更新的桌面聊天客户端在您的家庭桌面上本机运行任何 GPT4All 模型。请参阅GPT4All 网站,获取可与此功能强大的桌面应用程序一起运行的开源模型的完整列表。


GPT4All 在C/C++模型后端之上为不同语言提供特定的绑定。目前,提供以下语言绑定:

要安装 GPT4All,请导航至https://gpt4all.io/index.html,选择您的操作系统,然后下载可执行文件。

下载数据模型
启动应用程序时,您首先看到的是一个窗口,可让您决定要下载的数据模型。在本例中,选择GPT4All Falcon并单击“下载”按钮。此过程可能需要一些时间,但最终您将下载模型。该模型是一个获得 Apache-2 许可的聊天机器人,经过大量精心策划的助理交互语料库的训练,包括文字问题、多轮对话、代码、诗歌、歌曲和故事。

下载过程完成后,模型将显示在本地磁盘上。该位置显示在“下载路径Download Path”字段旁边。

构建 Quarkus 应用程序
首先,访问https://code.quarkus.io/并使用 RESTEasy 和 Reactive Jackson 扩展生成一个新的 Quarkus 应用程序。您可以通过单击此链接,使用所有必要的依赖项预先填充这些选项。

在所需的集成开发环境 (IDE) 中打开解压缩的项目。在下一节中,您将添加GPT4All Java绑定依赖项并修改 REST 端点,添加 Quarkus 和 GPT4All 之间的集成代码。

实施 REST API
我们要实现一种 REST 服务,它能接收作为正文内容的问题,使用 GPT4All 本地模型查找答案,然后向调用者回复一条消息。

在添加任何新代码之前,将默认路径定义从 @Path("/hello") 重命名为 @Path("/interact"),以准确显示端点的目的。

接下来,打开 GreetingResource.java 文件并创建以下方法,开始定义 REST 方法签名:

import jakarta.ws.rs.POST;



@POST

@Consumes(MediaType.TEXT_PLAIN)

@Produces(MediaType.TEXT_PLAIN)

public String hello(String content) { }

该方法将包含与 GPT4All 交互所需的所有逻辑,并在类路径中添加 GPT4All Java 绑定。

添加 GPT4All Java 绑定
打开 pom.xml 文件并添加以下依赖项:

<dependency>
   <groupId>com.hexadevlabs</groupId>
   <artifactId>gpt4all-java-binding</artifactId>
   <version>1.1.5</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-simple</artifactId>
  <version>1.7.36</version>
</dependency>

第一个依赖项是 Java 绑定库,用于从 Java 代码与模型进行交互;第二个依赖项不是强制性的,但被 GPT4All 用作日志框架。

接下来,在查询模型之前,需要配置和初始化 Java 绑定类。再次打开 GreetingResource.java 类,复制以下代码,配置包含数据模型的位置和文件(记得在上一节中,我们提到过这个目录很重要),并初始化所有内容。

// Import required
import com.hexadevlabs.gpt4all.LLModel;

// Quarkus configuration properties specifying the location of the data model
@ConfigProperty(name = "model.path")
String baseModelPath;

@ConfigProperty(name = "model.file")
String modelFilePath;

// GPT4All facade classes
LLModel model;
LLModel.GenerationConfig config;

// Configures and Initialize the model
@PostConstruct
public void initModel() {

    java.nio.file.Path modelPath = java.nio.file.Path.of(baseModelPath, modelFilePath);

    model = new LLModel(modelPath);
    config = LLModel.config()
            .withNPredict(4096).build();
}

// When the application is shutting down, the model is closed
@PreDestroy
public void cleanModel() throws Exception {
    model.close();
}
下一步是在 hello 方法中填入集成代码:

// Creates the object required by GPT4All to send the question



final List<Map<String, String>> message = List.of(createMessage(content));



// Sends the question to the model

final LLModel.ChatCompletionResponse chatCompletionResponse =

       model.chatCompletion(message, config);



// Returns the response

return chatCompletionResponse.choices.toString();



The full version of the GreetingResource.java class is shown in the following snippet:



package org.acme;



import com.hexadevlabs.gpt4all.LLModel;

import jakarta.annotation.PostConstruct;

import jakarta.annotation.PreDestroy;

import jakarta.ws.rs.Consumes;

import jakarta.ws.rs.POST;

import jakarta.ws.rs.Path;

import jakarta.ws.rs.Produces;

import jakarta.ws.rs.core.MediaType;

import java.util.List;

import java.util.Map;

import org.eclipse.microprofile.config.inject.ConfigProperty;



@Path(
"/interact")

public class GreetingResource {



    @ConfigProperty(name =
"model.path")

    String baseModelPath;



    @ConfigProperty(name =
"model.file")

    String modelFilePath;



    LLModel model;



    LLModel.GenerationConfig config;



    @PostConstruct

    public void initModel() {

        java.nio.file.Path modelPath = java.nio.file.Path.of(baseModelPath, modelFilePath);

        model = new LLModel(modelPath);

        config = LLModel.config()

            .withNPredict(4096).build();

    }



    @PreDestroy

    public void cleanModel() throws Exception {

        model.close();

    }



    @POST

    @Consumes(MediaType.TEXT_PLAIN)

    @Produces(MediaType.TEXT_PLAIN)

    public String hello(String content) {



       
// Creates the object required by GPT4All to send the question

        final List<Map<String, String>> message = List.of(createMessage(content));



       
// Sends the question to the model

        final LLModel.ChatCompletionResponse chatCompletionResponse =

            model.chatCompletion(message, config);



       
// Returns the response

        return chatCompletionResponse.choices.toString();

    }



    private Map<String, String> createMessage(String content) {

        return Map.of(
"role", "user", "content", content);

    }

}

在部署解决方案之前,最后要做的是在 application.properties 文件中配置位置参数:

# Model file, if you download another model, then the file is a different one

model.file=ggml-model-gpt4all-falcon-q4_0.bin



# Directory where model is stored

model.path=/Users/asotobu/Library/Application Support/nomic.ai/GPT4All


运行应用程序
我们将像任何其他 Java 应用程序一样打包并启动该应用程序。打开终端窗口并从项目的根目录运行以下命令:

./mvnw clean package -DskipTests

该过程完成后,通过运行以下命令启动应用程序:

java -Xms8G -Xmx8G -jar target/quarkus-app/quarkus-run.jar

在另一个终端窗口中,让我们向我们的服务发送一个请求,要求编写一个将字符串编码为 Base64 的 Java 类:

curl -X 'POST' \

  'http://localhost:8080/hello' \

  -H 'accept: text/plain' \

  -H 'Content-Type: text/plain' \

  -d 'write a Java program that encodes to base64'

此操作可能需要几秒钟或几分钟,具体取决于启动应用程序时定义的内存量、CPU 和 JVM 堆等。可能需要一些耐心。

输出应类似于以下内容:

[{role=assistant, content=

下面是将字符串编码为 Base64 的示例 Java 程序:

java

import java.util.Base64;



public class Base64Encoder {

    public static void main(String[] args) throws Exception {

        String str = "Hello World!";

        String base64Encoded = Base64.encodeToString(str.getBytes(), Base64.DEFAULT);

        System.out.println(base64Encoded);

    }

}

该程序使用包Base64中的类java.util将字符串编码"Hello World!"为 Base64。该Base64.encodeToString方法将要编码的字符串和编码算法(在本例中Base64.DEFAULT为 )作为参数,并返回 Base64 编码的字符串。

请注意,该类Base64也可用于解码 Base64 编码的字符串。

到目前为止,令人印象深刻 - 该应用程序在您的本地计算机上运行,​​没有任何外部方参与。

解析输出
输出为 Markdown 格式,代码块内提供代码,以文本形式解释代码的功能和一些提示。要自动生成源代码文件,您必须解析内容并仅提取 Java 代码块。

commonmark是可用于从 Markdown 文档中解析和提取数据的 Java 库之一。

打开pom.xml文件并添加以下依赖项:

<dependency>
  <groupId>org.commonmark</groupId>
  <artifactId>commonmark</artifactId>
  <version>0.20.0</version>
</dependency>