Merge pull request #258 from StevenJack666/v3.0.0

修改千问模型调用逻辑
This commit is contained in:
ageerle
2026-02-14 10:47:14 +08:00
committed by GitHub
5 changed files with 173 additions and 54 deletions

View File

@@ -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启动成功 ლ(´ڡ`ლ)゙");
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -73,6 +73,9 @@ public abstract class AbstractStreamingChatService implements IChatService {
*/
private static final Map<Object, MessageWindowChatMemory> memoryCache = new ConcurrentHashMap<>();
/**
* 定义聊天流程骨架
*/

View File

@@ -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();