聊天评语聊天模块

develop
PL 4 months ago
parent fd9909a5c4
commit 1b67461abb

@ -3,6 +3,7 @@ package cn.teammodel.controller.frontend;
import cn.teammodel.common.IdRequest;
import cn.teammodel.common.R;
import cn.teammodel.model.dto.ai.*;
import cn.teammodel.model.dto.ai.comment.ChatCommentsDto;
import cn.teammodel.model.entity.TmdUserDetail;
import cn.teammodel.model.entity.ai.ChatApp;
import cn.teammodel.model.entity.ai.ChatSession;
@ -10,11 +11,9 @@ import cn.teammodel.security.utils.SecurityUtil;
import cn.teammodel.service.ChatAppService;
import cn.teammodel.service.ChatMessageService;
import cn.teammodel.service.ChatSessionService;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@ -151,7 +150,7 @@ public class AiController {
@PostMapping("chat/comments")
@ApiOperation("设置评语")
public SseEmitter chatComments(@RequestBody @Valid ChatCompletionReqDto chatCompletionReqDto) {
public SseEmitter chatComments(@RequestBody @Valid ChatCommentsDto chatCommentsDto) {
/*
Authentication user0 = SecurityUtil.getAuthentication();
Object user01 = SecurityUtil.getAuthentication().getPrincipal();
@ -164,7 +163,16 @@ public class AiController {
// 获取getClaims时为空
String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getUser().getId();
String userName = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getUser().getName();
return chatMessageService.chatComments(chatCompletionReqDto, userId, userName);
//获取会话id 看是否有sessionId 有则直接赋值 没有则赋值userId
String sessionId = StringUtils.isBlank(chatCommentsDto.getSessionId()) ? userId: chatCommentsDto.getSessionId();
chatCommentsDto.setSessionId(sessionId);
int repeat = chatCommentsDto.getRepeat();
if (repeat <= 1)
{
chatCommentsDto.setRepeat(repeat);
}
return chatMessageService.chatComments(chatCommentsDto, userId, userName);
}
}

@ -0,0 +1,21 @@
package cn.teammodel.model.dto.ai;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
@Data
public class ChatModelDto {
public String type;
public String name;
public String role;
public String chat;
public ArrayList<String> cycleChats;
public String end;
}

@ -0,0 +1,14 @@
package cn.teammodel.model.dto.ai;
import lombok.Data;
import java.util.List;
/**
* json
*/
@Data
public class ChatModelReqDto {
public List<ChatModelDto> chatModel;
}

@ -0,0 +1,48 @@
package cn.teammodel.model.dto.ai.comment;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
*
*/
@Data
public class ChatCommentsDto {
/**
* id
*/
private String sessionId;
/**
* id
*/
private String appId;
/**
*
*/
@NotBlank(message = "评语类型不能为空")
private String type;
@NotBlank(message = "教师学段")
private String period;
@NotBlank(message = "教师学科")
private String subject;
/**
*
*/
private int repeat;
/**
*
*/
private int size;
/**
*
*/
private String text;
/**
*
*/
private Object data;
}

@ -0,0 +1,43 @@
package cn.teammodel.model.dto.ai.comment;
import lombok.Data;
import java.util.List; // 添加这一行来导入List类
/**
*
*/
@Data
public class WisdomCommentsDto {
//学生名称
public String name;
//学年
public String year;
//学期
public String semester;
//总次数
public int joinAll;
//课中参加
public int lessonMiddle;
//线上评测
public int onLine;
//阅卷评测
public int marking;
//评价
public int average;
//评分等级
public String level;
//占比
public int proportion;
//维度信息
public List<Dimension> dims;
public static class Dimension{
//维度名称
public String name;
//维度数据
public int[] data = new int[5];
}
}

@ -0,0 +1,29 @@
package cn.teammodel.model.dto.ai.comment;
import lombok.Data;
/**
*
*/
@Data
public class WisdomExamCommentsDto {
/**
*
*/
public String name;
/**
*
*/
public String time;
/**
*
*/
public int score;
/**
*
*/
public int scoreRate;
/**
*
*/
public int banking;
}

@ -0,0 +1,54 @@
package cn.teammodel.model.dto.ai.comment;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.List;
/**
*
*/
@Data
public class WisdomSubjectComments {
/**
*
*/
@NotBlank(message = "请输入消息内容")
public String subjectName;
/**
* 10
*/
public List<StuInfo> rankings;
/**
*
*/
public StuInfo claasRanking;
/**
*
*/
public static class StuInfo
{
/**
*
*/
public String name;
/**
*
*/
public int ranking;
/**
*
*/
public int scoreRate;
/**
*
*/
public int average;
}
}

@ -0,0 +1,16 @@
package cn.teammodel.model.dto.ai.comment;
import lombok.Data;
@Data
public class artCommentsDto {
public String quotaN1;
public String quotaN2;
public String quotaN3;
public int quotaP1;
public int quotaP2;
public int quotaP3;
public int scoreD;
public String percent;
public String level;
}

@ -0,0 +1,22 @@
package cn.teammodel.model.dto.ai.comment;
import lombok.Data;
import java.util.List;
@Data
public class artSubjectCommentsDto {
public String subjectName;
public List<PointPdfs> pointPdfs;
public static class PointPdfs{
public String dimension;
public String block;
public String point;
public String score;
public String percent;
public String totalScore;
}
}

@ -1,5 +1,6 @@
package cn.teammodel.service;
import cn.teammodel.model.dto.ai.comment.ChatCommentsDto;
import cn.teammodel.model.dto.ai.ChatCompletionReqDto;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@ -15,8 +16,8 @@ public interface ChatMessageService {
/**
* AI
* @param chatCompletionReqDto
* @param chatCommentsDto
* @return
*/
SseEmitter chatComments(ChatCompletionReqDto chatCompletionReqDto, String userId,String userName);
SseEmitter chatComments(ChatCommentsDto chatCommentsDto, String userId, String userName);
}

@ -1,6 +1,5 @@
package cn.teammodel.service.impl;
import cn.hutool.core.lang.UUID;
import cn.teammodel.ai.SparkGptClient;
import cn.teammodel.ai.SseHelper;
import cn.teammodel.ai.cache.HistoryCache;
@ -9,15 +8,22 @@ import cn.teammodel.ai.listener.SparkGptStreamListener;
import cn.teammodel.common.ErrorCode;
import cn.teammodel.common.PK;
import cn.teammodel.config.exception.ServiceException;
import cn.teammodel.model.dto.ai.*;
import cn.teammodel.model.dto.ai.comment.ChatCommentsDto;
import cn.teammodel.model.dto.ai.comment.WisdomCommentsDto;
import cn.teammodel.model.dto.ai.comment.WisdomExamCommentsDto;
import cn.teammodel.model.dto.ai.comment.WisdomSubjectComments;
import cn.teammodel.model.entity.ai.ChatApp;
import cn.teammodel.repository.ChatAppRepository;
import cn.teammodel.repository.ChatSessionRepository;
import cn.teammodel.model.dto.ai.ChatCompletionReqDto;
import cn.teammodel.model.entity.User;
import cn.teammodel.model.entity.ai.ChatSession;
import cn.teammodel.security.utils.SecurityUtil;
import cn.teammodel.service.ChatMessageService;
import cn.teammodel.utils.FileUtil;
import cn.teammodel.utils.RepositoryUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.azure.cosmos.models.CosmosPatchOperations;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
@ -26,10 +32,9 @@ import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.annotation.Resource;
import java.io.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
/**
* @author winter
@ -61,18 +66,31 @@ public class ChatMessageServiceImpl implements ChatMessageService {
/**
*
* @param chatCompletionReqDto
*
* @param chatCommentsDto
* @param userId
* @param userName
* @return
*/
@Override
public SseEmitter chatComments(ChatCompletionReqDto chatCompletionReqDto, String userId,String userName) {
public SseEmitter chatComments(ChatCommentsDto chatCommentsDto, String userId, String userName) {
// 目前仅使用讯飞星火大模型
String appId = chatCompletionReqDto.getAppId();
String appId = chatCommentsDto.getAppId();
String text = commentsTemplate(chatCommentsDto);
if (!StringUtils.isEmpty(text)) {
chatCommentsDto.setText(text);
} else {
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "参数错误");
}
SseEmitter sseEmitter;
if (StringUtils.isEmpty(appId)) {
sseEmitter = commentsBySession(chatCompletionReqDto, userId,userName);
sseEmitter = commentsBySession(chatCommentsDto, userId, userName);
} else {
ChatCompletionReqDto chatCompletionReqDto = new ChatCompletionReqDto();
chatCompletionReqDto.setAppId(chatCommentsDto.getAppId());
chatCompletionReqDto.setSessionId(chatCommentsDto.getSessionId());
chatCompletionReqDto.setText(chatCommentsDto.getText());
sseEmitter = completionByApp(chatCompletionReqDto, false);
}
return sseEmitter;
@ -182,18 +200,20 @@ public class ChatMessageServiceImpl implements ChatMessageService {
/**
*
*/
private SseEmitter commentsBySession(ChatCompletionReqDto chatCompletionReqDto, String userId,String userName) {
String userPrompt = chatCompletionReqDto.getText();
String sessionId = chatCompletionReqDto.getSessionId();
private SseEmitter commentsBySession(ChatCommentsDto chatCommentsDto, String userId, String userName) {
String userPrompt = chatCommentsDto.getText();
Object data = chatCommentsDto.getData();
//获取会话id 看是否有sessionId 有则直接赋值 没有则赋值userId
String sessionId = chatCommentsDto.getSessionId();
ChatSession session = null;
List<ChatSession> sessions = chatSessionRepository.findCommentsById(userId);
List<ChatSession> sessions = chatSessionRepository.findCommentsById(sessionId);
if (sessions.size() == 0) {
// 初始化欢迎语
ChatSession.Message message = ChatSession.Message.of("", "你好" + userName + " ,我是你的私人 AI 助手小豆,你可以问我任何包括但不仅限于教育的问题,我会尽力为您解答!");
ChatSession.Message message = ChatSession.Message.of("", "你好" + userName + " ,我是你的私人 AI 助手小豆," +
"你可以问我任何包括但不仅限于教育的问题,我会尽力为您解答!");
List<ChatSession.Message> history = Collections.singletonList(message);
session = new ChatSession();
session.setId(userId);
session.setId(sessionId);
session.setCode(PK.CHAT_SESSION);
session.setTitle("评语");
session.setUserId(userId);
@ -202,7 +222,7 @@ public class ChatMessageServiceImpl implements ChatMessageService {
session.setHistory(history);
chatSessionRepository.save(session);
} else {
session = RepositoryUtil.findOne(chatSessionRepository.findBySessionId(userId), "该会话不存在");
session = RepositoryUtil.findOne(chatSessionRepository.findBySessionId(sessionId), "该会话不存在");
}
/*
@ -224,11 +244,11 @@ public class ChatMessageServiceImpl implements ChatMessageService {
SseHelper.send(sseEmitter, "[DONE]");
// 处理完成后的事件: 保存消息记录, 缓存更改
ChatSession.Message message = ChatSession.Message.of(userPrompt, s);
HistoryCache.updateContext(userId, message);
HistoryCache.updateContext(sessionId, message);
CosmosPatchOperations options = CosmosPatchOperations.create()
.replace("/updateTime", Instant.now().toEpochMilli())
.add("/history/-", message);
chatSessionRepository.save(userId, PK.of(PK.CHAT_SESSION), ChatSession.class, options);
chatSessionRepository.save(sessionId, PK.of(PK.CHAT_SESSION), ChatSession.class, options);
});
// 错误的回调
listener.setOnError((s) -> {
@ -266,4 +286,197 @@ public class ChatMessageServiceImpl implements ChatMessageService {
paramMessages.add(SparkChatRequestParam.Message.ofUser(prompt));
return paramMessages;
}
/**
*
*
* @return
*/
private String commentsTemplate(ChatCommentsDto chatCommentsDto) {
StringBuilder builder = new StringBuilder();
String strData = JSON.toJSONString(chatCommentsDto.getData());
File file = new File("src/main/resources/Json/ChatModel.json");
List<ChatModelDto> chatModels = new ArrayList<>();
ChatModelDto chatModel = null;
//chatModel = readerMethod(file);
chatModels = readerMethod(file);
if(chatModels.size() <= 0)
{
throw new ServiceException(ErrorCode.NOT_FOUND_ERROR.getCode(), "评语模版未配置");
}
//循环查找对应的模型数据
for(ChatModelDto chatModelTemp : chatModels)
{
//判断评语类型
if(chatCommentsDto.getType().equals(chatModelTemp.getType()))
{
chatModel = chatModelTemp;
break;
}
}
if(chatModel != null)
{
//角色条件
builder.append(String.format(chatModel.getRole(),chatCommentsDto.getPeriod(),chatCommentsDto.getSubject()));
}
ChatModelDto finalChatModel = chatModel;
//模版
switch (chatCommentsDto.getType()) {
//智育 总体评语模版
case "wisdom":
{
WisdomCommentsDto wisdomComments;
//转换问题
try {
//转换方式
wisdomComments = JSON.parseObject(strData, WisdomCommentsDto.class);
} catch (Exception e) {
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "类型转换失败");
}
if(wisdomComments.getName() == null)
{
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "请求参数异常");
}
// 使用String.format方法生成最终的字符串
builder.append(String.format(
finalChatModel.getChat(),
wisdomComments.getName(),
wisdomComments.getYear(),
wisdomComments.getSemester(),
wisdomComments.getJoinAll(),
wisdomComments.getLessonMiddle(),
wisdomComments.getOnLine(),
wisdomComments.getMarking(),
wisdomComments.getAverage(),
wisdomComments.getLevel(),
wisdomComments.getProportion(),
wisdomComments.getName()
));
wisdomComments.getDims().forEach(item -> {
builder.append(String.format(finalChatModel.getCycleChats().get(0),
item.name,
item.data[0],
item.data[1],
item.data[2],
item.data[3],
item.data[4]
));
});
builder.append(String.format(finalChatModel.getEnd(),wisdomComments.getName()));
break;
}
//智育 表现模版
case "wisdomExam":
{
List<WisdomExamCommentsDto> examComments = new ArrayList<>();
try {
examComments = JSON.parseObject(strData, new TypeReference<List<WisdomExamCommentsDto>>() {});
} catch (Exception e) {
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "类型转换失败");
}
if(examComments.size() <= 1){
if (examComments.isEmpty()){
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "请求参数异常");
}
if (examComments.get(0).name == null){
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "请求参数异常");
}
}
int count = 1;
for (WisdomExamCommentsDto examComment : examComments) {
builder.append(String.format(finalChatModel.getCycleChats().get(0), count, examComment.getName(), examComment.getTime(), examComment.getScore(), examComment.getScoreRate(), examComment.getBanking()));
if (count < examComments.size())
{
count++;
}
}
builder.append(String.format(finalChatModel.getEnd(),count));
break;
}
// 智育 学科评语模版
case "windomSubject":
{
List<WisdomSubjectComments> subjectComments;
try {
subjectComments = JSON.parseObject(strData, new TypeReference<List<WisdomSubjectComments>>() {});
}
catch (Exception e) {
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "请求参数异常");
}
if(subjectComments.size() <= 1){
if (subjectComments.isEmpty()){
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "请求参数异常");
}
}
builder.append(finalChatModel.getChat());
//拼接学科数组
for (WisdomSubjectComments comments : subjectComments){
builder.append(String.format(finalChatModel.getCycleChats().get(0), comments.subjectName));
for (WisdomSubjectComments.StuInfo stuInfo : comments.getRankings()){
builder.append(String.format(finalChatModel.getCycleChats().get(1), stuInfo.ranking, stuInfo.name, stuInfo.scoreRate));
}
builder.append(String.format(finalChatModel.getCycleChats().get(2), comments.getClaasRanking().ranking, comments.getClaasRanking().scoreRate,comments.getClaasRanking().average));
}
builder.append(String.format(finalChatModel.getEnd(),subjectComments.size()));
break;
}
//艺术 考核指标纬度评语
case "artDimensions":
builder.append("请按照以下格式回复:\n");
builder.append("1. 艺术作品:\n");
builder.append("2. 艺术作品说明:\n");
builder.append("3. 艺术作品示例:\n");
builder.append("4. 艺术作品示例说明:\n");
break;
case "sport":
builder.append("请按照以下格式回复:\n");
builder.append("1. 运动作品:\n");
builder.append("2. 运动作品说明:\n");
builder.append("3. 运动作品示例:\n");
builder.append("4. 运动作品示例说明:\n");
break;
default:
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "评语类型异常");
}
if (chatCommentsDto.getSize() > 0 ){
builder.append("字数限制在:")
.append(chatCommentsDto.getSize())
.append("字左右");
}
return builder.toString();
}
/**
*
* @param file
* @return
*/
private static List<ChatModelDto> readerMethod(File file) {
//读取文件信息并返回string字符串 并改成json格式
String fileTxt = FileUtil.readFile(file);
String strData =JSON.toJSONString(fileTxt);
//获取聊天字段中的数据
Object str = JSON.parseObject(strData).get("chatModel");
String strData2 = JSON.toJSONString(str);
List<ChatModelDto> chatModelDtos = new ArrayList<>();
//转换问题
try {
//转换方式
chatModelDtos = JSON.parseObject(strData2, new TypeReference<List<ChatModelDto>>() {});
} catch (Exception e) {
throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "类型转换失败");
}
return chatModelDtos;
}
}

@ -0,0 +1,35 @@
package cn.teammodel.utils;
import cn.teammodel.common.ErrorCode;
import cn.teammodel.config.exception.ServiceException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
public class FileUtil {
/**
*
* @param file
* @return
*/
public static String readFile(File file) {
StringBuilder sb = new StringBuilder();
try {
FileReader fileReader = new FileReader(file);
Reader reader = new InputStreamReader(Files.newInputStream(file.toPath()), StandardCharsets.UTF_8);
int ch = 0;
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
fileReader.close();
reader.close();
return sb.toString();
}catch (IOException e)
{
throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), e.getMessage());
}
}
}

@ -0,0 +1,36 @@
{
"chatModel": [
{
"type": "wisdom",
"name": "智育 总体评语模版",
"role": "请扮演[%s%s]老师角色\n",
"chat": "%s同学在%s学年度第%s学期中共计参加了%s次评测其中课中评测%s次、线上评测%s次、阅卷评测%s次智育评价分为%s分素养等级为%s已超过班级%s%% 同学。\n %s同学总体表现",
"cycleChats": ["在%s中学习态度为%s、学习成效%s、合作能力%s、评价能力%s、评价能力%s ;\n"],
"end": "根据我提供的数据,对这 %s 次考试成绩表现做一个评价\n"
},
{
"type": "wisdomExam",
"name": "智育 成绩表现模版",
"role": "请扮演[%s%s]老师角色\n",
"chat": "",
"cycleChats": ["参加第%s次考试结果名称:%s时间:%s得分%s考试得分率%s %% 排名:%s ;\n"],
"end": "根据我提供的数据,对这 %s 次考试成绩表现做一个评价。\n"
},
{
"type": "windomSubject",
"name": "智育 学科评语模版",
"role": "请扮演[%s%s]老师角色\n ",
"chat": "各科评测表现",
"cycleChats":["科目:%s 班级top10"," 排名:%s姓名%s得分率%s %% ,\n " ,"自己在班级排名:%s,得分率:%s %%,排在班级平均:(%s %%)"],
"end": "根据我提供的数据,对这%s次学科表现做一个整体的评价。 \n"
},
{
"type": "artDimensions",
"name": "艺术 考核指标评语模版",
"role": "请扮演[%s%s]老师角色\n ",
"chat": "考核指标评纬度\n",
"cycleChats": [" 指标:%s 、得分率:%s %% \n"],
"end": "根据我提供的【考核指标纬度】做一个整体的评价。 \n"
}
]
}
Loading…
Cancel
Save