mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-15 21:03:40 +00:00
Merge branch 'work_flow_v3.0' into v3.0.0
# Conflicts: # ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/Service/IChatService.java # ruoyi-modules/ruoyi-aiflow/src/main/java/org/ruoyi/workflow/workflow/WorkflowUtil.java # ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/AbstractStreamingChatService.java # ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java # ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/QianWenChatServiceImpl.java
This commit is contained in:
@@ -11,6 +11,17 @@ spring.boot.admin.client:
|
|||||||
username: @monitor.username@
|
username: @monitor.username@
|
||||||
password: @monitor.password@
|
password: @monitor.password@
|
||||||
|
|
||||||
|
--- # mcp配置信息
|
||||||
|
mcp:
|
||||||
|
sse:
|
||||||
|
enabled: false
|
||||||
|
url: http://localhost:8085/sse
|
||||||
|
|
||||||
|
--- # 上传文件地址
|
||||||
|
sys:
|
||||||
|
upload:
|
||||||
|
path: D:\\DownLoad
|
||||||
|
|
||||||
--- # snail-job 配置
|
--- # snail-job 配置
|
||||||
snail-job:
|
snail-job:
|
||||||
enabled: false
|
enabled: false
|
||||||
@@ -47,9 +58,9 @@ spring:
|
|||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
driverClassName: com.mysql.cj.jdbc.Driver
|
||||||
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
||||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-ai-v3?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
url: jdbc:mysql://127.0.0.1:3306/mysql?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||||
username: ruoyi-ai-v3
|
username: root
|
||||||
password: ruoyi-ai-v3
|
password: 123456
|
||||||
hikari:
|
hikari:
|
||||||
# 最大连接池数量
|
# 最大连接池数量
|
||||||
maxPoolSize: 20
|
maxPoolSize: 20
|
||||||
@@ -87,7 +98,7 @@ spring.data:
|
|||||||
# 数据库索引
|
# 数据库索引
|
||||||
database: 0
|
database: 0
|
||||||
# redis 密码必须配置
|
# redis 密码必须配置
|
||||||
password: 123456
|
# password: 123456
|
||||||
# 连接超时时间
|
# 连接超时时间
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
# 是否开启ssl
|
# 是否开启ssl
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ security:
|
|||||||
- /*/api-docs
|
- /*/api-docs
|
||||||
- /*/api-docs/**
|
- /*/api-docs/**
|
||||||
- /warm-flow-ui/config
|
- /warm-flow-ui/config
|
||||||
|
- /workflow/run
|
||||||
|
|
||||||
# 多租户配置
|
# 多租户配置
|
||||||
tenant:
|
tenant:
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.ruoyi.common.chat.Service;
|
package org.ruoyi.common.chat.Service;
|
||||||
|
|
||||||
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
|
import jakarta.validation.Valid;
|
||||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,12 +12,7 @@ public interface IChatService {
|
|||||||
/**
|
/**
|
||||||
* 客户端发送对话消息到服务端
|
* 客户端发送对话消息到服务端
|
||||||
*/
|
*/
|
||||||
SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue);
|
SseEmitter chat(@Valid ChatContext chatContext);
|
||||||
|
|
||||||
/**
|
|
||||||
* 工作流专用对话
|
|
||||||
*/
|
|
||||||
SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest,SseEmitter emitter,Long userId,String tokenValue, StreamingChatResponseHandler handler);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取服务提供商名称
|
* 获取服务提供商名称
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package org.ruoyi.common.chat.domain.entity.chat;
|
||||||
|
|
||||||
|
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 聊天对话上下文对象
|
||||||
|
*
|
||||||
|
* @author zengxb
|
||||||
|
* @date 2026-02-14
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Builder
|
||||||
|
public class ChatContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型管理视图对象
|
||||||
|
*/
|
||||||
|
@NotNull(message = "模型管理视图对象不能为空")
|
||||||
|
private ChatModelVo chatModelVo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话请求对象
|
||||||
|
*/
|
||||||
|
@NotNull(message = "对话请求对象不能为空")
|
||||||
|
private ChatRequest chatRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSe连接对象
|
||||||
|
*/
|
||||||
|
@NotNull(message = "SSe连接对象不能为空")
|
||||||
|
private SseEmitter emitter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "用户ID不能为空")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token
|
||||||
|
*/
|
||||||
|
@NotNull(message = "Token不能为空")
|
||||||
|
private String tokenValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应处理器
|
||||||
|
*/
|
||||||
|
private StreamingChatResponseHandler handler;
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import org.bsc.langgraph4j.state.AgentState;
|
|||||||
import org.ruoyi.common.chat.Service.IChatModelService;
|
import org.ruoyi.common.chat.Service.IChatModelService;
|
||||||
import org.ruoyi.common.chat.Service.IChatService;
|
import org.ruoyi.common.chat.Service.IChatService;
|
||||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
import org.ruoyi.common.chat.factory.ChatServiceFactory;
|
import org.ruoyi.common.chat.factory.ChatServiceFactory;
|
||||||
import org.ruoyi.workflow.base.NodeInputConfigTypeHandler;
|
import org.ruoyi.workflow.base.NodeInputConfigTypeHandler;
|
||||||
@@ -135,9 +136,11 @@ public class WorkflowUtil {
|
|||||||
.startingState(state)
|
.startingState(state)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
// 获取用户信息和Token以及SSe连接对象(对话接口需要使用)
|
||||||
Long userId = wfState.getUserId();
|
Long userId = wfState.getUserId();
|
||||||
String tokenValue = wfState.getTokenValue();
|
String tokenValue = wfState.getTokenValue();
|
||||||
SseEmitter sseEmitter = wfState.getSseEmitter();
|
SseEmitter sseEmitter = wfState.getSseEmitter();
|
||||||
|
StreamingChatResponseHandler handler = streamingGenerator.handler();
|
||||||
|
|
||||||
// 构建 ruoyi-ai 的 ChatRequest
|
// 构建 ruoyi-ai 的 ChatRequest
|
||||||
List<ChatMessage> chatMessages = new ArrayList<>();
|
List<ChatMessage> chatMessages = new ArrayList<>();
|
||||||
@@ -151,9 +154,18 @@ public class WorkflowUtil {
|
|||||||
chatRequest.setModel(modelName);
|
chatRequest.setModel(modelName);
|
||||||
chatRequest.setChatMessages(chatMessages);
|
chatRequest.setChatMessages(chatMessages);
|
||||||
|
|
||||||
|
//构建聊天对话上下文参数
|
||||||
|
ChatContext chatContext = ChatContext.builder()
|
||||||
|
.chatModelVo(chatModelVo)
|
||||||
|
.chatRequest(chatRequest)
|
||||||
|
.emitter(sseEmitter)
|
||||||
|
.userId(userId)
|
||||||
|
.tokenValue(tokenValue)
|
||||||
|
.handler(handler)
|
||||||
|
.build();
|
||||||
|
|
||||||
// 使用工作流专用方法
|
// 使用工作流专用方法
|
||||||
StreamingChatResponseHandler handler = streamingGenerator.handler();
|
chatService.chat(chatContext);
|
||||||
chatService.chat(chatModelVo, chatRequest, sseEmitter, userId, tokenValue, handler);
|
|
||||||
wfState.getNodeToStreamingGenerator().put(node.getUuid(), streamingGenerator);
|
wfState.getNodeToStreamingGenerator().put(node.getUuid(), streamingGenerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.ruoyi.agent.tool.QueryAllTablesTool;
|
|||||||
import org.ruoyi.agent.tool.QueryTableSchemaTool;
|
import org.ruoyi.agent.tool.QueryTableSchemaTool;
|
||||||
import org.ruoyi.common.chat.Service.IChatService;
|
import org.ruoyi.common.chat.Service.IChatService;
|
||||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
import org.ruoyi.common.core.utils.ObjectUtils;
|
import org.ruoyi.common.core.utils.ObjectUtils;
|
||||||
import org.ruoyi.common.core.utils.SpringUtils;
|
import org.ruoyi.common.core.utils.SpringUtils;
|
||||||
@@ -36,6 +37,7 @@ import org.ruoyi.enums.RoleType;
|
|||||||
import org.ruoyi.service.chat.IChatMessageService;
|
import org.ruoyi.service.chat.IChatMessageService;
|
||||||
import org.ruoyi.service.chat.impl.memory.PersistentChatMemoryStore;
|
import org.ruoyi.service.chat.impl.memory.PersistentChatMemoryStore;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -55,6 +57,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
* @date 2025/12/13
|
* @date 2025/12/13
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@Validated
|
||||||
public abstract class AbstractStreamingChatService implements IChatService {
|
public abstract class AbstractStreamingChatService implements IChatService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,29 +76,23 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
|||||||
*/
|
*/
|
||||||
private static final Map<Object, MessageWindowChatMemory> memoryCache = new ConcurrentHashMap<>();
|
private static final Map<Object, MessageWindowChatMemory> memoryCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义聊天流程骨架
|
* 定义聊天流程骨架
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue) {
|
public SseEmitter chat(ChatContext chatContext) {
|
||||||
return executeChat(chatModelVo, chatRequest, emitter, userId, tokenValue, null);
|
// 获取模型管理视图对象
|
||||||
}
|
ChatModelVo chatModelVo = chatContext.getChatModelVo();
|
||||||
|
// 获取对话请求对象
|
||||||
/**
|
ChatRequest chatRequest = chatContext.getChatRequest();
|
||||||
* 定义聊天流程骨架(包含流式回调结构)
|
// 获取SSe连接对象
|
||||||
*/
|
SseEmitter emitter = chatContext.getEmitter();
|
||||||
@Override
|
// 获取用户ID
|
||||||
public SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue, StreamingChatResponseHandler handler) {
|
Long userId = chatContext.getUserId();
|
||||||
return executeChat(chatModelVo, chatRequest, emitter, userId, tokenValue, handler);
|
// 获取Token
|
||||||
}
|
String tokenValue = chatContext.getTokenValue();
|
||||||
|
// 获取响应处理器
|
||||||
/**
|
StreamingChatResponseHandler handler = chatContext.getHandler();
|
||||||
* 定义聊天流程骨架
|
|
||||||
*/
|
|
||||||
public SseEmitter executeChat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue, StreamingChatResponseHandler handler) {
|
|
||||||
try {
|
try {
|
||||||
String content = Optional.ofNullable(chatRequest.getMessages()).filter(messages -> !messages.isEmpty())
|
String content = Optional.ofNullable(chatRequest.getMessages()).filter(messages -> !messages.isEmpty())
|
||||||
// 对话逻辑:从 messages 筛选第一个元素
|
// 对话逻辑:从 messages 筛选第一个元素
|
||||||
@@ -103,11 +100,11 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
|||||||
.filter(StringUtils::isNotBlank)
|
.filter(StringUtils::isNotBlank)
|
||||||
// 工作流逻辑:从 chatMessages 筛选 UserMessage 的文本
|
// 工作流逻辑:从 chatMessages 筛选 UserMessage 的文本
|
||||||
.orElseGet(() -> Optional.ofNullable(chatRequest.getChatMessages()).orElse(List.of()).stream()
|
.orElseGet(() -> Optional.ofNullable(chatRequest.getChatMessages()).orElse(List.of()).stream()
|
||||||
.filter(message -> message instanceof UserMessage um)
|
.filter(message -> message instanceof UserMessage um)
|
||||||
.map(message -> ((UserMessage) message).singleText())
|
.map(message -> ((UserMessage) message).singleText())
|
||||||
.filter(StringUtils::isNotBlank)
|
.filter(StringUtils::isNotBlank)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(""));
|
.orElse(""));
|
||||||
|
|
||||||
// 保存用户消息
|
// 保存用户消息
|
||||||
saveChatMessage(chatRequest, userId, content, RoleType.USER.getName(), chatModelVo);
|
saveChatMessage(chatRequest, userId, content, RoleType.USER.getName(), chatModelVo);
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
package org.ruoyi.service.chat.impl;
|
package org.ruoyi.service.chat.impl;
|
||||||
|
|
||||||
import java.util.List;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.ruoyi.common.chat.Service.IChatModelService;
|
import org.ruoyi.common.chat.Service.IChatModelService;
|
||||||
import org.ruoyi.common.chat.Service.IChatService;
|
import org.ruoyi.common.chat.Service.IChatService;
|
||||||
import org.ruoyi.common.chat.domain.dto.ChatMessageDTO;
|
import org.ruoyi.common.chat.domain.dto.ChatMessageDTO;
|
||||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
import org.ruoyi.common.chat.factory.ChatServiceFactory;
|
import org.ruoyi.common.chat.factory.ChatServiceFactory;
|
||||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||||
import org.ruoyi.common.sse.core.SseEmitterManager;
|
import org.ruoyi.common.sse.core.SseEmitterManager;
|
||||||
import org.ruoyi.domain.bo.vector.QueryVectorBo;
|
import org.ruoyi.domain.bo.vector.QueryVectorBo;
|
||||||
import org.ruoyi.domain.vo.knowledge.KnowledgeInfoVo;
|
import org.ruoyi.domain.vo.knowledge.KnowledgeInfoVo;
|
||||||
|
;
|
||||||
import org.ruoyi.service.knowledge.IKnowledgeInfoService;
|
import org.ruoyi.service.knowledge.IKnowledgeInfoService;
|
||||||
import org.ruoyi.service.vector.VectorStoreService;
|
import org.ruoyi.service.vector.VectorStoreService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import java.util.List;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 聊天服务业务实现
|
* 聊天服务业务实现
|
||||||
@@ -71,7 +72,16 @@ public class ChatServiceFacade {
|
|||||||
Long userId = LoginHelper.getUserId();
|
Long userId = LoginHelper.getUserId();
|
||||||
String tokenValue = StpUtil.getTokenValue();
|
String tokenValue = StpUtil.getTokenValue();
|
||||||
SseEmitter emitter = sseEmitterManager.connect(userId, tokenValue);
|
SseEmitter emitter = sseEmitterManager.connect(userId, tokenValue);
|
||||||
return chatService.chat(chatModelVo, chatRequest,emitter,userId, tokenValue);
|
|
||||||
|
// 5. 创建对话上下文对象
|
||||||
|
ChatContext chatContext = ChatContext.builder()
|
||||||
|
.chatModelVo(chatModelVo)
|
||||||
|
.chatRequest(chatRequest)
|
||||||
|
.emitter(emitter)
|
||||||
|
.userId(userId)
|
||||||
|
.tokenValue(tokenValue)
|
||||||
|
.build();
|
||||||
|
return chatService.chat(chatContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,18 +1,6 @@
|
|||||||
package org.ruoyi.service.chat.impl.provider;
|
package org.ruoyi.service.chat.impl.provider;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import dev.langchain4j.agent.tool.ToolSpecification;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.ruoyi.agent.EchartsAgent;
|
|
||||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
|
||||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
|
||||||
import org.ruoyi.config.McpSseConfig;
|
|
||||||
import org.ruoyi.enums.ChatModeType;
|
|
||||||
import org.ruoyi.service.chat.impl.AbstractStreamingChatService;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
|
|
||||||
import dev.langchain4j.agentic.AgenticServices;
|
import dev.langchain4j.agentic.AgenticServices;
|
||||||
import dev.langchain4j.agentic.supervisor.SupervisorAgent;
|
import dev.langchain4j.agentic.supervisor.SupervisorAgent;
|
||||||
import dev.langchain4j.agentic.supervisor.SupervisorResponseStrategy;
|
import dev.langchain4j.agentic.supervisor.SupervisorResponseStrategy;
|
||||||
@@ -25,11 +13,23 @@ import dev.langchain4j.mcp.McpToolProvider;
|
|||||||
import dev.langchain4j.mcp.client.DefaultMcpClient;
|
import dev.langchain4j.mcp.client.DefaultMcpClient;
|
||||||
import dev.langchain4j.mcp.client.McpClient;
|
import dev.langchain4j.mcp.client.McpClient;
|
||||||
import dev.langchain4j.mcp.client.transport.McpTransport;
|
import dev.langchain4j.mcp.client.transport.McpTransport;
|
||||||
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;
|
import dev.langchain4j.mcp.client.transport.http.StreamableHttpMcpTransport;
|
||||||
import dev.langchain4j.model.chat.StreamingChatModel;
|
import dev.langchain4j.model.chat.StreamingChatModel;
|
||||||
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
|
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
|
||||||
import dev.langchain4j.service.tool.ToolProvider;
|
import dev.langchain4j.service.tool.ToolProvider;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.ruoyi.agent.McpAgent;
|
||||||
|
import org.ruoyi.config.McpSseConfig;
|
||||||
|
import org.ruoyi.enums.ChatModeType;
|
||||||
|
import org.ruoyi.service.chat.impl.AbstractStreamingChatService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qianWenAI服务调用
|
* qianWenAI服务调用
|
||||||
@@ -41,6 +41,13 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class QianWenChatServiceImpl extends AbstractStreamingChatService {
|
public class QianWenChatServiceImpl extends AbstractStreamingChatService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private McpSseConfig mcpSseConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 千问开发者默认地址
|
||||||
|
*/
|
||||||
|
private static final String QWEN_API_HOST = "https://dashscope.aliyuncs.com/api/v1";
|
||||||
|
|
||||||
// 添加文档解析的前缀字段
|
// 添加文档解析的前缀字段
|
||||||
private static final String UPLOAD_FILE_API_PREFIX = "fileid";
|
private static final String UPLOAD_FILE_API_PREFIX = "fileid";
|
||||||
@@ -101,45 +108,49 @@ public class QianWenChatServiceImpl extends AbstractStreamingChatService {
|
|||||||
* @return 返回LLM信息
|
* @return 返回LLM信息
|
||||||
*/
|
*/
|
||||||
protected String doAgent(String userMessage,ChatModelVo chatModelVo) {
|
protected String doAgent(String userMessage,ChatModelVo chatModelVo) {
|
||||||
|
// 判断是否开启MCP服务
|
||||||
|
if (!mcpSseConfig.isEnabled()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤1:根据SSE对外暴露端点连接
|
||||||
|
McpTransport httpMcpTransport = new StreamableHttpMcpTransport.Builder().
|
||||||
|
url(mcpSseConfig.getUrl()).
|
||||||
|
logRequests(true).
|
||||||
|
build();
|
||||||
|
|
||||||
|
// 步骤2:开启客户端连接
|
||||||
|
McpClient mcpClient = new DefaultMcpClient.Builder()
|
||||||
|
.transport(httpMcpTransport)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 获取所有mcp工具
|
||||||
|
List<ToolSpecification> toolSpecifications = mcpClient.listTools();
|
||||||
|
System.out.println(toolSpecifications);
|
||||||
|
|
||||||
|
// 步骤3:将mcp对象包装
|
||||||
|
ToolProvider toolProvider = McpToolProvider.builder()
|
||||||
|
.mcpClients(List.of(mcpClient))
|
||||||
|
.build();
|
||||||
|
|
||||||
// 步骤4:加载LLM模型对话
|
// 步骤4:加载LLM模型对话
|
||||||
QwenChatModel qwenChatModel = QwenChatModel.builder()
|
QwenChatModel qwenChatModel = QwenChatModel.builder()
|
||||||
|
.baseUrl(QWEN_API_HOST)
|
||||||
.apiKey(chatModelVo.getApiKey())
|
.apiKey(chatModelVo.getApiKey())
|
||||||
.modelName(chatModelVo.getModelName())
|
.modelName(chatModelVo.getModelName())
|
||||||
.build();
|
.build();
|
||||||
McpTransport echart = new StdioMcpTransport.Builder()
|
|
||||||
.command(List.of("uv",
|
|
||||||
"--directory",
|
|
||||||
"/Users/zhangmingming/data/coder/LLM/MCP/cicd-pipeline-example/",
|
|
||||||
"run",
|
|
||||||
"text2sql-mcp"
|
|
||||||
))
|
|
||||||
.logEvents(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 步骤2: 创建MCP客户端
|
// 步骤5:将MCP对象由智能体Agent管控
|
||||||
McpClient echartClient = new DefaultMcpClient.Builder()
|
McpAgent mcpAgent = AgenticServices.agentBuilder(McpAgent.class)
|
||||||
.transport(echart)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 步骤3: 配置工具提供者
|
|
||||||
ToolProvider echartToolProvider = McpToolProvider.builder()
|
|
||||||
.mcpClients(List.of(
|
|
||||||
echartClient))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
EchartsAgent chartGenerationAgent = AgenticServices.agentBuilder(
|
|
||||||
EchartsAgent.class)
|
|
||||||
.chatModel(qwenChatModel)
|
.chatModel(qwenChatModel)
|
||||||
.toolProvider(echartToolProvider)
|
.toolProvider(toolProvider)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
// 步骤6:将所有MCP对象由超级智能体管控
|
// 步骤6:将所有MCP对象由超级智能体管控
|
||||||
SupervisorAgent supervisor = AgenticServices
|
SupervisorAgent supervisor = AgenticServices
|
||||||
.supervisorBuilder()
|
.supervisorBuilder()
|
||||||
.chatModel(qwenChatModel)
|
.chatModel(qwenChatModel)
|
||||||
.subAgents(chartGenerationAgent)
|
.subAgents(mcpAgent)
|
||||||
.responseStrategy(SupervisorResponseStrategy.LAST)
|
.responseStrategy(SupervisorResponseStrategy.LAST)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user