Java调用ChatGPT(基于SpringBoot和Vue),实现可连续对话和流式输出的ChatGPT API(可自定义实现AI助手)

源码及更详细的介绍说明参见Git上的ReadME.md文档
https://github.com/asleepyfish/chatgpt

本文结合SpringBoot的Demo地址:https://github.com/asleepyfish/chatgpt-demo

流式输出结合Vue的Demo地址:https://github.com/asleepyfish/chatgpt-vue

注意:流式输出在2.4节,请仔细阅读到最后。
如果能对你有所帮助,Github帮忙star⭐一下,点赞收藏,谢谢~

版本更新说明

  • 1.1.5 增加查询账单功能billingUsage(单位:美元),可以选择传入开始和结束日期查询(最多100天),或者不传入参,此时表示查询所有日期账单。
  • 1.1.6 增加自定义OpenAiProxyService功能,支持单个SpringBoot中添加多个OpenAiProxyService实例,每个实例可以拥有个性化的参数;查询账单功能优化。

1. 配置阶段

1.1 依赖引入

pom.xml中引入依赖(当前最新版本为1.1.6,可前往Github页面查看当前最新版本)

        <dependency>
            <groupId>io.github.asleepyfish</groupId>
            <artifactId>chatgpt</artifactId>
            <version>1.1.6</version>
        </dependency>

1.2 配置application.yml文件

Configure related parameters in application.ymlthe file (Optional is an optional parameter)

parameter explain
token Application API KEYS
proxy-host proxy ip
proxy-port proxy port
model (Optional) The model can be filled or not, the default is text-davinci-003
chat-model (Optional) It can be filled or not, and the default is gpt-3.5-turbo (ChatGPT is currently the strongest model, and this model is used to generate answers)
retries (Optional) It refers to the number of re-requests when the chatgpt request fails for the first time. 5)
session-expiration-time (Optional) (unit (min)) is how long the session will be destroyed after no access. When this value is not filled, it means that all questions and answers are under the same session, and the session of the same user will never be destroyed (increasing request consumption)

example:

chatgpt:
  token: sk-xxxxxxxxxxxxxxx
  proxy-host: 127.0.0.1
  proxy-port: xxxx
  session-expiration-time: 30

Among them, token, proxy-host, and proxy-port are required

The session-expiration-time parameter above is very important. It is used to indicate how long the session will be destroyed after no access, so as to achieve continuous dialogue in connection with the context.

The implementation method is to distinguish a session through the user in the ChatCompletionRequest, and the session-expiration-time indicates how long the session will be destroyed after no access.

If you don't understand here, please see the example in Section 2.1

1.3 Adding annotations

Adding the annotations in the figure to the startup class will inject the service into Spring.
insert image description here

2. use

2.1 Generating Answers

A tool class is provided OpenAiUtils, which provides related methods for calling.
The easiest of these to use is:

OpenAiUtils.createChatCompletion(content);// 不建议使用

The input contentparameter is the string of the question entered. But not recommended.

It is recommended to use the following method here. By passing in the value of user and combining session-expiration-timeparameters, it is possible to specify a certain session or a continuous dialogue of a certain user.

OpenAiUtils.createChatCompletion(content, user);// 建议使用

Also provides a generic static method that is

public static List<String> createChatCompletion(ChatCompletionRequest chatCompletionRequest) {
    
    ...}

The input parameters ChatCompletionRequest contain some adjustable parameters of the model.

OpenAiUtilsThe class also provides a number of optional static methods, which can be viewed by yourself.

The return parameter of the above method is a list, because the answer returned by adjusting the parameter ncan return multiple different answers at one time ( a parameter nin ChatCompletionRequestthe class).

2.1.1 Testing

Test code:

@PostMapping("/chatTest")
public List<String> chatTest(String content) {
    
    
    return OpenAiUtils.createChatCompletion(content, "testUser");
}

Post request

Parameter input: Java serialization method

return result:

[
  "\n\nJava序列化是将Java对象转换为字节序列的过程,以便在网络上传输或将其保存到磁盘上。Java提供了两种序列化方式:\n\n1. 基于Serializable接口的序列化\n\nSerializable接口是Java提供的一个标记接口,用于标记一个类可以被序列化。如果一个类实现了Serializable接口,那么它的所有非瞬态字段都会被序列化。序列化的过程可以通过ObjectOutputStream类来实现,反序列化的过程可以通过ObjectInputStream类来实现。\n\n2. 基于Externalizable接口的序列化\n\nExternalizable接口也是Java提供的一个标记接口,用于标记一个类可以被序列化。与Serializable接口不同的是,Externalizable接口需要实现writeExternal和readExternal方法,这两个方法分别用于序列化和反序列化。在序列化的过程中,只有被writeExternal方法显式写入的字段才会被序列化,而在反序列化的过程中,只有被readExternal方法显式读取的字段才会被反序列化。\n\n总的来说,基于Serializable接口的序列化更加简单,但是它会序列化所有非瞬态字段,包括一些不需要序列化的字段,而基于Externalizable接口的序列化可以更加灵活地控制序列化的过程。"
]

Enter again: Is there a more efficient serialization framework

return result:

[
  "是的,Java中有很多高效的序列化框架,以下是一些常用的序列化框架:\n\n1. Protobuf\n\nProtobuf是Google开发的一种高效的序列化框架,它可以将结构化数据序列化为二进制格式,支持多种编程语言。相比于Java自带的序列化方式,Protobuf序列化后的数据更小,解析速度更快。\n\n2. Kryo\n\nKryo是一个快速、高效的Java序列化框架,它可以将Java对象序列化为字节数组,支持多种数据类型。Kryo序列化的速度比Java自带的序列化方式快很多,序列化后的数据也更小。\n\n3. FST\n\nFST是一个高性能的Java序列化框架,它可以将Java对象序列化为字节数组,支持多种数据类型。FST序列化的速度比Java自带的序列化方式快很多,序列化后的数据也更小。\n\n4. Avro\n\nAvro是一个高效的数据序列化系统,它可以将结构化数据序列化为二进制格式,支持多种编程语言。Avro序列化后的数据比Java自带的序列化方式更小,解析速度也更快。\n\n总的来说,这些高效的序列化框架都比Java自带的序列化方式更快、更小、更灵活,可以根据具体的需求选择合适的框架。"
]

It can be seen that the above two questions and answers are in one session, and the parameters mentioned above are the session-expiration-timedestruction time when the session represented by this user has not been accessed for a long time. unit (min)

2.2 Generate pictures

The easiest way to use is

OpenAiUtils.createImage(prompt);

The input parameter represents the description text of the generated image, and also provides a general static method

public static List<String> createImage(CreateImageRequest createImageRequest) {
    
    ...}

CreateImageRequestThere are some parameters that can be used in the input parameters, which nindicate the number of generated pictures and responseFormatthe format of the generated pictures. The format is divided into urltwo b64_jsontypes. If you want to return a url, the returned url will disappear an hour after it is generated. The default value is url.

2.2.1 Testing

test code

    @Test
    public void testGenerateImg() {
    
    
        OpenAiUtils.createImage("英短").forEach(System.out::println);
    }

Results
By default, a url will be generated, click to see the picture.
insert image description here

2.3 Download pictures

Optimized on the basis of 3.2, use it directly responseFormatand b64_jsonthen parse it into a picture and return it. Simple usage is as follows:

OpenAiUtils.downloadImage(prompt, response);

The general method is as follows:

public static void downloadImage(CreateImageRequest createImageRequest, HttpServletResponse response) {
    
    ...}

When CreateImageRequestthe return parameter set in the object nis greater than 1, the picture will be packaged into a zip package and returned, and when it nis equal to 1, the picture will be returned directly.

2.3.1 Testing

test code

@RestController
public class ChatGPTController {
    
    
    @GetMapping("/downloadImage")
    public void downloadImage(String prompt, HttpServletResponse response) {
    
    
        OpenAiUtils.downloadImage(prompt, response);
    }
}

Send a get request, then select Send and Download
insert image description here

The get tool I use is the plug-in Fast Request downloaded in the idea. It is also possible to use Postman, but you have to choose Send and Download. The green arrow in the above picture is Send, and the blue one is Send and Download.

insert image description here

2.4 Generating Streaming Answers

The method for generating streaming answers is OpenAiUtilsthe createStreamChatCompletionmethod. This tool class overloads the methods with multiple parameters of the same name, and the most general method is

public static void createStreamChatCompletion(ChatCompletionRequest chatCompletionRequest, OutputStream os) {
    
    ...}

The easiest way is to

public static void createStreamChatCompletion(String content) {
    
    ...}

The content is the question of this dialogue.

What needs to be emphasized here is that the above-mentioned first method OutputStream osis actually an object that must be passed, and the above-mentioned simplest method is actually System.outthe osobject that is passed by default, that is, the result of the streaming question and answer is displayed on the console of IDEA .

If you need to display the results of the streaming question and answer to other interfaces that can be passed in spontaneously OutputStream os, here is a convenient method:

public static void createStreamChatCompletion(String content, OutputStream os) {
    
    ...}

You only need to input questions, and output stream objects.

An example will be given below for specific description. (Example address of all Demos in this article: https://github.com/asleepyfish/chatgpt-demo )

2.4.1 Streaming answer output to IDEA console

code show as below:

@GetMapping("/streamChat")
public void streamChat(String content) {
    
    
    // OpenAiUtils.createStreamChatCompletion(content, System.out);
    // 下面的默认和上面这句代码一样,是输出结果到控制台
    OpenAiUtils.createStreamChatCompletion(content);
}

Then use Postman or other tools that can send Get requests to send requests.

The results of this test are shown in the Gif below

insert image description here

2.4.2 Streaming answer output to browser page

In the above method, the output stream is passed in System.outan object, which is actually an object PrintStreamand will display the output result to the console.

If you need to display the output results in the browser, you can pass in an HttpServletResponse responseobject from the front end, and responseafter getting this, pass response.getOutputStream()the output stream object createStreamChatCompletioninto the input parameter of the method. At the same time, in order to avoid garbled characters when the result is output to the browser and support streaming output, ContentTypeand is required CharacterEncoding.

The specific code is as follows:

@GetMapping("/streamChatWithWeb")
public void streamChatWithWeb(String content, HttpServletResponse response) throws IOException {
    
    
    // 需要指定response的ContentType为流式输出,且字符编码为UTF-8
    response.setContentType("text/event-stream");
    response.setCharacterEncoding("UTF-8");
    // 禁用缓存
    response.setHeader("Cache-Control", "no-cache");    
    OpenAiUtils.createStreamChatCompletion(content, response.getOutputStream());
}

The Gif diagram of the test result process is as follows:

insert image description here

2.4.3 Streaming answers combined with Vue output to the front-end interface

The called back-end method is the same as 2.4.2the method streamChatWithWebin the section. The front-end only needs to pass in a question on the interface, and click the question button to return the result and stream it into the text box.

The Gif diagram of the test result process is as follows:

insert image description here
Vue3The address of Demo Gitis at the beginning of the article~

2.5 Query bill

Two methods are provided for inquiring bills. The unit of amount is both 美元(USD), and the decimal place is not intercepted in both. You can choose to retain the number of decimal places in the result according to your needs.

The first one is to pass in the start and end dates, and query according to the specified date range:

public String billingUsage(String startDate, String endDate) {
    
    ...}

The range of the startDateneutralization endDateinterval shall not exceed 100 days.

The second method is to query 2022年1月1日the bills from the present:

public String billingUsage() {
    
    ...}

2.5.1 Testing

The test code is as follows:

@GetMapping("/billingUsage")
public void billingUsage() {
    
    
	String monthUsage = OpenAiUtils.billingUsage("2023-04-01", "2023-05-01");
	System.out.println("四月使用:" + monthUsage + "美元");
	String totalUsage = OpenAiUtils.billingUsage();
	System.out.println("一共使用:" + totalUsage + "美元");
}

The test results are as follows:

四月使用:0.9864320000000001美元
一共使用:1.120594美元

3. Expansion

3.1 Customize OpenAiProxyService

OpenAiProxyServiceDue to the initialization and initialization of @Bean in the previous version OpenAiUtils, an instance in SpringBoot is unique.

But sometimes you need to customize multiple OpenAiProxyServiceinstances in the project to assemble different ChatGPTPropertiesinformation (you can instantiate multiple Tokens (sk-xxxxxxxxxxx) for use).

So 1.1.6new custom OpenAiProxyServicefeatures have been added in the version. On the basis of maintaining a global OpenAiUtilsinstance in the original SpringBoot project, different OpenAiProxyServiceinstances can now be customized, and the properties between instances are completely isolated.

Below is a demo to show how to use it.

@GetMapping("/customToken")
public void customToken() {
    
    
	ChatGPTProperties chatGPTProperties = new ChatGPTProperties();
	chatGPTProperties.setToken("sk-002xxxxxxxxxxxxxxxxxxxxxxxxx");
	chatGPTProperties.setProxyHost("127.0.0.1");
	chatGPTProperties.setProxyPort(7890);
	OpenAiProxyService openAiProxyService = new OpenAiProxyService(chatGPTProperties, Duration.ZERO);
	// 直接使用new出来的openAiProxyService来调用方法,每个OpenAiProxyService都拥有自己的Token。
	// 这样在一个SpringBoot项目中,就可以有多个Token,可以有更多的免费额度供使用了
	openAiProxyService.createStreamChatCompletion("Java的三大特性是什么");
}

In the above method, a new ChatGPTPropertiesobject is newly created, and it is set tokenas sk-002xxxxxxxxxxxxxxxxxxxxxxxxx(here, there is no need to set other attributes except token, proxyHostand , because other attributes have default values. If you need to modify other attributes, you can set them yourself. Note: There is no default value for sessionExpirationTime, which means that the session has no expiration time. If you need to set the session expiration time, please set this value. )proxyPortChatGPTProperties

And application.ymlset in token, sk-001xxxxxxxxxxxxxxxxxxxxxxxxxthis token is the only one for the whole world OpenAitils, so that OpenAiProxyServicea new OpenAiProxyServiceinstance can be generated through the new construction method, and the second parameter of the construction method Duration.ZEROcan be filled directly, indicating that the Http call request has not timed out In time, in subsequent version updates, I will add a construction method with only one input parameter.

In this way, you can directly use the new one openAiProxyServiceto call the method, each OpenAiProxyServiceof which has its own Token.

In a SpringBoot project, there can be more than one Token, and more free quotas can be used.

3 AI assistant display

Access the WeChat official account, and the AI ​​assistant can automatically reply.
insert image description here

Guess you like

Origin blog.csdn.net/qq_41821963/article/details/128992539