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,12 +4,10 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; 6 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
7 -import cn.iocoder.yudao.framework.ai.core.pojo.AiToolContext;  
8 import cn.iocoder.yudao.framework.ai.core.util.AiUtils; 7 import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
9 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 8 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
10 import cn.iocoder.yudao.framework.common.pojo.PageResult; 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
12 -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;  
13 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 11 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
14 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; 12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
15 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; 13 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
@@ -103,8 +101,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -103,8 +101,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
103 ChatModel chatModel = modalService.getChatModel(model.getId()); 101 ChatModel chatModel = modalService.getChatModel(model.getId());
104 102
105 // 2. 知识库找回 103 // 2. 知识库找回
106 - List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),  
107 - conversation); 104 + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), conversation);
108 105
109 // 3. 插入 user 发送消息 106 // 3. 插入 user 发送消息
110 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, 107 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
@@ -117,7 +114,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -117,7 +114,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
117 knowledgeSegments); 114 knowledgeSegments);
118 115
119 // 3.2 创建 chat 需要的 Prompt 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 ChatResponse chatResponse = chatModel.call(prompt); 118 ChatResponse chatResponse = chatModel.call(prompt);
122 119
123 // 3.3 更新响应内容 120 // 3.3 更新响应内容
@@ -166,7 +163,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -166,7 +163,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
166 knowledgeSegments); 163 knowledgeSegments);
167 164
168 // 4.2 构建 Prompt,并进行调用 165 // 4.2 构建 Prompt,并进行调用
169 - Prompt prompt = buildPrompt(chatModel, conversation, historyMessages, knowledgeSegments, model, sendReqVO); 166 + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
170 Flux<ChatResponse> streamResponse = chatModel.stream(prompt); 167 Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
171 168
172 // 4.3 流式返回 169 // 4.3 流式返回
@@ -222,9 +219,9 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -222,9 +219,9 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
222 return knowledgeSegments; 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 List<Message> chatMessages = new ArrayList<>(); 225 List<Message> chatMessages = new ArrayList<>();
229 // 1.1 System Context 角色设定 226 // 1.1 System Context 角色设定
230 if (StrUtil.isNotBlank(conversation.getSystemMessage())) { 227 if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
@@ -254,11 +251,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -254,11 +251,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
254 AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId()); 251 AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId());
255 if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) { 252 if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) {
256 toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName); 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 // 2.2 构建 ChatOptions 对象 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,6 +30,13 @@ public interface AiKnowledgeService {
30 void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO); 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 * @param id 编号 42 * @param id 编号
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
1 package cn.iocoder.yudao.module.ai.service.knowledge; 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2
3 import cn.hutool.core.util.ObjUtil; 3 import cn.hutool.core.util.ObjUtil;
4 -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;  
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; 6 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
8 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; 7 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
9 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; 8 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
10 -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;  
11 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; 9 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
12 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; 10 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
13 import cn.iocoder.yudao.module.ai.service.model.AiModelService; 11 import cn.iocoder.yudao.module.ai.service.model.AiModelService;
@@ -68,6 +66,11 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { @@ -68,6 +66,11 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
68 } 66 }
69 67
70 @Override 68 @Override
  69 + public void deleteKnowledge(Long id) {
  70 +
  71 + }
  72 +
  73 + @Override
71 public AiKnowledgeDO getKnowledge(Long id) { 74 public AiKnowledgeDO getKnowledge(Long id) {
72 return knowledgeMapper.selectById(id); 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,6 +24,18 @@
24 <artifactId>yudao-common</artifactId> 24 <artifactId>yudao-common</artifactId>
25 </dependency> 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 <!-- Spring AI Model 模型接入 --> 39 <!-- Spring AI Model 模型接入 -->
28 <dependency> 40 <dependency>
29 <groupId>org.springframework.ai</groupId> 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 \ No newline at end of file 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,6 +3,8 @@ package cn.iocoder.yudao.framework.ai.core.util;
3 import cn.hutool.core.util.ObjUtil; 3 import cn.hutool.core.util.ObjUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; 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 import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; 8 import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
7 import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; 9 import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
8 import org.springframework.ai.chat.messages.*; 10 import org.springframework.ai.chat.messages.*;
@@ -15,6 +17,7 @@ import org.springframework.ai.qianfan.QianFanChatOptions; @@ -15,6 +17,7 @@ import org.springframework.ai.qianfan.QianFanChatOptions;
15 import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; 17 import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
16 18
17 import java.util.Collections; 19 import java.util.Collections;
  20 +import java.util.HashMap;
18 import java.util.Map; 21 import java.util.Map;
19 import java.util.Set; 22 import java.util.Set;
20 23
@@ -25,8 +28,11 @@ import java.util.Set; @@ -25,8 +28,11 @@ import java.util.Set;
25 */ 28 */
26 public class AiUtils { 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 public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { 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 public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens, 38 public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens,
@@ -85,4 +91,11 @@ public class AiUtils { @@ -85,4 +91,11 @@ public class AiUtils {
85 throw new IllegalArgumentException(StrUtil.format("未知消息类型({})", type)); 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 \ No newline at end of file 102 \ No newline at end of file