mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-16 21:33:40 +00:00
refactor: 抽离特殊聊天模式处理逻辑
- 将工作流、人机交互恢复、思考模式处理逻辑抽离为独立方法 - 新增 handleSpecialChatModes 方法统一处理特殊模式 - 新增 handleThinkingMode 方法专门处理思考模式 - 简化 sseChat 方法结构,提高代码可读性 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,38 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package org.ruoyi.agent;
|
|
||||||
|
|
||||||
import dev.langchain4j.agentic.Agent;
|
|
||||||
import dev.langchain4j.service.SystemMessage;
|
|
||||||
import dev.langchain4j.service.UserMessage;
|
|
||||||
import dev.langchain4j.service.V;
|
|
||||||
|
|
||||||
public interface McpAgent extends Agent {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 系统提示词:通用工具调用智能体
|
|
||||||
* 不限定具体工具类型,让 LangChain4j 自动传递工具描述给 LLM
|
|
||||||
*/
|
|
||||||
@SystemMessage("""
|
|
||||||
你是一个AI助手,可以通过调用各种工具来帮助用户完成不同的任务。
|
|
||||||
|
|
||||||
【工具使用规则】
|
|
||||||
1. 根据用户的请求,判断需要使用哪些工具
|
|
||||||
2. 仔细阅读每个工具的描述,确保理解工具的功能和参数要求
|
|
||||||
3. 使用正确的参数调用工具
|
|
||||||
4. 如果工具执行失败,向用户友好地说明错误原因,并尝试提供替代方案
|
|
||||||
5. 对于复杂任务,可以分步骤使用多个工具完成
|
|
||||||
6. 将工具执行结果以清晰易懂的方式呈现给用户
|
|
||||||
|
|
||||||
【响应格式】
|
|
||||||
- 直接回答用户的问题
|
|
||||||
- 如果使用了工具,说明使用了什么工具以及结果
|
|
||||||
- 如果遇到错误,提供友好的错误信息和解决建议
|
|
||||||
""")
|
|
||||||
|
|
||||||
@UserMessage("""
|
|
||||||
{{query}}
|
|
||||||
""")
|
|
||||||
|
|
||||||
@Agent("通用工具调用智能体")
|
|
||||||
/**
|
|
||||||
* 智能体对外调用入口
|
|
||||||
* @param query 用户的自然语言请求
|
|
||||||
* @return 处理结果
|
|
||||||
*/
|
|
||||||
String callMcpTool(@V("query") String query);
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
//package org.ruoyi.service.chat.handler;
|
|
||||||
//
|
|
||||||
//import dev.langchain4j.agentic.AgenticServices;
|
|
||||||
//import dev.langchain4j.community.model.dashscope.QwenChatModel;
|
|
||||||
//import dev.langchain4j.service.tool.ToolProvider;
|
|
||||||
//import lombok.RequiredArgsConstructor;
|
|
||||||
//import lombok.extern.slf4j.Slf4j;
|
|
||||||
//import org.ruoyi.agent.McpAgent;
|
|
||||||
//import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
|
||||||
//import org.ruoyi.common.chat.entity.chat.ChatContext;
|
|
||||||
//import org.ruoyi.common.chat.service.chatMessage.IChatMessageService;
|
|
||||||
//import org.ruoyi.common.sse.utils.SseMessageUtils;
|
|
||||||
//import org.ruoyi.mcp.service.core.ToolProviderFactory;
|
|
||||||
//import org.springframework.core.annotation.Order;
|
|
||||||
//import org.springframework.stereotype.Component;
|
|
||||||
//import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
|
||||||
//
|
|
||||||
//import java.util.ArrayList;
|
|
||||||
//import java.util.List;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Agent 深度思考处理器
|
|
||||||
// * <p>
|
|
||||||
// * 处理 enableThinking=true 的场景,使用 Agent 进行深度思考和工具调用
|
|
||||||
// *
|
|
||||||
// * @author ageerle@163.com
|
|
||||||
// * @date 2025/12/13
|
|
||||||
// */
|
|
||||||
//@Slf4j
|
|
||||||
//@Component
|
|
||||||
//@Order(3)
|
|
||||||
//@RequiredArgsConstructor
|
|
||||||
//public class AgentChatHandler implements ChatHandler {
|
|
||||||
//
|
|
||||||
// private final ToolProviderFactory toolProviderFactory;
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public boolean supports(ChatContext context) {
|
|
||||||
// Boolean enableThinking = context.getChatRequest().getEnableThinking();
|
|
||||||
// return enableThinking != null && enableThinking;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public SseEmitter handle(ChatContext context) {
|
|
||||||
// log.info("处理 Agent 深度思考,用户: {}", context.getUserId());
|
|
||||||
//
|
|
||||||
// Long userId = context.getUserId();
|
|
||||||
// String tokenValue = context.getTokenValue();
|
|
||||||
// ChatModelVo chatModelVo = context.getChatModelVo();
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// // 1. 保存用户消息
|
|
||||||
// String content = extractUserContent(context);
|
|
||||||
//// saveChatMessage(context.getChatRequest(), userId, content,
|
|
||||||
//// RoleType.USER.getName(), chatModelVo);
|
|
||||||
//
|
|
||||||
// // 2. 执行 Agent 任务
|
|
||||||
// String result = doAgent(content, chatModelVo);
|
|
||||||
//
|
|
||||||
// // 3. 发送结果并保存
|
|
||||||
// SseMessageUtils.sendMessage(userId, result);
|
|
||||||
// SseMessageUtils.completeConnection(userId, tokenValue);
|
|
||||||
//
|
|
||||||
//// saveChatMessage(context.getChatRequest(), userId, result,
|
|
||||||
//// RoleType.ASSISTANT.getName(), chatModelVo);
|
|
||||||
// // todo 保存消息
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// log.error("Agent 执行失败: {}", e.getMessage(), e);
|
|
||||||
// SseMessageUtils.sendMessage(userId, "Agent 执行失败:" + e.getMessage());
|
|
||||||
// SseMessageUtils.completeConnection(userId, tokenValue);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return context.getEmitter();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 执行 Agent 任务
|
|
||||||
// */
|
|
||||||
// private String doAgent(String userMessage, ChatModelVo chatModelVo) {
|
|
||||||
// log.info("执行 Agent 任务,消息: {}", userMessage);
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// // 1. 加载 LLM 模型
|
|
||||||
// QwenChatModel qwenChatModel = QwenChatModel.builder()
|
|
||||||
// .apiKey(chatModelVo.getApiKey())
|
|
||||||
// .modelName(chatModelVo.getModelName())
|
|
||||||
// .build();
|
|
||||||
//
|
|
||||||
// // 2. 获取内置工具
|
|
||||||
// List<Object> builtinTools = toolProviderFactory.getAllBuiltinToolObjects();
|
|
||||||
// List<Object> allTools = new ArrayList<>(builtinTools);
|
|
||||||
// log.debug("加载 {} 个内置工具", builtinTools.size());
|
|
||||||
//
|
|
||||||
// // 3. 获取 MCP 工具提供者
|
|
||||||
// ToolProvider mcpToolProvider = toolProviderFactory.getAllEnabledMcpToolsProvider();
|
|
||||||
//
|
|
||||||
// // 4. 创建 MCP Agent
|
|
||||||
// var agentBuilder = AgenticServices.agentBuilder(McpAgent.class)
|
|
||||||
// .chatModel(qwenChatModel);
|
|
||||||
//
|
|
||||||
// if (!allTools.isEmpty()) {
|
|
||||||
// agentBuilder.tools(allTools.toArray(new Object[0]));
|
|
||||||
// }
|
|
||||||
// if (mcpToolProvider != null) {
|
|
||||||
// agentBuilder.toolProvider(mcpToolProvider);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// McpAgent mcpAgent = agentBuilder.build();
|
|
||||||
//
|
|
||||||
// // 5. 调用 Agent
|
|
||||||
// String result = mcpAgent.callMcpTool(userMessage);
|
|
||||||
// log.info("Agent 执行完成,结果长度: {}", result.length());
|
|
||||||
// return result;
|
|
||||||
//
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// log.error("Agent 模式执行失败: {}", e.getMessage(), e);
|
|
||||||
// return "Agent 执行失败: " + e.getMessage();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 提取用户消息内容
|
|
||||||
// */
|
|
||||||
// private String extractUserContent(ChatContext context) {
|
|
||||||
// var messages = context.getChatRequest().getMessages();
|
|
||||||
// if (messages != null && !messages.isEmpty()) {
|
|
||||||
// return messages.get(0).getContent();
|
|
||||||
// }
|
|
||||||
// return "";
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
//package org.ruoyi.service.chat.handler;
|
|
||||||
//
|
|
||||||
//import lombok.RequiredArgsConstructor;
|
|
||||||
//import lombok.extern.slf4j.Slf4j;
|
|
||||||
//import org.ruoyi.common.chat.domain.dto.request.ReSumeRunner;
|
|
||||||
//import org.ruoyi.common.chat.entity.chat.ChatContext;
|
|
||||||
//import org.ruoyi.common.chat.service.workFlow.IWorkFlowStarterService;
|
|
||||||
//import org.ruoyi.common.core.utils.ObjectUtils;
|
|
||||||
//import org.springframework.core.annotation.Order;
|
|
||||||
//import org.springframework.stereotype.Component;
|
|
||||||
//import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * 人机交互恢复处理器
|
|
||||||
// * <p>
|
|
||||||
// * 处理 isResume=true 的场景,恢复工作流的人机交互
|
|
||||||
// *
|
|
||||||
// * @author ageerle@163.com
|
|
||||||
// * @date 2025/12/13
|
|
||||||
// */
|
|
||||||
//@Slf4j
|
|
||||||
//@Component
|
|
||||||
//@Order(1)
|
|
||||||
//@RequiredArgsConstructor
|
|
||||||
//public class ResumeChatHandler implements ChatHandler {
|
|
||||||
//
|
|
||||||
// private final IWorkFlowStarterService workFlowStarterService;
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public boolean supports(ChatContext context) {
|
|
||||||
// Boolean isResume = context.getChatRequest().getIsResume();
|
|
||||||
// return isResume != null && isResume;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public SseEmitter handle(ChatContext context) {
|
|
||||||
// log.info("处理人机交互恢复,用户: {}", context.getUserId());
|
|
||||||
//
|
|
||||||
// ReSumeRunner reSumeRunner = context.getChatRequest().getReSumeRunner();
|
|
||||||
// if (ObjectUtils.isEmpty(reSumeRunner)) {
|
|
||||||
// log.warn("人机交互恢复参数为空");
|
|
||||||
// return context.getEmitter();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// workFlowStarterService.resumeFlow(
|
|
||||||
// reSumeRunner.getRuntimeUuid(),
|
|
||||||
// reSumeRunner.getFeedbackContent(),
|
|
||||||
// context.getEmitter()
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// return context.getEmitter();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
//package org.ruoyi.service.chat.handler;
|
|
||||||
//
|
|
||||||
//import lombok.RequiredArgsConstructor;
|
|
||||||
//import lombok.extern.slf4j.Slf4j;
|
|
||||||
//import org.ruoyi.common.chat.base.ThreadContext;
|
|
||||||
//import org.ruoyi.common.chat.domain.dto.request.WorkFlowRunner;
|
|
||||||
//import org.ruoyi.common.chat.entity.chat.ChatContext;
|
|
||||||
//import org.ruoyi.common.chat.service.workFlow.IWorkFlowStarterService;
|
|
||||||
//import org.ruoyi.common.core.utils.ObjectUtils;
|
|
||||||
//import org.springframework.core.annotation.Order;
|
|
||||||
//import org.springframework.stereotype.Component;
|
|
||||||
//import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * 工作流对话处理器
|
|
||||||
// * <p>
|
|
||||||
// * 处理 enableWorkFlow=true 的场景,启动工作流对话
|
|
||||||
// *
|
|
||||||
// * @author ageerle@163.com
|
|
||||||
// * @date 2025/12/13
|
|
||||||
// */
|
|
||||||
//@Slf4j
|
|
||||||
//@Component
|
|
||||||
//@Order(2)
|
|
||||||
//@RequiredArgsConstructor
|
|
||||||
//public class WorkflowChatHandler implements ChatHandler {
|
|
||||||
//
|
|
||||||
// private final IWorkFlowStarterService workFlowStarterService;
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public boolean supports(ChatContext context) {
|
|
||||||
// Boolean enableWorkFlow = context.getChatRequest().getEnableWorkFlow();
|
|
||||||
// return enableWorkFlow != null && enableWorkFlow;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public SseEmitter handle(ChatContext context) {
|
|
||||||
// log.info("处理工作流对话,用户: {}, 会话: {}",
|
|
||||||
// context.getUserId(), context.getChatRequest().getSessionId());
|
|
||||||
//
|
|
||||||
// WorkFlowRunner runner = context.getChatRequest().getWorkFlowRunner();
|
|
||||||
// if (ObjectUtils.isEmpty(runner)) {
|
|
||||||
// log.warn("工作流参数为空");
|
|
||||||
// return context.getEmitter();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return workFlowStarterService.streaming(
|
|
||||||
// ThreadContext.getCurrentUser(),
|
|
||||||
// runner.getUuid(),
|
|
||||||
// runner.getInputs(),
|
|
||||||
// context.getChatRequest().getSessionId()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@@ -1,25 +1,48 @@
|
|||||||
package org.ruoyi.service.chat.impl;
|
package org.ruoyi.service.chat.impl;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import dev.langchain4j.agentic.AgenticServices;
|
||||||
|
import dev.langchain4j.agentic.supervisor.SupervisorAgent;
|
||||||
|
import dev.langchain4j.agentic.supervisor.SupervisorResponseStrategy;
|
||||||
|
import dev.langchain4j.community.model.dashscope.QwenChatModel;
|
||||||
import dev.langchain4j.data.message.*;
|
import dev.langchain4j.data.message.*;
|
||||||
|
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.stdio.StdioMcpTransport;
|
||||||
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
|
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
|
||||||
import dev.langchain4j.model.chat.StreamingChatModel;
|
import dev.langchain4j.model.chat.StreamingChatModel;
|
||||||
import dev.langchain4j.model.chat.response.ChatResponse;
|
import dev.langchain4j.model.chat.response.ChatResponse;
|
||||||
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
|
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
|
||||||
|
import dev.langchain4j.model.openai.OpenAiChatModel;
|
||||||
|
import dev.langchain4j.service.tool.ToolProvider;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.ruoyi.agent.ChartGenerationAgent;
|
||||||
|
import org.ruoyi.agent.SqlAgent;
|
||||||
|
import org.ruoyi.agent.WebSearchAgent;
|
||||||
|
import org.ruoyi.agent.tool.ExecuteSqlQueryTool;
|
||||||
|
import org.ruoyi.agent.tool.QueryAllTablesTool;
|
||||||
|
import org.ruoyi.agent.tool.QueryTableSchemaTool;
|
||||||
|
import org.ruoyi.common.chat.base.ThreadContext;
|
||||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.dto.request.ReSumeRunner;
|
||||||
|
import org.ruoyi.common.chat.domain.dto.request.WorkFlowRunner;
|
||||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
import org.ruoyi.common.chat.enums.RoleType;
|
import org.ruoyi.common.chat.enums.RoleType;
|
||||||
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
||||||
import org.ruoyi.common.chat.service.chat.IChatService;
|
import org.ruoyi.common.chat.service.chat.IChatService;
|
||||||
|
import org.ruoyi.common.chat.service.workFlow.IWorkFlowStarterService;
|
||||||
|
import org.ruoyi.common.core.utils.ObjectUtils;
|
||||||
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.common.sse.utils.SseMessageUtils;
|
import org.ruoyi.common.sse.utils.SseMessageUtils;
|
||||||
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.factory.ChatServiceFactory;
|
import org.ruoyi.factory.ChatServiceFactory;
|
||||||
|
import org.ruoyi.mcp.service.core.ToolProviderFactory;
|
||||||
import org.ruoyi.service.chat.AbstractChatService;
|
import org.ruoyi.service.chat.AbstractChatService;
|
||||||
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;
|
||||||
@@ -62,12 +85,18 @@ public class ChatServiceFacade implements IChatService {
|
|||||||
|
|
||||||
private final IChatMessageService chatMessageService;
|
private final IChatMessageService chatMessageService;
|
||||||
|
|
||||||
|
private final IWorkFlowStarterService workFlowStarterService;
|
||||||
|
|
||||||
|
private final ToolProviderFactory toolProviderFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内存实例缓存,避免同一会话重复创建
|
* 内存实例缓存,避免同一会话重复创建
|
||||||
* Key: sessionId, Value: MessageWindowChatMemory实例
|
* Key: sessionId, Value: MessageWindowChatMemory实例
|
||||||
*/
|
*/
|
||||||
private static final Map<Object, MessageWindowChatMemory> memoryCache = new ConcurrentHashMap<>();
|
private static final Map<Object, MessageWindowChatMemory> memoryCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统一聊天入口 - SSE流式响应
|
* 统一聊天入口 - SSE流式响应
|
||||||
*
|
*
|
||||||
@@ -76,6 +105,11 @@ public class ChatServiceFacade implements IChatService {
|
|||||||
*/
|
*/
|
||||||
public SseEmitter sseChat(ChatRequest chatRequest) {
|
public SseEmitter sseChat(ChatRequest chatRequest) {
|
||||||
|
|
||||||
|
// 4. 具体的服务实现
|
||||||
|
Long userId = LoginHelper.getUserId();
|
||||||
|
String tokenValue = StpUtil.getTokenValue();
|
||||||
|
SseEmitter emitter = sseEmitterManager.connect(userId, tokenValue);
|
||||||
|
|
||||||
// 1. 根据模型名称查询完整配置
|
// 1. 根据模型名称查询完整配置
|
||||||
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
||||||
if (chatModelVo == null) {
|
if (chatModelVo == null) {
|
||||||
@@ -85,14 +119,17 @@ public class ChatServiceFacade implements IChatService {
|
|||||||
// 2. 构建上下文消息列表
|
// 2. 构建上下文消息列表
|
||||||
List<ChatMessage> contextMessages = buildContextMessages(chatRequest);
|
List<ChatMessage> contextMessages = buildContextMessages(chatRequest);
|
||||||
|
|
||||||
// 3. 路由服务提供商
|
// 3. 处理特殊聊天模式(工作流、人机交互恢复、思考模式)
|
||||||
|
SseEmitter specialResult = handleSpecialChatModes(chatRequest, contextMessages, chatModelVo, emitter);
|
||||||
|
if (specialResult != null) {
|
||||||
|
return specialResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 路由服务提供商
|
||||||
String providerCode = chatModelVo.getProviderCode();
|
String providerCode = chatModelVo.getProviderCode();
|
||||||
log.info("路由到服务提供商: {}, 模型: {}", providerCode, chatRequest.getModel());
|
log.info("路由到服务提供商: {}, 模型: {}", providerCode, chatRequest.getModel());
|
||||||
AbstractChatService chatService = chatServiceFactory.getOriginalService(providerCode);
|
AbstractChatService chatService = chatServiceFactory.getOriginalService(providerCode);
|
||||||
// 4. 具体的服务实现
|
|
||||||
Long userId = LoginHelper.getUserId();
|
|
||||||
String tokenValue = StpUtil.getTokenValue();
|
|
||||||
SseEmitter emitter = sseEmitterManager.connect(userId, tokenValue);
|
|
||||||
|
|
||||||
StreamingChatResponseHandler handler = createResponseHandler(userId, tokenValue,chatRequest);
|
StreamingChatResponseHandler handler = createResponseHandler(userId, tokenValue,chatRequest);
|
||||||
|
|
||||||
@@ -105,6 +142,131 @@ public class ChatServiceFacade implements IChatService {
|
|||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理特殊聊天模式(工作流、人机交互恢复、思考模式)
|
||||||
|
*
|
||||||
|
* @param chatRequest 聊天请求
|
||||||
|
* @param contextMessages 上下文消息列表(可能被修改)
|
||||||
|
* @param chatModelVo 聊天模型配置
|
||||||
|
* @param emitter SSE发射器
|
||||||
|
* @return 如果需要提前返回则返回SseEmitter,否则返回null
|
||||||
|
*/
|
||||||
|
private SseEmitter handleSpecialChatModes(ChatRequest chatRequest, List<ChatMessage> contextMessages,
|
||||||
|
ChatModelVo chatModelVo, SseEmitter emitter) {
|
||||||
|
// 处理工作流对话
|
||||||
|
if (chatRequest.getEnableWorkFlow()) {
|
||||||
|
log.info("处理工作流对话,会话: {}", chatRequest.getSessionId());
|
||||||
|
|
||||||
|
WorkFlowRunner runner = chatRequest.getWorkFlowRunner();
|
||||||
|
if (ObjectUtils.isEmpty(runner)) {
|
||||||
|
log.warn("工作流参数为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
return workFlowStarterService.streaming(
|
||||||
|
ThreadContext.getCurrentUser(),
|
||||||
|
runner.getUuid(),
|
||||||
|
runner.getInputs(),
|
||||||
|
chatRequest.getSessionId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理人机交互恢复
|
||||||
|
if (chatRequest.getIsResume()) {
|
||||||
|
log.info("处理人机交互恢复");
|
||||||
|
|
||||||
|
ReSumeRunner reSumeRunner = chatRequest.getReSumeRunner();
|
||||||
|
if (ObjectUtils.isEmpty(reSumeRunner)) {
|
||||||
|
log.warn("人机交互恢复参数为空");
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
workFlowStarterService.resumeFlow(
|
||||||
|
reSumeRunner.getRuntimeUuid(),
|
||||||
|
reSumeRunner.getFeedbackContent(),
|
||||||
|
emitter
|
||||||
|
);
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理思考模式
|
||||||
|
if (chatRequest.getEnableThinking()) {
|
||||||
|
handleThinkingMode(chatRequest, contextMessages, chatModelVo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理思考模式
|
||||||
|
*
|
||||||
|
* @param chatRequest 聊天请求
|
||||||
|
* @param contextMessages 上下文消息列表
|
||||||
|
* @param chatModelVo 聊天模型配置
|
||||||
|
*/
|
||||||
|
private void handleThinkingMode(ChatRequest chatRequest, List<ChatMessage> contextMessages, ChatModelVo chatModelVo) {
|
||||||
|
// 步骤1: 配置MCP传输层 - 连接到bing-cn-mcp服务器
|
||||||
|
McpTransport transport = new StdioMcpTransport.Builder()
|
||||||
|
.command(List.of("C:\\Program Files\\nodejs\\npx.cmd", "-y", "bing-cn-mcp"))
|
||||||
|
.logEvents(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
McpClient mcpClient = new DefaultMcpClient.Builder()
|
||||||
|
.transport(transport)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ToolProvider toolProvider = McpToolProvider.builder()
|
||||||
|
.mcpClients(List.of(mcpClient))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 配置echarts MCP
|
||||||
|
McpTransport transport1 = new StdioMcpTransport.Builder()
|
||||||
|
.command(List.of("C:\\Program Files\\nodejs\\npx.cmd", "-y", "mcp-echarts"))
|
||||||
|
.logEvents(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
McpClient mcpClient1 = new DefaultMcpClient.Builder()
|
||||||
|
.transport(transport1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ToolProvider toolProvider1 = McpToolProvider.builder()
|
||||||
|
.mcpClients(List.of(mcpClient1))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 配置模型
|
||||||
|
OpenAiChatModel plannerModel = OpenAiChatModel.builder()
|
||||||
|
.baseUrl(chatModelVo.getApiHost())
|
||||||
|
.apiKey(chatModelVo.getApiKey())
|
||||||
|
.modelName(chatModelVo.getModelName())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 构建各Agent
|
||||||
|
SqlAgent sqlAgent = AgenticServices.agentBuilder(SqlAgent.class)
|
||||||
|
.chatModel(plannerModel)
|
||||||
|
.tools(new QueryAllTablesTool(), new QueryTableSchemaTool(), new ExecuteSqlQueryTool())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
WebSearchAgent searchAgent = AgenticServices.agentBuilder(WebSearchAgent.class)
|
||||||
|
.chatModel(plannerModel)
|
||||||
|
.toolProvider(toolProvider)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ChartGenerationAgent chartGenerationAgent = AgenticServices.agentBuilder(ChartGenerationAgent.class)
|
||||||
|
.chatModel(plannerModel)
|
||||||
|
.toolProvider(toolProvider1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 构建监督者Agent
|
||||||
|
SupervisorAgent supervisor = AgenticServices.supervisorBuilder()
|
||||||
|
.chatModel(plannerModel)
|
||||||
|
.subAgents(sqlAgent, chartGenerationAgent)
|
||||||
|
.responseStrategy(SupervisorResponseStrategy.LAST)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String invoke = supervisor.invoke(chatRequest.getContent());
|
||||||
|
contextMessages.add(AiMessage.from(invoke));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支持外部 handler 的对话接口(跨模块调用)
|
* 支持外部 handler 的对话接口(跨模块调用)
|
||||||
* 同时发送到 SSE 和外部 handler
|
* 同时发送到 SSE 和外部 handler
|
||||||
|
|||||||
Reference in New Issue
Block a user