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 +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
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); |