Commit b4014bf2df7825a867da97662165a738b1f926e1

Authored by YunaiV
1 parent b8a443fb

【代码评审】AI:写作部分的建议

yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java
@@ -7,7 +7,7 @@ import lombok.Getter; @@ -7,7 +7,7 @@ import lombok.Getter;
7 import java.util.Arrays; 7 import java.util.Arrays;
8 8
9 /** 9 /**
10 - * AI 写作类型的枚举 10 + * AI 内置聊天角色的枚举
11 * 11 *
12 * @author xiaoxin 12 * @author xiaoxin
13 */ 13 */
@@ -21,6 +21,7 @@ public enum AiChatRoleEnum implements IntArrayValuable { @@ -21,6 +21,7 @@ public enum AiChatRoleEnum implements IntArrayValuable {
21 2. 回复生成:根据用户提供的场景和提示词,生成合适的对话或文字回复,确保语气和风格符合场景需求。 21 2. 回复生成:根据用户提供的场景和提示词,生成合适的对话或文字回复,确保语气和风格符合场景需求。
22 除此之外不需要除了正文内容外的其他回复,如标题、开头、任何解释性语句或道歉。 22 除此之外不需要除了正文内容外的其他回复,如标题、开头、任何解释性语句或道歉。
23 """), 23 """),
  24 +
24 AI_MIND_MAP_ROLE(2, "脑图助手", """ 25 AI_MIND_MAP_ROLE(2, "脑图助手", """
25 你是一位非常优秀的思维导图助手,你会把用户的所有提问都总结成思维导图,然后以 Markdown 格式输出。markdown 只需要输出一级标题,二级标题,三级标题,四级标题,最多输出四级,除此之外不要输出任何其他 markdown 标记。下面是一个合格的例子: 26 你是一位非常优秀的思维导图助手,你会把用户的所有提问都总结成思维导图,然后以 Markdown 格式输出。markdown 只需要输出一级标题,二级标题,三级标题,四级标题,最多输出四级,除此之外不要输出任何其他 markdown 标记。下面是一个合格的例子:
26 # Geek-AI 助手 27 # Geek-AI 助手
@@ -38,7 +39,6 @@ public enum AiChatRoleEnum implements IntArrayValuable { @@ -38,7 +39,6 @@ public enum AiChatRoleEnum implements IntArrayValuable {
38 除此之外不要任何解释性语句。 39 除此之外不要任何解释性语句。
39 """); 40 """);
40 41
41 -  
42 /** 42 /**
43 * 角色 43 * 角色
44 */ 44 */
@@ -51,7 +51,7 @@ public enum AiChatRoleEnum implements IntArrayValuable { @@ -51,7 +51,7 @@ public enum AiChatRoleEnum implements IntArrayValuable {
51 /** 51 /**
52 * 角色设定 52 * 角色设定
53 */ 53 */
54 - private final String prompt; 54 + private final String systemMessage;
55 55
56 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiChatRoleEnum::getRole).toArray(); 56 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiChatRoleEnum::getRole).toArray();
57 57
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapGenerateReqVO.java
@@ -7,7 +7,9 @@ import lombok.Data; @@ -7,7 +7,9 @@ import lombok.Data;
7 @Schema(description = "管理后台 - AI 思维导图生成 Request VO") 7 @Schema(description = "管理后台 - AI 思维导图生成 Request VO")
8 @Data 8 @Data
9 public class AiMindMapGenerateReqVO { 9 public class AiMindMapGenerateReqVO {
  10 +
10 @Schema(description = "思维导图内容提示", example = "Java 学习路线") 11 @Schema(description = "思维导图内容提示", example = "Java 学习路线")
11 @NotBlank(message = "思维导图内容提示不能为空") 12 @NotBlank(message = "思维导图内容提示不能为空")
12 private String prompt; 13 private String prompt;
  14 +
13 } 15 }
14 \ No newline at end of file 16 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java
@@ -12,6 +12,7 @@ import lombok.Data; @@ -12,6 +12,7 @@ import lombok.Data;
12 * 12 *
13 * @author xiaoxin 13 * @author xiaoxin
14 */ 14 */
  15 +// TODO @xin:如果没 typehandler 的需求,autoResultMap 可以去掉哈
15 @TableName(value = "ai_mind_map", autoResultMap = true) 16 @TableName(value = "ai_mind_map", autoResultMap = true)
16 @Data 17 @Data
17 public class AiMindMapDO extends BaseDO { 18 public class AiMindMapDO extends BaseDO {
@@ -24,20 +25,21 @@ public class AiMindMapDO extends BaseDO { @@ -24,20 +25,21 @@ public class AiMindMapDO extends BaseDO {
24 25
25 /** 26 /**
26 * 用户编号 27 * 用户编号
  28 + *
  29 + * 关联 AdminUserDO 的 userId 字段
27 */ 30 */
28 private Long userId; 31 private Long userId;
29 32
30 /** 33 /**
31 - * 模型  
32 - */  
33 - private String model;  
34 -  
35 - /**  
36 * 平台 34 * 平台
37 * <p> 35 * <p>
38 * 枚举 {@link AiPlatformEnum} 36 * 枚举 {@link AiPlatformEnum}
39 */ 37 */
40 private String platform; 38 private String platform;
  39 + /**
  40 + * 模型
  41 + */
  42 + private String model;
41 43
42 /** 44 /**
43 * 生成内容提示 45 * 生成内容提示
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java
@@ -2,18 +2,18 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.write; @@ -2,18 +2,18 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.write;
2 2
3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; 3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
5 import com.baomidou.mybatisplus.annotation.IdType; 6 import com.baomidou.mybatisplus.annotation.IdType;
6 import com.baomidou.mybatisplus.annotation.TableId; 7 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 8 import com.baomidou.mybatisplus.annotation.TableName;
8 import lombok.Data; 9 import lombok.Data;
9 -import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;  
10 10
11 /** 11 /**
12 * AI 写作 DO 12 * AI 写作 DO
13 * 13 *
14 * @author xiaoxin 14 * @author xiaoxin
15 */ 15 */
16 -@TableName(value = "ai_write", autoResultMap = true) 16 +@TableName("ai_write")
17 @Data 17 @Data
18 public class AiWriteDO extends BaseDO { 18 public class AiWriteDO extends BaseDO {
19 19
@@ -25,6 +25,8 @@ public class AiWriteDO extends BaseDO { @@ -25,6 +25,8 @@ public class AiWriteDO extends BaseDO {
25 25
26 /** 26 /**
27 * 用户编号 27 * 用户编号
  28 + *
  29 + * 关联 AdminUserDO 的 userId 字段
28 */ 30 */
29 private Long userId; 31 private Long userId;
30 32
@@ -36,16 +38,15 @@ public class AiWriteDO extends BaseDO { @@ -36,16 +38,15 @@ public class AiWriteDO extends BaseDO {
36 private Integer type; 38 private Integer type;
37 39
38 /** 40 /**
39 - * 模型  
40 - */  
41 - private String model;  
42 -  
43 - /**  
44 * 平台 41 * 平台
45 * 42 *
46 * 枚举 {@link AiPlatformEnum} 43 * 枚举 {@link AiPlatformEnum}
47 */ 44 */
48 private String platform; 45 private String platform;
  46 + /**
  47 + * 模型
  48 + */
  49 + private String model;
49 50
50 /** 51 /**
51 * 生成内容提示 52 * 生成内容提示
@@ -56,7 +57,6 @@ public class AiWriteDO extends BaseDO { @@ -56,7 +57,6 @@ public class AiWriteDO extends BaseDO {
56 * 生成的内容 57 * 生成的内容
57 */ 58 */
58 private String generatedContent; 59 private String generatedContent;
59 -  
60 /** 60 /**
61 * 原文 61 * 原文
62 */ 62 */
@@ -64,21 +64,26 @@ public class AiWriteDO extends BaseDO { @@ -64,21 +64,26 @@ public class AiWriteDO extends BaseDO {
64 64
65 /** 65 /**
66 * 长度提示词 66 * 长度提示词
  67 + *
  68 + * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LENGTH}
67 */ 69 */
68 private Integer length; 70 private Integer length;
69 -  
70 /** 71 /**
71 * 格式提示词 72 * 格式提示词
  73 + *
  74 + * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_FORMAT}
72 */ 75 */
73 private Integer format; 76 private Integer format;
74 -  
75 /** 77 /**
76 * 语气提示词 78 * 语气提示词
  79 + *
  80 + * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_TONE}
77 */ 81 */
78 private Integer tone; 82 private Integer tone;
79 -  
80 /** 83 /**
81 * 语言提示词 84 * 语言提示词
  85 + *
  86 + * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LANGUAGE}
82 */ 87 */
83 private Integer language; 88 private Integer language;
84 89
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java
@@ -66,7 +66,7 @@ public class AiMindMapServiceImpl implements AiMindMapService { @@ -66,7 +66,7 @@ public class AiMindMapServiceImpl implements AiMindMapService {
66 systemMessage = mindMapRole.getSystemMessage(); 66 systemMessage = mindMapRole.getSystemMessage();
67 } else { 67 } else {
68 model = chatModalService.getRequiredDefaultChatModel(); 68 model = chatModalService.getRequiredDefaultChatModel();
69 - systemMessage = AiChatRoleEnum.AI_MIND_MAP_ROLE.getPrompt(); 69 + systemMessage = AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage();
70 } 70 }
71 71
72 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); 72 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java
@@ -120,6 +120,7 @@ public interface AiChatRoleService { @@ -120,6 +120,7 @@ public interface AiChatRoleService {
120 120
121 /** 121 /**
122 * 根据名字获得聊天角色 122 * 根据名字获得聊天角色
  123 + *
123 * @param name 名字 124 * @param name 名字
124 * @return 聊天角色列表 125 * @return 聊天角色列表
125 */ 126 */
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java
@@ -65,6 +65,7 @@ public class AiWriteServiceImpl implements AiWriteService { @@ -65,6 +65,7 @@ public class AiWriteServiceImpl implements AiWriteService {
65 public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) { 65 public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) {
66 // 1.1 获取写作模型 尝试获取写作助手角色,如果没有则使用默认模型 66 // 1.1 获取写作模型 尝试获取写作助手角色,如果没有则使用默认模型
67 AiChatRoleDO writeRole = CollUtil.getFirst(chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName())); 67 AiChatRoleDO writeRole = CollUtil.getFirst(chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName()));
  68 + // TODO @xin:如果有 writeRole,但是没 modeId,是不是也可以用 systemMessage 哈?建议的写法是:先通过 modelId 获取 model。如果 model == null,则 chatModalService.getRequiredDefaultChatModel();如果还是 null,则抛出异常;。。。。。。。。。。。。。。然后,systemMessage = writeRole != null && writeRole.systemPrompt != "" 这样处理。
68 AiChatModelDO model; 69 AiChatModelDO model;
69 String systemMessage; 70 String systemMessage;
70 if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) { 71 if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) {
@@ -72,18 +73,21 @@ public class AiWriteServiceImpl implements AiWriteService { @@ -72,18 +73,21 @@ public class AiWriteServiceImpl implements AiWriteService {
72 systemMessage = writeRole.getSystemMessage(); 73 systemMessage = writeRole.getSystemMessage();
73 } else { 74 } else {
74 model = chatModalService.getRequiredDefaultChatModel(); 75 model = chatModalService.getRequiredDefaultChatModel();
75 - systemMessage = AiChatRoleEnum.AI_WRITE_ROLE.getPrompt(); 76 + systemMessage = AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage();
76 } 77 }
77 // 1.2 校验平台 78 // 1.2 校验平台
78 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); 79 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
79 StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); 80 StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
80 81
81 // 2. 插入写作信息 82 // 2. 插入写作信息
82 - AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, e -> e.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform())); 83 + AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class,
  84 + write -> write.setUserId(userId).setPlatform(platform.getPlatform()).setModel(model.getModel()));
83 writeMapper.insert(writeDO); 85 writeMapper.insert(writeDO);
84 86
  87 + // 3. 调用大模型,写作生成
85 ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens()); 88 ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens());
86 // 3.1 角色设定 89 // 3.1 角色设定
  90 + // TODO @xin:要不把 90 到 97 这部分,合并到一个方法里。目的是:让这个方法的主干更明确
87 List<Message> chatMessages = new ArrayList<>(); 91 List<Message> chatMessages = new ArrayList<>();
88 if (StrUtil.isNotBlank(systemMessage)) { 92 if (StrUtil.isNotBlank(systemMessage)) {
89 chatMessages.add(new SystemMessage(systemMessage)); 93 chatMessages.add(new SystemMessage(systemMessage));
@@ -94,7 +98,7 @@ public class AiWriteServiceImpl implements AiWriteService { @@ -94,7 +98,7 @@ public class AiWriteServiceImpl implements AiWriteService {
94 Prompt prompt = new Prompt(chatMessages, chatOptions); 98 Prompt prompt = new Prompt(chatMessages, chatOptions);
95 Flux<ChatResponse> streamResponse = chatModel.stream(prompt); 99 Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
96 100
97 - // 3.2 流式返回 101 + // 4. 流式返回
98 StringBuffer contentBuffer = new StringBuffer(); 102 StringBuffer contentBuffer = new StringBuffer();
99 return streamResponse.map(chunk -> { 103 return streamResponse.map(chunk -> {
100 String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; 104 String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null;
@@ -115,13 +119,13 @@ public class AiWriteServiceImpl implements AiWriteService { @@ -115,13 +119,13 @@ public class AiWriteServiceImpl implements AiWriteService {
115 } 119 }
116 120
117 private String buildWritingPrompt(AiWriteGenerateReqVO generateReqVO) { 121 private String buildWritingPrompt(AiWriteGenerateReqVO generateReqVO) {
118 - Integer type = generateReqVO.getType();  
119 String format = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_FORMAT, generateReqVO.getFormat()); 122 String format = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_FORMAT, generateReqVO.getFormat());
120 String tone = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_TONE, generateReqVO.getTone()); 123 String tone = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_TONE, generateReqVO.getTone());
121 String language = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LANGUAGE, generateReqVO.getLanguage()); 124 String language = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LANGUAGE, generateReqVO.getLanguage());
122 String length = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LENGTH, generateReqVO.getLength()); 125 String length = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LENGTH, generateReqVO.getLength());
  126 + // 格式化 prompt
123 String prompt = generateReqVO.getPrompt(); 127 String prompt = generateReqVO.getPrompt();
124 - if (Objects.equals(type, AiWriteTypeEnum.WRITING.getType())) { 128 + if (Objects.equals(generateReqVO.getType(), AiWriteTypeEnum.WRITING.getType())) {
125 return StrUtil.format(AiWriteTypeEnum.WRITING.getPrompt(), prompt, format, tone, language, length); 129 return StrUtil.format(AiWriteTypeEnum.WRITING.getPrompt(), prompt, format, tone, language, length);
126 } else { 130 } else {
127 return StrUtil.format(AiWriteTypeEnum.REPLY.getPrompt(), generateReqVO.getOriginalContent(), prompt, format, tone, language, length); 131 return StrUtil.format(AiWriteTypeEnum.REPLY.getPrompt(), generateReqVO.getOriginalContent(), prompt, format, tone, language, length);