|
|
@@ -2,11 +2,13 @@ package edu.nju.software.aipaasagent.agent.core.reactagent;
|
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import edu.nju.software.aipaasagent.agent.config.AgentConfiguration;
|
|
|
import edu.nju.software.aipaasagent.agent.manager.AgentState;
|
|
|
+import edu.nju.software.aipaasagent.dto.StreamEvent;
|
|
|
import edu.nju.software.aipaasagent.mcp.manage.ToolRegister;
|
|
|
-import edu.nju.software.aipaasagent.memory.chat.RedisBasedChatMemory;
|
|
|
-import edu.nju.software.aipaasagent.service.RagService;
|
|
|
+import edu.nju.software.aipaasagent.rag.dto.RagResult;
|
|
|
+import edu.nju.software.aipaasagent.rag.service.RagService;
|
|
|
import lombok.Getter;
|
|
|
import lombok.Setter;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
@@ -15,185 +17,470 @@ import org.springframework.ai.chat.memory.ChatMemory;
|
|
|
import org.springframework.ai.chat.messages.AssistantMessage;
|
|
|
import org.springframework.ai.chat.messages.Message;
|
|
|
import org.springframework.ai.chat.messages.ToolResponseMessage;
|
|
|
+import org.springframework.ai.chat.messages.UserMessage;
|
|
|
import org.springframework.ai.chat.model.ChatModel;
|
|
|
import org.springframework.ai.chat.model.ChatResponse;
|
|
|
import org.springframework.ai.chat.prompt.ChatOptions;
|
|
|
-import org.springframework.ai.chat.prompt.DefaultChatOptions;
|
|
|
import org.springframework.ai.chat.prompt.Prompt;
|
|
|
import org.springframework.ai.model.tool.ToolCallingManager;
|
|
|
import org.springframework.ai.model.tool.ToolExecutionResult;
|
|
|
import org.springframework.ai.tool.ToolCallback;
|
|
|
-
|
|
|
+import reactor.core.publisher.Flux;
|
|
|
+import reactor.core.publisher.Sinks;
|
|
|
+import reactor.core.scheduler.Schedulers;
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
|
import java.util.stream.Collectors;
|
|
|
-import reactor.core.publisher.Flux;
|
|
|
|
|
|
/**
|
|
|
* 工具调用 Agent 类
|
|
|
- * 继承 ReActAgent,核心实现思考和行动逻辑
|
|
|
- * 重点:禁用 Spring AI 内置工具托管,手动控制流程
|
|
|
+ * - 循环中保存历史上下文的问题
|
|
|
+ * - 添加 SSE 事件流支持
|
|
|
+ * - 优化提示词,确保 AI 能正确使用 terminate 工具
|
|
|
*/
|
|
|
@Setter
|
|
|
@Getter
|
|
|
@Slf4j
|
|
|
public class ToolCallAgent extends ReActAgent {
|
|
|
|
|
|
- /**
|
|
|
- * 可用的工具列表
|
|
|
- */
|
|
|
protected ToolCallback[] availableTools;
|
|
|
-
|
|
|
- /**
|
|
|
- * ChatClient 实例(大模型客户端)
|
|
|
- */
|
|
|
protected ChatClient chatClient;
|
|
|
+ protected ToolCallingManager toolCallingManager;
|
|
|
+ protected ChatOptions chatOptions;
|
|
|
+ protected ChatResponse toolCallChatResponse;
|
|
|
|
|
|
/**
|
|
|
- * 工具调用管理器
|
|
|
+ * 当前对话的消息列表(内存中维护完整上下文)
|
|
|
+ * 这个列表用于每次 think() 时传递完整的对话历史
|
|
|
*/
|
|
|
- protected ToolCallingManager toolCallingManager;
|
|
|
+ protected List<Message> currentConversationMessages = new ArrayList<>();
|
|
|
|
|
|
/**
|
|
|
- * 聊天选项配置
|
|
|
+ * SSE 事件发射器(用于流式输出)
|
|
|
*/
|
|
|
- protected ChatOptions chatOptions;
|
|
|
+ protected Sinks.Many<StreamEvent> eventSink;
|
|
|
+
|
|
|
+ private static final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+
|
|
|
+ public ToolCallAgent(ChatModel chatModel, ChatClient chatClient,
|
|
|
+ AgentConfiguration agentConfiguration, RagService ragService,
|
|
|
+ ChatMemory chatMemory, ToolRegister toolRegister) {
|
|
|
+ super(chatModel, agentConfiguration, chatMemory, ragService, toolRegister);
|
|
|
+ this.chatClient = chatClient;
|
|
|
+ this.toolCallingManager = ToolCallingManager.builder().build();
|
|
|
+ this.chatOptions = getDefaultOptions();
|
|
|
+
|
|
|
+ log.info("[ToolCallAgent] 初始化完成,可用工具数量:{}",
|
|
|
+ availableTools != null ? availableTools.length : 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected String executeReActStrategy(String userPrompt, String chatId, Message ragKeyInfoMessage) {
|
|
|
+ log.info("[ToolCallAgent] 开始 ReAct 策略 - chatId: {}", chatId);
|
|
|
+
|
|
|
+ if (this.state != AgentState.IDLE) {
|
|
|
+ throw new RuntimeException("Cannot run agent from state: " + this.state);
|
|
|
+ }
|
|
|
+ if (userPrompt == null || userPrompt.trim().isEmpty()) {
|
|
|
+ throw new RuntimeException("Cannot run agent with empty user prompt");
|
|
|
+ }
|
|
|
+
|
|
|
+ state = AgentState.RUNNING;
|
|
|
+ List<String> results = new ArrayList<>();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 1. 从 Redis 加载历史对话!(非常重要)
|
|
|
+ log.info("[ToolCallAgent] 从 Redis 加载历史对话 - chatId: {}", chatId);
|
|
|
+ List<Message> historyMessages = chatMemory.get(chatId);
|
|
|
+
|
|
|
+ // 2. 初始化对话上下文:历史 + 当前用户消息 + RAG关键信息
|
|
|
+ currentConversationMessages.clear();
|
|
|
+ if (historyMessages != null && !historyMessages.isEmpty()) {
|
|
|
+ currentConversationMessages.addAll(historyMessages);
|
|
|
+ log.info("[ToolCallAgent] 已加载历史消息 {} 条", historyMessages.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 【重要】记录初始大小,后面只收集本轮新增的消息
|
|
|
+ int initialSize = currentConversationMessages.size();
|
|
|
+
|
|
|
+ // 3. 添加 RAG 关键信息(如果有)
|
|
|
+ if (ragKeyInfoMessage != null) {
|
|
|
+ currentConversationMessages.add(ragKeyInfoMessage);
|
|
|
+ log.info("[ToolCallAgent] 添加 RAG 关键信息到上下文");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 添加当前用户消息(已经过 BaseAgent 的 RAG 增强)
|
|
|
+ currentConversationMessages.add(new UserMessage(userPrompt));
|
|
|
+ log.info("[ToolCallAgent] 添加当前用户消息到上下文");
|
|
|
+
|
|
|
+ // 5. 【重要】存储这一轮的开始消息到 Redis
|
|
|
+ List<Message> roundStartMessages = new ArrayList<>();
|
|
|
+ if (ragKeyInfoMessage != null) {
|
|
|
+ roundStartMessages.add(ragKeyInfoMessage);
|
|
|
+ }
|
|
|
+ roundStartMessages.add(new UserMessage(userPrompt));
|
|
|
+
|
|
|
+ log.info("[ToolCallAgent] 存储本轮开始消息到 Redis - chatId: {}, messagesCount: {}",
|
|
|
+ chatId, roundStartMessages.size());
|
|
|
+ chatMemory.add(chatId, roundStartMessages);
|
|
|
+
|
|
|
+ // 3. 执行 ReAct 循环
|
|
|
+ for (int i = 0; i < maxSteps && state != AgentState.FINISHED; i++) {
|
|
|
+ currentStep = i + 1;
|
|
|
+ log.info("[ToolCallAgent] 执行第 {} 步", currentStep);
|
|
|
+
|
|
|
+ String stepResult = step(userPrompt, chatId);
|
|
|
+ results.add("Step " + currentStep + ": " + stepResult);
|
|
|
+
|
|
|
+ // 检查是否检测到 terminate 工具
|
|
|
+ if (state == AgentState.FINISHED) {
|
|
|
+ log.info("[ToolCallAgent] 检测到 terminate,提前结束");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (currentStep >= maxSteps) {
|
|
|
+ state = AgentState.FINISHED;
|
|
|
+ results.add("Terminated: Reached max steps (" + maxSteps + ")");
|
|
|
+ }
|
|
|
+
|
|
|
+ String finalResult = String.join("\n", results);
|
|
|
+
|
|
|
+ // 4. 收集本轮对话的所有新增消息(跳过初始历史)
|
|
|
+ // 注意:roundStartMessages 已经存了 RAG 关键信息和用户消息
|
|
|
+ List<Message> roundMessages = new ArrayList<>();
|
|
|
+ for (int i = initialSize + roundStartMessages.size(); i < currentConversationMessages.size(); i++) {
|
|
|
+ roundMessages.add(currentConversationMessages.get(i));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 【重要】批量存储这一轮的剩余消息到 Redis
|
|
|
+ log.info("[ToolCallAgent] 存储本轮剩余消息到 Redis - chatId: {}, messagesCount: {}",
|
|
|
+ chatId, roundMessages.size());
|
|
|
+ if (!roundMessages.isEmpty()) {
|
|
|
+ chatMemory.add(chatId, roundMessages);
|
|
|
+ }
|
|
|
+
|
|
|
+ return finalResult;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ state = AgentState.ERROR;
|
|
|
+ log.error("[ToolCallAgent] 执行错误: {}", e.getMessage(), e);
|
|
|
+ return "执行错误: " + e.getMessage();
|
|
|
+ } finally {
|
|
|
+ this.cleanup();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected Flux<String> executeReActStreamStrategy(String originalUserPrompt, String chatId, Message unusedRagKeyInfoMessage) {
|
|
|
+ log.info("[ToolCallAgent] 开始流式 ReAct 策略 - chatId: {}", chatId);
|
|
|
+
|
|
|
+ if (this.state != AgentState.IDLE) {
|
|
|
+ return Flux.error(new RuntimeException("Cannot run agent from state: " + this.state));
|
|
|
+ }
|
|
|
+ if (originalUserPrompt == null || originalUserPrompt.trim().isEmpty()) {
|
|
|
+ return Flux.error(new RuntimeException("Cannot run agent with empty user prompt"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建 SSE 事件发射器
|
|
|
+ eventSink = Sinks.many().unicast().onBackpressureBuffer();
|
|
|
+ state = AgentState.RUNNING;
|
|
|
+
|
|
|
+ // 异步执行
|
|
|
+ Sinks.Many<StreamEvent> sink = eventSink;
|
|
|
+ Schedulers.boundedElastic().schedule(() -> {
|
|
|
+ try {
|
|
|
+ // 1. 【重要】先调用 RAG(在创建了事件发射器之后!)
|
|
|
+ // 这样可以发射 RAG 相关的事件给前端
|
|
|
+ log.info("[ToolCallAgent] 流式 - 执行 RAG 检索 - chatId: {}", chatId);
|
|
|
+ RagResult ragResult;
|
|
|
+ if (isRagEnabled()) {
|
|
|
+ List<Message> history = getHistoryFromMemory(chatId);
|
|
|
+ ragResult = ragService.executeRagAndBuildMessage(
|
|
|
+ originalUserPrompt, chatId, agentConfiguration.getRag(), history, this::emitEvent
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ ragResult = new RagResult(originalUserPrompt, null);
|
|
|
+ }
|
|
|
+ String finalUserMessage = ragResult.getEnhancedUserMessage();
|
|
|
+ Message ragKeyInfoMessage = ragResult.getRagKeyInfoMessage();
|
|
|
+
|
|
|
+ // 2. 从 Redis 加载历史对话!(非常重要)
|
|
|
+ log.info("[ToolCallAgent] 流式 - 从 Redis 加载历史对话 - chatId: {}", chatId);
|
|
|
+ List<Message> historyMessages = chatMemory.get(chatId);
|
|
|
+
|
|
|
+ // 3. 初始化对话上下文:历史 + RAG关键信息 + 当前用户消息
|
|
|
+ currentConversationMessages.clear();
|
|
|
+ if (historyMessages != null && !historyMessages.isEmpty()) {
|
|
|
+ currentConversationMessages.addAll(historyMessages);
|
|
|
+ log.info("[ToolCallAgent] 流式 - 已加载历史消息 {} 条", historyMessages.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 【重要】记录初始大小,后面只收集本轮新增的消息
|
|
|
+ int initialSize = currentConversationMessages.size();
|
|
|
+
|
|
|
+ // 4. 添加 RAG 关键信息(如果有)
|
|
|
+ if (ragKeyInfoMessage != null) {
|
|
|
+ currentConversationMessages.add(ragKeyInfoMessage);
|
|
|
+ log.info("[ToolCallAgent] 流式 - 添加 RAG 关键信息到上下文");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 添加当前用户消息(已经过 RAG 增强)
|
|
|
+ currentConversationMessages.add(new UserMessage(finalUserMessage));
|
|
|
+ log.info("[ToolCallAgent] 流式 - 添加当前用户消息到上下文");
|
|
|
+
|
|
|
+ // 6. 【重要】存储这一轮的开始消息到 Redis
|
|
|
+ List<Message> roundStartMessages = new ArrayList<>();
|
|
|
+ if (ragKeyInfoMessage != null) {
|
|
|
+ roundStartMessages.add(ragKeyInfoMessage);
|
|
|
+ }
|
|
|
+ roundStartMessages.add(new UserMessage(finalUserMessage));
|
|
|
+
|
|
|
+ log.info("[ToolCallAgent] 流式 - 存储本轮开始消息到 Redis - chatId: {}, messagesCount: {}",
|
|
|
+ chatId, roundStartMessages.size());
|
|
|
+ chatMemory.add(chatId, roundStartMessages);
|
|
|
+
|
|
|
+ // 发射 thinking_start 事件
|
|
|
+ emitEvent(StreamEvent.thinkingStart());
|
|
|
+
|
|
|
+ // 2. 执行 ReAct 循环
|
|
|
+ for (int i = 0; i < maxSteps && state != AgentState.FINISHED; i++) {
|
|
|
+ currentStep = i + 1;
|
|
|
+ log.info("[ToolCallAgent] 流式执行第 {} 步", currentStep);
|
|
|
+
|
|
|
+ // 执行单步
|
|
|
+ boolean shouldContinue = executeStreamStep(finalUserMessage, chatId, sink);
|
|
|
+ if (!shouldContinue) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (currentStep >= maxSteps) {
|
|
|
+ state = AgentState.FINISHED;
|
|
|
+ emitEvent(StreamEvent.contentChunk("Terminated: Reached max steps (" + maxSteps + ")"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 存储本轮对话的所有新增消息(跳过初始历史)
|
|
|
+ // 注意:roundStartMessages 已经存了 RAG 关键信息和用户消息
|
|
|
+ List<Message> roundMessages = new ArrayList<>();
|
|
|
+ for (int i = initialSize + roundStartMessages.size(); i < currentConversationMessages.size(); i++) {
|
|
|
+ roundMessages.add(currentConversationMessages.get(i));
|
|
|
+ }
|
|
|
+ log.info("[ToolCallAgent] 流式 - 存储本轮剩余消息到 Redis - chatId: {}, messagesCount: {}",
|
|
|
+ chatId, roundMessages.size());
|
|
|
+ if (!roundMessages.isEmpty()) {
|
|
|
+ chatMemory.add(chatId, roundMessages);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发射 done 事件
|
|
|
+ emitEvent(StreamEvent.done());
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ state = AgentState.ERROR;
|
|
|
+ log.error("[ToolCallAgent] 流式执行错误: {}", e.getMessage(), e);
|
|
|
+ emitEvent(StreamEvent.error(e.getMessage()));
|
|
|
+ } finally {
|
|
|
+ this.cleanup();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 返回事件流(转换为 JSON 字符串)
|
|
|
+ return sink.asFlux()
|
|
|
+ .map(this::eventToSse)
|
|
|
+ .doOnCancel(() -> {
|
|
|
+ log.warn("[ToolCallAgent] 客户端取消订阅 - chatId: {}", chatId);
|
|
|
+ state = AgentState.IDLE;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected Flux<StreamEvent> executeReActStreamStrategySSE(String originalUserPrompt, String chatId, Message unusedRagKeyInfoMessage) {
|
|
|
+ log.info("[ToolCallAgent] 开始 SSE 流式 ReAct 策略 - chatId: {}", chatId);
|
|
|
+
|
|
|
+ if (this.state != AgentState.IDLE) {
|
|
|
+ return Flux.error(new RuntimeException("Cannot run agent from state: " + this.state));
|
|
|
+ }
|
|
|
+ if (originalUserPrompt == null || originalUserPrompt.trim().isEmpty()) {
|
|
|
+ return Flux.error(new RuntimeException("Cannot run agent with empty user prompt"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建 SSE 事件发射器
|
|
|
+ eventSink = Sinks.many().unicast().onBackpressureBuffer();
|
|
|
+ state = AgentState.RUNNING;
|
|
|
+
|
|
|
+ // 异步执行
|
|
|
+ Sinks.Many<StreamEvent> sink = eventSink;
|
|
|
+ Schedulers.boundedElastic().schedule(() -> {
|
|
|
+ try {
|
|
|
+ // 1. 【重要】先调用 RAG(在创建了事件发射器之后!)
|
|
|
+ // 这样可以发射 RAG 相关的事件给前端
|
|
|
+ log.info("[ToolCallAgent] SSE流式 - 执行 RAG 检索 - chatId: {}", chatId);
|
|
|
+ RagResult ragResult;
|
|
|
+ if (isRagEnabled()) {
|
|
|
+ List<Message> history = getHistoryFromMemory(chatId);
|
|
|
+ ragResult = ragService.executeRagAndBuildMessage(
|
|
|
+ originalUserPrompt, chatId, agentConfiguration.getRag(), history, this::emitEvent
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ ragResult = new RagResult(originalUserPrompt, null);
|
|
|
+ }
|
|
|
+ String finalUserMessage = ragResult.getEnhancedUserMessage();
|
|
|
+ Message ragKeyInfoMessage = ragResult.getRagKeyInfoMessage();
|
|
|
+
|
|
|
+ // 2. 从 Redis 加载历史对话!(非常重要)
|
|
|
+ log.info("[ToolCallAgent] SSE流式 - 从 Redis 加载历史对话 - chatId: {}", chatId);
|
|
|
+ List<Message> historyMessages = chatMemory.get(chatId);
|
|
|
+
|
|
|
+ // 3. 初始化对话上下文:历史 + RAG关键信息 + 当前用户消息
|
|
|
+ currentConversationMessages.clear();
|
|
|
+ if (historyMessages != null && !historyMessages.isEmpty()) {
|
|
|
+ currentConversationMessages.addAll(historyMessages);
|
|
|
+ log.info("[ToolCallAgent] SSE流式 - 已加载历史消息 {} 条", historyMessages.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 【重要】记录初始大小,后面只收集本轮新增的消息
|
|
|
+ int initialSize = currentConversationMessages.size();
|
|
|
+
|
|
|
+ // 4. 添加 RAG 关键信息(如果有)
|
|
|
+ if (ragKeyInfoMessage != null) {
|
|
|
+ currentConversationMessages.add(ragKeyInfoMessage);
|
|
|
+ log.info("[ToolCallAgent] SSE流式 - 添加 RAG 关键信息到上下文");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 添加当前用户消息(已经过 RAG 增强)
|
|
|
+ currentConversationMessages.add(new UserMessage(finalUserMessage));
|
|
|
+ log.info("[ToolCallAgent] SSE流式 - 添加当前用户消息到上下文");
|
|
|
+
|
|
|
+ // 6. 【重要】存储这一轮的开始消息到 Redis
|
|
|
+ List<Message> roundStartMessages = new ArrayList<>();
|
|
|
+ if (ragKeyInfoMessage != null) {
|
|
|
+ roundStartMessages.add(ragKeyInfoMessage);
|
|
|
+ }
|
|
|
+ roundStartMessages.add(new UserMessage(finalUserMessage));
|
|
|
+
|
|
|
+ log.info("[ToolCallAgent] SSE流式 - 存储本轮开始消息到 Redis - chatId: {}, messagesCount: {}",
|
|
|
+ chatId, roundStartMessages.size());
|
|
|
+ chatMemory.add(chatId, roundStartMessages);
|
|
|
+
|
|
|
+ // 发射 thinking_start 事件
|
|
|
+ emitEvent(StreamEvent.thinkingStart());
|
|
|
+
|
|
|
+ // 2. 执行 ReAct 循环
|
|
|
+ for (int i = 0; i < maxSteps && state != AgentState.FINISHED; i++) {
|
|
|
+ currentStep = i + 1;
|
|
|
+ log.info("[ToolCallAgent] SSE流式执行第 {} 步", currentStep);
|
|
|
+
|
|
|
+ // 执行单步
|
|
|
+ boolean shouldContinue = executeStreamStep(finalUserMessage, chatId, sink);
|
|
|
+ if (!shouldContinue) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (currentStep >= maxSteps) {
|
|
|
+ state = AgentState.FINISHED;
|
|
|
+ emitEvent(StreamEvent.contentChunk("Terminated: Reached max steps (" + maxSteps + ")"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 存储本轮对话的所有新增消息(跳过初始历史)
|
|
|
+ // 注意:roundStartMessages 已经存了 RAG 关键信息和用户消息
|
|
|
+ List<Message> roundMessages = new ArrayList<>();
|
|
|
+ for (int i = initialSize + roundStartMessages.size(); i < currentConversationMessages.size(); i++) {
|
|
|
+ roundMessages.add(currentConversationMessages.get(i));
|
|
|
+ }
|
|
|
+ log.info("[ToolCallAgent] SSE流式 - 存储本轮剩余消息到 Redis - chatId: {}, messagesCount: {}",
|
|
|
+ chatId, roundMessages.size());
|
|
|
+ if (!roundMessages.isEmpty()) {
|
|
|
+ chatMemory.add(chatId, roundMessages);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发射 done 事件
|
|
|
+ emitEvent(StreamEvent.done());
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ state = AgentState.ERROR;
|
|
|
+ log.error("[ToolCallAgent] SSE流式执行错误: {}", e.getMessage(), e);
|
|
|
+ emitEvent(StreamEvent.error(e.getMessage()));
|
|
|
+ } finally {
|
|
|
+ this.cleanup();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 直接返回 StreamEvent 流
|
|
|
+ return sink.asFlux()
|
|
|
+ .doOnCancel(() -> {
|
|
|
+ log.warn("[ToolCallAgent] SSE流式 - 客户端取消订阅 - chatId: {}", chatId);
|
|
|
+ state = AgentState.IDLE;
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
- * 工具调用响应(用于 act() 阶段)
|
|
|
+ * 执行流式单步
|
|
|
*/
|
|
|
- protected ChatResponse toolCallChatResponse;
|
|
|
+ private boolean executeStreamStep(String userPrompt, String chatId, Sinks.Many<StreamEvent> sink) {
|
|
|
+ try {
|
|
|
+ // Think 阶段
|
|
|
+ boolean shouldAct = think(userPrompt, chatId);
|
|
|
+ if (!shouldAct) {
|
|
|
+ log.info("[ToolCallAgent] 无需行动,直接返回结果");
|
|
|
+ emitEvent(StreamEvent.thinkingEnd());
|
|
|
+ // 获取最后一条消息作为结果
|
|
|
+ Message lastMsg = CollUtil.getLast(currentConversationMessages);
|
|
|
+ if (lastMsg instanceof AssistantMessage) {
|
|
|
+ emitEvent(StreamEvent.contentChunk(((AssistantMessage) lastMsg).getText()));
|
|
|
+ }
|
|
|
+ state = AgentState.FINISHED;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- // 注意:agentConfiguration 继承自 BaseAgent,不要在这里重新定义
|
|
|
-
|
|
|
-// /**
|
|
|
-// * 执行对话
|
|
|
-// * 重写父类方法,提供 ToolCallAgent 特有的对话实现
|
|
|
-// * 支持 RAG 检索
|
|
|
-// *
|
|
|
-// * @param message 用户消息
|
|
|
-// * @param chatId 会话 ID(用于记忆管理)
|
|
|
-// * @return AI 回复
|
|
|
-// */
|
|
|
-// @Override
|
|
|
-// public String doChat(String message, String chatId) {
|
|
|
-// log.info("ToolCallAgent 收到对话请求 - chatId: {}, message: {}", chatId, message);
|
|
|
-//
|
|
|
-// if (chatClient == null) {
|
|
|
-// throw new IllegalStateException("ChatClient 未初始化");
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 1. 如果启用 RAG,先执行检索
|
|
|
-// String finalUserMessage = message;
|
|
|
-// if (isRagEnabled()) {
|
|
|
-// List<Message> history = getHistoryFromMemory(chatId);
|
|
|
-// finalUserMessage = ragService.executeRagAndBuildMessage(message, chatId, agentConfiguration.getRag(), history);
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 2. 使用 ReAct 模式执行对话
|
|
|
-// return run(finalUserMessage);
|
|
|
-// }
|
|
|
-
|
|
|
-// /**
|
|
|
-// * 执行流式对话
|
|
|
-// * 重写父类方法,提供 ToolCallAgent 特有的流式对话实现
|
|
|
-// * 支持 RAG 检索
|
|
|
-// *
|
|
|
-// * @param message 用户消息
|
|
|
-// * @param chatId 会话 ID(用于记忆管理)
|
|
|
-// * @return 流式 AI 回复
|
|
|
-// */
|
|
|
-// @Override
|
|
|
-// public Flux<String> doStreamChat(String message, String chatId) {
|
|
|
-// log.info("ToolCallAgent 收到流式对话请求 - chatId: {}, message: {}", chatId, message);
|
|
|
-//
|
|
|
-// if (chatClient == null) {
|
|
|
-// throw new IllegalStateException("ChatClient 未初始化");
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 1. 如果启用 RAG,先执行检索
|
|
|
-// String finalUserMessage = message;
|
|
|
-// if (isRagEnabled()) {
|
|
|
-// List<Message> history = getHistoryFromMemory(chatId);
|
|
|
-// finalUserMessage = ragService.executeRagAndBuildMessage(message, chatId, agentConfiguration.getRag(), history);
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 2. 直接使用 ChatClient 进行流式对话
|
|
|
-// return chatClient
|
|
|
-// .prompt()
|
|
|
-// .system(getSystemPrompt())
|
|
|
-// .user(finalUserMessage)
|
|
|
-// .options(chatOptions)
|
|
|
-// .tools(availableTools)
|
|
|
-// .stream()
|
|
|
-// .content()
|
|
|
-// .doOnNext(chunk -> log.debug("ToolCallAgent 流式输出 - chatId: {}, chunk: {}", chatId, chunk))
|
|
|
-// .doOnComplete(() -> log.info("ToolCallAgent 流式对话完成 - chatId: {}", chatId));
|
|
|
-// }
|
|
|
+ // Act 阶段
|
|
|
+ String actResult = act(userPrompt, chatId);
|
|
|
+ emitEvent(StreamEvent.contentChunk("Step " + currentStep + ": " + actResult));
|
|
|
|
|
|
+ return state != AgentState.FINISHED;
|
|
|
|
|
|
- public String getSystemPrompt() {
|
|
|
- return agentConfiguration.getSystemPrompt();
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[ToolCallAgent] 流式步骤执行失败: {}", e.getMessage(), e);
|
|
|
+ emitEvent(StreamEvent.error("步骤执行失败: " + e.getMessage()));
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
- * 构造函数
|
|
|
- *
|
|
|
- * @param chatClient ChatClient 实例
|
|
|
- * @param agentConfiguration Agent 配置
|
|
|
- * @param ragService RAG 服务
|
|
|
- * @param chatMemory Redis 对话记忆
|
|
|
+ * 发射 SSE 事件
|
|
|
*/
|
|
|
- public ToolCallAgent(ChatModel chatModel, ChatClient chatClient, AgentConfiguration agentConfiguration, RagService ragService, ChatMemory chatMemory, ToolRegister toolRegister) {
|
|
|
- super(chatModel, agentConfiguration, chatMemory, ragService, toolRegister);
|
|
|
- this.chatClient = chatClient;
|
|
|
- this.toolCallingManager = ToolCallingManager.builder().build();
|
|
|
- this.chatOptions=getDefaultOptions();
|
|
|
- // 根据模型配置创建聊天选项
|
|
|
- log.info("[{}] ToolCallAgent 初始化完成,可用工具数量:{}, RAG: {}", name,
|
|
|
- availableTools != null ? availableTools.length : 0,
|
|
|
- agentConfiguration != null && agentConfiguration.getRag() != null && Boolean.TRUE.equals(agentConfiguration.getRag().getEnabled()) ? "enabled" : "disabled");
|
|
|
+ protected void emitEvent(StreamEvent event) {
|
|
|
+ if (eventSink != null) {
|
|
|
+ Sinks.EmitResult result = eventSink.tryEmitNext(event);
|
|
|
+ if (result.isFailure()) {
|
|
|
+ log.warn("[ToolCallAgent] 事件发射失败: {}", result);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 思考阶段实现
|
|
|
- * 发送消息给大模型,判断是否需要调用工具
|
|
|
- * 模型通过工具的 description 来判断应该使用哪个工具
|
|
|
- *
|
|
|
- * @return true - 需要调用工具,false - 无需调用工具
|
|
|
+ * 将事件转换为 SSE 格式字符串
|
|
|
*/
|
|
|
+ private String eventToSse(StreamEvent event) {
|
|
|
+ try {
|
|
|
+ return "data: " + objectMapper.writeValueAsString(event) + "\n\n";
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[ToolCallAgent] 事件序列化失败", e);
|
|
|
+ return "data: {\"type\":\"error\",\"data\":\"序列化失败\"}\n\n";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public boolean think(String userPrompt, String chatID) {
|
|
|
- ChatOptions manualOptions= ChatOptions.builder().build().copy();
|
|
|
- Prompt prompt = new Prompt(userPrompt,manualOptions);
|
|
|
+ log.info("[ToolCallAgent] 开始思考阶段 - chatID: {}, 历史消息数: {}", chatID, currentConversationMessages.size());
|
|
|
|
|
|
try {
|
|
|
- //log.info("[{}] 思考阶段 - 发送消息到模型,当前消息数:{}", name, getMessageList().size());
|
|
|
-
|
|
|
- // 打印上一轮消息的关键信息(用于调试)
|
|
|
-// if (getMessageList().size() > 1) {
|
|
|
-// Message lastMessage = getMessageList().get(getMessageList().size() - 1);
|
|
|
-// log.info("[{}] 上一轮消息类型:{}", name, lastMessage.getMessageType());
|
|
|
-// if (lastMessage instanceof ToolResponseMessage) {
|
|
|
-// ToolResponseMessage toolResp = (ToolResponseMessage) lastMessage;
|
|
|
-// log.info("[{}] 上一轮工具响应数:{}", name, toolResp.getResponses().size());
|
|
|
-// } else if (lastMessage instanceof AssistantMessage) {
|
|
|
-// AssistantMessage assistantResp = (AssistantMessage) lastMessage;
|
|
|
-// String text = assistantResp.getText();
|
|
|
-// log.info("[{}] 上一轮助手消息:{}", name, text != null && text.length() > 100 ? text.substring(0, 100) + "..." : text);
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
- // 构建工具定义的 JSON 描述
|
|
|
- //String toolsJson = buildToolsJson();
|
|
|
-
|
|
|
- // 构建增强的系统提示词,
|
|
|
String enhancedSystemPrompt = buildEnhancedSystemPrompt();
|
|
|
|
|
|
- //TODO:注入工具
|
|
|
- // 调用大模型,传入工具定义(模型通过工具描述判断是否需要调用)
|
|
|
- ChatResponse chatResponse = chatClient.prompt(prompt)
|
|
|
+ // 使用完整的对话历史
|
|
|
+ ChatResponse chatResponse = chatClient.prompt()
|
|
|
.system(enhancedSystemPrompt)
|
|
|
+ .messages(currentConversationMessages)
|
|
|
.toolCallbacks(toolRegister.getTools().toArray(new ToolCallback[0]))
|
|
|
.call()
|
|
|
.chatResponse();
|
|
|
@@ -202,202 +489,143 @@ public class ToolCallAgent extends ReActAgent {
|
|
|
AssistantMessage assistantMessage = chatResponse.getResult().getOutput();
|
|
|
List<AssistantMessage.ToolCall> toolCallList = assistantMessage.getToolCalls();
|
|
|
|
|
|
- log.info("[{}] 思考阶段 - 模型回复:{}, 工具调用数:{}",
|
|
|
- name,
|
|
|
- assistantMessage.getText() != null ? assistantMessage.getText().substring(0, Math.min(50, assistantMessage.getText().length())) + "..." : "null",
|
|
|
+ log.info("[ToolCallAgent] 思考阶段 - 模型回复:{}, 工具调用数:{}",
|
|
|
+ assistantMessage.getText() != null ?
|
|
|
+ assistantMessage.getText().substring(0, Math.min(50, assistantMessage.getText().length())) + "..." : "null",
|
|
|
toolCallList != null ? toolCallList.size() : 0);
|
|
|
|
|
|
+ // 将 AI 的回复添加到对话历史
|
|
|
+ currentConversationMessages.add(assistantMessage);
|
|
|
|
|
|
- // 如果没有工具调用
|
|
|
- if (toolCallList == null || toolCallList.isEmpty()) {
|
|
|
- // 检查是否有 JSON 格式的工具调用
|
|
|
- String content = assistantMessage.getText();
|
|
|
- if (content != null && content.contains("\"tool_calls\"")) {
|
|
|
- log.info("[{}] 检测到 JSON 格式工具调用,进入行动阶段", name);
|
|
|
- // 将 Assistant 消息添加到列表,然后进入行动阶段
|
|
|
- //getMessageList().add(assistantMessage);
|
|
|
- return true; // 有 JSON 格式工具调用,需要执行
|
|
|
- }
|
|
|
-
|
|
|
- // 检查是否包含工具调用标记
|
|
|
- boolean hasToolCall = content.contains("```tool_code") ||
|
|
|
- content.contains("```json") ||
|
|
|
- (content.contains("\"name\"") && content.contains("\"input\""));
|
|
|
-
|
|
|
- if (hasToolCall) {
|
|
|
- log.info("[{}] 检测到工具调用,进入行动阶段", name);
|
|
|
- // getMessageList().add(assistantMessage);
|
|
|
- return true;
|
|
|
- }
|
|
|
+ // 检查是否有工具调用
|
|
|
+ if (toolCallList != null && !toolCallList.isEmpty()) {
|
|
|
+ log.info("[ToolCallAgent] 检测到 {} 个工具调用", toolCallList.size());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- // 其他情况:直接返回文本
|
|
|
- log.info("[{}] 模型返回文本,无需调用工具", name);
|
|
|
- //getMessageList().add(assistantMessage);
|
|
|
+ // 检查是否有 terminate 意图
|
|
|
+ String content = assistantMessage.getText();
|
|
|
+ if (content != null && isTerminateIntent(content)) {
|
|
|
+ log.info("[ToolCallAgent] 检测到终止意图");
|
|
|
+ state = AgentState.FINISHED;
|
|
|
return false;
|
|
|
-
|
|
|
- // force 模式:强制工具调用,返回需要调用工具
|
|
|
- // auto 模式:将模型回复添加到消息列表,返回无需调用工具
|
|
|
- } else {
|
|
|
- // 有工具调用,将包含工具调用的消息添加到列表
|
|
|
- log.info("[{}] 检测到 {} 个工具调用,进入行动阶段", name, toolCallList.size());
|
|
|
- //getMessageList().add(assistantMessage);
|
|
|
- return true;
|
|
|
}
|
|
|
+
|
|
|
+ // 没有工具调用,直接返回
|
|
|
+ log.info("[ToolCallAgent] 无需调用工具");
|
|
|
+ return false;
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
|
- log.error("[{}] 思考异常:{}", name, e.getMessage(), e);
|
|
|
- //getMessageList().add(new AssistantMessage("处理时遇到错误:" + e.getMessage()));
|
|
|
+ log.error("[ToolCallAgent] 思考异常:{}", e.getMessage(), e);
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- /**
|
|
|
- * 构建增强的系统提示词
|
|
|
- */
|
|
|
- private String buildEnhancedSystemPrompt() {
|
|
|
- return getSystemPrompt() + "\n\n" +
|
|
|
- "## 使用说明\n" +
|
|
|
- "1. 如果你需要调用工具,请直接回复工具调用请求\n" +
|
|
|
- "2. 工具调用完成后,你会看到工具执行结果\n" +
|
|
|
- "3. 当你已经完成任务或不需要再调用工具时,请使用 terminate 终止工具终止\n" +
|
|
|
- "4. 如果你不需要调用工具,直接回复文本即可\n" +
|
|
|
- "5. 重要:一旦你得到工具执行结果并且可以回答用户问题,立即返回最终答案,不要再调用工具";
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 行动阶段实现
|
|
|
- * 执行工具调用并处理结果
|
|
|
- *
|
|
|
- * @return 行动结果
|
|
|
- */
|
|
|
@Override
|
|
|
public String act(String userPrompt, String chartID) {
|
|
|
if (toolCallChatResponse == null) {
|
|
|
return "没有工具调用";
|
|
|
}
|
|
|
|
|
|
- // 检查是否有 Spring AI 原生工具调用
|
|
|
AssistantMessage assistantMessage = toolCallChatResponse.getResult().getOutput();
|
|
|
List<AssistantMessage.ToolCall> toolCallList = assistantMessage.getToolCalls();
|
|
|
|
|
|
- // 如果有原生工具调用,使用 ToolCallingManager 执行
|
|
|
- if (toolCallList != null && !toolCallList.isEmpty()) {
|
|
|
- try {
|
|
|
- log.info("[{}] 行动阶段 - 执行原生工具调用,数量:{}", name, toolCallList.size());
|
|
|
-
|
|
|
- // 创建 Prompt
|
|
|
- Prompt prompt = new Prompt(userPrompt, chatOptions);
|
|
|
-
|
|
|
- // 执行工具调用
|
|
|
- ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, toolCallChatResponse);
|
|
|
-
|
|
|
- // 更新消息列表(包含工具调用结果)
|
|
|
- // setMessageList(toolExecutionResult.conversationHistory());
|
|
|
-
|
|
|
- // 获取最后一个工具响应消息
|
|
|
- Message lastMessage = CollUtil.getLast(toolExecutionResult.conversationHistory());
|
|
|
+ if (toolCallList == null || toolCallList.isEmpty()) {
|
|
|
+ return "没有工具调用";
|
|
|
+ }
|
|
|
|
|
|
- if (lastMessage instanceof ToolResponseMessage) {
|
|
|
- ToolResponseMessage toolResponseMessage = (ToolResponseMessage) lastMessage;
|
|
|
+ try {
|
|
|
+ log.info("[ToolCallAgent] 行动阶段 - 执行 {} 个工具调用", toolCallList.size());
|
|
|
|
|
|
- // 构建结果字符串
|
|
|
- String results = toolResponseMessage.getResponses().stream()
|
|
|
- .map(response -> "工具 " + response.name() + " 完成了它的任务!结果:" + response.responseData())
|
|
|
- .collect(Collectors.joining("\n"));
|
|
|
+ // 发射工具调用开始事件
|
|
|
+ for (AssistantMessage.ToolCall toolCall : toolCallList) {
|
|
|
+ log.info("[ToolCallAgent] 调用工具: {}", toolCall.name());
|
|
|
+ if (eventSink != null) {
|
|
|
+ emitEvent(StreamEvent.toolCallStart(toolCall.name(), toolCall.arguments()));
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- log.info("[{}] 行动阶段 - 工具执行完成", name);
|
|
|
- log.info("[{}] 工具响应数:{}", name, toolResponseMessage.getResponses().size());
|
|
|
+ // 执行工具调用
|
|
|
+ Prompt prompt = new Prompt(currentConversationMessages, chatOptions);
|
|
|
+ ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, toolCallChatResponse);
|
|
|
|
|
|
- // 关键:添加一条助手消息,告诉模型任务已完成,请返回最终答案
|
|
|
- String assistantMessages = "工具调用已完成。\n" + results + "\n\n请根据以上结果,直接返回最终答案给用户。不需要再调用工具。";
|
|
|
- // getMessageList().add(new AssistantMessage(assistantMessages));
|
|
|
+ // 获取工具响应消息
|
|
|
+ Message lastMessage = CollUtil.getLast(toolExecutionResult.conversationHistory());
|
|
|
+ String results = "";
|
|
|
|
|
|
- log.info("[{}] 已添加助手消息到对话上下文,引导模型返回最终答案", name);
|
|
|
- // log.info("[{}] 下一轮对话消息数:{}", name, getMessageList().size());
|
|
|
+ if (lastMessage instanceof ToolResponseMessage) {
|
|
|
+ ToolResponseMessage toolResponseMessage = (ToolResponseMessage) lastMessage;
|
|
|
+
|
|
|
+ results = toolResponseMessage.getResponses().stream()
|
|
|
+ .map(response -> "工具 " + response.name() + " 完成!结果:" +
|
|
|
+ (response.responseData() != null ? response.responseData().toString().substring(0, Math.min(100, response.responseData().toString().length())) + "..." : "无结果"))
|
|
|
+ .collect(Collectors.joining("\n"));
|
|
|
|
|
|
- // 检测终止工具调用,更新状态
|
|
|
- boolean terminateToolCalled = toolResponseMessage.getResponses().stream()
|
|
|
- .anyMatch(response -> "terminate".equals(response.name()) || "doTerminate".equals(response.name()));
|
|
|
+ log.info("[ToolCallAgent] 工具执行完成");
|
|
|
|
|
|
- if (terminateToolCalled) {
|
|
|
- setState(AgentState.FINISHED);
|
|
|
- log.info("[{}] 检测到终止工具调用,任务结束", name);
|
|
|
+ // 发射工具调用结束事件
|
|
|
+ for (ToolResponseMessage.ToolResponse response : toolResponseMessage.getResponses()) {
|
|
|
+ if (eventSink != null) {
|
|
|
+ emitEvent(StreamEvent.toolCallEnd(response.name(), response.responseData()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否是 terminate 工具
|
|
|
+ if ("terminate".equals(response.name()) || "doTerminate".equals(response.name())) {
|
|
|
+ log.info("[ToolCallAgent] 检测到 terminate 工具调用");
|
|
|
+ state = AgentState.FINISHED;
|
|
|
}
|
|
|
-
|
|
|
- return results;
|
|
|
}
|
|
|
|
|
|
- return "工具执行完成";
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("[{}] 行动阶段异常:{}", name, e.getMessage(), e);
|
|
|
- return "工具执行失败:" + e.getMessage();
|
|
|
+ // 将工具响应添加到对话历史
|
|
|
+ currentConversationMessages.add(lastMessage);
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
-// // 没有原生工具调用,尝试解析 JSON 格式的工具调用请求
|
|
|
-// String content = assistantMessage.getText();
|
|
|
-// if (content != null && content.contains("\"tool_calls\"")) {
|
|
|
-// try {
|
|
|
-// log.info("[{}] 行动阶段 - 解析 JSON 格式工具调用", name);
|
|
|
-// return parseAndExecuteToolCalls(content);
|
|
|
-// } catch (Exception e) {
|
|
|
-// log.error("[{}] 解析 JSON 工具调用失败:{}", name, e.getMessage(), e);
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
- // 检查是否为 force 模式
|
|
|
- boolean forceMode = false;
|
|
|
- if (agentConfiguration != null && agentConfiguration.getMcp() != null
|
|
|
- && agentConfiguration.getMcp().getPolicy() != null
|
|
|
- && "force".equals(agentConfiguration.getMcp().getPolicy().getMode())) {
|
|
|
- forceMode = true;
|
|
|
+ return results;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[ToolCallAgent] 行动阶段异常:{}", e.getMessage(), e);
|
|
|
+ return "工具执行失败:" + e.getMessage();
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (forceMode) {
|
|
|
- // force 模式:使用第一个可用的 MCP 工具进行调用
|
|
|
- if (availableTools != null && availableTools.length > 0) {
|
|
|
- // 找到第一个 MCP 工具
|
|
|
- for (ToolCallback tool : availableTools) {
|
|
|
- //tool instanceof edu.nju.software.aipaasagent.client.mcp.McpTool
|
|
|
- if (true) {
|
|
|
- String toolName = tool.getToolDefinition().name();
|
|
|
- log.info("[{}] force 模式:使用 MCP 工具 {} 进行调用", name, toolName);
|
|
|
-
|
|
|
- try {
|
|
|
- // 执行工具调用
|
|
|
- String result = tool.call("{}");
|
|
|
- log.info("[{}] force 模式:工具 {} 执行结果:{}", name, toolName, result);
|
|
|
- return "force 模式:工具 " + toolName + " 执行结果:" + result;
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("[{}] force 模式:工具调用失败:{}", name, e.getMessage(), e);
|
|
|
- return "force 模式:工具调用失败:" + e.getMessage();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * 构建优化的系统提示词
|
|
|
+ * 明确告诉 AI 如何使用工具和 terminate
|
|
|
+ */
|
|
|
+ private String buildEnhancedSystemPrompt() {
|
|
|
+ return getSystemPrompt() + "\n\n" +
|
|
|
+ "## 工具使用说明\n" +
|
|
|
+ "1. 当你需要获取信息或执行操作时,请调用相应的工具\n" +
|
|
|
+ "2. 工具执行后,你会收到工具的执行结果\n" +
|
|
|
+ "3. 【重要】当你已经获得足够信息可以回答用户问题时,**必须调用 terminate 工具**来结束对话\n" +
|
|
|
+ "4. 【重要】不要忘记调用 terminate 工具!这是结束对话的唯一方式\n" +
|
|
|
+ "5. 如果你不需要调用工具,直接回答用户即可\n" +
|
|
|
+ "6. 只在以下情况调用 terminate:\n" +
|
|
|
+ " - 你已经可以完整回答用户的问题\n" +
|
|
|
+ " - 任务已经完成\n" +
|
|
|
+ " - 你确定不需要再调用任何工具";
|
|
|
+ }
|
|
|
|
|
|
- // 如果没有 MCP 工具,使用第一个可用工具
|
|
|
- ToolCallback firstTool = availableTools[0];
|
|
|
- String toolName = firstTool.getToolDefinition().name();
|
|
|
- log.info("[{}] force 模式:使用第一个可用工具 {} 进行调用", name, toolName);
|
|
|
-
|
|
|
- try {
|
|
|
- String result = firstTool.call("{}");
|
|
|
- log.info("[{}] force 模式:工具 {} 执行结果:{}", name, toolName, result);
|
|
|
- return "force 模式:工具 " + toolName + " 执行结果:" + result;
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("[{}] force 模式:工具调用失败:{}", name, e.getMessage(), e);
|
|
|
- return "force 模式:工具调用失败:" + e.getMessage();
|
|
|
- }
|
|
|
- } else {
|
|
|
- return "force 模式:无可用工具";
|
|
|
- }
|
|
|
- } else {
|
|
|
- return "没有工具调用";
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * 检查是否有终止意图(文本中包含结束信号)
|
|
|
+ */
|
|
|
+ private boolean isTerminateIntent(String content) {
|
|
|
+ if (content == null) return false;
|
|
|
+ String lowerContent = content.toLowerCase();
|
|
|
+ return lowerContent.contains("terminate") ||
|
|
|
+ lowerContent.contains("结束") ||
|
|
|
+ lowerContent.contains("完成") ||
|
|
|
+ lowerContent.contains("任务完成");
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getSystemPrompt() {
|
|
|
+ return agentConfiguration.getSystemPrompt();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected String executeNativeStrategy(String message, String chatId) {
|
|
|
+ log.info("[ToolCallAgent] 执行 Native 策略 - chatId: {}", chatId);
|
|
|
+
|
|
|
ChatResponse response = chatClient
|
|
|
.prompt()
|
|
|
.user(message)
|
|
|
@@ -407,12 +635,15 @@ public class ToolCallAgent extends ReActAgent {
|
|
|
.param(ChatMemory.CONVERSATION_ID, chatId))
|
|
|
.call()
|
|
|
.chatResponse();
|
|
|
+
|
|
|
assert response != null;
|
|
|
return response.toString();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected Flux<String> executeNativeStreamStrategy(String message, String chatId) {
|
|
|
+ log.info("[ToolCallAgent] 执行 Native 流式策略 - chatId: {}", chatId);
|
|
|
+
|
|
|
return chatClient
|
|
|
.prompt()
|
|
|
.user(message)
|
|
|
@@ -422,8 +653,7 @@ public class ToolCallAgent extends ReActAgent {
|
|
|
.param(ChatMemory.CONVERSATION_ID, chatId))
|
|
|
.stream()
|
|
|
.content()
|
|
|
- .doOnNext(chunk -> log.debug("流式输出 - chatId: {}, chunk: {}", chatId, chunk))
|
|
|
- .doOnComplete(() -> log.info("流式对话完成 - chatId: {}", chatId));
|
|
|
-
|
|
|
+ .doOnNext(chunk -> log.debug("[ToolCallAgent] 流式输出 - chatId: {}, chunk: {}", chatId, chunk))
|
|
|
+ .doOnComplete(() -> log.info("[ToolCallAgent] 流式对话完成 - chatId: {}", chatId));
|
|
|
}
|
|
|
-}
|
|
|
+}
|