diff --git a/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java b/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java index ce7d12a..3f6e016 100644 --- a/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java +++ b/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java @@ -8,6 +8,7 @@ import com.vetti.common.ai.gpt.OpenAiStreamClient; import com.vetti.common.ai.gpt.service.OpenAiStreamListenerService; import com.vetti.common.ai.whisper.WhisperClient; import com.vetti.common.config.RuoYiConfig; +import com.vetti.common.core.redis.RedisCache; import com.vetti.common.utils.spring.SpringUtils; import lombok.extern.slf4j.Slf4j; import okhttp3.*; @@ -24,6 +25,7 @@ import java.io.*; import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; /** * 语音面试 web处理器 @@ -60,6 +62,16 @@ public class ChatWebSocketHandler { */ private final Map cacheReplyFlag = new ConcurrentHashMap<>(); + /** + * 缓存客户端,面试回答信息 + */ + private final Map cacheMsgMapData = new ConcurrentHashMap<>(); + + /** + * 缓存客户端,AI提问的问题结果信息 + */ + private final Map cacheQuestionResult = new ConcurrentHashMap<>(); + // 语音文件保存目录 private static final String VOICE_STORAGE_DIR = "/voice_files/"; @@ -92,6 +104,10 @@ public class ChatWebSocketHandler { createWhisperRealtimeSocket(session.getId()); //是初次自我介绍后的问答环节 cacheReplyFlag.put(session.getId(),"YES"); + //初始化面试回答数据记录 + cacheMsgMapData.put(session.getId(),""); + //初始化面试问题 + cacheQuestionResult.put(session.getId(),""); //发送初始化面试官语音流 String openingPathUrl = RuoYiConfig.getProfile() + VOICE_SYSTEM_DIR + "opening.wav"; try { @@ -128,10 +144,11 @@ public class ChatWebSocketHandler { String startFlag = cacheReplyFlag.get(session.getId()); //语音结束,开始进行回答解析 String cacheResultText = cacheClientTts.get(clientId); - log.info("返回的结果为:{}", cacheResultText); + log.info("面试者回答信息为:{}", cacheResultText); if (StrUtil.isEmpty(cacheResultText)) { cacheResultText = "Hi."; } + String promptJson = ""; if("YES".equals(startFlag)) { //自我介绍结束后马上返回一个Good //发送初始化面试官语音流 @@ -146,7 +163,37 @@ public class ChatWebSocketHandler { } catch (IOException e) { e.printStackTrace(); } - cacheResultText = "你是面试官,根据Construction Labourer候选人回答生成追问。只要一个问题"; + Map mapEntity = new HashMap<>(); + mapEntity.put("role","system"); + mapEntity.put("content","你是面试官,根据Construction Labourer候选人回答生成追问。只要一个问题,问题不要重复"); + List> list = new LinkedList(); + list.add(mapEntity); + promptJson = JSONUtil.toJsonStr(list); + //记录缓存中 + cacheMsgMapData.put(session.getId(),promptJson); + }else{ + //开始根据面试者回答的问题,进行追问回答 +// { +// role: "system", +// content: "你是面试官,根据Construction Labourer候选人回答生成追问。" +// }, +// { +// role: "user", +// content: `问题:${question}\n候选人回答:${answer}` +// } + //获取面试者回答信息 + //获取缓存记录 + String msgMapData = cacheMsgMapData.get(session.getId()); + if(StrUtil.isNotEmpty(msgMapData)){ + List list = JSONUtil.toList(msgMapData, Map.class); + //获取最后一条数据记录 + Map mapEntity = list.get(list.size()-1); + //更新问题记录 + String content = mapEntity.get("content"); + mapEntity.put("content", StrUtil.format(content, cacheResultText)); + promptJson = JSONUtil.toJsonStr(list); + cacheMsgMapData.put(session.getId(),promptJson); + } } //获取完问答数据,直接清空缓存数据 cacheClientTts.put(clientId,""); @@ -154,10 +201,18 @@ public class ChatWebSocketHandler { log.info("1、开始进行AI回答时间:{}", System.currentTimeMillis() / 1000); //把提问的文字发送给CPT(流式处理) OpenAiStreamClient aiStreamClient = SpringUtils.getBean(OpenAiStreamClient.class); - aiStreamClient.streamChat(cacheResultText, new OpenAiStreamListenerService() { + log.info("AI提示词为:{}",promptJson); + aiStreamClient.streamChat(promptJson, new OpenAiStreamListenerService() { @Override public void onMessage(String content) { log.info("返回AI结果:{}", content); + String questionResult = cacheQuestionResult.get(session.getId()); + if(StrUtil.isEmpty(questionResult)){ + questionResult = content; + }else{ + questionResult = questionResult + content; + } + cacheQuestionResult.put(session.getId(),questionResult); // 实时输出内容 //开始进行语音输出-流式持续输出 log.info("2、开始进行AI回答时间:{}", System.currentTimeMillis() / 1000); @@ -185,6 +240,21 @@ public class ChatWebSocketHandler { @Override public void onComplete() { try { + //开始往缓存中记录提问的问题 + String questionResult = cacheQuestionResult.get(session.getId()); + //获取缓存记录 + String msgMapData = cacheMsgMapData.get(session.getId()); + if(StrUtil.isNotEmpty(msgMapData)){ + List list = JSONUtil.toList(msgMapData, Map.class); + Map mapEntity = new HashMap<>(); + mapEntity.put("role","system"); + mapEntity.put("content","问题:"+questionResult+"\\n候选人回答:{}"); + list.add(mapEntity); + cacheMsgMapData.put(session.getId(),JSONUtil.toJsonStr(list)); + } + //清空问题 + cacheQuestionResult.put(session.getId(),""); + Map resultEntity = new HashMap<>(); resultEntity.put("msg", "done"); //发送通知告诉客户端已经回答结束了 @@ -454,6 +524,5 @@ public class ChatWebSocketHandler { } } - } diff --git a/vetti-common/src/main/java/com/vetti/common/ai/gpt/OpenAiStreamClient.java b/vetti-common/src/main/java/com/vetti/common/ai/gpt/OpenAiStreamClient.java index 3d18f80..e9b2d19 100644 --- a/vetti-common/src/main/java/com/vetti/common/ai/gpt/OpenAiStreamClient.java +++ b/vetti-common/src/main/java/com/vetti/common/ai/gpt/OpenAiStreamClient.java @@ -10,6 +10,7 @@ import org.springframework.stereotype.Component; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -37,10 +38,10 @@ public class OpenAiStreamClient { /** * 发送流式请求 * - * @param prompt 提示词 + * @param promptJson 提示词json数据集合 * @param listener 流式响应监听器 */ - public void streamChat(String prompt, OpenAiStreamListenerService listener) { + public void streamChat(String promptJson, OpenAiStreamListenerService listener) { OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) @@ -53,13 +54,18 @@ public class OpenAiStreamClient { Map requestBody = new HashMap<>(); requestBody.put("model", model); requestBody.put("stream", true); - // 构建消息 - Map message = new HashMap<>(); - message.put("role", role); - message.put("content", prompt); - requestBody.put("messages", new Object[]{message}); - + if(StrUtil.isNotEmpty(promptJson)) { + List promptList = JSONUtil.toList(promptJson, Map.class); + Object[] objects = new Object[promptList.size()]; + for (int i = 0; i < promptList.size(); i++) { + objects[i] = promptList.get(i); + } + //获取到的提示 + requestBody.put("messages", objects); + } + //开始给AI发送请求数据 + System.out.println("请求AI数据参数为:"+JSONUtil.toJsonStr(requestBody)); // 创建请求 Request request = new Request.Builder() .url(apiUrl)