飞书接入 chaptgpt
参考1:https://github.com/PlexPt/chatgpt-java
参考2:https://www.51cto.com/article/747923.html
配置
- FastJson2JsonRedisSerializer
import java.nio.charset.Charset;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;/*** Redis使用FastJson序列化** @author gxsldl*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");private Class<T> clazz;public FastJson2JsonRedisSerializer(Class<T> clazz) {super();this.clazz = clazz;}@Overridepublic byte[] serialize(T t) throws SerializationException {if (t == null) {return new byte[0];}return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);}@Overridepublic T deserialize(byte[] bytes) throws SerializationException {if (bytes == null || bytes.length <= 0) {return null;}String str = new String(bytes, DEFAULT_CHARSET);return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType);}
}
- RedisConfig
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** redis配置** @author gxsldl*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {@Bean@SuppressWarnings(value = {"unchecked", "rawtypes"})public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}@Beanpublic DefaultRedisScript<Long> limitScript() {DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();redisScript.setScriptText(limitScriptText());redisScript.setResultType(Long.class);return redisScript;}/*** 限流脚本*/private String limitScriptText() {return "local key = KEYS[1]\n" +"local count = tonumber(ARGV[1])\n" +"local time = tonumber(ARGV[2])\n" +"local current = redis.call('get', key);\n" +"if current and tonumber(current) > count then\n" +" return tonumber(current);\n" +"end\n" +"current = redis.call('incr', key)\n" +"if tonumber(current) == 1 then\n" +" redis.call('expire', key, time)\n" +"end\n" +"return tonumber(current);";}
}
- application.yml
server:port: 12300servlet:context-path: /tomcat:uri-encoding: UTF-8accept-count: 1000threads:max: 800min-spare: 100feishu:messages-reply: https://open.feishu.cn/open-apis/im/v1/messages/%s/replytoken-url: https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal/openai:token: sk-MMiKbgBZMbGIwPkgc6*********** # 填写自己的 API Keyproxy:port: 7890spring:redis:host: localhostport: 6379database: 15password:timeout: 10slettuce:pool:min-idle: 0max-idle: 8max-active: 8max-wait: -1mslogging:level:root: info
- maven
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>3.0.5</version></dependency><!-- fastjson2 --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.26</version></dependency><dependency><groupId>com.github.plexpt</groupId><artifactId>chatgpt</artifactId><version>4.0.5</version></dependency><!-- hutool-http --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-http</artifactId><version>5.8.18</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version><scope>provided</scope></dependency>
常量类
public class RedisConstant {public static final Integer MESSAGE_EVENT_ID_EXPIRE = 30;
}
public class SystemConstant {public static final String TENANT_ACCESS_TOKEN = "tenant:access:token:";
}
controller 类
package com.feishu.chatgpt.controller;import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.feishu.chatgpt.constant.RedisConstant;
import com.feishu.chatgpt.entity.FeishuEventDTO;
import com.feishu.chatgpt.entity.FeishuEventParams;
import com.feishu.chatgpt.entity.FeishuResponse;
import com.feishu.chatgpt.utils.FeishuUtils;
import com.feishu.chatgpt.utils.RedisCache;
import com.plexpt.chatgpt.util.Proxys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.net.ConnectException;
import java.net.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;@Slf4j
@RestController
@RequestMapping(value = "/query")
public class QueryController {@Value("${feishu.messages-reply}")private String messagesReply;@Value("${openai.token}")private String openaiToken;@Value("${proxy.port}")private Integer proxyPort;@Autowiredprivate RedisCache redisCache;@Autowiredprivate FeishuUtils feishuUtils;@PostMapping(value = "/message")public String message(@RequestBody String body) {log.info("收到飞书消息:{}", body);JSONObject jsonObject = JSONObject.parseObject(body);JSONObject header = jsonObject.getJSONObject("header");//请头为空,或消息已经回复过,则不再回复if(eventCheck(header)){return null;}String eventType = header.getString("event_type");if ("im.message.receive_v1".equals(eventType)) {JSONObject event = jsonObject.getJSONObject("event");JSONObject message = event.getJSONObject("message");String messageType = message.getString("message_type");if ("text".equals(messageType)) {String messageId = message.getString("message_id");String content = message.getString("content");JSONObject contentJson = JSONObject.parseObject(content);String text = contentJson.getString("text");FeishuResponse feishuResponse = new FeishuResponse();feishuResponse.setMessageId(messageId);feishuResponse.setQuery(text);log.debug("投递用户消息,{}", JSON.toJSONString(feishuResponse));//String query = queryCompletions(content); //text-davinci-003 模型String query = queryChat(content); // gpt-3.5-turbo 模型reply(feishuResponse, query);} else {log.debug("非文本消息");}}return "success";}/*** 回复飞书机器人* @param poll* @param rs* @return*/private String reply(FeishuResponse poll, String rs) {JSONObject params = new JSONObject();params.put("uuid", RandomUtil.randomNumbers(10));params.put("msg_type", "text");JSONObject content = new JSONObject();content.put("text", rs);params.put("content", content.toJSONString());String url = String.format(messagesReply, poll.getMessageId());String tenantAccessToken = feishuUtils.getTenantAccessToken();String body = null;try (HttpResponse authorization = HttpUtil.createPost(url).header("Authorization", "Bearer " + tenantAccessToken).body(params.toJSONString()).execute()) {body = authorization.body();}return body;}/*** v1/chat/completions 下的模型调用方法*/public String queryChat(String queryMsg){Map<String,String> headers = new HashMap<String,String>();headers.put("Content-Type","application/json");JSONObject json = new JSONObject();//选择模型json.put("model","gpt-3.5-turbo");//添加我们需要输入的内容JSONObject msg = new JSONObject();msg.put("role", "user");msg.put("content", queryMsg);JSONArray array = new JSONArray();array.add(msg);json.put("messages", array);json.put("temperature",0);json.put("max_tokens",2048);json.put("top_p",1);json.put("frequency_penalty",0.0);json.put("presence_penalty",0.0);try{Proxy proxy = Proxys.http("127.0.0.1", proxyPort);HttpResponse response = HttpRequest.post("https://api.openai.com/v1/chat/completions") //gpt-3.5-turbo.headerMap(headers, false).bearerAuth(openaiToken).setProxy(proxy).body(String.valueOf(json)).timeout(600000).execute();String result = response.body();log.debug("query:{},获取响应:{}", queryMsg, JSON.toJSONString(result));JSONObject parseObject = JSONObject.parseObject(result);JSONArray choices = parseObject.getJSONArray("choices");JSONObject resMsg = choices.getJSONObject(0).getJSONObject("message");return resMsg.getString("content");} catch (IORuntimeException e){log.error("openai 接口调用异常:{}", e.getMessage());if(e.getMessage().contains("Connection timed out")){return "Error: 连接超时,请检查vpn节点是否正常";}return "Error: 连接被拒绝,请检查vpn是否已开启";} catch (Exception e){e.printStackTrace();log.error("openai 接口调用异常:{}", e.getMessage());return "openai API 调用异常";}}/*** v1/completions 下的模型调用方法*/public String queryCompletions(String query){Map<String,String> headers = new HashMap<String,String>();headers.put("Content-Type","application/json");JSONObject json = new JSONObject();//选择模型json.put("model","text-davinci-003");//添加我们需要输入的内容json.put("prompt", query);json.put("temperature",0);json.put("max_tokens",2048);json.put("top_p",1);json.put("frequency_penalty",0.0);json.put("presence_penalty",0.0);try{Proxy proxy = Proxys.http("127.0.0.1", proxyPort); // 代码中设置代理,端口号从代理软件中获取HttpResponse response = HttpRequest.post("https://api.openai.com/v1/completions").headerMap(headers, false).bearerAuth(openaiToken).setProxy(proxy).body(String.valueOf(json)).timeout(600000).execute();JSONObject resultJson = JSONObject.parseObject(response.body());JSONObject message = resultJson.getJSONArray("choices").getJSONObject(0);String content = message.getString("text");System.err.println(message);if(content.indexOf("\n\n") == 0){content = content.substring(2);}return content;} catch (IORuntimeException e){log.error("openai 接口调用异常:{}", e.getMessage());if(e.getMessage().contains("Connection timed out")){return "Error: 连接超时,请检查vpn节点是否正常";}return "Error: 连接被拒绝,请检查vpn是否已开启";} catch (Exception e){e.printStackTrace();log.error("openai 接口调用异常:{}", e.getMessage());return "openai API 调用异常";}}/*** 检查请求头是否为空或消息事件是否重复* @param header* @return*/public boolean eventCheck(JSONObject header){if (header == null){return true;}String eventId = header.getString("event_id");if(redisCache.hasKey(eventId)){//消息事件重复return true;}else{//设置30天过期,飞书机器人不定时读取到已回复过的消息,导致重复调用接口重复回复已回复过的消息redisCache.setCacheObject(eventId, 1, RedisConstant.MESSAGE_EVENT_ID_EXPIRE, TimeUnit.DAYS);}return false;}/*** 飞书开放平台上配置【事件订阅】时请求地址配置填写使用* 与收消息用同一个地址,在平台上验证成功后注释掉即可* @param body* @return*///@PostMapping(value = "/message")public FeishuEventDTO messageFeishuCheck(@RequestBody String body) {log.debug("收到消息:{}", body);FeishuEventParams feishuEventParams = JSON.parseObject(body, FeishuEventParams.class);FeishuEventDTO eventDTO = new FeishuEventDTO();eventDTO.setChallenge(feishuEventParams.getChallenge());return eventDTO;}
}
- 飞书平台验证,接口需要外网能访问

entity类
import lombok.Data;@Data
public class FeishuEventDTO {private String challenge;
}
import lombok.Data;@Data
public class FeishuEventParams {private String challenge;private String token;private String type;
}
import lombok.Data;@Data
public class FeishuResponse {private String messageId;private String query;
}
工具类
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.feishu.chatgpt.constant.SystemConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Slf4j
@Component
public class FeishuUtils {@Autowiredprivate RedisCache redisCache;@Value("${feishu.token-url}")private String tokenUrl;//这个是飞书应用的appid和key,可以在创建的飞书应用中找到public static final String appId = "cli_a4cf4*********";public static final String appKey = "0ib7gAmiJjs5Hnm****************";public String getTenantAccessToken() {String token = null;if(redisCache.hasKey(SystemConstant.TENANT_ACCESS_TOKEN)){token = redisCache.getCacheObject(SystemConstant.TENANT_ACCESS_TOKEN);}else{JSONObject params = new JSONObject();params.put("app_id", appId);params.put("app_secret", appKey);String body;HttpResponse execute = HttpUtil.createPost(tokenUrl).body(params.toJSONString()).execute();body = execute.body();log.debug("获取飞书token:{}", body);if (StrUtil.isNotBlank(body)) {String tenantAccessToken = JSON.parseObject(body).getString("tenant_access_token");redisCache.setCacheObject("tenantAccessToken", tenantAccessToken);return tenantAccessToken;}}return token;}
}
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;/*** spring redis 工具类** @author gxsldl**/
@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class RedisCache {@Autowiredpublic RedisTemplate redisTemplate;/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值*/public <T> void setCacheObject(final String key, final T value) {redisTemplate.opsForValue().set(key, value);}/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值* @param timeout 时间* @param timeUnit 时间颗粒度*/public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/*** 设置有效时间** @param key Redis键* @param timeout 超时时间* @return true=设置成功;false=设置失败*/public boolean expire(final String key, final long timeout) {return expire(key, timeout, TimeUnit.SECONDS);}/*** 设置有效时间** @param key Redis键* @param timeout 超时时间* @param unit 时间单位* @return true=设置成功;false=设置失败*/public boolean expire(final String key, final long timeout, final TimeUnit unit) {return redisTemplate.expire(key, timeout, unit);}/*** 获取有效时间** @param key Redis键* @return 有效时间*/public long getExpire(final String key) {return redisTemplate.getExpire(key);}/*** 判断 key是否存在** @param key 键* @return true 存在 false不存在*/public Boolean hasKey(String key) {return redisTemplate.hasKey(key);}/*** 获得缓存的基本对象。** @param key 缓存键值* @return 缓存键值对应的数据*/public <T> T getCacheObject(final String key) {ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/*** 删除单个对象** @param key*/public boolean deleteObject(final String key) {return redisTemplate.delete(key);}/*** 删除集合对象** @param collection 多个对象* @return*/public boolean deleteObject(final Collection collection) {return redisTemplate.delete(collection) > 0;}/*** 缓存List数据** @param key 缓存的键值* @param dataList 待缓存的List数据* @return 缓存的对象*/public <T> long setCacheList(final String key, final List<T> dataList) {Long count = redisTemplate.opsForList().rightPushAll(key, dataList);return count == null ? 0 : count;}/*** 获得缓存的list对象** @param key 缓存的键值* @return 缓存键值对应的数据*/public <T> List<T> getCacheList(final String key) {return redisTemplate.opsForList().range(key, 0, -1);}/*** 缓存Set** @param key 缓存键值* @param dataSet 缓存的数据* @return 缓存数据的对象*/public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);Iterator<T> it = dataSet.iterator();while (it.hasNext()) {setOperation.add(it.next());}return setOperation;}/*** 获得缓存的set** @param key* @return*/public <T> Set<T> getCacheSet(final String key) {return redisTemplate.opsForSet().members(key);}/*** 缓存Map** @param key* @param dataMap*/public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {if (dataMap != null) {redisTemplate.opsForHash().putAll(key, dataMap);}}/*** 获得缓存的Map** @param key* @return*/public <T> Map<String, T> getCacheMap(final String key) {return redisTemplate.opsForHash().entries(key);}/*** 往Hash中存入数据** @param key Redis键* @param hKey Hash键* @param value 值*/public <T> void setCacheMapValue(final String key, final String hKey, final T value) {redisTemplate.opsForHash().put(key, hKey, value);}/*** 获取Hash中的数据** @param key Redis键* @param hKey Hash键* @return Hash中的对象*/public <T> T getCacheMapValue(final String key, final String hKey) {HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();return opsForHash.get(key, hKey);}/*** 获取多个Hash中的数据** @param key Redis键* @param hKeys Hash键集合* @return Hash对象集合*/public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {return redisTemplate.opsForHash().multiGet(key, hKeys);}/*** 删除Hash中的某条数据** @param key Redis键* @param hKey Hash键* @return 是否成功*/public boolean deleteCacheMapValue(final String key, final String hKey) {return redisTemplate.opsForHash().delete(key, hKey) > 0;}/*** 获得缓存的基本对象列表** @param pattern 字符串前缀* @return 对象列表*/public Collection<String> keys(final String pattern) {return redisTemplate.keys(pattern);}
}
日志
<?xml version="1.0" encoding="UTF-8"?>
<configuration><!-- 日志存放路径 -->
<!-- <property name="log.path" value="/data/feishu-chatgpt/logs" />--><property name="log.path" value="D:\\logs\\feishu-chatgpt"/><!-- 日志输出格式 --><property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/><!-- 控制台输出 --><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${log.pattern}</pattern></encoder></appender><!-- 系统日志输出 --><appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/sys-info.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern><!-- 日志最大的历史 60天 --><maxHistory>60</maxHistory></rollingPolicy><encoder><pattern>${log.pattern}</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 过滤的级别 --><level>INFO</level><!-- 匹配时的操作:接收(记录) --><onMatch>ACCEPT</onMatch><!-- 不匹配时的操作:拒绝(不记录) --><onMismatch>DENY</onMismatch></filter></appender><appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/sys-error.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern><!-- 日志最大的历史 60天 --><maxHistory>60</maxHistory></rollingPolicy><encoder><pattern>${log.pattern}</pattern></encoder><filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 过滤的级别 --><level>ERROR</level><!-- 匹配时的操作:接收(记录) --><onMatch>ACCEPT</onMatch><!-- 不匹配时的操作:拒绝(不记录) --><onMismatch>DENY</onMismatch></filter></appender><!-- 用户访问日志输出 --><appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/sys-user.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 按天回滚 daily --><fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern><!-- 日志最大的历史 60天 --><maxHistory>60</maxHistory></rollingPolicy><encoder><pattern>${log.pattern}</pattern></encoder></appender><!-- 系统模块日志级别控制 --><logger name="com.gxsldl" level="info"/><!-- Spring日志级别控制 --><logger name="org.springframework" level="warn"/><root level="info"><appender-ref ref="console"/></root><!--系统操作日志--><root level="info"><appender-ref ref="file_info"/><appender-ref ref="file_error"/></root><!--系统用户操作日志--><logger name="sys-user" level="info"><appender-ref ref="sys-user"/></logger>
</configuration>
2023-05-31:修改为可根据会话上下文回答问题
- QueryController 中的 message() 方法
获取飞消息中的 openId,并在 queryChat()中将openId作为redis缓存键值,保存用户的提示的内容数组
/*** 接收消息* @param body* @return*/@PostMapping(value = "/message")public String message(@RequestBody String body) {log.info("收到飞书消息:{}", body);JSONObject jsonObject = JSONObject.parseObject(body);String challenge = jsonObject.getString("challenge");if(challenge != null){//飞书开放平台上配置【事件订阅】时请求验证JSONObject rJson = new JSONObject();rJson.put("challenge", challenge);return rJson.toString();}JSONObject header = jsonObject.getJSONObject("header");//请头为空,或消息已经回复过,则不再回复if(eventCheck(header)){return null;}String eventType = header.getString("event_type");if ("im.message.receive_v1".equals(eventType)) {JSONObject event = jsonObject.getJSONObject("event");JSONObject message = event.getJSONObject("message");String messageType = message.getString("message_type");if ("text".equals(messageType)) {String messageId = message.getString("message_id");String content = message.getString("content");JSONObject contentJson = JSONObject.parseObject(content);String text = contentJson.getString("text");FeishuResponse feishuResponse = new FeishuResponse();feishuResponse.setMessageId(messageId);feishuResponse.setQuery(text);log.debug("投递用户消息,{}", JSON.toJSONString(feishuResponse));JSONObject sender = event.getJSONObject("sender");JSONObject senderId = sender.getJSONObject("sender_id");String openId = senderId.getString("open_id");// String query = queryCompletions(content, openId); //text-davinci-003 模型String query = queryChat(content, openId); // gpt-3.5-turbo 模型reply(feishuResponse, query);} else {log.error("非文本消息");}}return "success";}
- QueryController 中的 queryChat() 方法
- 将用户提问的每个问题添加到数组中,角色
role = user - 将chatgpt回答的内容保存到数组中,角色
role = assistant - 将问答内容数组保存到redis中,每次提问将整个数组作为提问参数传递
- 将用户提问的每个问题添加到数组中,角色
/*** v1/chat/completions 下的模型调用方法*/public String queryChat(String queryMsg, String openId){Map<String,String> headers = new HashMap<String,String>();headers.put("Content-Type","application/json");JSONObject json = new JSONObject();//选择模型json.put("model","gpt-3.5-turbo");//添加我们需要输入的内容JSONObject msg = new JSONObject();msg.put("role", "user");msg.put("content", queryMsg);JSONArray array = null;String key = RedisConstant.OPENID_MODEL_GPT_35_TURBO + openId;if(redisCache.hasKey(key)){array = redisCache.getCacheObject(key);//只保留10条历史提问内容if (array.size() >= 10){array.remove(0);}}else{array = new JSONArray();}array.add(msg);json.put("messages", array);json.put("temperature",0);json.put("max_tokens",2048);json.put("top_p",0.5);json.put("frequency_penalty",0.8);json.put("presence_penalty",0.5);try{Proxy proxy = Proxys.http("127.0.0.1", proxyPort);HttpResponse response = HttpRequest.post("https://api.openai.com/v1/chat/completions") //gpt-3.5-turbo.headerMap(headers, false).bearerAuth(openaiToken).setProxy(proxy).body(String.valueOf(json)).timeout(600000).execute();String result = response.body();log.debug("query:{},获取响应:{}", queryMsg, JSON.toJSONString(result));JSONObject parseObject = JSONObject.parseObject(result);JSONArray choices = parseObject.getJSONArray("choices");JSONObject resMsg = choices.getJSONObject(0).getJSONObject("message");//助理角色JSONObject res = new JSONObject();res.put("role", "assistant");res.put("content", resMsg.getString("content"));array.add(res);//存入缓存redisCache.setCacheObject(key, array);return resMsg.getString("content");} catch (IORuntimeException e){log.error("openai 接口调用异常:{}", e.getMessage());if(e.getMessage().contains("Connection timed out")){return "Error: 连接超时,请检查vpn节点是否正常";}return "Error: 连接被拒绝,请检查vpn是否已开启";} catch (Exception e){e.printStackTrace();log.error("openai 接口调用异常:{}", e.getMessage());return "openai API 调用异常";}}
- RedisConstant 类添加属性:OPENID_MODEL_GPT_35_TURBO
OPENID_MODEL_GPT_35_TURBO 用做redis缓存键
public class RedisConstant {public static final Integer MESSAGE_EVENT_ID_EXPIRE = 30;public static final String OPENID_MODEL_GPT_35_TURBO = "openid:gpt35turbo:";}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
