文章目录

  • 1.前言
  • 2.对接流程
    • 2.1.API-Key的获取
    • 2.2.API用量的查看
    • 2.3.核心代码实现
      • 2.3.1.pom依赖
      • 2.3.2.实体类ChatMessage.java
      • 2.3.3.实体类ChatCompletionRequest.java
      • 2.3.4.实体类ExecuteRet .java
      • 2.3.5.实体类ChatCompletionChoice .java
      • 2.3.6.接口调用核心类OpenAiApi .java
      • 2.3.7.定义接口常量类PathConstant.class
      • 2.3.8.接口调用调试单元测试类OpenAiApplicationTests.class
      • 2.3.9.结果演示
    • 2.4.连续对话实现
      • 2.4.1连续对话的功能实现
      • 2.4.2结果如下:
  • 4.常见问题
      • 4.1.OpenAi接口调用不通
      • 4.2.接口返回401
  • 5.总结

1.前言

大家好,我是王老狮,近期OpenAI开放了chatGPT的最新gpt-3.5-turbo模型,据介绍该模型是和当前官网使用的相同的模型,如果你还没体验过ChatGPT,那么今天就教大家如何打破网络壁垒,打造一个属于自己的智能助手把。本文包括API Key的申请以及网络代理的搭建,那么事不宜迟,我们现在开始。

2.对接流程

2.1.API-Key的获取

首先第一步要获取OpenAI接口的API Key,该Key是你用来调用接口的token,主要用于接口鉴权。获取该key首先要注册OpenAi的账号,具体可以见我的另外一篇文章,ChatGPT保姆级注册教程。

  1. 打开https://platform.openai.com/网站,点击view API Key,

  1. 点击创建key

  1. 弹窗显示生成的key,记得把key复制,不然等会就找不到这个key了,只能重新创建。

将API Key保存好以备用

2.2.API用量的查看

这里可以查看API的使用情况,新账号注册默认有5美元的试用额度,之前都是18美元,API成本降了之后试用额度也狠狠地砍了一刀啊,哈哈。

2.3.核心代码实现

2.3.1.pom依赖

4.0.0com.webtapwebtap0.0.1jarorg.springframework.bootspring-boot-starter-parent2.1.2.RELEASEorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-thymeleafnz.net.ultraq.thymeleafthymeleaf-layout-dialectorg.springframework.bootspring-boot-starter-data-jpaorg.springframework.bootspring-boot-devtoolsorg.springframework.bootspring-boot-starter-testorg.springframework.bootspring-boot-starter-mailmysqlmysql-connector-javaorg.apache.commonscommons-lang33.4commons-codeccommons-codecorg.jsoupjsoup1.9.2com.alibabafastjson1.2.56net.sourceforge.nekohtmlnekohtml1.9.22com.github.pagehelperpagehelper-spring-boot-starter1.4.1org.projectlomboklombokorg.apache.httpcomponentshttpasyncclient4.0.2org.apache.httpcomponentshttpcore-nio4.3.2org.apache.httpcomponentshttpclient4.3.5commons-codeccommons-codeccommons-httpclientcommons-httpclient3.1commons-codeccommons-codecorg.mybatis.spring.bootmybatis-spring-boot-starter1.3.1com.github.ulisesbocchiojasypt-spring-boot-starter2.0.0org.springframework.bootspring-boot-maven-plugin

2.3.2.实体类ChatMessage.java

用于存放发送的消息信息,注解使用了lombok,如果没有使用lombok可以自动生成构造方法以及get和set方法

@Data@NoArgsConstructor@AllArgsConstructorpublic class ChatMessage {//消息角色String role;//消息内容String content;}

2.3.3.实体类ChatCompletionRequest.java

用于发送的请求的参数实体类,参数释义如下:

model:选择使用的模型,如gpt-3.5-turbo

messages :发送的消息列表

temperature :温度,参数从0-2,越低表示越精准,越高表示越广发,回答的内容重复率越低

n :回复条数,一次对话回复的条数

stream :是否流式处理,就像ChatGPT一样的处理方式,会增量的发送信息。

max_tokens :生成的答案允许的最大token数

user :对话用户

@Data@Builderpublic class ChatCompletionRequest {String model;List messages;Double temperature;Integer n;Boolean stream;List stop;Integer max_tokens;String user;}

2.3.4.实体类ExecuteRet .java

用于接收请求返回的信息以及执行结果

/** * 调用返回 */public class ExecuteRet {/** * 操作是否成功 */private final boolean success;/** * 返回的内容 */private final String respStr;/** * 请求的地址 */private final HttpMethod method;/** * statusCode */private final int statusCode;public ExecuteRet(booleansuccess, StringrespStr, HttpMethodmethod, intstatusCode) {this.success =success;this.respStr =respStr;this.method =method;this.statusCode =statusCode;}@Overridepublic String toString() {return String.format("[success:%s,respStr:%s,statusCode:%s]", success, respStr, statusCode);}/** *@returnthe isSuccess */public boolean isSuccess() {return success;}/** *@returnthe !isSuccess */public boolean isNotSuccess() {return !success;}/** *@returnthe respStr */public String getRespStr() {return respStr;}/** *@returnthe statusCode */public int getStatusCode() {return statusCode;}/** *@returnthe method */public HttpMethod getMethod() {return method;}}

2.3.5.实体类ChatCompletionChoice .java

用于接收ChatGPT返回的数据

@Datapublic class ChatCompletionChoice {Integer index;ChatMessage message;String finishReason;}

2.3.6.接口调用核心类OpenAiApi .java

使用httpclient用于进行api接口的调用,支持post和get方法请求。

url为配置文件open.ai.url的值,表示调用api的地址:https://api.openai.com/ ,token为获取的api-key。
执行post或者get方法时增加头部信息headers.put("Authorization", "Bearer " + token); 用于通过接口鉴权。

@Slf4j@Componentpublic class OpenAiApi {@Value("${open.ai.url}")private String url;@Value("${open.ai.token}")private String token;private static final MultiThreadedHttpConnectionManagerCONNECTION_MANAGER= new MultiThreadedHttpConnectionManager();static {// 默认单个host最大链接数CONNECTION_MANAGER.getParams().setDefaultMaxConnectionsPerHost(Integer.valueOf(20));// 最大总连接数,默认20CONNECTION_MANAGER.getParams().setMaxTotalConnections(20);// 连接超时时间CONNECTION_MANAGER.getParams().setConnectionTimeout(60000);// 读取超时时间CONNECTION_MANAGER.getParams().setSoTimeout(60000);}public ExecuteRet get(Stringpath, Map headers) {GetMethod method = new GetMethod(url +path);if (headers== null) {headers = new HashMap();}headers.put("Authorization", "Bearer " + token);for (Map.Entry h : headers.entrySet()) {method.setRequestHeader(h.getKey(), h.getValue());}return execute(method);}public ExecuteRet post(Stringpath, Stringjson, Map headers) {try {PostMethod method = new PostMethod(url +path);//log.info("POST Url is {} ", url + path);// 输出传入参数log.info(String.format("POST JSON HttpMethod's Params = %s",json));StringRequestEntity entity = new StringRequestEntity(json, "application/json", "UTF-8");method.setRequestEntity(entity);if (headers== null) {headers = new HashMap();}headers.put("Authorization", "Bearer " + token);for (Map.Entry h : headers.entrySet()) {method.setRequestHeader(h.getKey(), h.getValue());}return execute(method);} catch (UnsupportedEncodingExceptionex) {log.error(ex.getMessage(),ex);}return new ExecuteRet(false, "", null, -1);}public ExecuteRet execute(HttpMethodmethod) {HttpClient client = new HttpClient(CONNECTION_MANAGER);int statusCode = -1;String respStr = null;boolean isSuccess = false;try {client.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF8");statusCode = client.executeMethod(method);method.getRequestHeaders();// log.info("执行结果statusCode = " + statusCode);InputStreamReader inputStreamReader = new InputStreamReader(method.getResponseBodyAsStream(), "UTF-8");BufferedReader reader = new BufferedReader(inputStreamReader);StringBuilder stringBuffer = new StringBuilder(100);String str;while ((str = reader.readLine()) != null) {log.debug("逐行读取String = " + str);stringBuffer.append(str.trim());}respStr = stringBuffer.toString();if (respStr != null) { log.info(String.format("执行结果String = %s, Length = %d", respStr, respStr.length()));} inputStreamReader.close();reader.close();// 返回200,接口调用成功isSuccess = (statusCode == HttpStatus.SC_OK);} catch (IOExceptionex) {} finally {method.releaseConnection();}return new ExecuteRet(isSuccess, respStr,method, statusCode);}}

2.3.7.定义接口常量类PathConstant.class

用于维护支持的api接口列表

public class PathConstant {public static class MODEL {//获取模型列表public static String MODEL_LIST = "/v1/models";}public static class COMPLETIONS {public static String CREATE_COMPLETION = "/v1/completions";//创建对话public static String CREATE_CHAT_COMPLETION = "/v1/chat/completions";}}

2.3.8.接口调用调试单元测试类OpenAiApplicationTests.class

核心代码都已经准备完毕,接下来写个单元测试测试下接口调用情况。

@SpringBootTest@RunWith(SpringRunner.class)public class OpenAiApplicationTests {@Autowiredprivate OpenAiApi openAiApi;@Testpublic void createChatCompletion2() {Scanner in = new Scanner(System.in);String input = in.next();ChatMessage systemMessage = new ChatMessage('user', input);messages.add(systemMessage);ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder().model("gpt-3.5-turbo-0301").messages(messages).user("testing").max_tokens(500).temperature(1.0).build();ExecuteRet executeRet = openAiApi.post(PathConstant.COMPLETIONS.CREATE_CHAT_COMPLETION, JSONObject.toJSONString(chatCompletionRequest),null);JSONObject result = JSONObject.parseObject(executeRet.getRespStr());List choices = result.getJSONArray("choices").toJavaList(ChatCompletionChoice.class);System.out.println(choices.get(0).getMessage().getContent());ChatMessage context = new ChatMessage(choices.get(0).getMessage().getRole(), choices.get(0).getMessage().getContent());System.out.println(context.getContent());}}
  • 使用Scanner 用于控制台输入信息,如果单元测试时控制台不能输入,那么进入IDEA的安装目录,修改以下文件。增加最后一行增加-Deditable.java.test.console=true即可。


  • 创建ChatMessage对象,用于存放参数,role有user,system,assistant,一般接口返回的响应为assistant角色,我们一般使用user就好。

  • 定义请求参数ChatCompletionRequest,这里我们使用3.1日发布的最新模型gpt-3.5-turbo-0301。具体都有哪些模型大家可以调用v1/model接口查看支持的模型。

  • 之后调用openAiApi.post进行接口的请求,并将请求结果转为JSON对象。取其中的choices字段转为ChatCompletionChoice对象,该对象是存放api返回的具体信息。

    接口返回信息格式如下:

    {"id": "chatcmpl-6rNPw1hqm5xMVMsyf6PXClRHtNQAI","object": "chat.completion","created": 1678179420,"model": "gpt-3.5-turbo-0301","usage": {"prompt_tokens": 16,"completion_tokens": 339,"total_tokens": 355},"choices": [{"message": {"role": "assistant","content": "\n\nI. 介绍数字孪生的概念和背景\nA. 数字孪生的定义和意义\nB. 数字孪生的发展历程\nC. 数字孪生在现代工业的应用\n\nII. 数字孪生的构建方法\nA. 数字孪生的数据采集和处理\nB. 数字孪生的建模和仿真\nC. 数字孪生的验证和测试\n\nIII. 数字孪生的应用领域和案例分析\nA. 制造业领域中的数字孪生应用\nB. 建筑和城市领域中的数字孪生应用\nC. 医疗和健康领域中的数字孪生应用\n\nIV. 数字孪生的挑战和发展趋势\nA. 数字孪生的技术挑战\nB. 数字孪生的实践难点\nC. 数字孪生的未来发展趋势\n\nV. 结论和展望\nA. 总结数字孪生的意义和价值\nB. 展望数字孪生的未来发展趋势和研究方向"},"finish_reason": "stop","index": 0}]}
  • 输出对应的信息。

2.3.9.结果演示

2.4.连续对话实现

2.4.1连续对话的功能实现

基本接口调通之后,发现一次会话之后,没有返回完,输入继续又重新发起了新的会话。那么那么我们该如何实现联系上下文呢?其实只要做一些简单地改动,将每次对话的信息都保存到一个消息列表中,这样问答就支持上下文了,代码如下:

List messages = new ArrayList();@Testpublic void createChatCompletion() {Scanner in = new Scanner(System.in);String input = in.next();while (!"exit".equals(input)) {ChatMessage systemMessage = new ChatMessage(ChatMessageRole.USER.value(), input);messages.add(systemMessage);ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder().model("gpt-3.5-turbo-0301").messages(messages).user("testing").max_tokens(500).temperature(1.0).build();ExecuteRet executeRet = openAiApi.post(PathConstant.COMPLETIONS.CREATE_CHAT_COMPLETION, JSONObject.toJSONString(chatCompletionRequest),null);JSONObject result = JSONObject.parseObject(executeRet.getRespStr());List choices = result.getJSONArray("choices").toJavaList(ChatCompletionChoice.class);System.out.println(choices.get(0).getMessage().getContent());ChatMessage context = new ChatMessage(choices.get(0).getMessage().getRole(), choices.get(0).getMessage().getContent());messages.add(context);in = new Scanner(System.in);input = in.next();}}

因为OpenAi的/v1/chat/completions接口消息参数是个list,这个是用来保存我们的上下文的,因此我们只要将每次对话的内容用list进行保存即可。

2.4.2结果如下:

4.常见问题

4.1.OpenAi接口调用不通

因为https://api.openai.com/地址也被限制了,但是接口没有对地区做校验,因此可以自己搭建一个代理

4.2.接口返回401

检查请求方法是否增加token字段以及key是否正确

5.总结

至此JAVA对OpenAI对接就已经完成了,并且也支持连续对话,大家可以在此基础上不断地完善和桥接到web服务,定制自己的ChatGPT助手了。我自己也搭建了个平台,不断地在完善中,具体可见下图,后续会开源出来,想要体验的可以私信我获取地址和账号哈