Commit bee22bb0 authored by ligaowei's avatar ligaowei

重构ReAct执行器架构:1. 在ReactExecutor接口中添加executeWithAgent和executeStreamWithAgent方法...

重构ReAct执行器架构:1. 在ReactExecutor接口中添加executeWithAgent和executeStreamWithAgent方法 2. 修改ReActService中消除具体类型判断 3. 移动相关类到react包下
parent 46bfcc21
......@@ -39,6 +39,9 @@ public class ToolExecutionLoggerAspect {
String className = signature.getDeclaringType().getSimpleName();
String fullMethodName = className + "." + methodName;
// 使用完整的工具名称(类名)以确保开始和完成事件能够正确匹配
String toolName = className;
// 获取工具描述
String toolDescription = tool.description();
......@@ -62,7 +65,7 @@ public class ToolExecutionLoggerAspect {
// 记录工具调用开始
if (workPanelDataCollector != null) {
try {
workPanelDataCollector.recordToolCallStart(className, methodName, inputParams);
workPanelDataCollector.recordToolCallStart(toolName, methodName, inputParams);
} catch (Exception e) {
log.warn("记录工具调用开始时发生错误: {}", e.getMessage());
}
......@@ -81,7 +84,7 @@ public class ToolExecutionLoggerAspect {
// 记录工具调用完成
if (workPanelDataCollector != null) {
try {
workPanelDataCollector.recordToolCallComplete(className, result, "success", executionTime);
workPanelDataCollector.recordToolCallComplete(toolName, result, "success", executionTime);
} catch (Exception e) {
log.warn("记录工具调用完成时发生错误: {}", e.getMessage());
}
......@@ -99,7 +102,7 @@ public class ToolExecutionLoggerAspect {
// 记录工具调用错误
if (workPanelDataCollector != null) {
try {
workPanelDataCollector.recordToolCallComplete(className, e.getMessage(), "error", executionTime);
workPanelDataCollector.recordToolCallComplete(toolName, e.getMessage(), "error", executionTime);
} catch (Exception ex) {
log.warn("记录工具调用错误时发生错误: {}", ex.getMessage());
}
......
......@@ -7,7 +7,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import pangea.hiagent.agent.AgentChatService;
import pangea.hiagent.core.AgentChatService;
import pangea.hiagent.workpanel.SseEventManager;
import pangea.hiagent.dto.AgentRequest;
import pangea.hiagent.dto.ChatRequest;
......
......@@ -190,35 +190,46 @@ public class TimelineEventController {
sb.append("\"toolAction\":\"").append(sanitizeJsonString(event.getToolAction())).append("\",");
// 正确序列化 toolInput 为 JSON 对象
if (event.getToolInput() != null) {
try {
// 无论toolInput是否为null,都要包含这个字段以确保前端能正确识别
try {
if (event.getToolInput() != null) {
String toolInputJson = objectMapper.writeValueAsString(event.getToolInput());
sb.append("\"toolInput\":").append(toolInputJson).append(",");
log.debug("[toolInput序列化成功] 工具={}, JSON={}", event.getToolName(), toolInputJson);
} catch (Exception e) {
// 如果序列化失败,记录警告并回退到字符串表示
log.warn("[序列化toolInput失败] 工具={}, 错误={}, 已回退为字符串表示", event.getToolName(), e.getMessage());
} else {
sb.append("\"toolInput\":null,");
log.debug("[toolInput为null] 工具={}, 类型={}", event.getToolName(), event.getEventType());
}
} catch (Exception e) {
// 如果序列化失败,记录警告并回退到字符串表示
log.warn("[序列化toolInput失败] 工具={}, 错误={}, 已回退为字符串表示", event.getToolName(), e.getMessage());
if (event.getToolInput() != null) {
sb.append("\"toolInput\":\"").append(sanitizeJsonString(event.getToolInput().toString())).append("\",");
} else {
sb.append("\"toolInput\":null,");
}
} else {
log.debug("[toolInput为null] 工具={}, 类型={}", event.getToolName(), event.getEventType());
}
// 正确序列化 toolOutput 为 JSON 对象
if (event.getToolOutput() != null) {
try {
// 无论toolOutput是否为null,都要包含这个字段以确保前端能正确识别
try {
if (event.getToolOutput() != null) {
String toolOutputJson = objectMapper.writeValueAsString(event.getToolOutput());
sb.append("\"toolOutput\":").append(toolOutputJson).append(",");
log.debug("[toolOutput序列化成功] 工具={}, JSON={}", event.getToolName(), toolOutputJson);
} catch (Exception e) {
// 如果序列化失败,记录警告并回退到字符串表示
log.warn("[序列化toolOutput失败] 工具={}, 错误={}, 已回退为字符串表示", event.getToolName(), e.getMessage());
} else {
sb.append("\"toolOutput\":null,");
log.debug("[toolOutput为null] 工具={}, 类型={}", event.getToolName(), event.getEventType());
}
} catch (Exception e) {
// 如果序列化失败,记录警告并回退到字符串表示
log.warn("[序列化toolOutput失败] 工具={}, 错误={}, 已回退为字符串表示", event.getToolName(), e.getMessage());
if (event.getToolOutput() != null) {
sb.append("\"toolOutput\":\"").append(sanitizeJsonString(String.valueOf(event.getToolOutput()))).append("\",");
} else {
sb.append("\"toolOutput\":null,");
}
} else {
log.debug("[toolOutput为null] 工具={}, 类型={}", event.getToolName(), event.getEventType());
}
}
if (event.getToolStatus() != null) {
sb.append("\"toolStatus\":\"").append(sanitizeJsonString(event.getToolStatus())).append("\",");
}
......
package pangea.hiagent.agent;
package pangea.hiagent.core;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.messages.AssistantMessage;
......@@ -15,7 +15,7 @@ import pangea.hiagent.dto.WorkPanelEvent;
import pangea.hiagent.model.Agent;
import pangea.hiagent.model.AgentDialogue;
import pangea.hiagent.service.AgentService;
import pangea.hiagent.agent.ReActService;
import pangea.hiagent.core.ReActService;
import pangea.hiagent.workpanel.SseEventManager;
import pangea.hiagent.memory.SmartHistorySummarizer;
import java.util.ArrayList;
......@@ -51,7 +51,7 @@ public class AgentChatService {
public String handleReActAgentRequest(Agent agent, AgentRequest request, String userId) {
log.info("使用ReAct Agent处理请求");
// 使用ReAct Agent处理请求,传递userId以支持记忆功能
String responseContent = reActService.processRequestWithUserId(agent, request.getUserMessage(), userId);
String responseContent = reActService.processRequest(agent, request.getUserMessage(), userId);
// 保存对话记录并返回结果
return responseContent;
......@@ -120,7 +120,7 @@ public class AgentChatService {
Consumer<String> tokenConsumer) {
log.info("使用ReAct Agent处理流式请求");
// 使用ReAct Agent流式处理请求,传递userId以支持记忆功能
reActService.processRequestStreamWithUserId(agent, request.getUserMessage(), tokenConsumer, userId);
reActService.processRequestStream(agent, request.getUserMessage(), tokenConsumer, userId);
}
/**
......
package pangea.hiagent.core;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import pangea.hiagent.model.Agent;
import pangea.hiagent.model.Tool;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Agent工具管理服务类
* 负责管理Agent可用的工具列表
*/
@Slf4j
@Service
public class AgentToolManager {
@Autowired
private pangea.hiagent.service.ToolService toolService;
@Autowired
private ApplicationContext applicationContext;
/**
* 获取Agent可用的工具列表
* @param agent Agent对象
* @return 工具列表
*/
public List<Tool> getAvailableTools(Agent agent) {
try {
log.info("获取Agent可用工具列表,Agent ID: {}, 名称: {}", agent.getId(), agent.getName());
// 获取Agent所有者的所有活跃工具
List<Tool> allTools = toolService.getUserToolsByStatus(agent.getOwner(), "active");
log.info("用户所有活跃工具数量: {}", allTools != null ? allTools.size() : 0);
if (allTools == null || allTools.isEmpty()) {
log.warn("Agent: {} 没有配置可用的工具", agent.getId());
return List.of();
}
// 如果Agent配置了特定的工具列表,则只返回配置的工具
List<String> toolNames = agent.getToolNames();
log.info("Agent配置的工具名称列表: {}", toolNames);
if (toolNames != null && !toolNames.isEmpty()) {
// 根据工具名称筛选工具
List<Tool> filteredTools = filterToolsByName(allTools, toolNames);
log.info("筛选后的工具数量: {}", filteredTools.size());
return filteredTools;
}
return allTools;
} catch (Exception e) {
log.error("获取Agent可用工具时发生错误", e);
return List.of();
}
}
/**
* 根据工具名称筛选工具
* @param allTools 所有工具
* @param toolNames 工具名称列表
* @return 筛选后的工具列表
*/
public List<Tool> filterToolsByName(List<Tool> allTools, List<String> toolNames) {
return allTools.stream()
.filter(tool -> toolNames.contains(tool.getName()))
.collect(Collectors.toList());
}
/**
* 根据工具名称集合筛选工具实例(用于ReActService)
* @param allTools 所有工具实例
* @param toolNames 工具名称集合
* @return 筛选后的工具实例列表
*/
public List<Object> filterToolsByInstances(List<Object> allTools, Set<String> toolNames) {
log.debug("开始筛选工具实例,工具名称集合: {}", toolNames);
if (toolNames == null || toolNames.isEmpty()) {
log.debug("工具名称集合为空,返回所有工具实例");
return allTools;
}
List<Object> filteredTools = allTools.stream()
.filter(tool -> {
// 获取工具类名(不含包名)
String className = tool.getClass().getSimpleName();
log.debug("检查工具类: {}", className);
// 检查类名是否匹配
boolean isMatch = toolNames.contains(className) ||
toolNames.stream().anyMatch(name ->
className.toLowerCase().contains(name.toLowerCase()));
if (isMatch) {
log.debug("工具 {} 匹配成功", className);
}
return isMatch;
})
.collect(Collectors.toList());
log.debug("筛选完成,返回 {} 个工具实例", filteredTools.size());
return filteredTools;
}
/**
* 构建工具描述文本
* @param tools 工具列表
* @return 工具描述文本
*/
public String buildToolsDescription(List<Tool> tools) {
if (tools.isEmpty()) {
return "(暂无可用工具)";
}
StringBuilder description = new StringBuilder();
for (int i = 0; i < tools.size(); i++) {
Tool tool = tools.get(i);
description.append(i + 1).append(". ");
description.append(tool.getName());
if (hasValue(tool.getDisplayName())) {
description.append(" - ").append(tool.getDisplayName());
}
if (hasValue(tool.getDescription())) {
description.append(" - ").append(tool.getDescription());
}
description.append("\n");
}
return description.toString();
}
/**
* 检查字符串是否有值
* @param value 字符串值
* @return 是否有值
*/
private boolean hasValue(String value) {
return value != null && !value.isEmpty();
}
/**
* 根据Agent获取可用的工具实例
* @param agent Agent对象
* @return 工具实例列表
*/
public List<Object> getAvailableToolInstances(Agent agent) {
// 获取Agent可用的工具定义
List<Tool> availableTools = getAvailableTools(agent);
// 获取所有Spring管理的bean名称
String[] beanNames = applicationContext.getBeanDefinitionNames();
// 根据工具名称筛选对应的工具实例
List<Object> toolInstances = new ArrayList<>();
Set<String> availableToolNames = availableTools.stream()
.map(Tool::getName)
.collect(Collectors.toSet());
for (String beanName : beanNames) {
Object bean = applicationContext.getBean(beanName);
String simpleClassName = bean.getClass().getSimpleName();
// 检查bean的类名是否与可用工具名称匹配
if (availableToolNames.contains(simpleClassName)) {
toolInstances.add(bean);
}
}
log.info("智能体{}获取到的工具实例数量: {}", agent.getName(), toolInstances.size());
return toolInstances;
}
}
\ No newline at end of file
package pangea.hiagent.prompt;
package pangea.hiagent.core;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pangea.hiagent.model.Agent;
import pangea.hiagent.model.Tool;
import pangea.hiagent.service.ToolService;
import pangea.hiagent.core.AgentToolManager;
import java.util.List;
import java.util.stream.Collectors;
/**
* 提示词服务类
......@@ -19,7 +18,7 @@ import java.util.stream.Collectors;
public class PromptService {
@Autowired
private ToolService toolService;
private AgentToolManager agentToolManager;
/**
* 构建系统提示词 - 根据Agent配置的工具动态生成
......@@ -34,7 +33,7 @@ public class PromptService {
try {
// 获取Agent配置的可用工具列表
List<Tool> agentTools = getAvailableTools(agent);
List<Tool> agentTools = agentToolManager.getAvailableTools(agent);
// 如果没有工具,直接返回默认提示词
if (agentTools.isEmpty()) {
......@@ -42,7 +41,7 @@ public class PromptService {
}
// 构建工具描述部分
String toolsDescription = buildToolsDescription(agentTools);
String toolsDescription = agentToolManager.buildToolsDescription(agentTools);
String toolsList = buildToolsList(agentTools);
// 构建默认系统提示词,包含动态生成的工具信息
......@@ -54,90 +53,6 @@ public class PromptService {
}
}
/**
* 获取Agent可用的工具列表
* @param agent Agent对象
* @return 工具列表
*/
private List<Tool> getAvailableTools(Agent agent) {
try {
log.info("获取Agent可用工具列表,Agent ID: {}, 名称: {}", agent.getId(), agent.getName());
// 获取Agent所有者的所有活跃工具
List<Tool> allTools = toolService.getUserToolsByStatus(agent.getOwner(), "active");
log.info("用户所有活跃工具数量: {}", allTools != null ? allTools.size() : 0);
if (allTools == null || allTools.isEmpty()) {
log.warn("Agent: {} 没有配置可用的工具", agent.getId());
return List.of();
}
// 如果Agent配置了特定的工具列表,则只返回配置的工具
List<String> toolNames = agent.getToolNames();
log.info("Agent配置的工具名称列表: {}", toolNames);
if (toolNames != null && !toolNames.isEmpty()) {
// 根据工具名称筛选工具
List<Tool> filteredTools = filterToolsByName(allTools, toolNames);
log.info("筛选后的工具数量: {}", filteredTools.size());
return filteredTools;
}
return allTools;
} catch (Exception e) {
log.error("获取Agent可用工具时发生错误", e);
return List.of();
}
}
/**
* 根据工具名称筛选工具
* @param allTools 所有工具
* @param toolNames 工具名称列表
* @return 筛选后的工具列表
*/
private List<Tool> filterToolsByName(List<Tool> allTools, List<String> toolNames) {
return allTools.stream()
.filter(tool -> toolNames.contains(tool.getName()))
.collect(Collectors.toList());
}
/**
* 构建工具描述文本
* @param tools 工具列表
* @return 工具描述文本
*/
private String buildToolsDescription(List<Tool> tools) {
if (tools.isEmpty()) {
return "(暂无可用工具)";
}
StringBuilder description = new StringBuilder();
for (int i = 0; i < tools.size(); i++) {
Tool tool = tools.get(i);
description.append(i + 1).append(". ");
description.append(tool.getName());
if (hasValue(tool.getDisplayName())) {
description.append(" - ").append(tool.getDisplayName());
}
if (hasValue(tool.getDescription())) {
description.append(" - ").append(tool.getDescription());
}
description.append("\n");
}
return description.toString();
}
/**
* 检查字符串是否有值
* @param value 字符串值
* @return 是否有值
*/
private boolean hasValue(String value) {
return value != null && !value.isEmpty();
}
/**
* 构建工具名称列表(用于Action的可选值)
* @param tools 工具列表
......@@ -150,7 +65,7 @@ public class PromptService {
return tools.stream()
.map(Tool::getName)
.collect(Collectors.joining(", "));
.collect(java.util.stream.Collectors.joining(", "));
}
/**
......
package pangea.hiagent.tool;
package pangea.hiagent.react;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......
package pangea.hiagent.tool;
package pangea.hiagent.react;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
......@@ -9,7 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.context.annotation.Lazy;
import pangea.hiagent.workpanel.IWorkPanelDataCollector;
import pangea.hiagent.agent.AgentChatService;
import pangea.hiagent.core.AgentChatService;
import pangea.hiagent.memory.MemoryService;
import pangea.hiagent.model.Agent;
import java.util.List;
......
package pangea.hiagent.tool;
package pangea.hiagent.react;
/**
* ReAct回调接口,用于捕获ReAct执行的每一步
......
package pangea.hiagent.tool;
package pangea.hiagent.react;
import org.springframework.ai.chat.client.ChatClient;
import pangea.hiagent.model.Agent;
import java.util.List;
import java.util.function.Consumer;
......@@ -18,6 +19,18 @@ public interface ReactExecutor {
*/
String execute(ChatClient chatClient, String userInput, List<Object> tools);
/**
* 执行ReAct流程(同步方式)- 支持Agent配置
* @param chatClient ChatClient实例
* @param userInput 用户输入
* @param tools 工具列表
* @param agent Agent对象(可选)
* @return 最终答案
*/
default String executeWithAgent(ChatClient chatClient, String userInput, List<Object> tools, Agent agent) {
return execute(chatClient, userInput, tools);
}
/**
* 流式执行ReAct流程
* @param chatClient ChatClient实例
......@@ -27,6 +40,18 @@ public interface ReactExecutor {
*/
void executeStream(ChatClient chatClient, String userInput, List<Object> tools, Consumer<String> tokenConsumer);
/**
* 流式执行ReAct流程 - 支持Agent配置
* @param chatClient ChatClient实例
* @param userInput 用户输入
* @param tools 工具列表
* @param tokenConsumer token处理回调函数
* @param agent Agent对象(可选)
*/
default void executeStreamWithAgent(ChatClient chatClient, String userInput, List<Object> tools, Consumer<String> tokenConsumer, Agent agent) {
executeStream(chatClient, userInput, tools, tokenConsumer);
}
/**
* 添加ReAct回调
* @param callback ReAct回调
......
package pangea.hiagent.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pangea.hiagent.model.Agent;
import pangea.hiagent.model.Tool;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Agent工具管理服务类
* 负责管理Agent可用的工具列表
*/
@Slf4j
@Service
public class AgentToolManager {
@Autowired
private ToolService toolService;
/**
* 获取Agent可用的工具列表
* @param agent Agent对象
* @return 工具列表
*/
public List<Tool> getAvailableTools(Agent agent) {
try {
log.info("获取Agent可用工具列表,Agent ID: {}, 名称: {}", agent.getId(), agent.getName());
// 获取Agent所有者的所有活跃工具
List<Tool> allTools = toolService.getUserToolsByStatus(agent.getOwner(), "active");
log.info("用户所有活跃工具数量: {}", allTools != null ? allTools.size() : 0);
if (allTools == null || allTools.isEmpty()) {
log.warn("Agent: {} 没有配置可用的工具", agent.getId());
return List.of();
}
// 如果Agent配置了特定的工具列表,则只返回配置的工具
List<String> toolNames = agent.getToolNames();
log.info("Agent配置的工具名称列表: {}", toolNames);
if (toolNames != null && !toolNames.isEmpty()) {
// 根据工具名称筛选工具
List<Tool> filteredTools = filterToolsByName(allTools, toolNames);
log.info("筛选后的工具数量: {}", filteredTools.size());
return filteredTools;
}
return allTools;
} catch (Exception e) {
log.error("获取Agent可用工具时发生错误", e);
return List.of();
}
}
/**
* 根据工具名称筛选工具
* @param allTools 所有工具
* @param toolNames 工具名称列表
* @return 筛选后的工具列表
*/
public List<Tool> filterToolsByName(List<Tool> allTools, List<String> toolNames) {
return allTools.stream()
.filter(tool -> toolNames.contains(tool.getName()))
.collect(Collectors.toList());
}
/**
* 根据工具名称集合筛选工具实例(用于ReActService)
* @param allTools 所有工具实例
* @param toolNames 工具名称集合
* @return 筛选后的工具实例列表
*/
public List<Object> filterToolsByInstances(List<Object> allTools, Set<String> toolNames) {
log.debug("开始筛选工具实例,工具名称集合: {}", toolNames);
if (toolNames == null || toolNames.isEmpty()) {
log.debug("工具名称集合为空,返回所有工具实例");
return allTools;
}
List<Object> filteredTools = allTools.stream()
.filter(tool -> {
// 获取工具类名(不含包名)
String className = tool.getClass().getSimpleName();
log.debug("检查工具类: {}", className);
// 检查类名是否匹配
boolean isMatch = toolNames.contains(className) ||
toolNames.stream().anyMatch(name ->
className.toLowerCase().contains(name.toLowerCase()));
if (isMatch) {
log.debug("工具 {} 匹配成功", className);
}
return isMatch;
})
.collect(Collectors.toList());
log.debug("筛选完成,返回 {} 个工具实例", filteredTools.size());
return filteredTools;
}
/**
* 构建工具描述文本
* @param tools 工具列表
* @return 工具描述文本
*/
public String buildToolsDescription(List<Tool> tools) {
if (tools.isEmpty()) {
return "(暂无可用工具)";
}
StringBuilder description = new StringBuilder();
for (int i = 0; i < tools.size(); i++) {
Tool tool = tools.get(i);
description.append(i + 1).append(". ");
description.append(tool.getName());
if (hasValue(tool.getDisplayName())) {
description.append(" - ").append(tool.getDisplayName());
}
if (hasValue(tool.getDescription())) {
description.append(" - ").append(tool.getDescription());
}
description.append("\n");
}
return description.toString();
}
/**
* 检查字符串是否有值
* @param value 字符串值
* @return 是否有值
*/
private boolean hasValue(String value) {
return value != null && !value.isEmpty();
}
}
\ No newline at end of file
......@@ -26,7 +26,7 @@ public class WorkPanelService {
private AgentService agentService;
@Autowired
private pangea.hiagent.agent.ReActService reActService;
private pangea.hiagent.core.ReActService reActService;
// 用于跟踪已发送的事件ID,防止重复发送
private final Map<String, Set<String>> sentEventIds = new ConcurrentHashMap<>();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment