mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-03-13 20:53:42 +08:00
Compare commits
5 Commits
1d1b72c6e2
...
ace7e961a4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ace7e961a4 | ||
|
|
5dc21653c0 | ||
|
|
d0d4b2229f | ||
|
|
07c9727345 | ||
|
|
689adf079d |
@@ -16,7 +16,7 @@ public class RuoYiAIApplication {
|
||||
SpringApplication application = new SpringApplication(RuoYiAIApplication.class);
|
||||
application.setApplicationStartup(new BufferingApplicationStartup(2048));
|
||||
application.run(args);
|
||||
System.out.println("(♥◠‿◠)ノ゙ RuoYi-AI启动成功 ლ(´ڡ`ლ)゙");
|
||||
System.out.println("(♥◠‿◠)ノ゙ RuoYi-AI启动成功 ლ(´ڡ`ლ)゙");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package org.ruoyi.agent;
|
||||
|
||||
import dev.langchain4j.agentic.Agent;
|
||||
import dev.langchain4j.service.SystemMessage;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.V;
|
||||
|
||||
/**
|
||||
* Text2SQL and Echarts Chart Generation Agent
|
||||
* An intelligent assistant that converts natural language queries into SQL,
|
||||
* executes database queries, and generates Echarts visualizations.
|
||||
*/
|
||||
public interface EchartsAgent {
|
||||
|
||||
@SystemMessage("""
|
||||
You are a data visualization assistant that generates Echarts chart configurations.
|
||||
|
||||
CRITICAL OUTPUT REQUIREMENTS:
|
||||
- Return Echarts JSON wrapped in markdown code block
|
||||
- Use this exact format: ```json\n{...}\n```
|
||||
- The JSON inside must be valid Echarts configuration
|
||||
- Frontend expects markdown format for proper parsing
|
||||
|
||||
Your workflow:
|
||||
1. Use MCP tools to query the database and get data
|
||||
2. The MCP tool returns data in this structure:
|
||||
{"data": [{"dict_type": "value1", "count": 10}, {"dict_type": "value2", "count": 20}, ...]}
|
||||
3. Transform this data into Echarts configuration
|
||||
4. Return ONLY the Echarts JSON
|
||||
|
||||
Data transformation rules:
|
||||
- Extract array elements into xAxis categories and series data
|
||||
- For the example above: xAxis.data = ["value1", "value2"], series.data = [10, 20]
|
||||
- Choose chart type based on request: bar (default), line, pie, etc.
|
||||
|
||||
Expected output format (bar chart example):
|
||||
```json
|
||||
{
|
||||
"title": {
|
||||
"text": "Dict Type Distribution",
|
||||
"left": "center"
|
||||
},
|
||||
"tooltip": {
|
||||
"trigger": "axis"
|
||||
},
|
||||
"xAxis": {
|
||||
"type": "category",
|
||||
"data": ["type1", "type2", "type3"]
|
||||
},
|
||||
"yAxis": {
|
||||
"type": "value"
|
||||
},
|
||||
"series": [
|
||||
{
|
||||
"name": "数量",
|
||||
"type": "bar",
|
||||
"data": [10, 20, 15]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
For pie charts:
|
||||
{
|
||||
"title": {"text": "Chart Title", "left": "center"},
|
||||
"tooltip": {"trigger": "item"},
|
||||
"series": [{
|
||||
"type": "pie",
|
||||
"radius": "50%",
|
||||
"data": [
|
||||
{"value": 10, "name": "Category1"},
|
||||
{"value": 20, "name": "Category2"}
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
REMEMBER:
|
||||
- Always wrap JSON in ```json and ``` markers
|
||||
- Use proper formatting with indentation
|
||||
- This is the expected format for frontend parsing
|
||||
""")
|
||||
@UserMessage("""
|
||||
Generate an Echarts chart for: {{query}}
|
||||
|
||||
IMPORTANT: Return the Echarts configuration JSON wrapped in markdown code block (```json...```).
|
||||
""")
|
||||
@Agent("Data visualization assistant that returns Echarts JSON configurations for frontend rendering")
|
||||
String search(@V("query") String query);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.ruoyi.agent;
|
||||
|
||||
import dev.langchain4j.agentic.Agent;
|
||||
import dev.langchain4j.service.SystemMessage;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.V;
|
||||
|
||||
/**
|
||||
* User Name Retrieval Agent
|
||||
* A simple assistant that retrieves user names using the get_name tool.
|
||||
*/
|
||||
public interface GetNameInfo {
|
||||
|
||||
@SystemMessage("""
|
||||
You are a user identity assistant. You MUST always use tools to get information.
|
||||
|
||||
MANDATORY REQUIREMENTS:
|
||||
- You MUST call the get_user_name_by_id tool for ANY question about names or identity
|
||||
- NEVER respond without calling the get_user_name_by_id tool first
|
||||
- Return ONLY the exact string returned by the get_user_name_by_id tool
|
||||
- Do not make up names like "John Doe" or any other default names
|
||||
- Do not use your knowledge to answer - ALWAYS use the tool
|
||||
|
||||
Your workflow:
|
||||
1. Extract userId from the query (if mentioned), or use "1" as default
|
||||
2. ALWAYS call the get_user_name_by_id tool with the userId parameter
|
||||
3. Return the exact result as plain text with no additions
|
||||
|
||||
CRITICAL: If you don't call the get_user_name_by_id tool, your response is wrong.
|
||||
""")
|
||||
@UserMessage("""
|
||||
Get the user name using the get_user_name_by_id tool. Query: {{query}}
|
||||
|
||||
IMPORTANT: Return only the exact result from the tool.
|
||||
""")
|
||||
@Agent("User identity assistant that returns user name from get_name tool")
|
||||
String search(@V("query") String query);
|
||||
}
|
||||
@@ -73,6 +73,9 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
||||
*/
|
||||
private static final Map<Object, MessageWindowChatMemory> memoryCache = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 定义聊天流程骨架
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
package org.ruoyi.service.chat.impl.provider;
|
||||
|
||||
import dev.langchain4j.agent.tool.ToolSpecification;
|
||||
import java.util.ArrayList;
|
||||
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.supervisor.SupervisorAgent;
|
||||
import dev.langchain4j.agentic.supervisor.SupervisorResponseStrategy;
|
||||
@@ -13,23 +25,11 @@ import dev.langchain4j.mcp.McpToolProvider;
|
||||
import dev.langchain4j.mcp.client.DefaultMcpClient;
|
||||
import dev.langchain4j.mcp.client.McpClient;
|
||||
import dev.langchain4j.mcp.client.transport.McpTransport;
|
||||
import dev.langchain4j.mcp.client.transport.http.StreamableHttpMcpTransport;
|
||||
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;
|
||||
import dev.langchain4j.model.chat.StreamingChatModel;
|
||||
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
|
||||
import dev.langchain4j.service.tool.ToolProvider;
|
||||
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服务调用
|
||||
@@ -41,13 +41,6 @@ import java.util.List;
|
||||
@Slf4j
|
||||
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";
|
||||
@@ -108,49 +101,45 @@ public class QianWenChatServiceImpl extends AbstractStreamingChatService {
|
||||
* @return 返回LLM信息
|
||||
*/
|
||||
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模型对话
|
||||
QwenChatModel qwenChatModel = QwenChatModel.builder()
|
||||
.baseUrl(QWEN_API_HOST)
|
||||
.apiKey(chatModelVo.getApiKey())
|
||||
.modelName(chatModelVo.getModelName())
|
||||
.build();
|
||||
|
||||
// 步骤5:将MCP对象由智能体Agent管控
|
||||
McpAgent mcpAgent = AgenticServices.agentBuilder(McpAgent.class)
|
||||
.chatModel(qwenChatModel)
|
||||
.toolProvider(toolProvider)
|
||||
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客户端
|
||||
McpClient echartClient = new DefaultMcpClient.Builder()
|
||||
.transport(echart)
|
||||
.build();
|
||||
|
||||
// 步骤3: 配置工具提供者
|
||||
ToolProvider echartToolProvider = McpToolProvider.builder()
|
||||
.mcpClients(List.of(
|
||||
echartClient))
|
||||
.build();
|
||||
|
||||
EchartsAgent chartGenerationAgent = AgenticServices.agentBuilder(
|
||||
EchartsAgent.class)
|
||||
.chatModel(qwenChatModel)
|
||||
.toolProvider(echartToolProvider)
|
||||
.build();
|
||||
|
||||
|
||||
// 步骤6:将所有MCP对象由超级智能体管控
|
||||
SupervisorAgent supervisor = AgenticServices
|
||||
.supervisorBuilder()
|
||||
.chatModel(qwenChatModel)
|
||||
.subAgents(mcpAgent)
|
||||
.subAgents(chartGenerationAgent)
|
||||
.responseStrategy(SupervisorResponseStrategy.LAST)
|
||||
.build();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user