mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-13 11:53:48 +00:00
优化mcp模块功能
This commit is contained in:
@@ -216,6 +216,8 @@ springdoc:
|
|||||||
packages-to-scan: org.ruoyi.generator
|
packages-to-scan: org.ruoyi.generator
|
||||||
- group: 5.工作流模块
|
- group: 5.工作流模块
|
||||||
packages-to-scan: org.ruoyi.workflow
|
packages-to-scan: org.ruoyi.workflow
|
||||||
|
- group: 6.MCP模块
|
||||||
|
packages-to-scan: org.ruoyi.mcp
|
||||||
|
|
||||||
# 防止XSS攻击
|
# 防止XSS攻击
|
||||||
xss:
|
xss:
|
||||||
@@ -357,3 +359,14 @@ knowledge:
|
|||||||
cache-enabled: true
|
cache-enabled: true
|
||||||
# 缓存过期时间(分钟)
|
# 缓存过期时间(分钟)
|
||||||
cache-expire-minutes: 60
|
cache-expire-minutes: 60
|
||||||
|
|
||||||
|
--- # MCP 模块配置
|
||||||
|
app:
|
||||||
|
mcp:
|
||||||
|
client:
|
||||||
|
# 请求超时时间(秒)
|
||||||
|
request-timeout: 30
|
||||||
|
# 连接超时时间(秒)
|
||||||
|
connection-timeout: 10
|
||||||
|
# 最大重试次数
|
||||||
|
max-retries: 3
|
||||||
|
|||||||
@@ -7,40 +7,35 @@ import dev.langchain4j.service.V;
|
|||||||
|
|
||||||
public interface McpAgent {
|
public interface McpAgent {
|
||||||
/**
|
/**
|
||||||
* 系统提示词:定义智能体身份、核心职责、强制遵守的规则
|
* 系统提示词:通用工具调用智能体
|
||||||
* 适配SSE流式特性,明确工具全来自远端MCP服务,仅做代理调用和结果整理
|
* 不限定具体工具类型,让 LangChain4j 自动传递工具描述给 LLM
|
||||||
*/
|
*/
|
||||||
@SystemMessage("""
|
@SystemMessage("""
|
||||||
你是专业的MCP服务工具代理智能体,核心能力是通过HTTP SSE流式传输协议,调用本地http://localhost:8085/sse地址上MCP服务端注册的所有工具。
|
你是一个AI助手,可以通过调用各种工具来帮助用户完成不同的任务。
|
||||||
你的核心工作职责:
|
|
||||||
1. 准确理解用户的自然语言请求,判断需要调用MCP服务端的哪一个/哪些工具;
|
|
||||||
2. 通过绑定的工具提供者,向MCP服务端发起工具调用请求,传递完整的工具执行参数;
|
|
||||||
3. 实时接收MCP服务端通过SSE流式返回的工具执行结果,保证结果片段的完整性;
|
|
||||||
4. 将流式结果按原始顺序整理为清晰、易懂的自然语言答案,返回给用户。
|
|
||||||
|
|
||||||
【强制遵守的核心规则 - 无例外】
|
【工具使用规则】
|
||||||
1. 所有工具调用必须通过远端MCP服务执行,严禁尝试本地执行任何业务逻辑;
|
1. 根据用户的请求,判断需要使用哪些工具
|
||||||
2. 处理SSE流式结果时,严格保留结果片段的返回顺序,不得打乱或遗漏;
|
2. 仔细阅读每个工具的描述,确保理解工具的功能和参数要求
|
||||||
3. 若MCP服务返回错误(如工具未找到、参数错误、执行失败),直接将错误信息友好反馈给用户,无需额外推理;
|
3. 使用正确的参数调用工具
|
||||||
4. 工具执行结果若为结构化数据(如JSON、表格),需格式化后返回,提升可读性。
|
4. 如果工具执行失败,向用户友好地说明错误原因,并尝试提供替代方案
|
||||||
|
5. 对于复杂任务,可以分步骤使用多个工具完成
|
||||||
|
6. 将工具执行结果以清晰易懂的方式呈现给用户
|
||||||
|
|
||||||
|
【响应格式】
|
||||||
|
- 直接回答用户的问题
|
||||||
|
- 如果使用了工具,说明使用了什么工具以及结果
|
||||||
|
- 如果遇到错误,提供友好的错误信息和解决建议
|
||||||
""")
|
""")
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户消息模板:{{query}}为参数占位符,与方法入参的@V("query")绑定
|
|
||||||
*/
|
|
||||||
@UserMessage("""
|
@UserMessage("""
|
||||||
请通过调用MCP服务端的工具,处理用户的以下请求:
|
|
||||||
{{query}}
|
{{query}}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
@Agent("通用工具调用智能体")
|
||||||
/**
|
/**
|
||||||
* 智能体标识:用于日志打印、监控追踪、多智能体协作时的身份识别
|
* 智能体对外调用入口
|
||||||
*/
|
* @param query 用户的自然语言请求
|
||||||
@Agent("MCP服务SSE流式代理智能体-连接本地8085端口")
|
* @return 处理结果
|
||||||
/**
|
|
||||||
* 智能体对外调用入口方法
|
|
||||||
* @param query 用户的自然语言请求(如:生成订单数据柱状图、查询今日天气)
|
|
||||||
* @V("query") 将方法入参值绑定到@UserMessage的{{query}}占位符中
|
|
||||||
* @return 整理后的MCP工具执行结果(流式结果会自动拼接为完整字符串)
|
|
||||||
*/
|
*/
|
||||||
String callMcpTool(@V("query") String query);
|
String callMcpTool(@V("query") String query);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ public class LangChain4jMcpToolProviderService {
|
|||||||
public ToolProvider getAllEnabledToolsProvider() {
|
public ToolProvider getAllEnabledToolsProvider() {
|
||||||
List<McpTool> enabledTools = mcpToolMapper.selectList(
|
List<McpTool> enabledTools = mcpToolMapper.selectList(
|
||||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<McpTool>()
|
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<McpTool>()
|
||||||
|
.ne(McpTool::getType,"BUILTIN")
|
||||||
.eq(McpTool::getStatus, McpToolStatus.ENABLED.getValue())
|
.eq(McpTool::getStatus, McpToolStatus.ENABLED.getValue())
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -144,47 +145,49 @@ public class LangChain4jMcpToolProviderService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取或创建 MCP Client
|
* 获取或创建 MCP Client
|
||||||
* 包含健康检查和失败重试逻辑
|
|
||||||
*/
|
*/
|
||||||
private McpClient getOrCreateClient(Long toolId) {
|
private McpClient getOrCreateClient(Long toolId) {
|
||||||
// 检查工具是否被禁用
|
// 检查工具是否被禁用
|
||||||
if (isToolDisabled(toolId)) {
|
if (isToolDisabled(toolId)) {
|
||||||
log.warn("Tool {} is temporarily disabled due to previous failures", toolId);
|
log.warn("Tool {} is temporarily disabled", toolId);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试从缓存获取
|
// 返回缓存的客户端
|
||||||
McpClient cachedClient = activeClients.get(toolId);
|
McpClient cachedClient = activeClients.get(toolId);
|
||||||
if (cachedClient != null && isToolHealthy(toolId)) {
|
if (cachedClient != null && isToolHealthy(toolId)) {
|
||||||
return cachedClient;
|
return cachedClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建新的客户端
|
// 查询工具配置
|
||||||
return activeClients.compute(toolId, (id, existingClient) -> {
|
McpTool tool = mcpToolMapper.selectById(toolId);
|
||||||
McpTool tool = mcpToolMapper.selectById(id);
|
if (tool == null || !McpToolStatus.isEnabled(tool.getStatus())) {
|
||||||
if (tool == null || !McpToolStatus.isEnabled(tool.getStatus())) {
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 跳过内置工具(BUILTIN 类型)
|
String toolType = tool.getType();
|
||||||
if ("BUILTIN".equals(tool.getType())) {
|
// 只支持 LOCAL 和 REMOTE 类型
|
||||||
log.debug("Skipping builtin tool: {}", tool.getName());
|
if (!ToolProviderFactory.TYPE_LOCAL.equals(toolType) && !ToolProviderFactory.TYPE_REMOTE.equals(toolType)) {
|
||||||
return null;
|
log.warn("Unsupported tool type: {} for tool: {}", toolType, tool.getName());
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
// 创建并缓存新客户端
|
||||||
McpClient client = createMcpClient(tool);
|
try {
|
||||||
// 标记工具为健康状态
|
McpClient client = createMcpClient(tool);
|
||||||
markToolHealthy(id);
|
if (client == null) {
|
||||||
log.info("Successfully created LangChain4j MCP client for tool: {}", tool.getName());
|
log.warn("Failed to create MCP client for tool: {}", tool.getName());
|
||||||
return client;
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to create MCP client for tool {}: {}", tool.getName(), e.getMessage());
|
|
||||||
// 记录失败并可能禁用工具
|
|
||||||
handleToolFailure(id);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
activeClients.put(toolId, client);
|
||||||
|
markToolHealthy(toolId);
|
||||||
|
log.info("Created MCP client for tool: {}", tool.getName());
|
||||||
|
return client;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to create MCP client for tool {}: {}", tool.getName(), e.getMessage(), e);
|
||||||
|
handleToolFailure(toolId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -360,11 +363,11 @@ public class LangChain4jMcpToolProviderService {
|
|||||||
|
|
||||||
JsonNode configNode = objectMapper.readTree(configJson);
|
JsonNode configNode = objectMapper.readTree(configJson);
|
||||||
|
|
||||||
if (!configNode.has("baseUrl")) {
|
if (!configNode.has("url")) {
|
||||||
throw new IllegalArgumentException("baseUrl is required in config JSON for REMOTE type tool");
|
throw new IllegalArgumentException("baseUrl is required in config JSON for REMOTE type tool");
|
||||||
}
|
}
|
||||||
|
|
||||||
String baseUrl = configNode.get("baseUrl").asText();
|
String baseUrl = configNode.get("url").asText();
|
||||||
log.info("Creating HTTP/SSE MCP client for tool: {}, baseUrl: {}", tool.getName(), baseUrl);
|
log.info("Creating HTTP/SSE MCP client for tool: {}, baseUrl: {}", tool.getName(), baseUrl);
|
||||||
|
|
||||||
// 创建 HTTP/SSE 传输层
|
// 创建 HTTP/SSE 传输层
|
||||||
|
|||||||
Reference in New Issue
Block a user