Commit e11ee654ef567b87474192cbac00772aa7f2ad9c

Authored by YunaiV
1 parent 3c87f953

feat:【AI 大模型】增加 AI ToolContext 上下文

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
... ... @@ -4,12 +4,10 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.ObjUtil;
5 5 import cn.hutool.core.util.StrUtil;
6 6 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
7   -import cn.iocoder.yudao.framework.ai.core.pojo.AiToolContext;
8 7 import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
9 8 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
10 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
11 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
12   -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
13 11 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
14 12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
15 13 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
... ... @@ -103,8 +101,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
103 101 ChatModel chatModel = modalService.getChatModel(model.getId());
104 102  
105 103 // 2. 知识库找回
106   - List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),
107   - conversation);
  104 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), conversation);
108 105  
109 106 // 3. 插入 user 发送消息
110 107 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
... ... @@ -117,7 +114,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
117 114 knowledgeSegments);
118 115  
119 116 // 3.2 创建 chat 需要的 Prompt
120   - Prompt prompt = buildPrompt(chatModel, conversation, historyMessages, knowledgeSegments, model, sendReqVO);
  117 + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
121 118 ChatResponse chatResponse = chatModel.call(prompt);
122 119  
123 120 // 3.3 更新响应内容
... ... @@ -166,7 +163,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
166 163 knowledgeSegments);
167 164  
168 165 // 4.2 构建 Prompt,并进行调用
169   - Prompt prompt = buildPrompt(chatModel, conversation, historyMessages, knowledgeSegments, model, sendReqVO);
  166 + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
170 167 Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
171 168  
172 169 // 4.3 流式返回
... ... @@ -222,9 +219,9 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
222 219 return knowledgeSegments;
223 220 }
224 221  
225   - private Prompt buildPrompt(StreamingChatModel chatModel, AiChatConversationDO conversation, List<AiChatMessageDO> messages,
226   - List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
227   - AiModelDO model, AiChatMessageSendReqVO sendReqVO) {
  222 + private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,
  223 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
  224 + AiModelDO model, AiChatMessageSendReqVO sendReqVO) {
228 225 List<Message> chatMessages = new ArrayList<>();
229 226 // 1.1 System Context 角色设定
230 227 if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
... ... @@ -254,11 +251,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
254 251 AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId());
255 252 if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) {
256 253 toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName);
257   - // 2.1.1 构建 Function Calling 的上下文参数
258   - toolContext = Map.of(
259   - AiToolContext.CONTEXT_KEY, new AiToolContext().setChatModel(chatModel).setUserId(SecurityFrameworkUtils.getLoginUserId())
260   - .setRoleId(conversation.getRoleId())
261   - .setConversationId(conversation.getId()));
  254 + toolContext = AiUtils.buildCommonToolContext();
262 255 }
263 256 }
264 257 // 2.2 构建 ChatOptions 对象
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
... ... @@ -30,6 +30,13 @@ public interface AiKnowledgeService {
30 30 void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO);
31 31  
32 32 /**
  33 + * 删除知识库
  34 + *
  35 + * @param id 知识库编号
  36 + */
  37 + void deleteKnowledge(Long id);
  38 +
  39 + /**
33 40 * 获得知识库
34 41 *
35 42 * @param id 编号
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
1 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
3 3 import cn.hutool.core.util.ObjUtil;
4   -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 6 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
8 7 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
9 8 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
10   -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
11 9 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
12 10 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
13 11 import cn.iocoder.yudao.module.ai.service.model.AiModelService;
... ... @@ -68,6 +66,11 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
68 66 }
69 67  
70 68 @Override
  69 + public void deleteKnowledge(Long id) {
  70 +
  71 + }
  72 +
  73 + @Override
71 74 public AiKnowledgeDO getKnowledge(Long id) {
72 75 return knowledgeMapper.selectById(id);
73 76 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserIdQueryToolFunction.java deleted
1   -package cn.iocoder.yudao.module.ai.service.model.tool;
2   -
3   -import cn.iocoder.yudao.framework.ai.core.pojo.AiToolContext;
4   -import com.fasterxml.jackson.annotation.JsonClassDescription;
5   -import lombok.AllArgsConstructor;
6   -import lombok.Data;
7   -import lombok.NoArgsConstructor;
8   -import org.springframework.ai.chat.model.ToolContext;
9   -import org.springframework.stereotype.Component;
10   -
11   -import java.util.function.BiFunction;
12   -
13   -/**
14   - * 工具:用户ID查询(上下文参数Demo)
15   - *
16   - * @author Ren
17   - */
18   -@Component("userid_query")
19   -public class UserIdQueryToolFunction
20   - implements BiFunction<UserIdQueryToolFunction.Request, ToolContext, UserIdQueryToolFunction.Response> {
21   -
22   - @Data
23   - @JsonClassDescription("用户ID查询")
24   - public static class Request { }
25   -
26   - @Data
27   - @AllArgsConstructor
28   - @NoArgsConstructor
29   - public static class Response {
30   - /**
31   - * 用户ID
32   - */
33   - private Long UserId;
34   -
35   - }
36   - @Override
37   - public UserIdQueryToolFunction.Response apply(UserIdQueryToolFunction.Request request, ToolContext toolContext) {
38   - // 获取当前登录用户
39   - AiToolContext context = (AiToolContext) toolContext.getContext().get(AiToolContext.CONTEXT_KEY);
40   -
41   - return new Response(context.getUserId());
42   - }
43   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserProfileQueryToolFunction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.model.tool;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.framework.security.core.LoginUser;
  6 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  7 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  8 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  9 +import com.fasterxml.jackson.annotation.JsonClassDescription;
  10 +import jakarta.annotation.Resource;
  11 +import lombok.AllArgsConstructor;
  12 +import lombok.Data;
  13 +import lombok.NoArgsConstructor;
  14 +import org.springframework.ai.chat.model.ToolContext;
  15 +import org.springframework.stereotype.Component;
  16 +
  17 +import java.util.function.BiFunction;
  18 +
  19 +/**
  20 + * 工具:当前用户信息查询
  21 + *
  22 + * 同时,也是展示 ToolContext 上下文的使用
  23 + *
  24 + * @author Ren
  25 + */
  26 +@Component("user_profile_query")
  27 +public class UserProfileQueryToolFunction
  28 + implements BiFunction<UserProfileQueryToolFunction.Request, ToolContext, UserProfileQueryToolFunction.Response> {
  29 +
  30 + @Resource
  31 + private AdminUserApi adminUserApi;
  32 +
  33 + @Data
  34 + @JsonClassDescription("当前用户信息查询")
  35 + public static class Request { }
  36 +
  37 + @Data
  38 + @AllArgsConstructor
  39 + @NoArgsConstructor
  40 + public static class Response {
  41 +
  42 + /**
  43 + * 用户ID
  44 + */
  45 + private Long id;
  46 + /**
  47 + * 用户昵称
  48 + */
  49 + private String nickname;
  50 +
  51 + /**
  52 + * 手机号码
  53 + */
  54 + private String mobile;
  55 + /**
  56 + * 用户头像
  57 + */
  58 + private String avatar;
  59 +
  60 + }
  61 +
  62 + @Override
  63 + public UserProfileQueryToolFunction.Response apply(UserProfileQueryToolFunction.Request request, ToolContext toolContext) {
  64 + LoginUser loginUser = (LoginUser) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_LOGIN_USER);
  65 + Long tenantId = (Long) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_TENANT_ID);
  66 + if (loginUser == null | tenantId == null) {
  67 + return null;
  68 + }
  69 + return TenantUtils.execute(tenantId, () -> {
  70 + AdminUserRespDTO user = adminUserApi.getUser(loginUser.getId());
  71 + return BeanUtils.toBean(user, Response.class);
  72 + });
  73 + }
  74 +
  75 +}
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
... ... @@ -24,6 +24,18 @@
24 24 <artifactId>yudao-common</artifactId>
25 25 </dependency>
26 26  
  27 + <!-- 业务组件 -->
  28 + <dependency>
  29 + <groupId>cn.iocoder.boot</groupId>
  30 + <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
  31 + </dependency>
  32 +
  33 + <!-- Web 相关 -->
  34 + <dependency>
  35 + <groupId>cn.iocoder.boot</groupId>
  36 + <artifactId>yudao-spring-boot-starter-security</artifactId>
  37 + </dependency>
  38 +
27 39 <!-- Spring AI Model 模型接入 -->
28 40 <dependency>
29 41 <groupId>org.springframework.ai</groupId>
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/pojo/AiToolContext.java deleted
1   -package cn.iocoder.yudao.framework.ai.core.pojo;
2   -
3   -import lombok.AllArgsConstructor;
4   -import lombok.Data;
5   -import lombok.NoArgsConstructor;
6   -import org.springframework.ai.chat.model.ChatModel;
7   -import org.springframework.ai.chat.model.StreamingChatModel;
8   -
9   -/**
10   - * 工具上下文参数 DTO,让AI工具可以处理当前用户的相关信息
11   - *
12   - */
13   -@Data
14   -@NoArgsConstructor
15   -@AllArgsConstructor
16   -public class AiToolContext {
17   - public static final String CONTEXT_KEY = "AI_TOOL_CONTEXT";
18   - /**
19   - * 用户ID
20   - */
21   - private Long userId;
22   -
23   - /**
24   - * 聊天模型
25   - */
26   - private StreamingChatModel chatModel;
27   -
28   - /**
29   - * 关联的聊天角色Id
30   - */
31   - private Long roleId;
32   -
33   - /**
34   - * 会话Id
35   - */
36   - private Long conversationId;
37   -}
38 0 \ No newline at end of file
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java
... ... @@ -3,6 +3,8 @@ package cn.iocoder.yudao.framework.ai.core.util;
3 3 import cn.hutool.core.util.ObjUtil;
4 4 import cn.hutool.core.util.StrUtil;
5 5 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  6 +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
  7 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
6 8 import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
7 9 import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
8 10 import org.springframework.ai.chat.messages.*;
... ... @@ -15,6 +17,7 @@ import org.springframework.ai.qianfan.QianFanChatOptions;
15 17 import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
16 18  
17 19 import java.util.Collections;
  20 +import java.util.HashMap;
18 21 import java.util.Map;
19 22 import java.util.Set;
20 23  
... ... @@ -25,8 +28,11 @@ import java.util.Set;
25 28 */
26 29 public class AiUtils {
27 30  
  31 + public static final String TOOL_CONTEXT_LOGIN_USER = "LOGIN_USER";
  32 + public static final String TOOL_CONTEXT_TENANT_ID = "TENANT_ID";
  33 +
28 34 public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) {
29   - return buildChatOptions(platform, model, temperature, maxTokens, null, Map.of());
  35 + return buildChatOptions(platform, model, temperature, maxTokens, null, null);
30 36 }
31 37  
32 38 public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens,
... ... @@ -85,4 +91,11 @@ public class AiUtils {
85 91 throw new IllegalArgumentException(StrUtil.format("未知消息类型({})", type));
86 92 }
87 93  
  94 + public static Map<String, Object> buildCommonToolContext() {
  95 + Map<String, Object> context = new HashMap<>();
  96 + context.put(TOOL_CONTEXT_LOGIN_USER, SecurityFrameworkUtils.getLoginUser());
  97 + context.put(TOOL_CONTEXT_TENANT_ID, TenantContextHolder.getTenantId());
  98 + return context;
  99 + }
  100 +
88 101 }
89 102 \ No newline at end of file
... ...