如果喜欢本文章,记得收藏哦!
关注我,一起学Java。
一、基于ChatGPT API的PC端软件开发过程遇到的问题的分析
最近这个OpenAI公司推出的GPT-4.0模型真是太火了。当然由于OpenAI目前还没有正式全面对外开放GPT-4.0 API,所以本次使用的是GPT-3.5 API。
首先来看一下效果图吧!
本客户端使用的是 JavaFX 开发的。JavaFX 相比于 Swing 来说,JavaFX 支持 CSS 样式,如果使用 Java 来开发 GUI 软件的话,还是推荐使用 JavaFX 的。JavaFX 是 2008 年由 Oracle 公司推出的项目。需要说明的是在高版本的 JDK 中不含有 JavaFX 相关的 API,所以你需要自己安装 JavaFX。因为我开发使用的是 JDK 8 所以无需自己另外安装 JavaFX,直接就可以调用 JavaFX API。
上面的图中我们可以看到使用 JavaFX 编写的 UI 界面不是太好看,没办法 Java 是我的主力编程语言,所以只能用 Java 来编写 UI 界面了。
主要有四个功能,分别是:发送,保存,查看,删除。其中发送是最核心的功能。在发送时会间接调用 GPT-3.5 API,这里为什么说是间接调用而不是直接调用呢,想必大家都知道,这个 OpenAI 公司是不对我们中国地区开放的,虽然我们可以直接访问OpenAI 的官方网站,但是不能访问 OpenAI 的产品ChatGPT。所以这里我们必须要自己使用一个国外的服务器作为中转服务器。很容易理解为啥使用中转服务器就可以访问GPT API,比如你是A,你可以访问B,但是你无法访问C,然而B是可以访问C的,那么你就可以告诉B,让B把信息传递给C。
这里我只讲开发客户端软件遇到的问题,不会讲解如何编写接口。
二、遇到的第一个问题,用户点击发送按钮后,线程阻塞
这个问题主要是因为用户点击了发送按钮后会调用 Hutool 工具类中的 HttpRequest.post() 方法将数据发送到自己定义的接口上。代码如下:
sendButton.setOnAction(e -> sendMessage());
private void sendMessage() {// 获取用户输入的消息并将其添加到聊天区域String prompt = inputArea.getText();// 获取当前时间String nowTime = getNowTime();chatArea.appendText(nowTime + "\n");chatArea.appendText("我说:" + prompt + "\n\n");// 清空输入框inputArea.setText("");// 存储上下文语境messages.add(new Message("user", prompt));// 获取 ChatGPT 的回复String reply = httpRequest(messages);// 机器人回复时间String replyTime = getNowTime();chatArea.appendText(replyTime + "\n");// 把内容显示到 UI 界面上chatArea.appendText("机器人说:" + reply + "\n\n");messages.add(new Message("assistant", reply));
}
在上面的代码中,运行的时候给用户的感觉是不好的,因为在调用 httpRequest(messages) 时会造成线程阻塞。因为在当前线程在进行网络请求时是非常耗时的操作,所以整个 main 线程会阻塞,导致应用卡顿,如果 ChatGPT 一直没有响应结果,那么会一直卡在那里。
或许你们想到的是创建一个新的线程来发送 http 请求就解决了,其实不是的,问题的根源在我们点击“发送”按钮,我们应该在点击发送按钮的时候创建新的线程,当然这里我在发送 http 的时候也是创建了新的线程。代码如下:
sendButton.setOnAction(e -> {Task<Void> task = new Task<Void>() {@Overrideprotected Void call() throws Exception {// 执行耗时操作,例如发送网络请求或执行计算密集型任务sendMessage();// 返回nullreturn null;}};// 在后台线程上执行Tasknew Thread(task).start();// 将操作提交到JavaFX应用程序线程队列中Platform.runLater(() -> {// 在此更新UI或执行其他需要在JavaFX应用程序线程上执行的操作});
});
private void sendMessage() {// 获取用户输入的消息并将其添加到聊天区域String prompt = inputArea.getText();// 获取当前时间String nowTime = getNowTime();chatArea.appendText(nowTime + "\n");chatArea.appendText("我说:" + prompt + "\n\n");// 清空输入框inputArea.setText("");// 存储上下文语境messages.add(new Message("user", prompt));// 获取 ChatGPT 的回复// String reply = httpRequest(messages);// 创建新的线程去发送 ChatGPT 请求FutureTask<String> task = new FutureTask<String>(new Callable<String>() {@Overridepublic String call() throws Exception {return httpRequest(messages);}});new Thread(task).start();try {String reply = task.get();// 机器人回复时间String replyTime = getNowTime();chatArea.appendText(replyTime + "\n");// 把内容显示到 UI 界面上chatArea.appendText("机器人说:" + reply + "\n\n");messages.add(new Message("assistant", reply));} catch (Exception e) {e.printStackTrace();}
}
注意:如果想要更新 UI 界面的内容,那么可以使用Platform.runLater()
。
三、第二个问题是ChatGPT无法进行连续对话,也就是没有上下文语境
这个问题折腾了好久。官方开发文档好像也没有特地说明这一点。我是研究了 GitHub 的代码,并且网上搜索了别人的想法,然后知道必须要把聊天记录再次发送给 ChatGPT API。但是这样就会消耗更多的资金。因为 OpenAI 并非真的是 Open。
我们每次把聊天记录发送给 ChatGPT 就行。
这里我们使用一个集合存放聊天记录,每次把聊天记录追加到集合里面即可。然后把 List 集合发送到 ChatGPT API。
// 存放上下文语境
private List<Message> messages = new ArrayList<>();
// 存储上下文语境
messages.add(new Message("system", "你是一个助手"));
messages.add(new Message("user", prompt));
messages.add(new Message("assistant", reply));
其中 Message 类代码如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Message {private String role;private String content;
}
ChatGPT 开发文档中说明了 role 有三种值,一个是 system,表示让 ChatGPT 充当什么角色,第二种取值是 user,表示用户,第三种是 assistant,表示 ChatGPT。而角色对应的内容存储到 content 变量中。这类似于 map 集合,也就是 role 是 key,content 是 value。
有更多问题欢迎访问博客。