diff --git a/pom.xml b/pom.xml
index 05cd2b8..46ae2c2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,15 +20,6 @@
-
-
-
-
-
-
-
- long-image-search-mcp-server
-
21
diff --git a/src/main/java/com/huangge1199/aiagent/agent/BaseAgent.java b/src/main/java/com/huangge1199/aiagent/agent/BaseAgent.java
new file mode 100644
index 0000000..1d637d4
--- /dev/null
+++ b/src/main/java/com/huangge1199/aiagent/agent/BaseAgent.java
@@ -0,0 +1,108 @@
+package com.huangge1199.aiagent.agent;
+
+import cn.hutool.core.util.StrUtil;
+import com.huangge1199.aiagent.agent.model.AgentState;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.UserMessage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * BaseAgent
+ *
+ * 抽象基础代理类,用于管理代理状态和执行流程。
+ *
+ * 提供状态转换、内存管理和基于步骤的执行循环的基础功能。
+ * 子类必须实现step方法。
+ *
+ * @author huangge1199
+ * @since 2025/6/4 17:01:45
+ */
+@Data
+@Slf4j
+public abstract class BaseAgent {
+
+ // 核心属性
+ private String name;
+
+ // 提示
+ private String systemPrompt;
+ private String nextStepPrompt;
+
+ // 状态
+ private AgentState state = AgentState.IDLE;
+
+ // 执行控制
+ private int maxSteps = 10;
+ private int currentStep = 0;
+
+ // LLM
+ private ChatClient chatClient;
+
+ // Memory(需要自主维护会话上下文)
+ private List messageList = new ArrayList<>();
+
+ /**
+ * 运行代理
+ *
+ * @param userPrompt 用户提示词
+ * @return 执行结果
+ */
+ public String run(String userPrompt) {
+ if (this.state != AgentState.IDLE) {
+ throw new RuntimeException("Cannot run agent from state: " + this.state);
+ }
+ if (StrUtil.isBlank(userPrompt)) {
+ throw new RuntimeException("Cannot run agent with empty user prompt");
+ }
+ // 更改状态
+ state = AgentState.RUNNING;
+ // 记录消息上下文
+ messageList.add(new UserMessage(userPrompt));
+ // 保存结果列表
+ List results = new ArrayList<>();
+ try {
+ for (int i = 0; i < maxSteps && state != AgentState.FINISHED; i++) {
+ int stepNumber = i + 1;
+ currentStep = stepNumber;
+ log.info("Executing step {}/{}", stepNumber, maxSteps);
+ // 单步执行
+ String stepResult = step();
+ String result = "Step " + stepNumber + ": " + stepResult;
+ results.add(result);
+ }
+ // 检查是否超出步骤限制
+ if (currentStep >= maxSteps) {
+ state = AgentState.FINISHED;
+ results.add("Terminated: Reached max steps (" + maxSteps + ")");
+ }
+ return String.join("\n", results);
+ } catch (Exception e) {
+ state = AgentState.ERROR;
+ log.error("Error executing agent", e);
+ return "执行错误" + e.getMessage();
+ } finally {
+ // 清理资源
+ this.cleanup();
+ }
+ }
+
+ /**
+ * 执行单个步骤
+ *
+ * @return 步骤执行结果
+ */
+ public abstract String step();
+
+ /**
+ * 清理资源
+ */
+ protected void cleanup() {
+ // 子类可以重写此方法来清理资源
+ }
+}
+
diff --git a/src/main/java/com/huangge1199/aiagent/agent/LongManus.java b/src/main/java/com/huangge1199/aiagent/agent/LongManus.java
new file mode 100644
index 0000000..74fff57
--- /dev/null
+++ b/src/main/java/com/huangge1199/aiagent/agent/LongManus.java
@@ -0,0 +1,41 @@
+package com.huangge1199.aiagent.agent;
+
+import com.huangge1199.aiagent.config.MyLoggerAdvisor;
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.tool.ToolCallback;
+import org.springframework.stereotype.Component;
+
+/**
+ * LongManus
+ * AI 超级智能体(拥有自主规划能力,可以直接使用)
+ *
+ * @author huangge1199
+ * @since 2025/6/4 17:08:52
+ */
+@Component
+public class LongManus extends ToolCallAgent {
+
+ public LongManus(ToolCallback[] allTools, ChatModel dashscopeChatModel) {
+ super(allTools);
+ this.setName("longManus");
+ String SYSTEM_PROMPT = """
+ You are LongManus, an all-capable AI assistant, aimed at solving any task presented by the user.
+ You have various tools at your disposal that you can call upon to efficiently complete complex requests.
+ """;
+ this.setSystemPrompt(SYSTEM_PROMPT);
+ String NEXT_STEP_PROMPT = """
+ Based on user needs, proactively select the most appropriate tool or combination of tools.
+ For complex tasks, you can break down the problem and use different tools step by step to solve it.
+ After using each tool, clearly explain the execution results and suggest the next steps.
+ If you want to stop the interaction at any point, use the `terminate` tool/function call.
+ """;
+ this.setNextStepPrompt(NEXT_STEP_PROMPT);
+ this.setMaxSteps(5);
+ // 初始化 AI 对话客户端
+ ChatClient chatClient = ChatClient.builder(dashscopeChatModel)
+ .defaultAdvisors(new MyLoggerAdvisor())
+ .build();
+ this.setChatClient(chatClient);
+ }
+}
diff --git a/src/main/java/com/huangge1199/aiagent/agent/ReActAgent.java b/src/main/java/com/huangge1199/aiagent/agent/ReActAgent.java
new file mode 100644
index 0000000..7bc014d
--- /dev/null
+++ b/src/main/java/com/huangge1199/aiagent/agent/ReActAgent.java
@@ -0,0 +1,57 @@
+package com.huangge1199.aiagent.agent;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * ReActAgent
+ * ReAct (Reasoning and Acting) 模式的代理抽象类
+ * 实现了思考-行动的循环模式
+ *
+ * @author huangge1199
+ * @since 2025/6/4 17:04:56
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Slf4j
+public abstract class ReActAgent extends BaseAgent {
+
+ /**
+ * 处理当前状态并决定下一步行动
+ *
+ * @return 是否需要执行行动,true表示需要执行,false表示不需要执行
+ */
+ public abstract boolean think();
+
+ /**
+ * 执行决定的行动
+ *
+ * @return 行动执行结果
+ */
+ public abstract String act();
+
+ /**
+ * 执行单个步骤:思考和行动
+ *
+ * @return 步骤执行结果
+ */
+ @Override
+ public String step() {
+ try {
+ // 先思考
+ boolean shouldAct = think();
+ if (!shouldAct) {
+ return "思考完成 - 无需行动";
+ }
+ // 再行动
+ return act();
+ } catch (Exception e) {
+ // 记录异常日志
+ e.printStackTrace();
+ return "步骤执行失败:" + e.getMessage();
+ }
+ }
+
+}
+
diff --git a/src/main/java/com/huangge1199/aiagent/agent/ToolCallAgent.java b/src/main/java/com/huangge1199/aiagent/agent/ToolCallAgent.java
new file mode 100644
index 0000000..a1bec00
--- /dev/null
+++ b/src/main/java/com/huangge1199/aiagent/agent/ToolCallAgent.java
@@ -0,0 +1,140 @@
+package com.huangge1199.aiagent.agent;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
+import com.huangge1199.aiagent.agent.model.AgentState;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
+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.ChatResponse;
+import org.springframework.ai.chat.prompt.ChatOptions;
+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 java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * ToolCallAgent
+ * 处理工具调用的基础代理类,具体实现了 think 和 act 方法,可以用作创建实例的父类
+ *
+ * @author huangge1199
+ * @since 2025/6/4 17:05:58
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Slf4j
+public class ToolCallAgent extends ReActAgent {
+
+ // 可用的工具
+ private final ToolCallback[] availableTools;
+
+ // 保存工具调用信息的响应结果(要调用那些工具)
+ private ChatResponse toolCallChatResponse;
+
+ // 工具调用管理者
+ private final ToolCallingManager toolCallingManager;
+
+ // 禁用 Spring AI 内置的工具调用机制,自己维护选项和消息上下文
+ private final ChatOptions chatOptions;
+
+ public ToolCallAgent(ToolCallback[] availableTools) {
+ super();
+ this.availableTools = availableTools;
+ this.toolCallingManager = ToolCallingManager.builder().build();
+ // 禁用 Spring AI 内置的工具调用机制,自己维护选项和消息上下文
+ this.chatOptions = DashScopeChatOptions.builder()
+ .withProxyToolCalls(true)
+ .build();
+ }
+
+ /**
+ * 处理当前状态并决定下一步行动
+ *
+ * @return 是否需要执行行动
+ */
+ @Override
+ public boolean think() {
+ // 1、校验提示词,拼接用户提示词
+ if (StrUtil.isNotBlank(getNextStepPrompt())) {
+ UserMessage userMessage = new UserMessage(getNextStepPrompt());
+ getMessageList().add(userMessage);
+ }
+ // 2、调用 AI 大模型,获取工具调用结果
+ List messageList = getMessageList();
+ Prompt prompt = new Prompt(messageList, this.chatOptions);
+ try {
+ ChatResponse chatResponse = getChatClient().prompt(prompt)
+ .system(getSystemPrompt())
+ .tools(availableTools)
+ .call()
+ .chatResponse();
+ // 记录响应,用于等下 Act
+ this.toolCallChatResponse = chatResponse;
+ // 3、解析工具调用结果,获取要调用的工具
+ // 助手消息
+ AssistantMessage assistantMessage = chatResponse.getResult().getOutput();
+ // 获取要调用的工具列表
+ List toolCallList = assistantMessage.getToolCalls();
+ // 输出提示信息
+ String result = assistantMessage.getText();
+ log.info("{}的思考:{}", getName(), result);
+ log.info("{}选择了 {} 个工具来使用", getName(), toolCallList.size());
+ String toolCallInfo = toolCallList.stream()
+ .map(toolCall -> String.format("工具名称:%s,参数:%s", toolCall.name(), toolCall.arguments()))
+ .collect(Collectors.joining("\n"));
+ log.info(toolCallInfo);
+ // 如果不需要调用工具,返回 false
+ if (toolCallList.isEmpty()) {
+ // 只有不调用工具时,才需要手动记录助手消息
+ getMessageList().add(assistantMessage);
+ return false;
+ } else {
+ // 需要调用工具时,无需记录助手消息,因为调用工具时会自动记录
+ return true;
+ }
+ } catch (Exception e) {
+ log.error("{}的思考过程遇到了问题:{}", getName(), e.getMessage());
+ getMessageList().add(new AssistantMessage("处理时遇到了错误:" + e.getMessage()));
+ return false;
+ }
+ }
+
+ /**
+ * 执行工具调用并处理结果
+ *
+ * @return 执行结果
+ */
+ @Override
+ public String act() {
+ if (!toolCallChatResponse.hasToolCalls()) {
+ return "没有工具需要调用";
+ }
+ // 调用工具
+ Prompt prompt = new Prompt(getMessageList(), this.chatOptions);
+ ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, toolCallChatResponse);
+ // 记录消息上下文,conversationHistory 已经包含了助手消息和工具调用返回的结果
+ setMessageList(toolExecutionResult.conversationHistory());
+ ToolResponseMessage toolResponseMessage = (ToolResponseMessage) CollUtil.getLast(toolExecutionResult.conversationHistory());
+ // 判断是否调用了终止工具
+ boolean terminateToolCalled = toolResponseMessage.getResponses().stream()
+ .anyMatch(response -> "doTerminate".equals(response.name()));
+ if (terminateToolCalled) {
+ // 任务结束,更改状态
+ setState(AgentState.FINISHED);
+ }
+ String results = toolResponseMessage.getResponses().stream()
+ .map(response -> "工具 " + response.name() + " 返回的结果:" + response.responseData())
+ .collect(Collectors.joining("\n"));
+ log.info(results);
+ return results;
+ }
+}
+
diff --git a/src/main/java/com/huangge1199/aiagent/agent/model/AgentState.java b/src/main/java/com/huangge1199/aiagent/agent/model/AgentState.java
new file mode 100644
index 0000000..dda3e8a
--- /dev/null
+++ b/src/main/java/com/huangge1199/aiagent/agent/model/AgentState.java
@@ -0,0 +1,33 @@
+package com.huangge1199.aiagent.agent.model;
+
+/**
+ * AgentState
+ *
+ * @author huangge1199
+ * @since 2025/6/4 17:00:54
+ */
+/**
+ * 代理执行状态的枚举类
+ */
+public enum AgentState {
+
+ /**
+ * 空闲状态
+ */
+ IDLE,
+
+ /**
+ * 运行中状态
+ */
+ RUNNING,
+
+ /**
+ * 已完成状态
+ */
+ FINISHED,
+
+ /**
+ * 错误状态
+ */
+ ERROR
+}
diff --git a/src/main/java/com/huangge1199/aiagent/config/ChatModelConfig.java b/src/main/java/com/huangge1199/aiagent/config/ChatModelConfig.java
index 1ac5d5a..351c2ea 100644
--- a/src/main/java/com/huangge1199/aiagent/config/ChatModelConfig.java
+++ b/src/main/java/com/huangge1199/aiagent/config/ChatModelConfig.java
@@ -1,5 +1,6 @@
package com.huangge1199.aiagent.config;
+import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.context.annotation.Bean;
@@ -16,16 +17,16 @@ import org.springframework.context.annotation.Primary;
public class ChatModelConfig {
// 选择DashScope作为默认模型
-// @Bean
-// @Primary
-// public ChatModel primaryChatModel(DashScopeChatModel dashscopeChatModel) {
-// return dashscopeChatModel;
-// }
+ @Bean
+ @Primary
+ public ChatModel primaryChatModel(DashScopeChatModel dashscopeChatModel) {
+ return dashscopeChatModel;
+ }
- // 或者选择Ollama:
- @Bean
- @Primary
- public ChatModel primaryChatModel(OllamaChatModel ollamaChatModel) {
- return ollamaChatModel;
- }
+// // 或者选择Ollama:
+// @Bean
+// @Primary
+// public ChatModel primaryChatModel(OllamaChatModel ollamaChatModel) {
+// return ollamaChatModel;
+// }
}
diff --git a/src/main/java/com/huangge1199/aiagent/controller/ResController.java b/src/main/java/com/huangge1199/aiagent/controller/ResController.java
index 122d2cf..ff220ea 100644
--- a/src/main/java/com/huangge1199/aiagent/controller/ResController.java
+++ b/src/main/java/com/huangge1199/aiagent/controller/ResController.java
@@ -1,5 +1,6 @@
package com.huangge1199.aiagent.controller;
+import com.huangge1199.aiagent.agent.LongManus;
import com.huangge1199.aiagent.common.R;
import com.huangge1199.aiagent.config.MyLoggerAdvisor;
import io.swagger.v3.oas.annotations.Operation;
@@ -42,6 +43,9 @@ public class ResController {
@Resource
private ToolCallbackProvider toolCallbackProvider;
+ @Resource
+ private LongManus longManus;
+
@PostMapping("/chatRes")
@Operation(summary = "返回 ChatResponse")
public R chatResRes(@RequestBody String question) {
@@ -108,4 +112,11 @@ public class ResController {
String content = chatResponse.getResult().getOutput().getText();
return R.ok(content);
}
+
+ @PostMapping("/manus")
+ @Operation(summary = "智能体测试")
+ public R manus(@RequestBody String question) {
+ String content = longManus.run(question);
+ return R.ok(content);
+ }
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 315066d..6ed6ce3 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -9,7 +9,7 @@ spring:
options:
model: qwen-plus
ollama:
- base-url: http://192.168.188.2:11435
+ base-url: http://192.168.188.2:11434
chat:
model: llama3.2:3b
vectorstore: