Java版的GPT的Client
- 可作为其他编程语言的参考
- 注意: 下面包名中的 xxx 可以换成自己的
1 )核心代码结构设计
- src
- main
- java
- com.xxx.gpt.client
- entity
- ChatCompletion.java
- ChatCompletionResponse.java
- ChatChoice.java
- …
- util
- Proxys.java
- …
- ChatApi.java
- ChatGPTClient.java
- entity
- com.xxx.gpt.client
- java
- test
- java
- com.xxx.gpt.client.test
- ChatGPTClientTest.java
- com.xxx.gpt.client.test
- java
- main
- pom.xml
2 ) pom 文件
- 在 pom 文件里面,我们引入了我们需要引用的依赖
- 对于 OpenAI 的API访问,由于它是一个HTTP的接口
- 我们使用的是 okhttp-see
- 然后通过 retrofit 进行一个封装
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>gpt-client</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version><scope>compile</scope><optional>true</optional></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.7</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>1.3.7</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.3.7</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.2</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.19</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.33</version></dependency><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp-sse</artifactId><version>3.14.9</version></dependency><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>logging-interceptor</artifactId><version>3.14.9</version></dependency><dependency><groupId>com.squareup.retrofit2</groupId><artifactId>retrofit</artifactId><version>2.9.0</version></dependency><dependency><groupId>com.squareup.retrofit2</groupId><artifactId>converter-jackson</artifactId><version>2.9.0</version></dependency><dependency><groupId>com.squareup.retrofit2</groupId><artifactId>adapter-rxjava2</artifactId><version>2.9.0</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><dependency><groupId>com.knuddels</groupId><artifactId>jtokkit</artifactId><version>0.4.0</version></dependency></dependencies>
</project>
3 )entity 目录
- 在这个包里面,开发了核心的chat completion相关接口
- 比如对于我们的ChatCompletion请求的一些参数
- 包括 model,包括需要传入的message和temperature以及top_p, functioncall等等的这些参数
- 然后对于它的返回值: ChatCompletionResponse 程序里面
- 包括 id, object, created, model, choice, usage
- 对于ChatChoice, 里面包含 delta, message 和 finishReason
- 比如对于我们的ChatCompletion请求的一些参数
- 这几个类和我们前面去访问 OpenAI 它的API文档里面所对应的相关的属性是一致的
- 这部分照着 API 手册去进行一下相关实体类的开发就可以了
ChatCompletion.java
package com.xxx.gpt.client.entity;import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.xxx.gpt.client.util.TokensUtil;
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;import java.io.Serializable;
import java.util.List;
import java.util.Map;@Data
@Builder
@Slf4j
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ChatCompletion implements Serializable {@NonNull@Builder.Defaultprivate String model = Model.GPT_3_5_TURBO_0613.getName();@NonNullprivate List<Message> messages;/*** 使用什么取样温度,0到2之间。越高越奔放。越低越保守。* <p>* 不要同时改这个和topP*/@Builder.Defaultprivate double temperature = 0.9;/*** 0-1* 建议0.9* 不要同时改这个和temperature*/@JsonProperty("top_p")@Builder.Defaultprivate double topP = 0.9;/*** auto*/String function_call;List<ChatFunction> functions;/*** 结果数。*/@Builder.Defaultprivate Integer n = 1;/*** 是否流式输出.* default:false*/@Builder.Defaultprivate boolean stream = false;/*** 停用词*/private List<String> stop;/*** 3.5 最大支持4096* 4.0 最大32k*/@JsonProperty("max_tokens")private Integer maxTokens;@JsonProperty("presence_penalty")private double presencePenalty;/*** -2.0 ~~ 2.0*/@JsonProperty("frequency_penalty")private double frequencyPenalty;@JsonProperty("logit_bias")private Map logitBias;/*** 用户唯一值,确保接口不被重复调用*/private String user;public int countTokens() {return TokensUtil.tokens(this.model, this.messages);}
}
ChatCompletionResponse.java
package com.xxx.gpt.client.entity;import lombok.Data;
import java.io.Serializable;
import java.util.List;@Data
public class ChatCompletionResponse implements Serializable {private String id;private String object;private long created;private String model;private List<ChatChoice> choices;private Usage usage;
}
ChatChoice.java
package com.xxx.gpt.client.entity;import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;import java.io.Serializable;@Data
public class ChatChoice implements Serializable {private long index;/*** 请求参数stream为true返回是delta*/@JsonProperty("delta")private Message delta;@JsonProperty("message")private Message message;@JsonProperty("finish_reason")private String finishReason;
}
3 )util 目录
- 由于国内的网络没办法直接的去进行访问, 我们添加一个 util/Proxys.java, 基于它去做一个代理
- 里面我们提供两个方法,都是传入我们代理的IP和代理的端口
- 然后返回 Proxy的Type是HTTP,这是对于HTTP的 Proxy
- 再来创建一个socks的 Proxy
Proxys.java
package com.xxx.gpt.client.util;import java.net.InetSocketAddress;
import java.net.Proxy;public class Proxys {public static Proxy http(String ip, int port) {return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ip, port));}public static Proxy socks5(String ip, int port) {return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(ip, port));}
}
4 )创建 ChatApi 接口
- 在ChatAPI里面,我们就添加我们需要去访问的 Open AI 的接口
- 接口是post请求,接口的URI是
v1/chat/completions
- 返回值是我们刚刚创建的实体类
ChatCompletionResponse
,参数是ChatCompleination
- 这是我们要访问的chatAPI它的核心的接口
ChatApi.java
package com.xxx.gpt.client;import com.xxx.gpt.client.entity.ChatCompletion;
import com.xxx.gpt.client.entity.ChatCompletionResponse;
import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.POST;public interface ChatApi {String CHAT_GPT_API_HOST = "https://api.openai.com/";@POST("v1/chat/completions")Single<ChatCompletionResponse> chatCompletion(@Body ChatCompletion chatCompletion);
}
5 )添加 ChatGPTClient
-
在这个类里面定义一些属性:
apiKey
,apiHost
,chatApi
,okHttpClient
,timeout
,proxy
-
再来添加一个init方法
- 在拦截器里添加apikey
- 设置timeout,默认300s
- 设置代理
- 通过 retrofit 实例化chatapi,供我们去进行使用
-
这样就完成了一个ChatGPTClient的一个实例化
-
实例化完成之后呢,我们添加一个调用的方法 chatCompletion,返回值就是我们请求的response
-
现在已经完成了java版本的ChatGPT的client
ChatGPTClient.java
package com.xxx.gpt.client;import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import com.alibaba.fastjson.JSON;
import com.xxx.gpt.client.entity.BaseResponse;
import com.xxx.gpt.client.entity.ChatCompletion;
import com.xxx.gpt.client.entity.ChatCompletionResponse;
import com.xxx.gpt.client.entity.Message;
import io.reactivex.Single;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory;import java.net.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;@Slf4j
@Getter
@Setter
@Builder
public class ChatGPTClient {private String apiKey;private List<String> apiKeyList;@Builder.Defaultprivate String apiHost = ChatApi.CHAT_GPT_API_HOST;private ChatApi apiClient;private OkHttpClient okHttpClient;/*** 超时 默认300*/@Builder.Defaultprivate long timeout = 300;/*** okhttp 代理*/@Builder.Defaultprivate Proxy proxy = Proxy.NO_PROXY;public ChatGPTClient init() {OkHttpClient.Builder client = new OkHttpClient.Builder();client.addInterceptor(chain -> {Request original = chain.request();String key = apiKey;if (apiKeyList != null && !apiKeyList.isEmpty()) {key = RandomUtil.randomEle(apiKeyList);}Request request = original.newBuilder().header(Header.AUTHORIZATION.getValue(), "Bearer " + key).header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue()).method(original.method(), original.body()).build();return chain.proceed(request);}).addInterceptor(chain -> {Request original = chain.request();Response response = chain.proceed(original);if (!response.isSuccessful()) {String errorMsg = response.body().string();log.error("请求异常:{}", errorMsg);BaseResponse baseResponse = JSON.parseObject(errorMsg, BaseResponse.class);if (Objects.nonNull(baseResponse.getError())) {log.error(baseResponse.getError().getMessage());throw new RuntimeException(baseResponse.getError().getMessage());}throw new RuntimeException(errorMsg);}return response;});client.connectTimeout(timeout, TimeUnit.SECONDS);client.writeTimeout(timeout, TimeUnit.SECONDS);client.readTimeout(timeout, TimeUnit.SECONDS);if (Objects.nonNull(proxy)) {client.proxy(proxy);}OkHttpClient httpClient = client.build();this.okHttpClient = httpClient;this.apiClient = new Retrofit.Builder().baseUrl(this.apiHost).client(okHttpClient).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).addConverterFactory(JacksonConverterFactory.create()).build().create(ChatApi.class);return this;}public ChatCompletionResponse chatCompletion(ChatCompletion chatCompletion) {Single<ChatCompletionResponse> chatCompletionResponse =this.apiClient.chatCompletion(chatCompletion);return chatCompletionResponse.blockingGet();}public String chat(String message) {ChatCompletion chatCompletion = ChatCompletion.builder().messages(Arrays.asList(Message.of(message))).build();ChatCompletionResponse response = this.chatCompletion(chatCompletion);return response.getChoices().get(0).getMessage().getContent();}
}
6 )添加测试类
- 需要先对我们的client去进行实例化
- 首先添加一下代理
- 再来添加一个测试的方法
ChatGPTClientTest.java
package com.xxx.gpt.client.test;import com.xxx.gpt.client.ChatGPTClient;
import com.xxx.gpt.client.entity.ChatCompletion;
import com.xxx.gpt.client.entity.ChatCompletionResponse;
import com.xxx.gpt.client.entity.Message;
import com.xxx.gpt.client.entity.Model;
import com.xxx.gpt.client.util.Proxys;
import org.junit.Before;
import org.junit.Test;import java.net.Proxy;
import java.util.Arrays;public class ChatGPTClientTest {private ChatGPTClient chatGPTClient;@Beforepublic void before() {Proxy proxy = Proxys.socks5("127.0.0.1", 7890);chatGPTClient = ChatGPTClient.builder().apiKey("sk-6kchn0DjDasdsdfdqOJqkc3aIso5ct").timeout(900).proxy(proxy).apiHost("https://api.openai.com/").build().init();}@Testpublic void chat() {Message system = Message.ofSystem("你是一个作家,学习过很多古诗");Message message = Message.of("写一首关于青春的七言绝句");ChatCompletion chatCompletion = ChatCompletion.builder().model(Model.GPT_3_5_TURBO.getName()).messages(Arrays.asList(system, message)).maxTokens(3000).temperature(0.9).build();ChatCompletionResponse response = chatGPTClient.chatCompletion(chatCompletion);Message res = response.getChoices().get(0).getMessage();System.out.println(res.getContent());}// @Testpublic void tokens() {Message system = Message.ofSystem("你是一个作家,学习过很多古诗");Message message = Message.of("写一首关于青春的七言绝句");ChatCompletion chatCompletion1 = ChatCompletion.builder().model(Model.GPT_3_5_TURBO.getName()).messages(Arrays.asList(system, message)).maxTokens(3000).temperature(0.9).build();ChatCompletion chatCompletion2 = ChatCompletion.builder().model(Model.TEXT_DAVINCI_003.getName()).messages(Arrays.asList(system, message)).maxTokens(3000).temperature(0.9).build();System.out.println(chatCompletion1.countTokens());System.out.println(chatCompletion2.countTokens());}
}
- 根据前面我们看到的API的文档,构建 Prompt(message)
- 我们构造一个system角色的一个message
- 告诉GPT: 你是一个作家, 写过很多诗,然后默认我们再以用户的角色去实例化一个message
- 让GPT帮我们去写一首关于青春的七言绝句
- 接下来构造我们的request参数
- 设置 model,message,maxTokens,temperature
- 之后执行 build()
- 这里完成了让GPT根据 Prompt 创作了一首诗歌
- 以上是 Java 版本的GPT相关核心代码(网上搜集)
- 可以作为转换成其他编程语言实现的参考