异常信息处理

This commit is contained in:
2025-11-06 20:14:01 +08:00
parent e1ad2d959c
commit 966dbe6a81
5 changed files with 162 additions and 171 deletions

View File

@@ -12,7 +12,6 @@ import com.vetti.common.config.RuoYiConfig;
import com.vetti.common.utils.spring.SpringUtils;
import com.vetti.hotake.domain.HotakeProblemBaseInfo;
import com.vetti.hotake.service.IHotakeProblemBaseInfoService;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Component;
@@ -177,42 +176,23 @@ public class ChatWebSocketHandler {
//把提问的文字发送给CPT(流式处理)
OpenAiStreamClient aiStreamClient = SpringUtils.getBean(OpenAiStreamClient.class);
log.info("AI提示词为:{}", promptJson);
//Score: 返回的是评分,后面只会跟着一个
//FOLLOW-UP: 返回的是问题,后面的每一行都是问题
//先获取回答的评分,是否符合要求
Boolean isEndFlag = getInterviewScore(promptJson, session, "");
if(isEndFlag){
log.info("面试回答符合条件规则,继续追问啦!!!!!");
aiStreamClient.streamChat(promptJson, new OpenAiStreamListenerService() {
String isScore = "0";
//是否结束面试
Boolean flag = true;
String resultText = "";
String resultEvaluate = "";
//是否遇到问题记录
Boolean isFlow = false;
int resultNum = (int) (Math.random() * 2) + 1;
@Override
public void onMessage(String content) {
log.info("返回AI结果{}", content.replaceAll("\n", ""));
if (StrUtil.isEmpty(resultText)) {
resultText = content;
} else {
resultText = resultText + content;
//获取1和2的随机数
if(resultNum == 1){
content = "";
}
resultNum = resultNum+1;
String contentData = content.replaceAll("\n", "");
//记录获取的分数,并且验证分数是否完成
//获取评分
if (contentData.contains(SCORE_FLAG)) {
isScore = "1";
}
if ("1".equals(isScore)) {
//获取的是评分,并且记录评分
flag = handleScoreRecord(content, session);
}
if (contentData.contains(QUESTION_FLAG)) {
isFlow = true;
}
if (flag) {
//返回是追问的问题
if (contentData.contains(QUESTION_FLAG)) {
//获取的是追问的问题
contentData = contentData.replace(QUESTION_FLAG, "");
if (StrUtil.isNotEmpty(contentData)) {
//对问题进行数据缓存
cacheQuestionResult.put(session.getId(), contentData);
@@ -228,16 +208,6 @@ public class ChatWebSocketHandler {
} catch (Exception e) {
e.printStackTrace();
}
}
}
if(!isFlow){
if (StrUtil.isEmpty(resultEvaluate)) {
resultEvaluate = content;
} else {
resultEvaluate = resultEvaluate + content;
}
}
}
}
@@ -246,7 +216,7 @@ public class ChatWebSocketHandler {
try {
//开始往缓存中记录提问的问题
String questionResult = cacheQuestionResult.get(session.getId());
if(StrUtil.isNotEmpty(questionResult)){
if (StrUtil.isNotEmpty(questionResult)) {
//获取缓存记录
String msgMapData = cacheMsgMapData.get(session.getId());
if (StrUtil.isNotEmpty(msgMapData)) {
@@ -260,11 +230,7 @@ public class ChatWebSocketHandler {
}
//清空问题
cacheQuestionResult.put(session.getId(), "");
if(!flag || !isFlow){
//理解结束面试
//发送面试结束的通知已经评分
handleInterviewEnd(session, resultEvaluate);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -276,43 +242,10 @@ public class ChatWebSocketHandler {
}
});
}
}
} else if ("end".equals(resultFlag)) {
//暂时的业务逻辑
//发送面试官结束语音流
String openingPathUrl = RuoYiConfig.getProfile() + VOICE_SYSTEM_DIR + "end.wav";
try {
//文件转换成文件流
ByteBuffer outByteBuffer = convertFileToByteBuffer(openingPathUrl);
//发送文件流数据
session.getBasicRemote().sendBinary(outByteBuffer);
// 发送响应确认
log.info("结束返回面试官语音信息:{}", System.currentTimeMillis() / 1000);
} catch (IOException e) {
e.printStackTrace();
}
//返回文本评分
//处理模型提问逻辑
String promptJson = "";
//获取缓存记录
String msgMapData = cacheMsgMapData.get(session.getId());
if (StrUtil.isNotEmpty(msgMapData)) {
List<Map> list = JSONUtil.toList(msgMapData, Map.class);
//获取第一条数据记录
Map<String, String> mapEntity = list.get(0);
//更新问题记录
mapEntity.put("role", "system");
mapEntity.put("content", "You are a construction industry interview expert. Rate Construction Labourer candidate responses on a 1-5 scale. IMPORTANT: If the answer is completely unrelated, contains technical errors, system messages, or is nonsensical, give it a score of 0/5 and explain why it's invalid.");
promptJson = JSONUtil.toJsonStr(list);
//结束回答要清空问答数据
cacheMsgMapData.put(session.getId(), "");
}
log.info("结束AI提示词为:{}", promptJson);
ChatGPTClient gptClient = SpringUtils.getBean(ChatGPTClient.class);
String resultMsg = gptClient.handleAiChat(promptJson, "QA");
Map<String, String> resultEntity = new HashMap<>();
resultEntity.put("content", resultMsg);
resultEntity.put("type", "score");
session.getBasicRemote().sendText(JSONUtil.toJsonStr(resultEntity));
log.info("面试结束啦!!!!!");
handleInterviewEnd(session,"");
}
}
} catch (Exception e) {
@@ -415,7 +348,7 @@ public class ChatWebSocketHandler {
List<Map<String, String>> list = new LinkedList();
Map<String, String> mapEntity = new HashMap<>();
mapEntity.put("role", "system");
mapEntity.put("content", "You are a construction industry interview expert. You MUST provide both an evaluation and follow-up questions for every response. Use this exact format:\\n\\nEVALUATION:\\n[Rate 0-5 and provide detailed assessment. If answer is unrelated, contains technical errors, system messages, or is nonsensical, give 0/5]\\n\\nFOLLOW-UP:\\n[Always generate 1-2 relevant follow-up questions. If answer was invalid, ask for clarification or repeat the question]\\n\\nIMPORTANT: You must include both EVALUATION and FOLLOW-UP sections in every response.");
mapEntity.put("content", "You are an interviewer. Generate in-depth follow-up questions based on candidate responses.");
list.add(mapEntity);
//获取预设问题-直接TTS转换返回语音结果
IHotakeProblemBaseInfoService problemBaseInfoService = SpringUtils.getBean(IHotakeProblemBaseInfoService.class);
@@ -447,47 +380,54 @@ public class ChatWebSocketHandler {
/**
* 处理面试结束业务逻辑
* 触发规则:
* 1、获得 0-1 分 大于1次 立即结束面试
* 2、获取 4-5 分 大于3次 立即结束面试
* 3、获取 2-3 分 大于3次 立即结束面试
* 4、获取 2-5 分 大于4次 立即结束面试
* 5、没有 FOLLOW-UP 理解结束面试
*
* @param session 客户端会话
* @param content 追问内容
* @param position 职位
*/
private void handleInterviewEnd(Session session, String content) {
//验证是否触发面试结束逻辑
try {
private void handleInterviewEnd(Session session,String position) {
//暂时的业务逻辑
//发送面试官结束语音流
String pathUrl = RuoYiConfig.getProfile() + VOICE_SYSTEM_DIR + "end.wav";
sendVoiceBuffer(pathUrl, session);
String openingPathUrl = RuoYiConfig.getProfile() + VOICE_SYSTEM_DIR + "end.wav";
sendVoiceBuffer(openingPathUrl, session);
//返回文本评分
//处理模型提问逻辑
//获取缓存记录
String msgMapData = cacheMsgMapData.get(session.getId());
String promptJson = "";
if (StrUtil.isNotEmpty(msgMapData)) {
List<Map> list = JSONUtil.toList(msgMapData, Map.class);
//获取第一条数据记录
Map<String, String> mapEntity = list.get(0);
//更新问题记录
mapEntity.put("role", "system");
mapEntity.put("content", "You are a construction industry interview expert. Rate candidate responses on a 1-5 scale and analyze key signals.");
//每个回答的内容前面要加上候选人的职位
if (StrUtil.isNotEmpty(position)) {
for (Map map : list) {
if ("user".equals(map.get("role").toString())) {
map.put("content", "Position: " + position + "\\n" + map.get("content"));
}
}
}
promptJson = JSONUtil.toJsonStr(list);
//结束回答要清空问答数据
cacheMsgMapData.put(session.getId(), "");
}
log.info("结束AI提示词为:{}", promptJson);
ChatGPTClient gptClient = SpringUtils.getBean(ChatGPTClient.class);
String resultMsg = gptClient.handleAiChat(promptJson, "QA");
Map<String, String> resultEntity = new HashMap<>();
resultEntity.put("content", content);
resultEntity.put("content", resultMsg);
resultEntity.put("type", "score");
try{
session.getBasicRemote().sendText(JSONUtil.toJsonStr(resultEntity));
} catch (Exception e) {
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 解析AI结果,获取随机问题
*
* @param content
* @return
*/
private String handleReturnQuestion(String content) {
return "";
}
/**
* 处理评分记录
* 触发规则:
@@ -503,7 +443,7 @@ public class ChatWebSocketHandler {
Map<String, Integer> scoreRecordMap = cacheScoreResult.get(session.getId());
//对评分进行处理
if (StrUtil.isNotEmpty(content)) {
String[] strs = content.split("\\\\");
String[] strs = content.split("/");
//取第一个数就是对应的评分
BigDecimal score = new BigDecimal(strs[0]);
//记录Key为1
@@ -542,5 +482,57 @@ public class ChatWebSocketHandler {
return true;
}
/**
* 获取面试回答评分,并且校验是否结束面试
*
* @param promptJson 提示词数据json
* @param session 客户端会话
* @param position 职位
*/
private Boolean getInterviewScore(String promptJson, Session session, String position) {
//返回文本评分
//获取缓存记录
String msgMapData = cacheMsgMapData.get(session.getId());
if (StrUtil.isNotEmpty(msgMapData)) {
List<Map> list = JSONUtil.toList(msgMapData, Map.class);
//获取第一条数据记录
Map<String, String> mapEntity = list.get(0);
//更新问题记录
mapEntity.put("role", "system");
mapEntity.put("content", "You are a construction industry interview expert. Rate candidate responses on a 1-5 scale and analyze key signals.");
//每个回答的内容前面要加上候选人的职位
if (StrUtil.isNotEmpty(position)) {
for (Map map : list) {
if ("user".equals(map.get("role").toString())) {
map.put("content", "Position: " + position + "\\n" + map.get("content"));
}
}
}
promptJson = JSONUtil.toJsonStr(list);
}
log.info("评分AI提示词为:{}", promptJson);
ChatGPTClient gptClient = SpringUtils.getBean(ChatGPTClient.class);
String resultMsg = gptClient.handleAiChat(promptJson, "QA");
//评论格式为: Score: 3/5\nAssessment: Basically correct answer but lacks detail
String resultScore = "";
if (StrUtil.isNotEmpty(resultMsg)) {
String[] resultMsgs = resultMsg.split("\\\\n");
resultScore = resultMsgs[0].replaceAll(SCORE_FLAG, "");
}
//校验面试是否结束
Boolean flag = handleScoreRecord(resultScore, session);
try {
if (!flag) {
Map<String, String> resultEntity = new HashMap<>();
resultEntity.put("content", resultMsg);
resultEntity.put("type", "score");
session.getBasicRemote().sendText(JSONUtil.toJsonStr(resultEntity));
}
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
}

View File

@@ -169,12 +169,10 @@ whisper:
chatGpt:
apiKey: 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
apiUrl: https://api.openai.com/v1/chat/completions
model: ft:gpt-3.5-turbo-0125:vetti:construction-labourer-test:CWKBNvE2
modelCV: ft:gpt-3.5-turbo-0125:vetti:vetti-resume-test:CWPinJQq
model: ft:gpt-3.5-turbo-0125:vetti::CYl9OBMN
modelCV: ft:gpt-3.5-turbo-0125:vetti:vetti-resume-full:CYT0C8JG
role: system
http:
client:
connect-timeout-seconds: 10

View File

@@ -2,8 +2,8 @@
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.not.exists=User does not exist/password incorrect
user.password.not.match=User does not exist/password incorrect
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除

View File

@@ -169,12 +169,10 @@ whisper:
chatGpt:
apiKey: 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
apiUrl: https://api.openai.com/v1/chat/completions
model: ft:gpt-3.5-turbo-0125:vetti:construction-labourer-test:CWKBNvE2
modelCV: ft:gpt-3.5-turbo-0125:vetti:vetti-resume-test:CWPinJQq
model: ft:gpt-3.5-turbo-0125:vetti::CYl9OBMN
modelCV: ft:gpt-3.5-turbo-0125:vetti:vetti-resume-full:CYT0C8JG
role: system
http:
client:
connect-timeout-seconds: 10

View File

@@ -65,6 +65,9 @@ public class GlobalExceptionHandler
{
log.error(e.getMessage(), e);
Integer code = e.getCode();
if(code == null){
code = HttpStatus.ERROR;
}
response.setStatus(code);
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
}