使用chatgpt实现微信聊天小程序的代码示例
作者:秃头披风侠. 发布时间:2022-04-26 17:18:24
前言
前一段时间使用java来调用chatgpt的接口,然后写了一个简单小程序,java调用chatgpt接口,实现专属于自己的人工智能助手,事实上,这个程序毛病挺多的,最不能让人接受的一点就是返回速度非常缓慢(即使使用非常好的外网服务器)。
现在,我改进了一下程序,使用异步请求的方式,基本可以实现秒回复。并且还基于webSocket编写了一个微信小程序来进行交互,可以直接使用微信小程序来进行体验。
效果展示
部分截图如下
原理说明
在 java调用chatgpt接口,实现专属于自己的人工智能助手 我说明了java调用chatgpt的基本原理,这里的代码就是对这个代码的改进,使用异步请求的方式来进行。
注意看官方文档,我们在请求时可以提供一个参数stream,然后就可以实现按照流的形式进行返回,这种方式基本可以做到没有延迟就给出答案。
由于这次改进的思路主要就是将请求改为了异步,其他的基本一样,所以就不做解释,直接给出代码了,代码上面都有注释
/**
* 这个方法用于测试的,可以在控制台打印输出结果
*
* @param chatGptRequestParameter 请求的参数
* @param question 问题
*/
public void printAnswer(ChatRequestParameter chatGptRequestParameter, String question) {
asyncClient.start();
// 创建一个post请求
AsyncRequestBuilder asyncRequest = AsyncRequestBuilder.post(url);
// 设置请求参数
chatGptRequestParameter.addMessages(new ChatMessage("user", question));
// 请求的参数转换为字符串
String valueAsString = null;
try {
valueAsString = objectMapper.writeValueAsString(chatGptRequestParameter);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// 设置编码和请求参数
ContentType contentType = ContentType.create("text/plain", charset);
asyncRequest.setEntity(valueAsString, contentType);
asyncRequest.setCharset(charset);
// 设置请求头
asyncRequest.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
// 设置登录凭证
asyncRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey);
// 下面就是生产者消费者模型
CountDownLatch latch = new CountDownLatch(1);
// 用于记录返回的答案
StringBuilder sb = new StringBuilder();
// 消费者
AbstractCharResponseConsumer<HttpResponse> consumer = new AbstractCharResponseConsumer<HttpResponse>() {
HttpResponse response;
@Override
protected void start(HttpResponse response, ContentType contentType) throws HttpException, IOException {
setCharset(charset);
this.response = response;
}
@Override
protected int capacityIncrement() {
return Integer.MAX_VALUE;
}
@Override
protected void data(CharBuffer src, boolean endOfStream) throws IOException {
// 收到一个请求就进行处理
String ss = src.toString();
// 通过data:进行分割,如果不进行此步,可能返回的答案会少一些内容
for (String s : ss.split("data:")) {
// 去除掉data:
if (s.startsWith("data:")) {
s = s.substring(5);
}
// 返回的数据可能是(DONE)
if (s.length() > 8) {
// 转换为对象
ChatResponseParameter responseParameter = objectMapper.readValue(s, ChatResponseParameter.class);
// 处理结果
for (Choice choice : responseParameter.getChoices()) {
String content = choice.getDelta().getContent();
if (content != null && !"".equals(content)) {
// 保存结果
sb.append(content);
// 将结果使用webSocket传送过去
System.out.print(content);
}
}
}
}
}
@Override
protected HttpResponse buildResult() throws IOException {
return response;
}
@Override
public void releaseResources() {
}
};
// 执行请求
asyncClient.execute(asyncRequest.build(), consumer, new FutureCallback<HttpResponse>() {
@Override
public void completed(HttpResponse response) {
latch.countDown();
chatGptRequestParameter.addMessages(new ChatMessage("assistant", sb.toString()));
System.out.println("回答结束!!!");
}
@Override
public void failed(Exception ex) {
latch.countDown();
System.out.println("failed");
ex.printStackTrace();
}
@Override
public void cancelled() {
latch.countDown();
System.out.println("cancelled");
}
});
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
大家代码可以直接不看,反正最终的效果就是可以实现问了问题就返回结果。运行效果如下
可以发现,输出就类似于官方的那种效果,一个字一个字的输出
服务器端代码说明
我使用java搭建了一个简单的服务器端程序,提供最基础的用户登录校验功能,以及提供了WebSocket通信。
用户校验的代码
package com.ttpfx.controller;
import com.ttpfx.entity.User;
import com.ttpfx.service.UserService;
import com.ttpfx.utils.R;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author ttpfx
* @date 2023/3/29
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
public static ConcurrentHashMap<String, User> loginUser = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, Long> loginUserKey = new ConcurrentHashMap<>();
@RequestMapping("/login")
public R login(String username, String password) {
if (username == null) return R.fail("必须填写用户名");
User user = userService.queryByName(username);
if (user == null) return R.fail("用户名不存在");
String targetPassword = user.getPassword();
if (targetPassword == null) return R.fail("用户密码异常");
if (!targetPassword.equals(password)) return R.fail("密码错误");
loginUser.put(username, user);
loginUserKey.put(username, System.currentTimeMillis());
return R.ok(String.valueOf(loginUserKey.get(username)));
}
@RequestMapping("/logout")
public R logout(String username) {
loginUser.remove(username);
loginUserKey.remove(username);
return R.ok();
}
@RequestMapping("/checkUserKey")
public R checkUserKey(String username, Long key){
if (username==null || key == null)return R.fail("用户校验异常");
if (!Objects.equals(loginUserKey.get(username), key)){
return R.fail("用户在其他地方登录!!!");
}
return R.ok();
}
@RequestMapping("/loginUser")
public R loginUser(){
return R.ok("success",loginUser.keySet());
}
}
基于webSocket通信的代码
package com.ttpfx.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ttpfx.entity.UserLog;
import com.ttpfx.model.ChatModel;
import com.ttpfx.service.UserLogService;
import com.ttpfx.service.UserService;
import com.ttpfx.vo.chat.ChatRequestParameter;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author ttpfx
* @date 2023/3/28
*/
@Component
@ServerEndpoint("/chatWebSocket/{username}")
public class ChatWebSocketServer {
/**
* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
*/
private static int onlineCount = 0;
/**
* concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象。
*/
private static ConcurrentHashMap<String, ChatWebSocketServer> chatWebSocketMap = new ConcurrentHashMap<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 接收的username
*/
private String username = "";
private UserLog userLog;
private static UserService userService;
private static UserLogService userLogService;
@Resource
public void setUserService(UserService userService) {
ChatWebSocketServer.userService = userService;
}
@Resource
public void setUserLogService(UserLogService userLogService) {
ChatWebSocketServer.userLogService = userLogService;
}
private ObjectMapper objectMapper = new ObjectMapper();
private static ChatModel chatModel;
@Resource
public void setChatModel(ChatModel chatModel) {
ChatWebSocketServer.chatModel = chatModel;
}
ChatRequestParameter chatRequestParameter = new ChatRequestParameter();
/**
* 建立连接
* @param session 会话
* @param username 连接用户名称
*/
@OnOpen
public void onOpen(Session session, @PathParam("username") String username) {
this.session = session;
this.username = username;
this.userLog = new UserLog();
// 这里的用户id不可能为null,出现null,那么就是非法请求
try {
this.userLog.setUserId(userService.queryByName(username).getId());
} catch (Exception e) {
e.printStackTrace();
try {
session.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
this.userLog.setUsername(username);
chatWebSocketMap.put(username, this);
onlineCount++;
System.out.println(username + "--open");
}
@OnClose
public void onClose() {
chatWebSocketMap.remove(username);
System.out.println(username + "--close");
}
@OnMessage
public void onMessage(String message, Session session) {
System.out.println(username + "--" + message);
// 记录日志
this.userLog.setDateTime(LocalDateTime.now());
this.userLog.setPreLogId(this.userLog.getLogId() == null ? -1 : this.userLog.getLogId());
this.userLog.setLogId(null);
this.userLog.setQuestion(message);
long start = System.currentTimeMillis();
// 这里就会返回结果
String answer = chatModel.getAnswer(session, chatRequestParameter, message);
long end = System.currentTimeMillis();
this.userLog.setConsumeTime(end - start);
this.userLog.setAnswer(answer);
userLogService.save(userLog);
}
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
public static void sendInfo(String message, String toUserId) throws IOException {
chatWebSocketMap.get(toUserId).sendMessage(message);
}
}
我们只需要编写简单的前端代码,就可以实现和后端的socket通信。对于后端,我们只需要改一下apiKey和数据库配置就可以直接运行了。
微信小程序代码说明
我写了一个简单微信小程序来和后端进行通信,界面如下
大家只需要下载源代码,然将程序中的ip改为自己服务器的ip即可
代码链接
github的地址为 https://github.com/c-ttpfx/chatgpt-java-wx
可以直接使用 git clone https://github.com/c-ttpfx/chatgpt-java-wx.git 下载代码到本地
我在github里面说明了安装使用的基本步骤,大家按照步骤使用即可
gpt:
proxy:
host: 127.0.0.1
port: 7890
来源:https://blog.csdn.net/m0_51545690/article/details/129886385


猜你喜欢
- 本文实例讲述了Android基于TextView实现的跑马灯效果。分享给大家供大家参考,具体如下:package sweet.venst.a
- 一、redis key数量为1千万时。存储value为"0",比较小。如果value较大,则存储内存会增多redis k
- 本文实例为大家分享了Viewpager2实现登录注册引导页面的具体代码,供大家参考,具体内容如下介绍屏幕滑动是两个完整屏幕之间的切换,在设置
- 一、选择结构大纲if单选择结构if双选择结构if多选择结构嵌套的if结构switch多选择结构二、if单选择结构我们很多时候需要去判断一个东
- 本文实例为大家分享了Java实现简单邮件发送的具体代码,供大家参考,具体内容如下需要的jar包:activation-1.1.1.jarma
- 在移动支付领域,支付宝支付占用巨大份额,根据艾瑞咨询公布的报告数据:2014Q3,支付宝斩
- 什么是Flyweight模式?享元模式(Flyweight Pattern)是一种软件开发中的设计模式,其主要解决的问题是通过类对象的共享,
- 区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的。这篇文章就是帮助你使用 Java
- 什么是响应式简单来说当数据发生变化时,对数据有依赖的代码会重新执行。例如在Vue中,当我们的数据发生改变,界面上对该数据的引用组件会重新渲染
- Android 消息分发使用EventBus的实例详解1. AndroidStudio使用dependencies {//最新版本 &nbs
- maven插件主要是为maven中生命周期中的阶段服务的,maven中只是定义了3套生命周期,以及每套生命周期中有哪些阶段,具体每个阶段中执
- 好久没有写过文章,最近发现直播特别的火,很多app都集成了直播的功能,发现有些直播是带有弹幕的,效果还不错,今天心血来潮,特地写了篇制作弹幕
- Java非法字符: ‘\ufeff‘Java中项目启动出现 非法字符: '\ufeff
- 前言在以SpringBoot开发Restful接口时, 对于接口的查询参数后台也是要进行校验的,同时还需要给出校验的返回信息放到上文我们统一
- 目录概述c#方法概述在微信支付中,当用户支付成功后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。接收微信支付异步
- 本文实例为大家分享了ToolBar的使用方法,供大家参考,具体内容如下ToolBar时应用的标准工具栏;用来替代ActionBar;使用To
- 阻塞、无饥饿、无障碍、无锁、无等待几种。阻塞一个线程是阻塞的,那么在其他线程释放资源之前,当前线程无法继续执行。当我们使用synchroni
- 案例:图书管理(SpringBoot+Thymeleaf+SpringData-JPA)添加图书:图书基本信息及封面图片的上传及入库图书详细
- 一、需求Jenkins大多数情况下都是用来部署Java项目,Java项目有一个特点是>需要编译和打包的,一般情况下编译和打包都是用ma
- 实例如下:private void Form1_Load(object sender, EventArgs e)