AI 提问追问环境demo 添加

This commit is contained in:
2025-10-27 00:26:08 +08:00
parent ca2d06d0a5
commit b2a781ef77
2 changed files with 87 additions and 12 deletions

View File

@@ -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<String,String> cacheReplyFlag = new ConcurrentHashMap<>();
/**
* 缓存客户端,面试回答信息
*/
private final Map<String,String> cacheMsgMapData = new ConcurrentHashMap<>();
/**
* 缓存客户端,AI提问的问题结果信息
*/
private final Map<String,String> 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<String,String> mapEntity = new HashMap<>();
mapEntity.put("role","system");
mapEntity.put("content","你是面试官根据Construction Labourer候选人回答生成追问。只要一个问题,问题不要重复");
List<Map<String,String>> 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<Map> list = JSONUtil.toList(msgMapData, Map.class);
//获取最后一条数据记录
Map<String,String> 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<Map> list = JSONUtil.toList(msgMapData, Map.class);
Map<String,String> 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<String, String> resultEntity = new HashMap<>();
resultEntity.put("msg", "done");
//发送通知告诉客户端已经回答结束了
@@ -454,6 +524,5 @@ public class ChatWebSocketHandler {
}
}
}

View File

@@ -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<String, Object> requestBody = new HashMap<>();
requestBody.put("model", model);
requestBody.put("stream", true);
// 构建消息
Map<String, String> message = new HashMap<>();
message.put("role", role);
message.put("content", prompt);
requestBody.put("messages", new Object[]{message});
if(StrUtil.isNotEmpty(promptJson)) {
List<Map> 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)