Commit f3a6ba834913094cd8b7f6dbf0d46ccd1c3c0bef

Authored by 芋道源码
Committed by Gitee
2 parents 870b1bf3 6e71b721

!12 【优化】AI 写作:1. 优先获取写作角色;2. 优化写作提示词

Merge pull request !12 from 小新/master-jdk21-ai
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java
1 1 package cn.iocoder.yudao.module.ai.enums.write;
2 2  
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.hutool.core.util.StrUtil;
3 5 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
4 6 import lombok.AllArgsConstructor;
5 7 import lombok.Getter;
... ... @@ -15,8 +17,8 @@ import java.util.Arrays;
15 17 @Getter
16 18 public enum AiWriteTypeEnum implements IntArrayValuable {
17 19  
18   - WRITING(1, "撰写"),
19   - REPLY(2, "回复");
  20 + WRITING(1, "撰写", "请撰写一篇关于 [{}] 的文章。文章的内容格式:{},语气:{},语言:{},长度:{}。请确保涵盖主要内容,不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。"),
  21 + REPLY(2, "回复", "请针对如下内容:[{}] 做个回复。回复内容参考:[{}], 回复格式:{},语气:{},语言:{},长度:{}。不需要除了正文内容外的其他回复,如标题、开头、额外的解释或道歉。");
20 22  
21 23 /**
22 24 * 类型
... ... @@ -27,6 +29,11 @@ public enum AiWriteTypeEnum implements IntArrayValuable {
27 29 */
28 30 private final String name;
29 31  
  32 + /**
  33 + * 模版
  34 + */
  35 + private final String template;
  36 +
30 37 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiWriteTypeEnum::getType).toArray();
31 38  
32 39 @Override
... ... @@ -34,4 +41,9 @@ public enum AiWriteTypeEnum implements IntArrayValuable {
34 41 return ARRAYS;
35 42 }
36 43  
  44 + public static void validateType(Integer type) {
  45 + if (ArrayUtil.contains(ARRAYS, type)) return;
  46 + throw new IllegalArgumentException(StrUtil.format("未知写作类型({})", type));
  47 + }
  48 +
37 49 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java
1 1 package cn.iocoder.yudao.module.ai.service.write;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.StrUtil;
4 5 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
5 6 import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
6 7 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  8 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 10 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
9 12 import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
10 13 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  14 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
11 15 import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
12 16 import cn.iocoder.yudao.module.ai.dal.mysql.write.AiWriteMapper;
13 17 import cn.iocoder.yudao.module.ai.enums.DictTypeConstants;
... ... @@ -15,6 +19,7 @@ import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
15 19 import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
16 20 import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
17 21 import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
  22 +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
18 23 import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
19 24 import jakarta.annotation.Resource;
20 25 import lombok.extern.slf4j.Slf4j;
... ... @@ -25,6 +30,7 @@ import org.springframework.ai.chat.prompt.Prompt;
25 30 import org.springframework.stereotype.Service;
26 31 import reactor.core.publisher.Flux;
27 32  
  33 +import java.util.List;
28 34 import java.util.Objects;
29 35  
30 36 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
... ... @@ -43,6 +49,8 @@ public class AiWriteServiceImpl implements AiWriteService {
43 49 private AiApiKeyService apiKeyService;
44 50 @Resource
45 51 private AiChatModelService chatModalService;
  52 + @Resource
  53 + private AiChatRoleService chatRoleService;
46 54  
47 55 @Resource
48 56 private DictDataApi dictDataApi;
... ... @@ -52,15 +60,22 @@ public class AiWriteServiceImpl implements AiWriteService {
52 60  
53 61 @Override
54 62 public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) {
55   - // 1.1 校验模型 TODO 芋艿 是不是取默认的模型也ok?;那可以,有限拿 chatRole 的角色;如果没有,则获取默认的;
56   - AiChatModelDO model = chatModalService.getRequiredDefaultChatModel();
57   - StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  63 + // 1.1 获取写作模型 尝试获取写作助手角色,如果没有则使用默认模型
  64 + AiChatRoleDO writeRole = selectOneWriteRole();
  65 + AiChatModelDO model;
  66 + if (Objects.nonNull(writeRole)) {
  67 + model = chatModalService.getChatModel(writeRole.getModelId());
  68 + } else {
  69 + model = chatModalService.getRequiredDefaultChatModel();
  70 + }
  71 +
58 72 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
59 73  
  74 + StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
  75 +
60 76 // 1.2 插入写作信息
61   - // TODO @xin:建议把 writeDO.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform()),写在 toBean 的 consumer 里;原因是,让这个 set 保持完整性
62   - AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class);
63   - writeMapper.insert(writeDO.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform()));
  77 + AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, e -> e.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform()));
  78 + writeMapper.insert(writeDO);
64 79  
65 80 // 2.1 构建提示词
66 81 ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens());
... ... @@ -87,23 +102,30 @@ public class AiWriteServiceImpl implements AiWriteService {
87 102 }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
88 103 }
89 104  
  105 + private AiChatRoleDO selectOneWriteRole() {
  106 + AiChatRoleDO chatRoleDO = null;
  107 + PageResult<AiChatRoleDO> writeRolePage = chatRoleService.getChatRolePage(new AiChatRolePageReqVO().setName("写作助手"));
  108 + List<AiChatRoleDO> list = writeRolePage.getList();
  109 + if (CollUtil.isNotEmpty(list)) {
  110 + chatRoleDO = list.get(0);
  111 + }
  112 + return chatRoleDO;
  113 + }
  114 +
90 115 private String buildWritingPrompt(AiWriteGenerateReqVO generateReqVO) {
91   - String template;
92   - Integer writeType = generateReqVO.getType();
  116 + Integer type = generateReqVO.getType();
93 117 String format = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_FORMAT, generateReqVO.getFormat());
94   - String tone = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_TONE, generateReqVO.getFormat());
95   - String language = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LANGUAGE, generateReqVO.getFormat());
96   - String length = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LENGTH, generateReqVO.getFormat());
97   - // TODO @xin:建议改成 if return 哈;更简洁;
98   - if (Objects.equals(writeType, AiWriteTypeEnum.WRITING.getType())) {
99   - // TODO @xin:写成静态枚举哈
100   - template = "请撰写一篇关于 [{}] 的文章。文章的内容格式为:[{}],语气为:[{}],语言为:[{}],长度为:[{}]。请确保涵盖主要内容,不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。";
101   - return StrUtil.format(template, generateReqVO.getPrompt(), format, tone, language, length);
102   - } else if (Objects.equals(writeType, AiWriteTypeEnum.REPLY.getType())) {
103   - template = "请针对如下内容:[{}] 做个回复。回复内容参考:[{}], 回复的内容格式为:[{}],语气为:[{}],语言为:[{}],长度为:[{}]。不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。";
104   - return StrUtil.format(template, generateReqVO.getOriginalContent(), generateReqVO.getPrompt(), format, tone, language, length);
  118 + String tone = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_TONE, generateReqVO.getTone());
  119 + String language = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LANGUAGE, generateReqVO.getLanguage());
  120 + String length = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LENGTH, generateReqVO.getLength());
  121 + String prompt = generateReqVO.getPrompt();
  122 + // 校验写作类型是否合法
  123 + AiWriteTypeEnum.validateType(type);
  124 +
  125 + if (Objects.equals(type, AiWriteTypeEnum.WRITING.getType())) {
  126 + return StrUtil.format(AiWriteTypeEnum.WRITING.getTemplate(), prompt, format, tone, language, length);
105 127 } else {
106   - throw new IllegalArgumentException(StrUtil.format("未知写作类型({})", writeType));
  128 + return StrUtil.format(AiWriteTypeEnum.REPLY.getTemplate(), generateReqVO.getOriginalContent(), prompt, format, tone, language, length);
107 129 }
108 130 }
109 131  
... ...