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